feat: slicing kamus page

This commit is contained in:
mphstar 2024-12-22 19:06:20 +07:00
parent e444361026
commit b28e4eae68
32 changed files with 131 additions and 7 deletions

48
package-lock.json generated
View File

@ -15,6 +15,8 @@
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"react-icons": "^5.3.0", "react-icons": "^5.3.0",
"react-router-dom": "^6.26.1", "react-router-dom": "^6.26.1",
"react-virtualized-auto-sizer": "^1.0.24",
"react-window": "^1.8.10",
"tailwind-merge": "^2.5.2", "tailwind-merge": "^2.5.2",
"zustand": "^4.5.5" "zustand": "^4.5.5"
}, },
@ -298,6 +300,22 @@
"@babel/core": "^7.0.0-0" "@babel/core": "^7.0.0-0"
} }
}, },
"node_modules/@babel/runtime": {
"version": "7.26.0",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz",
"integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==",
"dependencies": {
"regenerator-runtime": "^0.14.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/runtime/node_modules/regenerator-runtime": {
"version": "0.14.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
},
"node_modules/@babel/template": { "node_modules/@babel/template": {
"version": "7.25.0", "version": "7.25.0",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz",
@ -3225,6 +3243,11 @@
"yallist": "^3.0.2" "yallist": "^3.0.2"
} }
}, },
"node_modules/memoize-one": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz",
"integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q=="
},
"node_modules/merge2": { "node_modules/merge2": {
"version": "1.4.1", "version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
@ -3808,6 +3831,31 @@
"react-dom": ">=16.8" "react-dom": ">=16.8"
} }
}, },
"node_modules/react-virtualized-auto-sizer": {
"version": "1.0.24",
"resolved": "https://registry.npmjs.org/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.24.tgz",
"integrity": "sha512-3kCn7N9NEb3FlvJrSHWGQ4iVl+ydQObq2fHMn12i5wbtm74zHOPhz/i64OL3c1S1vi9i2GXtZqNqUJTQ+BnNfg==",
"peerDependencies": {
"react": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0",
"react-dom": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0"
}
},
"node_modules/react-window": {
"version": "1.8.10",
"resolved": "https://registry.npmjs.org/react-window/-/react-window-1.8.10.tgz",
"integrity": "sha512-Y0Cx+dnU6NLa5/EvoHukUD0BklJ8qITCtVEPY1C/nL8wwoZ0b5aEw8Ff1dOVHw7fCzMt55XfJDd8S8W8LCaUCg==",
"dependencies": {
"@babel/runtime": "^7.0.0",
"memoize-one": ">=3.1.1 <6"
},
"engines": {
"node": ">8.0.0"
},
"peerDependencies": {
"react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/read-cache": { "node_modules/read-cache": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",

View File

@ -17,6 +17,8 @@
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"react-icons": "^5.3.0", "react-icons": "^5.3.0",
"react-router-dom": "^6.26.1", "react-router-dom": "^6.26.1",
"react-virtualized-auto-sizer": "^1.0.24",
"react-window": "^1.8.10",
"tailwind-merge": "^2.5.2", "tailwind-merge": "^2.5.2",
"zustand": "^4.5.5" "zustand": "^4.5.5"
}, },

BIN
public/assets/kamus/a.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

BIN
public/assets/kamus/b.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

BIN
public/assets/kamus/c.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

BIN
public/assets/kamus/d.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

BIN
public/assets/kamus/e.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

BIN
public/assets/kamus/f.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

BIN
public/assets/kamus/g.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

BIN
public/assets/kamus/h.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

BIN
public/assets/kamus/i.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

BIN
public/assets/kamus/k.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

BIN
public/assets/kamus/l.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

BIN
public/assets/kamus/m.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

BIN
public/assets/kamus/n.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

BIN
public/assets/kamus/o.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

BIN
public/assets/kamus/p.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

BIN
public/assets/kamus/q.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

BIN
public/assets/kamus/r.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

BIN
public/assets/kamus/s.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

BIN
public/assets/kamus/t.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

BIN
public/assets/kamus/u.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

BIN
public/assets/kamus/v.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

BIN
public/assets/kamus/w.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

BIN
public/assets/kamus/x.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

BIN
public/assets/kamus/y.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

View File

@ -1,5 +1,6 @@
import { lazy, Suspense } from "react"; import { lazy, Suspense } from "react";
import { Route, Routes } from "react-router-dom"; import { Route, Routes } from "react-router-dom";
import Kamus from "./pages/Kamus";
const Home = lazy(() => import("@/pages/Home")); const Home = lazy(() => import("@/pages/Home"));
@ -15,7 +16,14 @@ const App = () => {
</Suspense> </Suspense>
} }
/> />
<Route path="/kamus" element={<div>Kamus</div>} /> <Route
path="/kamus"
element={
<Suspense fallback={<div>Loading...</div>}>
<Kamus />
</Suspense>
}
/>
</Route> </Route>
</Routes> </Routes>
); );

View File

