feat: impl cache & crud mock in devices
This commit is contained in:
parent
5e471b7e20
commit
778b05fd50
|
|
@ -1,2 +1,4 @@
|
|||
VITE_API_URL="http://localhost:8000"
|
||||
VITE_APP_KEY="4ae931db63f0d18f5082781be5e2c622"
|
||||
VITE_APP_KEY="4ae931db63f0d18f5082781be5e2c622"
|
||||
VITE_APP_CACHE_TIME_VALUE=30
|
||||
VITE_APP_CACHE_TIME_UNIT="seconds"
|
||||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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<string | null>("");
|
||||
|
||||
const onCloseError = () => {
|
||||
setErrorMessage(null);
|
||||
clearError()
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (error) {
|
||||
setErrorMessage(error as string);
|
||||
}
|
||||
setTimeout(() => {
|
||||
clearError()
|
||||
}, 500);
|
||||
}, [error]);
|
||||
|
||||
if (!errorMessage) return null;
|
||||
|
||||
return (
|
||||
<Box display={"flex"} mb={2}>
|
||||
<Alert
|
||||
severity="error"
|
||||
variant="border"
|
||||
onClose={onCloseError}
|
||||
sx={{ width: "100%" }}
|
||||
>
|
||||
{errorMessage}
|
||||
</Alert>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
|
@ -58,7 +58,7 @@ function ComponentReactTable<T extends object>({
|
|||
|
||||
return (
|
||||
<Box>
|
||||
<Box justifyContent={"space-between"} display="flex">
|
||||
<Box justifyContent={"space-between"} display="flex" sx={{ px: 2}}>
|
||||
<Box>
|
||||
|
||||
</Box>
|
||||
|
|
@ -106,7 +106,13 @@ function ComponentReactTable<T extends object>({
|
|||
))}
|
||||
</TableHead>
|
||||
<TableBody className={striped ? "striped" : undefined}>
|
||||
{table.getRowModel().rows.map((row) => (
|
||||
{table.getRowModel().rows.length === 0 ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={columns.length} align="center">
|
||||
No data.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : table.getRowModel().rows.map((row) => (
|
||||
<TableRow key={row.id}>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<TableCell key={cell.id}>
|
||||
|
|
|
|||
|
|
@ -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<UserData>[] = [
|
||||
const columns: ColumnDef<Device>[] = [
|
||||
{
|
||||
header: "Device Code",
|
||||
accessorKey: "device_code",
|
||||
|
|
@ -61,25 +46,41 @@ export default function Devices() {
|
|||
header: "Status",
|
||||
accessorKey: "status",
|
||||
cell: ({ row }) => (
|
||||
<Typography color={row.original.status === "Active" ? "success.main" : "error.main"}>
|
||||
<Typography
|
||||
color={
|
||||
row.original.status.toLowerCase() === "active"
|
||||
? "success.main"
|
||||
: row.original.status.toLowerCase() === "inactive"
|
||||
? "error.main"
|
||||
: "warning.main"
|
||||
}
|
||||
>
|
||||
{row.original.status}
|
||||
</Typography>
|
||||
)
|
||||
),
|
||||
},
|
||||
{
|
||||
header: '',
|
||||
header: "",
|
||||
accessorKey: "action",
|
||||
enableSorting: false,
|
||||
cell: () => (
|
||||
cell: ({ row }) => (
|
||||
<>
|
||||
<IconButton variant="text" color="secondary" >
|
||||
<IconButton variant="text" color="secondary" href={`/devices/${row.original.id}`}>
|
||||
<Edit variant="Bulk" color="currentColor" />
|
||||
</IconButton>
|
||||
</>
|
||||
)
|
||||
}
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
useEffect(() => {
|
||||
if (cacheExpired()) {
|
||||
(async () => {
|
||||
await getAll()
|
||||
})()
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Box component="main">
|
||||
<Grid container spacing={2} alignItems={"center"} mb={4}>
|
||||
|
|
@ -87,7 +88,11 @@ export default function Devices() {
|
|||
<Typography variant="h3">Devices</Typography>
|
||||
</Grid>
|
||||
<Grid size="auto">
|
||||
<Button href="/devices/create" variant="contained" startIcon={<AddIcon color="inherit" />}>
|
||||
<Button
|
||||
href="/devices/create"
|
||||
variant="contained"
|
||||
startIcon={<AddIcon color="inherit" />}
|
||||
>
|
||||
Add New
|
||||
</Button>
|
||||
</Grid>
|
||||
|
|
@ -101,16 +106,15 @@ export default function Devices() {
|
|||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<FlashMessage store={useDeviceStore} />
|
||||
|
||||
<Card>
|
||||
<Box sx={{ p: 2 }}>
|
||||
<Typography variant="h5">List Devices</Typography>
|
||||
</Box>
|
||||
<Divider />
|
||||
<CardContent sx={{ flex: "1 0 auto", pt: 2 }}>
|
||||
<ReactTable
|
||||
data={data}
|
||||
columns={columns}
|
||||
/>
|
||||
<CardContent sx={{ flex: "1 0 auto", pt: 2, px: 0 }}>
|
||||
<ReactTable data={data || []} columns={columns} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Box>
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<Box component="main">
|
||||
<Grid container spacing={2} alignItems={"center"} mb={4}>
|
||||
|
|
@ -62,11 +75,7 @@ export default function DeviceCreate() {
|
|||
</Grid>
|
||||
</Grid>
|
||||
|
||||
{error && (
|
||||
<Box display={"flex"} mb={2}>
|
||||
<Alert severity="error" variant="border" sx={{ width: "100%" }}>{error.toString()} asass</Alert>
|
||||
</Box>
|
||||
)}
|
||||
<FlashMessage store={useDeviceStore} />
|
||||
|
||||
<Card component="form" onSubmit={onSubmit}>
|
||||
<Box
|
||||
|
|
@ -75,8 +84,9 @@ export default function DeviceCreate() {
|
|||
justifyContent={"space-between"}
|
||||
alignItems={"center"}
|
||||
>
|
||||
<Typography variant="h5">Fill the form</Typography>
|
||||
<Typography variant="h5">Fill the form {device?.device_code}</Typography>
|
||||
<Button
|
||||
disabled={isLoading}
|
||||
type="submit"
|
||||
variant="contained"
|
||||
size="small"
|
||||
|
|
|
|||
|
|
@ -6,21 +6,34 @@ import InputLabel from "@mui/material/InputLabel";
|
|||
import MenuItem from "@mui/material/MenuItem";
|
||||
import Select from "@mui/material/Select";
|
||||
import TextField from "@mui/material/TextField";
|
||||
import { useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { DeviceStatus } from '../../types/device.types';
|
||||
import { useDeviceStore } from '../../stores/deviceStore';
|
||||
|
||||
export default function Form() {
|
||||
const { device, clearDevice } = useDeviceStore();
|
||||
const [select, setSelect] = useState<DeviceStatus>('active');
|
||||
|
||||
const STATUS = ["active", "inactive", "maintenance"];
|
||||
|
||||
useEffect(() => {
|
||||
setSelect(device?.status || 'active');
|
||||
|
||||
const clear = async () => {
|
||||
await clearDevice();
|
||||
};
|
||||
|
||||
return () => {
|
||||
clear();
|
||||
};
|
||||
}, [device]);
|
||||
|
||||
return (
|
||||
<Grid container spacing={2}>
|
||||
{/* Device Code */}
|
||||
<Grid size={{ md: 4, xs: 12 }}>
|
||||
<TextField fullWidth name='device_code' label="Device Code" variant="outlined" />
|
||||
<TextField fullWidth name='device_code' label="Device Code" variant="outlined" value={device?.device_code} />
|
||||
</Grid>
|
||||
|
||||
{/* Total Port */}
|
||||
<Grid size={{ md: 4, xs: 12 }}>
|
||||
<TextField
|
||||
|
|
@ -29,6 +42,7 @@ export default function Form() {
|
|||
label="Total Port"
|
||||
variant="outlined"
|
||||
type="number"
|
||||
value={device?.port_amount}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
|
|
@ -75,6 +89,7 @@ export default function Form() {
|
|||
label="Longitude"
|
||||
variant="outlined"
|
||||
type="number"
|
||||
value={device?.longitude}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
|
|
@ -85,6 +100,7 @@ export default function Form() {
|
|||
label="Latitude"
|
||||
variant="outlined"
|
||||
type="number"
|
||||
value={device?.latitude}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
|
|
|||
|
|
@ -6,15 +6,42 @@ const api = createAPI();
|
|||
|
||||
export const deviceRepository: DeviceRepositoryProps = {
|
||||
async getAll() {
|
||||
const response = await api.get<ResponseApi<Device[]>>("/api/v1/devices");
|
||||
// const response = await api.get<ResponseApi<Device[]>>("/api/v1/devices");
|
||||
|
||||
return response.data.data as Device[];
|
||||
// return response.data.data as Device[];
|
||||
|
||||
return Promise.resolve( [
|
||||
{
|
||||
id: "1",
|
||||
device_code: "DV-EX-001",
|
||||
device_type: "OTB",
|
||||
longitude: "1",
|
||||
latitude: "1",
|
||||
address: "Jl. Jend Sudirman No. XX",
|
||||
port_amount: 10,
|
||||
status: "active",
|
||||
created_at: "2024-01-01",
|
||||
updated_at: "2024-01-01",
|
||||
}
|
||||
])
|
||||
},
|
||||
|
||||
async getDeviceById(id: string) {
|
||||
const response = await api.get<ResponseApi<Device>>(`/api/v1/devices/${id}`);
|
||||
// const response = await api.get<ResponseApi<Device>>(`/api/v1/devices/${id}`);
|
||||
|
||||
return response.data.data as Device;
|
||||
// return response.data.data as Device;
|
||||
return Promise.resolve({
|
||||
id: "1",
|
||||
device_code: "DV-EX-001",
|
||||
device_type: "OTB",
|
||||
longitude: "1",
|
||||
latitude: "1",
|
||||
address: "Jl. Jend Sudirman No. XX",
|
||||
port_amount: 10,
|
||||
status: "active",
|
||||
created_at: "2024-01-01",
|
||||
updated_at: "2024-01-01",
|
||||
})
|
||||
},
|
||||
|
||||
async createDevice(data: Device) {
|
||||
|
|
|
|||
|
|
@ -51,6 +51,10 @@ export const useAuthStore = create<AuthState>()(
|
|||
} finally {
|
||||
set({ token: null, user: null, error: null, isLoading: false });
|
||||
}
|
||||
},
|
||||
|
||||
clearError: () => {
|
||||
set({ error: null });
|
||||
}
|
||||
}),
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,29 +2,34 @@ import { create } from "zustand";
|
|||
import { deviceRepository } from "../repositories/deviceRepository";
|
||||
import { persist } from "zustand/middleware";
|
||||
import { createJSONStorage } from "zustand/middleware";
|
||||
import { immer } from 'zustand/middleware/immer'
|
||||
import { secureStorage } from "./storage";
|
||||
import { Device, DeviceState } from "../types/device.types";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
export const useDeviceStore = create<DeviceState>()(
|
||||
persist(
|
||||
(set) => ({
|
||||
immer((set, get) => ({
|
||||
data: [],
|
||||
device: null,
|
||||
isLoading: false,
|
||||
error: null,
|
||||
lastUpdate: null,
|
||||
getAll: async () => {
|
||||
set({ isLoading: true });
|
||||
try {
|
||||
const devices = await deviceRepository.getAll();
|
||||
set({ data: devices });
|
||||
set({ data: devices, lastUpdate: dayjs().valueOf() });
|
||||
} catch (error: any) {
|
||||
if (error.status === 401) {
|
||||
set({ error: error.response.status.description });
|
||||
set({ error: error.response?.status?.description });
|
||||
} else {
|
||||
set({ error: error.response.status.description || error.message });
|
||||
set({
|
||||
error: error.response?.status?.description || error.message,
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
set({ isLoading: false });
|
||||
set({ isLoading: false, lastUpdate: dayjs().valueOf() });
|
||||
}
|
||||
},
|
||||
find: async (id: string) => {
|
||||
|
|
@ -37,45 +42,69 @@ export const useDeviceStore = create<DeviceState>()(
|
|||
if (error.status === 401) {
|
||||
set({ error: error.response.status.description });
|
||||
} else {
|
||||
set({ error: error.response?.status?.description || error.message });
|
||||
set({
|
||||
error: error.response?.status?.description || error.message,
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
set({ isLoading: false });
|
||||
}
|
||||
},
|
||||
create: async (device: Omit<Device, "id" & "created_at" & "updated_at">) => {
|
||||
create: async (
|
||||
device: Omit<Device, "id" & "created_at" & "updated_at">
|
||||
) => {
|
||||
set({ isLoading: true });
|
||||
try {
|
||||
await deviceRepository.createDevice(device);
|
||||
|
||||
} catch (error: any) {
|
||||
console.log(error);
|
||||
|
||||
|
||||
if (error.status === 401) {
|
||||
set({ error: error.response.status.description });
|
||||
} else {
|
||||
set({ error: error.response?.status?.description || error.message });
|
||||
set({
|
||||
error: error.response?.status?.description || error.message,
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
set({ isLoading: false });
|
||||
}
|
||||
},
|
||||
update: async (id: string, device: Omit<Device, "id" & "created_at" & "updated_at">) => {
|
||||
update: async (
|
||||
id: string,
|
||||
device: Omit<Device, "id" & "created_at" & "updated_at">
|
||||
) => {
|
||||
set({ isLoading: true });
|
||||
try {
|
||||
await deviceRepository.updateDevice(id, device);
|
||||
|
||||
} catch (error: any) {
|
||||
if (error.status === 401) {
|
||||
set({ error: error.response.status.description });
|
||||
} else {
|
||||
set({ error: error.response?.status?.description || error.message });
|
||||
set({
|
||||
error: error.response?.status?.description || error.message,
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
set({ isLoading: false });
|
||||
}
|
||||
},
|
||||
}),
|
||||
clearError: () => set({ error: null }),
|
||||
clearDevice: () => {
|
||||
set({ device: null })
|
||||
},
|
||||
cacheExpired: () => {
|
||||
return (
|
||||
get().lastUpdate === null ||
|
||||
dayjs(get().lastUpdate).isBefore(
|
||||
dayjs().subtract(
|
||||
import.meta.env.VITE_APP_CACHE_TIME_VALUE,
|
||||
import.meta.env.VITE_APP_CACHE_TIME_UNIT
|
||||
)
|
||||
)
|
||||
);
|
||||
},
|
||||
})),
|
||||
{ name: "devices", storage: createJSONStorage(() => secureStorage) }
|
||||
)
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { ErrorState } from "./store.types";
|
||||
|
||||
export interface LoginPayload {
|
||||
username: string;
|
||||
password: string;
|
||||
|
|
@ -22,11 +24,10 @@ export interface UserProfile {
|
|||
* Auth state
|
||||
* ===========
|
||||
*/
|
||||
export interface AuthState {
|
||||
export interface AuthState extends ErrorState<AuthErrorInputState> {
|
||||
token: string | null;
|
||||
user: UserProfile | null;
|
||||
isLoading: boolean;
|
||||
error: string | null | AuthErrorInputState;
|
||||
login: (payload: LoginPayload) => Promise<boolean>;
|
||||
logout: () => Promise<void>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { ResponseApi } from "./response.types";
|
||||
import { CacheState, ErrorState } from "./store.types";
|
||||
|
||||
export interface DeviceRepositoryProps {
|
||||
getAll: () => Promise<Device[] | []>;
|
||||
|
|
@ -14,7 +15,7 @@ export interface Device {
|
|||
longitude: string | number;
|
||||
latitude: string | number;
|
||||
port_amount: number;
|
||||
status: string;
|
||||
status: DeviceStatus;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
|
@ -27,15 +28,16 @@ export interface DeviceRequest extends Omit<Device, "id" & "created_at" & "updat
|
|||
* Device State
|
||||
* =============
|
||||
*/
|
||||
export interface DeviceState {
|
||||
export interface DeviceState extends ErrorState, CacheState {
|
||||
data?: Device[];
|
||||
device?: Device | null;
|
||||
isLoading: boolean;
|
||||
error: string | null | DeviceErrorInputState;
|
||||
lastUpdate: number | null;
|
||||
getAll: () => Promise<void>;
|
||||
find: (id: string) => Promise<void>;
|
||||
create: (device: DeviceRequest) => Promise<void>;
|
||||
update: (id: string, device: DeviceRequest) => Promise<void>;
|
||||
clearDevice: () => void;
|
||||
}
|
||||
|
||||
export interface DeviceErrorInputState {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
export interface ErrorState<T = undefined> {
|
||||
error: string | null | T;
|
||||
clearError: () => void;
|
||||
}
|
||||
|
||||
export interface CacheState {
|
||||
cacheExpired: () => boolean;
|
||||
}
|
||||
|
|
@ -20,7 +20,10 @@
|
|||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedSideEffectImports": true
|
||||
"noUncheckedSideEffectImports": true,
|
||||
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,12 @@
|
|||
/// <reference types="vite/client" />
|
||||
|
||||
import { ManipulateType } from "dayjs"
|
||||
|
||||
interface ImportMetaEnv {
|
||||
readonly VITE_APP_KEY: string
|
||||
readonly VITE_API_URL: string
|
||||
readonly VITE_APP_CACHE_TIME_VALUE: number
|
||||
readonly VITE_APP_CACHE_TIME_UNIT: ManipulateType
|
||||
// more env variables...
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue