Skip to content

Commit

Permalink
Merge pull request #52 from sora-vp/websocket-button-module
Browse files Browse the repository at this point in the history
Implementasi websocket untuk menjembatani modul tombol
  • Loading branch information
reacto11mecha authored Jul 8, 2024
2 parents ec77500 + 4d19381 commit cc73118
Show file tree
Hide file tree
Showing 15 changed files with 555 additions and 229 deletions.
2 changes: 1 addition & 1 deletion apps/clients/attendance/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const router = createBrowserRouter([
{
path: "settings",
lazy: async () => {
const { SettingsPage } = await import("@/components/settings-page");
const { SettingsPage } = await import("@/routes/setting-page");

return { Component: SettingsPage };
},
Expand Down
108 changes: 0 additions & 108 deletions apps/clients/attendance/src/components/settings-page.tsx

This file was deleted.

106 changes: 106 additions & 0 deletions apps/clients/attendance/src/routes/setting-page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { successTimeoutAtom } from "@/utils/atom";
import { zodResolver } from "@hookform/resolvers/zod";
import { useAtom } from "jotai";
import { ArrowLeft } from "lucide-react";
import { useForm } from "react-hook-form";
import { NavLink, useNavigate } from "react-router-dom";
import { z } from "zod";

import { Button } from "@sora-vp/ui/button";
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@sora-vp/ui/form";
import { Input } from "@sora-vp/ui/input";
import { toast } from "@sora-vp/ui/toast";

const formSchema = z.object({
timeout: z.coerce.number().min(500, {
message: "Durasi minimal adalah 500 milidetik (setengah detik).",
}),
});

export function SettingsPage() {
const [timeoutDuration, setDuration] = useAtom(successTimeoutAtom);
const navigate = useNavigate();

const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
timeout: timeoutDuration,
},
});

return (
<div className="flex h-screen w-full items-center justify-center">
<div className="flex max-w-3xl flex-col gap-5">
<div className="space-y-2">
<h2 className="mt-10 scroll-m-20 text-center text-3xl font-semibold tracking-tight transition-colors first:mt-0">
Halaman Pengaturan
</h2>
<p className="text-justify leading-7">
Pada halaman ini, anda dapat mengatur sebarapa lama durasi tampilnya
warna hijau ketika sukses melakukan presensi kehadiran. Nilai durasi
bawaan yaitu 5000 milidetik (5 detik).
</p>
</div>

<Form {...form}>
<form
onSubmit={form.handleSubmit((data) => {
setDuration(data.timeout);
toast.success("Berhasil mengubah durasi waktu tampil", {
description: `Berhasil di ubah menjadi ${data.timeout / 1000} detik`,
});

setTimeout(() => navigate("/"), 500);
})}
className="space-y-8"
>
<FormField
control={form.control}
name="timeout"
render={({ field }) => (
<FormItem>
<FormLabel>Durasi Waktu Tunggu</FormLabel>
<FormControl>
<div className="flex gap-5">
<Input type="number" placeholder="5000" {...field} />
<small className="text-center">
{Number.isNaN(field.value) ? "N/A" : field.value / 1000}{" "}
detik
</small>
</div>
</FormControl>
<FormDescription>
Tetapkan berapa lama waktu berhasil akan transisi kembali ke
halaman pindai QR.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<div className="flex gap-5">
<NavLink to="/" className="w-1/2">
{() => (
<Button className="w-full" variant="secondary">
<ArrowLeft className="mr-2 h-4 w-4" />
Kembali
</Button>
)}
</NavLink>
<Button type="submit" className="w-1/2">
Simpan
</Button>
</div>
</form>
</Form>
</div>
</div>
);
}
1 change: 1 addition & 0 deletions apps/clients/chooser/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^6.24.0",
"react-use-websocket": "^4.8.1",
"superjson": "2.2.1",
"zod": "^3.23.8"
},
Expand Down
15 changes: 9 additions & 6 deletions apps/clients/chooser/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import superjson from "superjson";

import { ClientNotFound } from "@sora-vp/ui/client-not-found";

import { KeyboardWebsocketProvider } from "./context/keyboard-websocket";
import { env } from "./env";

const router = createBrowserRouter([
Expand All @@ -29,7 +30,7 @@ const router = createBrowserRouter([
{
path: "settings",
lazy: async () => {
const { SettingsPage } = await import("@/components/settings-page");
const { SettingsPage } = await import("@/routes/setting-page");

return { Component: SettingsPage };
},
Expand Down Expand Up @@ -77,11 +78,13 @@ export default function App() {
return (
<api.Provider client={trpcClient} queryClient={queryClient}>
<QueryClientProvider client={queryClient}>
<ParticipantProvider>
<ServerSettingProvider>
<RouterProvider router={router} />
</ServerSettingProvider>
</ParticipantProvider>
<KeyboardWebsocketProvider>
<ParticipantProvider>
<ServerSettingProvider>
<RouterProvider router={router} />
</ServerSettingProvider>
</ParticipantProvider>
</KeyboardWebsocketProvider>
</QueryClientProvider>
</api.Provider>
);
Expand Down
21 changes: 20 additions & 1 deletion apps/clients/chooser/src/components/scanner/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useCallback, useState } from "react";
import { useCallback, useEffect, useState } from "react";
import { useKeyboardWebsocket } from "@/context/keyboard-websocket";
import { useParticipant } from "@/context/participant-context";
import { api } from "@/utils/api";
import { Navigate } from "react-router-dom";
Expand All @@ -9,6 +10,7 @@ import { MainScanner } from "./main-scanner";

export function ScannerComponent() {
const { qrId, setQRCode } = useParticipant();
const { wsEnabled, lastMessage } = useKeyboardWebsocket();

const [isQrInvalid, setInvalidQr] = useState(false);

Expand All @@ -19,6 +21,23 @@ export function ScannerComponent() {
},
});

useEffect(() => {
if (wsEnabled && lastMessage) {
// Precheck before consuming command
if (lastMessage.data.startsWith("SORA-KEYBIND-")) {
const actualCommand = lastMessage.data.replace("SORA-KEYBIND-", "");

switch (actualCommand) {
case "RELOAD": {
if (isQrInvalid || participantAttended.isError) location.reload();

break;
}
}
}
}
}, [isQrInvalid, participantAttended.isError, wsEnabled, lastMessage]);

const setIsQrValid = useCallback(
(invalid: boolean) => setInvalidQr(invalid),
[],
Expand Down
Loading

0 comments on commit cc73118

Please sign in to comment.