@ -7,7 +7,7 @@ import NavLink from "../molecules/NavLink";
const HeaderPage = () => { const HeaderPage = () => {
const navStore = useNavbarStore(); const navStore = useNavbarStore();
return ( return (
<div className="bg-white drop-shadow w-full h-fit sticky top-0"> <div className="bg-white drop-shadow w-full h-fit sticky top-0 z-[300]">
<header className="flex flex-row items-center gap-2 justify-between px-4 py-3 container max-w-[1200px]"> <header className="flex flex-row items-center gap-2 justify-between px-4 py-3 container max-w-[1200px]">
<div className="flex gap-1 items-center"> <div className="flex gap-1 items-center">
<img <img
@ -22,7 +22,7 @@ const HeaderPage = () => {
</div> </div>
<ul <ul
className={cn( className={cn(
"flex md:flex-row flex-col items-center justify-center fixed md:static min-h-svh md:min-h-0 w-full md:w-fit bg-white/50 md:bg-transparent md:backdrop-blur-none backdrop-blur-md z-[200] top-0 left-0", "flex md:flex-row flex-col items-center justify-center fixed md:static min-h-svh md:min-h-0 w-full md:w-fit bg-white/50 md:bg-transparent md:backdrop-blur-none backdrop-blur-md z-[999] top-0 left-0",
navStore.isOpen ? "translate-y-0" : "-translate-y-full", navStore.isOpen ? "translate-y-0" : "-translate-y-full",
"duration-300 ease-in-out md:translate-y-0" "duration-300 ease-in-out md:translate-y-0"
)} )}
@ -33,9 +33,21 @@ const HeaderPage = () => {
> >
<IoClose /> <IoClose />
</div> </div>
<NavLink href="/" name="Home" isActive={true} /> <NavLink
<NavLink href="/kamus" name="Kamus" isActive={false} /> href="/"
<NavLink href="/" name="Kuis" isActive={false} /> name="Home"
isActive={navStore.navSelected === "home"}
/>
<NavLink
href="/kamus"
name="Kamus"
isActive={navStore.navSelected === "kamus"}
/>
<NavLink
href="/"
name="Kuis"
isActive={navStore.navSelected === "kuis"}
/>
</ul> </ul>
<button <button
onClick={() => navStore.toggle()} onClick={() => navStore.toggle()}

View File

@ -6,6 +6,7 @@ import { FilesetResolver, HandLandmarker } from "@mediapipe/tasks-vision";
import calcLandmarkList from "@/utils/CalculateLandmark"; import calcLandmarkList from "@/utils/CalculateLandmark";
import preProcessLandmark from "@/utils/PreProcessLandmark"; import preProcessLandmark from "@/utils/PreProcessLandmark";
import ConvertResult from "@/utils/ConvertResult"; import ConvertResult from "@/utils/ConvertResult";
import useNavbarStore from "@/stores/NavbarStore";
type PredictResult = { type PredictResult = {
abjad: String; abjad: String;
@ -130,11 +131,19 @@ const Home = () => {
requestAnimationFrame(detectHands); requestAnimationFrame(detectHands);
}; };
const store = useNavbarStore();
useEffect(() => { useEffect(() => {
store.setNavSelected("home");
loadModel(); loadModel();
startWebcam(); startWebcam();
setLoadCamera(true); setLoadCamera(true);
return () => {
// stop camera
};
}, []); }, []);
return ( return (

41
src/pages/Kamus.tsx Normal file
View File

@ -0,0 +1,41 @@
import LayoutPage from "@/components/templates/LayoutPage";
import useNavbarStore from "@/stores/NavbarStore";
import { abjads } from "@/utils/ConvertResult";
import { useEffect } from "react";
const Kamus = () => {
const store = useNavbarStore();
useEffect(() => {
store.setNavSelected("kamus");
}, []);
return (
<LayoutPage>
<div className="flex flex-col flex-1 py-4">
<h1 className="font-semibold">Kamus SIBI</h1>
<div className="grid grid-cols-2 md:grid-cols-4 gap-6 mt-6">
{abjads.map((abjad, index) => (
<CardKamus
key={index}
title={`Abjad ${abjad.toLocaleUpperCase()}`}
image={`/assets/kamus/${abjad.toLocaleLowerCase()}.jpg`}
/>
))}
</div>
</div>
</LayoutPage>
);
};
const CardKamus = ({ title, image }: { title: string; image: string }) => {
return (
<div className="flex flex-col rounded-md bg-base-100 shadow-lg overflow-hidden">
<img className="h-[250px] object-cover" src={image} alt="Kamus SIBI" />
<div className="p-3 flex items-center justify-center font-semibold">
<p>{title}</p>
</div>
</div>
);
};
export default Kamus;

View File

@ -3,11 +3,15 @@ import { create } from "zustand";
type NavbarType = { type NavbarType = {
isOpen: boolean; isOpen: boolean;
toggle: () => void; toggle: () => void;
navSelected: "home" | "kamus" | "kuis";
setNavSelected: (nav: "home" | "kamus" | "kuis") => void;
}; };
const useNavbarStore = create<NavbarType>((set) => ({ const useNavbarStore = create<NavbarType>((set) => ({
isOpen: false, isOpen: false,
toggle: () => set((state) => ({ isOpen: !state.isOpen })), toggle: () => set((state) => ({ isOpen: !state.isOpen })),
navSelected: "home",
setNavSelected: (nav) => set({ navSelected: nav }),
})); }));
export default useNavbarStore; export default useNavbarStore;

View File

@ -1,4 +1,4 @@
const abjads = [ export const abjads = [
"A", "A",
"B", "B",
"C", "C",