From 778b05fd50f938293cdbdf3816be79f3327cbe7a Mon Sep 17 00:00:00 2001 From: HasanMu Date: Fri, 14 Feb 2025 17:00:08 +0700 Subject: [PATCH] feat: impl cache & crud mock in devices --- .env.example | 4 +- package-lock.json | 16 ++++++ package.json | 2 + src/components/FlashMessage.tsx | 43 ++++++++++++++++ src/components/ReactTable.tsx | 10 +++- src/pages/Devices.tsx | 74 +++++++++++++++------------- src/pages/Devices/Create.tsx | 30 +++++++---- src/pages/Devices/Form.tsx | 22 +++++++-- src/repositories/deviceRepository.ts | 35 +++++++++++-- src/stores/authStore.ts | 4 ++ src/stores/deviceStore.ts | 57 +++++++++++++++------ src/types/auth.types.ts | 5 +- src/types/device.types.ts | 8 +-- src/types/store.types.ts | 8 +++ tsconfig.app.json | 5 +- vite-env.d.ts | 4 ++ 16 files changed, 252 insertions(+), 75 deletions(-) create mode 100644 src/components/FlashMessage.tsx create mode 100644 src/types/store.types.ts diff --git a/.env.example b/.env.example index 5643237..8f65e41 100644 --- a/.env.example +++ b/.env.example @@ -1,2 +1,4 @@ VITE_API_URL="http://localhost:8000" -VITE_APP_KEY="4ae931db63f0d18f5082781be5e2c622" \ No newline at end of file +VITE_APP_KEY="4ae931db63f0d18f5082781be5e2c622" +VITE_APP_CACHE_TIME_VALUE=30 +VITE_APP_CACHE_TIME_UNIT="seconds" \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 1431b5c..1bafd24 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,9 @@ "@tanstack/react-table": "^8.21.2", "axios": "^1.7.9", "crypto-js": "^4.2.0", + "dayjs": "^1.11.13", "iconsax-react": "^0.0.8", + "immer": "^10.1.1", "lodash": "^4.17.21", "react": "^19.0.0", "react-dom": "^19.0.0", @@ -2414,6 +2416,11 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, + "node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==" + }, "node_modules/debug": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", @@ -2959,6 +2966,15 @@ "node": ">= 4" } }, + "node_modules/immer": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz", + "integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, "node_modules/import-fresh": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", diff --git a/package.json b/package.json index 3fb4cf2..987c281 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,9 @@ "@tanstack/react-table": "^8.21.2", "axios": "^1.7.9", "crypto-js": "^4.2.0", + "dayjs": "^1.11.13", "iconsax-react": "^0.0.8", + "immer": "^10.1.1", "lodash": "^4.17.21", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/src/components/FlashMessage.tsx b/src/components/FlashMessage.tsx new file mode 100644 index 0000000..5d5f612 --- /dev/null +++ b/src/components/FlashMessage.tsx @@ -0,0 +1,43 @@ +import { useEffect, useState } from "react"; +import Box from "@mui/material/Box"; +import Alert from "@mui/material/Alert"; +import { DeviceState } from "../types/device.types"; +import { AuthState } from "../types/auth.types"; + +interface FlashMessageProps { + store: () => DeviceState | AuthState; +} + +export default function FlashMessage(props: FlashMessageProps) { + const { error, clearError } = props.store(); + const [errorMessage, setErrorMessage] = useState(""); + + const onCloseError = () => { + setErrorMessage(null); + clearError() + }; + + useEffect(() => { + if (error) { + setErrorMessage(error as string); + } + setTimeout(() => { + clearError() + }, 500); + }, [error]); + + if (!errorMessage) return null; + + return ( + + + {errorMessage} + + + ); +} diff --git a/src/components/ReactTable.tsx b/src/components/ReactTable.tsx index 114cb16..fd4f1b5 100644 --- a/src/components/ReactTable.tsx +++ b/src/components/ReactTable.tsx @@ -58,7 +58,7 @@ function ComponentReactTable({ return ( - + @@ -106,7 +106,13 @@ function ComponentReactTable({ ))} - {table.getRowModel().rows.map((row) => ( + {table.getRowModel().rows.length === 0 ? ( + + + No data. + + + ) : table.getRowModel().rows.map((row) => ( {row.getVisibleCells().map((cell) => ( diff --git a/src/pages/Devices.tsx b/src/pages/Devices.tsx index f317764..e51689e 100644 --- a/src/pages/Devices.tsx +++ b/src/pages/Devices.tsx @@ -14,33 +14,18 @@ import { ColumnDef } from "@tanstack/react-table"; import IconButton from "../components/IconButton"; import { Edit } from "iconsax-react"; import useTitle from "../hooks/useTitle"; - -interface UserData { - device_code: string; - device_type: string; - address: string; - port_amount: string; - status: string; -} +import { useDeviceStore } from "../stores/deviceStore"; +import { Device } from "../types/device.types"; +import { useEffect } from "react"; +import FlashMessage from "../components/FlashMessage"; export default function Devices() { useTitle("Devices"); - - const data: UserData[] = [ - { device_code: 'DV-CODE-1', device_type: "OTB", address: "Jl. Contoh 1", port_amount: '16', status: "Active" }, - { device_code: 'DV-CODE-2', device_type: "OTB", address: "Jl. Contoh 2", port_amount: '16', status: "Active" }, - { device_code: 'DV-CODE-3', device_type: "OTB", address: "Jl. Contoh 3", port_amount: '16', status: "Active" }, - { device_code: 'DV-CODE-4', device_type: "OTB", address: "Jl. Contoh 4", port_amount: '16', status: "Active" }, - { device_code: 'DV-CODE-5', device_type: "OTB", address: "Jl. Contoh 5", port_amount: '16', status: "Active" }, - { device_code: 'DV-CODE-6', device_type: "OTB", address: "Jl. Contoh 6", port_amount: '16', status: "Active" }, - { device_code: 'DV-CODE-7', device_type: "OTB", address: "Jl. Contoh 7", port_amount: '16', status: "Active" }, - { device_code: 'DV-CODE-8', device_type: "OTB", address: "Jl. Contoh 8", port_amount: '16', status: "Active" }, - { device_code: 'DV-CODE-9', device_type: "OTB", address: "Jl. Contoh 9", port_amount: '16', status: "Active" }, - { device_code: 'DV-CODE-10', device_type: "OTB", address: "Jl. Contoh 10", port_amount: '16', status: "Active" }, - ]; + + const { data, cacheExpired, getAll } = useDeviceStore(); // Define columns - const columns: ColumnDef[] = [ + const columns: ColumnDef[] = [ { header: "Device Code", accessorKey: "device_code", @@ -61,25 +46,41 @@ export default function Devices() { header: "Status", accessorKey: "status", cell: ({ row }) => ( - + {row.original.status} - ) + ), }, { - header: '', + header: "", accessorKey: "action", enableSorting: false, - cell: () => ( + cell: ({ row }) => ( <> - + - ) - } + ), + }, ]; + useEffect(() => { + if (cacheExpired()) { + (async () => { + await getAll() + })() + } + }, []); + return ( @@ -87,7 +88,11 @@ export default function Devices() { Devices - @@ -101,16 +106,15 @@ export default function Devices() { + + List Devices - - + + diff --git a/src/pages/Devices/Create.tsx b/src/pages/Devices/Create.tsx index 6d06f3d..3b814cd 100644 --- a/src/pages/Devices/Create.tsx +++ b/src/pages/Devices/Create.tsx @@ -1,4 +1,3 @@ -import Alert from "@mui/material/Alert"; import Box from "@mui/material/Box"; import Breadcrumbs from "@mui/material/Breadcrumbs"; import Button from "@mui/material/Button"; @@ -14,14 +13,15 @@ import IconButton from "../../components/IconButton"; import { Back } from "iconsax-react"; import { useParams } from "react-router"; import Form from "./Form"; -import { FormEvent } from "react"; +import { FormEvent, useEffect } from "react"; import { useDeviceStore } from "../../stores/deviceStore"; import { DeviceRequest } from "../../types/device.types"; +import FlashMessage from "../../components/FlashMessage"; export default function DeviceCreate() { useTitle("Devices"); const { pid } = useParams(); - const { update, create, error } = useDeviceStore(); + const { update, create, isLoading, find, clearDevice, device } = useDeviceStore(); const IS_EDIT_PAGE = pid!!; @@ -36,8 +36,21 @@ export default function DeviceCreate() { await update(pid!!, data); } else { await create(data); - } + } } + + useEffect(() => { + if (pid!!) { + (async () => { + await find(pid!!); + })() + } else { + (async () => { + await clearDevice() + })() + } + }, []) + return ( @@ -62,11 +75,7 @@ export default function DeviceCreate() { - {error && ( - - {error.toString()} asass - - )} + - Fill the form + Fill the form {device?.device_code}