diff --git a/index.html b/index.html
index e4b78ea..12834a1 100644
--- a/index.html
+++ b/index.html
@@ -2,9 +2,9 @@
-
+
- Vite + React + TS
+ Kedai Susu Tuli - SIBI
diff --git a/package-lock.json b/package-lock.json
index c0ab93a..a7783cd 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,11 +8,16 @@
"name": "ksuli-sibi",
"version": "0.0.0",
"dependencies": {
+ "clsx": "^2.1.1",
"react": "^18.3.1",
- "react-dom": "^18.3.1"
+ "react-dom": "^18.3.1",
+ "react-icons": "^5.3.0",
+ "tailwind-merge": "^2.5.2",
+ "zustand": "^4.5.5"
},
"devDependencies": {
"@eslint/js": "^9.9.0",
+ "@types/node": "^22.4.2",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@vitejs/plugin-react": "^4.3.1",
@@ -1234,17 +1239,26 @@
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
"dev": true
},
+ "node_modules/@types/node": {
+ "version": "22.4.2",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.4.2.tgz",
+ "integrity": "sha512-nAvM3Ey230/XzxtyDcJ+VjvlzpzoHwLsF7JaDRfoI0ytO0mVheerNmM45CtA0yOILXwXXxOrcUWH3wltX+7PSw==",
+ "dev": true,
+ "dependencies": {
+ "undici-types": "~6.19.2"
+ }
+ },
"node_modules/@types/prop-types": {
"version": "15.7.12",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz",
"integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==",
- "dev": true
+ "devOptional": true
},
"node_modules/@types/react": {
"version": "18.3.4",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.4.tgz",
"integrity": "sha512-J7W30FTdfCxDDjmfRM+/JqLHBIyl7xUIp9kwK637FGmY7+mkSFSe6L4jpZzhj5QMfLssSDP4/i75AKkrdC7/Jw==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"@types/prop-types": "*",
"csstype": "^3.0.2"
@@ -1803,6 +1817,14 @@
"node": ">= 6"
}
},
+ "node_modules/clsx": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
+ "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
@@ -1879,7 +1901,7 @@
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
- "dev": true
+ "devOptional": true
},
"node_modules/culori": {
"version": "3.3.0",
@@ -3349,6 +3371,14 @@
"react": "^18.3.1"
}
},
+ "node_modules/react-icons": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.3.0.tgz",
+ "integrity": "sha512-DnUk8aFbTyQPSkCfF8dbX6kQjXA9DktMeJqfjrg6cK9vwQVMxmcA3BfP4QoiztVmEHtwlTgLFsPuH2NskKT6eg==",
+ "peerDependencies": {
+ "react": "*"
+ }
+ },
"node_modules/react-refresh": {
"version": "0.14.2",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz",
@@ -3689,6 +3719,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/tailwind-merge": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.5.2.tgz",
+ "integrity": "sha512-kjEBm+pvD+6eAwzJL2Bi+02/9LFLal1Gs61+QB7HvTfQQ0aXwC5LGT8PEt1gS0CWKktKe6ysPTAy3cBC5MeiIg==",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/dcastil"
+ }
+ },
"node_modules/tailwindcss": {
"version": "3.4.10",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.10.tgz",
@@ -3840,6 +3879,12 @@
}
}
},
+ "node_modules/undici-types": {
+ "version": "6.19.8",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
+ "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
+ "dev": true
+ },
"node_modules/update-browserslist-db": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz",
@@ -3879,6 +3924,14 @@
"punycode": "^2.1.0"
}
},
+ "node_modules/use-sync-external-store": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz",
+ "integrity": "sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@@ -4124,6 +4177,33 @@
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
+ },
+ "node_modules/zustand": {
+ "version": "4.5.5",
+ "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.5.tgz",
+ "integrity": "sha512-+0PALYNJNgK6hldkgDq2vLrw5f6g/jCInz52n9RTpropGgeAf/ioFUCdtsjCqu4gNhW9D01rUQBROoRjdzyn2Q==",
+ "dependencies": {
+ "use-sync-external-store": "1.2.2"
+ },
+ "engines": {
+ "node": ">=12.7.0"
+ },
+ "peerDependencies": {
+ "@types/react": ">=16.8",
+ "immer": ">=9.0.6",
+ "react": ">=16.8"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "immer": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ }
+ }
}
}
}
diff --git a/package.json b/package.json
index f309ca2..e5c63d4 100644
--- a/package.json
+++ b/package.json
@@ -10,11 +10,16 @@
"preview": "vite preview"
},
"dependencies": {
+ "clsx": "^2.1.1",
"react": "^18.3.1",
- "react-dom": "^18.3.1"
+ "react-dom": "^18.3.1",
+ "react-icons": "^5.3.0",
+ "tailwind-merge": "^2.5.2",
+ "zustand": "^4.5.5"
},
"devDependencies": {
"@eslint/js": "^9.9.0",
+ "@types/node": "^22.4.2",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@vitejs/plugin-react": "^4.3.1",
diff --git a/public/assets/images/logo.png b/public/assets/images/logo.png
new file mode 100644
index 0000000..3b860c7
Binary files /dev/null and b/public/assets/images/logo.png differ
diff --git a/src/App.tsx b/src/App.tsx
index 3a31e76..ea0d98b 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,6 +1,11 @@
+import LayoutPage from "./components/templates/LayoutPage";
const App = () => {
- return Hello World
;
+ return (
+
+ Hello World
+
+ );
};
export default App;
diff --git a/src/components/molecules/NavLink.tsx b/src/components/molecules/NavLink.tsx
new file mode 100644
index 0000000..d0b11d9
--- /dev/null
+++ b/src/components/molecules/NavLink.tsx
@@ -0,0 +1,24 @@
+import { cn } from "@/lib/utils";
+
+type NavLinkProps = {
+ href: string;
+ name: string;
+ isActive: boolean;
+};
+
+const NavLink = ({ href, name, isActive }: NavLinkProps) => {
+ return (
+
+
+ {name}
+
+
+ );
+};
+
+export default NavLink;
diff --git a/src/components/organisms/FooterPage.tsx b/src/components/organisms/FooterPage.tsx
new file mode 100644
index 0000000..6668e8f
--- /dev/null
+++ b/src/components/organisms/FooterPage.tsx
@@ -0,0 +1,14 @@
+const FooterPage = () => {
+ return (
+
+ );
+};
+
+export default FooterPage;
diff --git a/src/components/organisms/HeaderPage.tsx b/src/components/organisms/HeaderPage.tsx
new file mode 100644
index 0000000..5096784
--- /dev/null
+++ b/src/components/organisms/HeaderPage.tsx
@@ -0,0 +1,51 @@
+import useNavbarStore from "@/stores/NavbarStore";
+import { cn } from "@lib/utils";
+import { IoClose } from "react-icons/io5";
+import { RxHamburgerMenu } from "react-icons/rx";
+import NavLink from "../molecules/NavLink";
+
+const HeaderPage = () => {
+ const navStore = useNavbarStore();
+ return (
+
+ );
+};
+
+export default HeaderPage;
diff --git a/src/components/templates/LayoutPage.tsx b/src/components/templates/LayoutPage.tsx
new file mode 100644
index 0000000..2e101d2
--- /dev/null
+++ b/src/components/templates/LayoutPage.tsx
@@ -0,0 +1,17 @@
+import React from "react";
+import HeaderPage from "../organisms/HeaderPage";
+import FooterPage from "../organisms/FooterPage";
+
+const LayoutPage = ({ children }: { children: React.ReactNode }) => {
+ return (
+
+
+
+ {children}
+
+
+
+ );
+};
+
+export default LayoutPage;
diff --git a/src/lib/utils.ts b/src/lib/utils.ts
new file mode 100644
index 0000000..d084cca
--- /dev/null
+++ b/src/lib/utils.ts
@@ -0,0 +1,6 @@
+import { type ClassValue, clsx } from "clsx"
+import { twMerge } from "tailwind-merge"
+
+export function cn(...inputs: ClassValue[]) {
+ return twMerge(clsx(inputs))
+}
diff --git a/src/stores/NavbarStore.ts b/src/stores/NavbarStore.ts
new file mode 100644
index 0000000..5181556
--- /dev/null
+++ b/src/stores/NavbarStore.ts
@@ -0,0 +1,13 @@
+import { create } from "zustand";
+
+type NavbarType = {
+ isOpen: boolean;
+ toggle: () => void;
+};
+
+const useNavbarStore = create((set) => ({
+ isOpen: false,
+ toggle: () => set((state) => ({ isOpen: !state.isOpen })),
+}));
+
+export default useNavbarStore;
diff --git a/tailwind.config.js b/tailwind.config.js
index 691efea..c8b99a4 100644
--- a/tailwind.config.js
+++ b/tailwind.config.js
@@ -2,7 +2,32 @@
export default {
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
theme: {
- extend: {},
+ extend: {
+ container: {
+ center: true,
+ padding: "1rem",
+ },
+ colors: {
+ ground: "#FAF7EE",
+ },
+ },
+ },
+ daisyui: {
+ themes: [
+ {
+ mytheme: {
+ primary: "#fbbf24",
+ secondary: "#00b44a",
+ accent: "#0099db",
+ neutral: "#080f0e",
+ "base-100": "#f3f4f6",
+ info: "#00abf0",
+ success: "#00e5ab",
+ warning: "#fb923c",
+ error: "#e11d48",
+ },
+ },
+ ],
},
plugins: [require("daisyui")],
};
diff --git a/tsconfig.app.json b/tsconfig.app.json
index f0a2350..9d20443 100644
--- a/tsconfig.app.json
+++ b/tsconfig.app.json
@@ -18,7 +18,15 @@
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
- "noFallthroughCasesInSwitch": true
+ "noFallthroughCasesInSwitch": true,
+
+ /* Aliases */
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["src/*"],
+ "@components/*": ["src/components/*"],
+ "@lib/*": ["src/lib/*"]
+ }
},
"include": ["src"]
}
diff --git a/vite.config.ts b/vite.config.ts
index 5a33944..c271af4 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -1,7 +1,15 @@
-import { defineConfig } from 'vite'
-import react from '@vitejs/plugin-react'
+import { defineConfig } from "vite";
+import react from "@vitejs/plugin-react";
+import path from "path";
// https://vitejs.dev/config/
export default defineConfig({
+ resolve: {
+ alias: {
+ "@": path.resolve(__dirname, "./src"),
+ "@components": path.resolve(__dirname, "./src/components"),
+ "@lib": path.resolve(__dirname, "./src/lib"),
+ },
+ },
plugins: [react()],
-})
+});