feat: impl mock api for tower, backbone and change icon sidebar menu
This commit is contained in:
parent
f8aa6ad785
commit
ed88b1ed27
|
|
@ -0,0 +1,6 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
|
||||
<path d="m5 20l14-8L5 4z"/>
|
||||
<path d="M19 20L5 12l14-8z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 255 B |
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="currentColor" d="M9 21q-1.925 0-3.463-1.1T3.35 17h17.3q-.65 1.8-2.187 2.9T15 21zm-6-6V9q0-2.5 1.75-4.25T9 3h6q2.5 0 4.25 1.75T21 9v6z"/></svg>
|
||||
|
After Width: | Height: | Size: 237 B |
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 512 512"><path fill="currentColor" d="M190.406 51.97c-4.104 29.047-7.13 44.833-6.72 58.53c-8.64 1.623-17.247 4.38-25.75 8.375c-25.746-38.28-86.43-44.672-139.06-13.5c27.12 33.838 44.53 50.06 88.468 49.094c-23.64 40.44-16.737 66.468-3.094 106.405c50.213-29.74 75.816-83.433 61.906-125.22c6.763-3.156 13.588-5.373 20.47-6.75c3.344 11.76 9.795 25.62 20.75 48.032c5.394-22.616 8.44-37.168 9.03-48.906c10.954 1.452 21.88 4.78 32.688 9.657c-5.85 16.68-9.962 35.86-13.25 60.688c17.36-16.772 29.677-31.288 38.844-46.188c5.488 3.756 10.908 7.874 16.25 12.313c4.39 3.648 8.69 7.515 12.906 11.563c-17.963 19.995-33.863 41.93-46.688 65.218c24.165-10.115 46.634-25.15 67.156-43.28a318 318 0 0 1 23.344 31.5c-26.33 17.278-50.652 37.486-70.375 59.844c28.77-5.146 58.07-17.09 85.908-33.156c5.354 10.33 9.97 20.708 13.75 30.906c-30.085 15.35-57.335 37.104-81.75 65.28c47.72 50.605 103.617 99.44 170 110.25c2.28-5.74 4.45-11.502 6.5-17.312c-60.357-19.627-109.66-52.267-145.563-89.156l13.406-13.03c33.782 34.707 80.56 65.736 137.97 84.405c16.732-55.238 20.843-112.648-1.844-167.217c-29.13 2.03-56.363 8.342-81.72 18.906c-4.096-10.92-9.036-21.94-14.717-32.845c30.638-19.792 58.958-44.156 82.092-69.313c-31.87 8.903-65.846 23.524-97.718 42.47c-7.725-11.933-16.287-23.498-25.53-34.376c26.697-26.486 49.666-57.987 68.186-90.03c-31.647 16.955-62.495 40.003-89.563 67.343c-4.5-4.3-9.1-8.428-13.812-12.345a225 225 0 0 0-19.375-14.438c10.292-22.438 15.372-47.604 20.875-83.437c-22.557 26.39-37.655 45.848-48.28 68.344c-13.42-5.92-27.255-9.89-41.22-11.313c-3.22-14.055-11.237-28.646-24.47-57.31zM426.25 302.187c11.947-.273 23.58 6.173 29.375 17.53c8.063 15.802 1.77 35.125-14.03 43.188c-15.803 8.063-35.126 1.802-43.19-14c-8.062-15.8-1.8-35.156 14-43.22a32 32 0 0 1 13.845-3.498"/></svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 2v20M2 5h20M3 3v2m4-2v2m10-2v2m4-2v2m-2 0l-7 7l-7-7"/></svg>
|
||||
|
After Width: | Height: | Size: 254 B |
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 640 512"><path fill="currentColor" d="M96 224c35.3 0 64-28.7 64-64s-28.7-64-64-64s-64 28.7-64 64s28.7 64 64 64m448 0c35.3 0 64-28.7 64-64s-28.7-64-64-64s-64 28.7-64 64s28.7 64 64 64m32 32h-64c-17.6 0-33.5 7.1-45.1 18.6c40.3 22.1 68.9 62 75.1 109.4h66c17.7 0 32-14.3 32-32v-32c0-35.3-28.7-64-64-64m-256 0c61.9 0 112-50.1 112-112S381.9 32 320 32S208 82.1 208 144s50.1 112 112 112m76.8 32h-8.3c-20.8 10-43.9 16-68.5 16s-47.6-6-68.5-16h-8.3C179.6 288 128 339.6 128 403.2V432c0 26.5 21.5 48 48 48h288c26.5 0 48-21.5 48-48v-28.8c0-63.6-51.6-115.2-115.2-115.2m-223.7-13.4C161.5 263.1 145.6 256 128 256H64c-35.3 0-64 28.7-64 64v32c0 17.7 14.3 32 32 32h65.9c6.3-47.4 34.9-87.3 75.2-109.4"/></svg>
|
||||
|
After Width: | Height: | Size: 763 B |
|
|
@ -4,9 +4,11 @@ import Alert from "@mui/material/Alert";
|
|||
import { DeviceState } from "../types/device.types";
|
||||
import { AuthState } from "../types/auth.types";
|
||||
import { FishboneState } from "../types/fishbone.types";
|
||||
import { TowerState } from "../types/tower.types";
|
||||
import { BackboneState } from "../types/backbone.types";
|
||||
|
||||
interface FlashMessageProps {
|
||||
store: () => DeviceState | AuthState | FishboneState;
|
||||
store: () => DeviceState | AuthState | FishboneState | TowerState | BackboneState;
|
||||
}
|
||||
|
||||
export default function FlashMessage(props: FlashMessageProps) {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,11 @@ import { Link, Typography } from "@mui/material";
|
|||
import { useLocation } from "react-router";
|
||||
import { ReactSVG } from "react-svg";
|
||||
import Speedometer from "../../assets/speedometer.svg";
|
||||
import Backbone from "../../assets/backbone.svg";
|
||||
import Fishbone from "../../assets/fishbone.svg";
|
||||
import Device from "../../assets/device.svg";
|
||||
import Tower from "../../assets/tower.svg";
|
||||
import Users from "../../assets/users.svg";
|
||||
|
||||
interface Props {
|
||||
/**
|
||||
|
|
@ -37,22 +42,22 @@ const MENU = [
|
|||
{
|
||||
title: "Backbone",
|
||||
link: "/backbone",
|
||||
icon: Speedometer,
|
||||
icon: Backbone,
|
||||
},
|
||||
{
|
||||
title: "Fishbone",
|
||||
link: "/fishbone",
|
||||
icon: Speedometer,
|
||||
icon: Fishbone,
|
||||
},
|
||||
{
|
||||
title: "Devices",
|
||||
link: "/devices",
|
||||
icon: Speedometer,
|
||||
icon: Device,
|
||||
},
|
||||
{
|
||||
title: "Tower",
|
||||
link: "/towers",
|
||||
icon: Speedometer,
|
||||
icon: Tower,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
@ -62,7 +67,7 @@ const MENU = [
|
|||
{
|
||||
title: "User",
|
||||
link: "/user",
|
||||
icon: Speedometer,
|
||||
icon: Users,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import Breadcrumbs from "@mui/material/Breadcrumbs";
|
|||
import Button from "@mui/material/Button";
|
||||
import Card from "@mui/material/Card";
|
||||
import CardContent from "@mui/material/CardContent";
|
||||
import Divider from "@mui/material/Divider";
|
||||
import Link from "@mui/material/Link";
|
||||
import Grid from "@mui/material/Grid2";
|
||||
import Typography from "@mui/material/Typography";
|
||||
|
|
@ -13,6 +14,10 @@ import IconButton from "../components/IconButton";
|
|||
import { Edit } from "iconsax-react";
|
||||
import { ColumnDef } from "@tanstack/react-table";
|
||||
import useTitle from "../hooks/useTitle";
|
||||
import FlashMessage from "../components/FlashMessage";
|
||||
import { useBackboneStore } from "../stores/backboneStore";
|
||||
import { BackboneDataTable } from "../types/backbone.types";
|
||||
import { useEffect, useMemo } from "react";
|
||||
|
||||
interface UserData {
|
||||
backbone: string;
|
||||
|
|
@ -23,18 +28,15 @@ interface UserData {
|
|||
}
|
||||
|
||||
export default function Backbone() {
|
||||
useTitle("Backbone");
|
||||
|
||||
const data: UserData[] = [
|
||||
{ backbone: "BB-BDG-SMD", device_start: "DV-CODE-1", device_end: "DV-CODE-2", total_core: "24 Core", total_fishbone: "3" },
|
||||
{ backbone: "BB-BDG-SBG", device_start: "DV-CODE-3", device_end: "DV-CODE-4", total_core: "24 Core", total_fishbone: "5" },
|
||||
];
|
||||
useTitle("Backbone");
|
||||
|
||||
const { data, cacheExpired, getAll } = useBackboneStore();
|
||||
|
||||
// Define columns
|
||||
const columns: ColumnDef<UserData>[] = [
|
||||
const columns: ColumnDef<BackboneDataTable>[] = useMemo(() => [
|
||||
{
|
||||
header: "Backbone",
|
||||
accessorKey: "backbone",
|
||||
accessorKey: "backbone_code",
|
||||
},
|
||||
{
|
||||
header: "Device Start",
|
||||
|
|
@ -46,7 +48,7 @@ export default function Backbone() {
|
|||
},
|
||||
{
|
||||
header: "Total Core",
|
||||
accessorKey: "total_core",
|
||||
accessorKey: "core_amount",
|
||||
},
|
||||
{
|
||||
header: "Total Fishbone",
|
||||
|
|
@ -56,15 +58,23 @@ export default function Backbone() {
|
|||
header: "",
|
||||
accessorKey: "action",
|
||||
enableSorting: false,
|
||||
cell: () => (
|
||||
cell: ({ row }) => (
|
||||
<>
|
||||
<IconButton variant="light" color="info">
|
||||
<IconButton variant="text" color="secondary" href={`/backbone/${row.original.id}`}>
|
||||
<Edit variant="Bulk" color="currentColor" />
|
||||
</IconButton>
|
||||
</>
|
||||
),
|
||||
},
|
||||
];
|
||||
], []);
|
||||
|
||||
useEffect(() => {
|
||||
if (cacheExpired()) {
|
||||
(async () => {
|
||||
await getAll();
|
||||
})();
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Box component="main">
|
||||
|
|
@ -73,7 +83,7 @@ export default function Backbone() {
|
|||
<Typography variant="h3">Backbone</Typography>
|
||||
</Grid>
|
||||
<Grid size="auto">
|
||||
<Button variant="contained" startIcon={<AddIcon color="inherit" />}>
|
||||
<Button variant="contained" startIcon={<AddIcon color="inherit" />} href="/backbone/create">
|
||||
Add New
|
||||
</Button>
|
||||
</Grid>
|
||||
|
|
@ -87,12 +97,15 @@ export default function Backbone() {
|
|||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<Card sx={{ display: "flex" }}>
|
||||
<CardContent sx={{ flex: "1 0 auto" }}>
|
||||
<ReactTable
|
||||
data={data}
|
||||
columns={columns}
|
||||
/>
|
||||
{/* <FlashMessage store={useBackboneStore} /> */}
|
||||
|
||||
<Card>
|
||||
<Box sx={{ p: 2 }}>
|
||||
<Typography variant="h5">List Backbone</Typography>
|
||||
</Box>
|
||||
<Divider />
|
||||
<CardContent sx={{ flex: "1 0 auto", pt: 2, px: 0 }}>
|
||||
<ReactTable data={data || []} columns={columns} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Box>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,105 @@
|
|||
import Box from "@mui/material/Box";
|
||||
import Breadcrumbs from "@mui/material/Breadcrumbs";
|
||||
import Button from "@mui/material/Button";
|
||||
import Card from "@mui/material/Card";
|
||||
import Divider from "@mui/material/Divider";
|
||||
import CardContent from "@mui/material/CardContent";
|
||||
import Grid from "@mui/material/Grid2";
|
||||
import Link from "@mui/material/Link";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import { Home, Save } from "@mui/icons-material";
|
||||
import useTitle from "../../hooks/useTitle";
|
||||
import IconButton from "../../components/IconButton";
|
||||
import { Back } from "iconsax-react";
|
||||
import { useParams } from "react-router";
|
||||
import Form from "./Form";
|
||||
import { FormEvent, useEffect } from "react";
|
||||
import FlashMessage from "../../components/FlashMessage";
|
||||
import { useBackboneStore } from "../../stores/backboneStore";
|
||||
import { BackboneRequest } from "../../types/backbone.types";
|
||||
|
||||
export default function BackboneCreateOrEdit() {
|
||||
useTitle("Backbone");
|
||||
const { pid } = useParams();
|
||||
const { update, create, isLoading, find, clearBackbone } = useBackboneStore();
|
||||
|
||||
const IS_EDIT_PAGE = pid!!;
|
||||
|
||||
const onSubmit = async (e: FormEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
const formData = new FormData(e.target as HTMLFormElement);
|
||||
|
||||
const data = Object.fromEntries(formData.entries()) as unknown as BackboneRequest;
|
||||
|
||||
if (IS_EDIT_PAGE) {
|
||||
await update(pid!!, data);
|
||||
} else {
|
||||
await create(data);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (pid!!) {
|
||||
(async () => {
|
||||
await find(pid!!);
|
||||
})()
|
||||
} else {
|
||||
(async () => {
|
||||
await clearBackbone()
|
||||
})()
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Box component="main">
|
||||
<Grid container spacing={2} alignItems={"center"} mb={4}>
|
||||
<Grid size="auto">
|
||||
<Box display={"flex"} alignItems={"center"} gap={1}>
|
||||
<IconButton variant="dashed" color="primary" href="/backbone">
|
||||
<Back variant="Outline" color="currentColor" />
|
||||
</IconButton>
|
||||
<Typography variant="h3">{IS_EDIT_PAGE ? `Edit Backbone - ${pid}` : 'Create Backbone'}</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid size={12}>
|
||||
<Breadcrumbs aria-label="breadcrumb">
|
||||
<Link underline="hover" color="inherit" href="/dashboard">
|
||||
<Home />
|
||||
</Link>
|
||||
<Link underline="hover" color="inherit" href="/backbone">
|
||||
<Typography sx={{ color: "text.primary" }}>Backbone</Typography>
|
||||
</Link>
|
||||
<Typography sx={{ color: "text.primary" }}>{IS_EDIT_PAGE ? pid : 'Create'}</Typography>
|
||||
</Breadcrumbs>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<FlashMessage store={useBackboneStore} />
|
||||
|
||||
<Card component="form" onSubmit={onSubmit}>
|
||||
<Box
|
||||
sx={{ px: 3, py: 1 }}
|
||||
display={"flex"}
|
||||
justifyContent={"space-between"}
|
||||
alignItems={"center"}
|
||||
>
|
||||
<Typography variant="h5">Fill the form</Typography>
|
||||
<Button
|
||||
disabled={isLoading}
|
||||
type="submit"
|
||||
variant="contained"
|
||||
size="small"
|
||||
startIcon={<Save color="inherit" />}
|
||||
>
|
||||
{IS_EDIT_PAGE ? "Save Changes" : "Create"}
|
||||
</Button>
|
||||
</Box>
|
||||
<Divider />
|
||||
<CardContent>
|
||||
<Form />
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
import FormControl from "@mui/material/FormControl";
|
||||
import Grid from "@mui/material/Grid2";
|
||||
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 { useEffect, useState } from "react";
|
||||
import { useBackboneStore } from "../../stores/backboneStore";
|
||||
|
||||
export default function Form() {
|
||||
const {
|
||||
backbone,
|
||||
clearBackbone,
|
||||
deviceSelect,
|
||||
getDeviceSelect,
|
||||
} = useBackboneStore();
|
||||
const [deviceStart, setDeviceStart] = useState<string>("");
|
||||
const [deviceEnd, setDeviceEnd] = useState<string>("");
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
await getDeviceSelect();
|
||||
})();
|
||||
|
||||
if (backbone) {
|
||||
setDeviceStart(backbone.dev_start_id as string);
|
||||
setDeviceEnd(backbone.dev_end_id as string);
|
||||
}
|
||||
|
||||
const clear = async () => {
|
||||
await clearBackbone();
|
||||
};
|
||||
|
||||
return () => {
|
||||
clear();
|
||||
};
|
||||
}, [backbone]);
|
||||
|
||||
return (
|
||||
<Grid container spacing={2}>
|
||||
<Grid size={{ md: 6, xs: 12 }}>
|
||||
<TextField
|
||||
fullWidth
|
||||
name="backbone_code"
|
||||
label="Backbone Code"
|
||||
variant="outlined"
|
||||
value={backbone?.backbone_code}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ md: 6, xs: 12 }}>
|
||||
<TextField
|
||||
fullWidth
|
||||
name="core_amount"
|
||||
label="Core used"
|
||||
variant="outlined"
|
||||
type="number"
|
||||
value={backbone?.core_amount}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ md: 6, xs: 12 }}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel id="device-start-label">Device Start</InputLabel>
|
||||
<Select
|
||||
name="device-start"
|
||||
labelId="device-start-label"
|
||||
label="Device Start"
|
||||
value={deviceStart}
|
||||
onChange={(e) => setDeviceStart(e.target.value)}
|
||||
>
|
||||
{deviceSelect?.map((device, key) => (
|
||||
<MenuItem key={key} value={device.value}>
|
||||
{device.label.toUpperCase()}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
<Grid size={{ md: 6, xs: 12 }}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel id="device-end-label">Device End</InputLabel>
|
||||
<Select
|
||||
name="device-end"
|
||||
labelId="device-end-label"
|
||||
label="Device End"
|
||||
value={deviceEnd}
|
||||
onChange={(e) => setDeviceEnd(e.target.value)}
|
||||
>
|
||||
{deviceSelect
|
||||
?.filter((b) => b.value != deviceStart)
|
||||
.map((device, key) => (
|
||||
<MenuItem key={key} value={device.value}>
|
||||
{device.label.toUpperCase()}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
|
||||
<Grid size={{ md: 6, xs: 12 }}>
|
||||
<TextField
|
||||
fullWidth
|
||||
name="longitude"
|
||||
label="Longitude"
|
||||
variant="outlined"
|
||||
type="number"
|
||||
value={backbone?.longitude}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ md: 6, xs: 12 }}>
|
||||
<TextField
|
||||
fullWidth
|
||||
name="latitude"
|
||||
label="Latitude"
|
||||
variant="outlined"
|
||||
type="number"
|
||||
value={backbone?.latitude}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ import Breadcrumbs from "@mui/material/Breadcrumbs";
|
|||
import Button from "@mui/material/Button";
|
||||
import Card from "@mui/material/Card";
|
||||
import CardContent from "@mui/material/CardContent";
|
||||
import Divider from "@mui/material/Divider";
|
||||
import Link from "@mui/material/Link";
|
||||
import Grid from "@mui/material/Grid2";
|
||||
import Typography from "@mui/material/Typography";
|
||||
|
|
@ -13,6 +14,10 @@ import { ColumnDef } from "@tanstack/react-table";
|
|||
import IconButton from "../components/IconButton";
|
||||
import { Edit } from "iconsax-react";
|
||||
import useTitle from "../hooks/useTitle";
|
||||
import { useTowerStore } from "../stores/towerStore";
|
||||
import { useEffect, useMemo } from "react";
|
||||
import FlashMessage from "../components/FlashMessage";
|
||||
import { TowerDataTable } from "../types/tower.types";
|
||||
|
||||
interface UserData {
|
||||
tower_code: string;
|
||||
|
|
@ -22,29 +27,18 @@ interface UserData {
|
|||
|
||||
export default function Towers() {
|
||||
useTitle("Towers");
|
||||
|
||||
const data: UserData[] = [
|
||||
{ tower_code: 'TW-CODE-1', device: "DV-CODE-1", address: "Jl. Contoh 1" },
|
||||
{ tower_code: 'TW-CODE-2', device: "DV-CODE-2", address: "Jl. Contoh 2" },
|
||||
{ tower_code: 'TW-CODE-3', device: "", address: "Jl. Contoh 3" },
|
||||
{ tower_code: 'TW-CODE-4', device: "", address: "Jl. Contoh 4" },
|
||||
{ tower_code: 'TW-CODE-5', device: "DV-CODE-5", address: "Jl. Contoh 5" },
|
||||
{ tower_code: 'TW-CODE-6', device: "", address: "Jl. Contoh 6" },
|
||||
{ tower_code: 'TW-CODE-7', device: "", address: "Jl. Contoh 7" },
|
||||
{ tower_code: 'TW-CODE-8', device: "DV-CODE-8", address: "Jl. Contoh 8" },
|
||||
{ tower_code: 'TW-CODE-9', device: "", address: "Jl. Contoh 9" },
|
||||
{ tower_code: 'TW-CODE-10', device: "DV-CODE-10", address: "Jl. Contoh 10" },
|
||||
];
|
||||
|
||||
const { data, cacheExpired, getAll } = useTowerStore();
|
||||
|
||||
// Define columns
|
||||
const columns: ColumnDef<UserData>[] = [
|
||||
const columns: ColumnDef<TowerDataTable>[] = useMemo(() => [
|
||||
{
|
||||
header: "Tower Code",
|
||||
accessorKey: "tower_code",
|
||||
},
|
||||
{
|
||||
header: "Device",
|
||||
accessorKey: "device",
|
||||
accessorKey: "device_code",
|
||||
},
|
||||
{
|
||||
header: "Address",
|
||||
|
|
@ -54,15 +48,23 @@ export default function Towers() {
|
|||
header: '',
|
||||
accessorKey: "action",
|
||||
enableSorting: false,
|
||||
cell: () => (
|
||||
cell: ({ row }) => (
|
||||
<>
|
||||
<IconButton variant="light" color="info" >
|
||||
<IconButton variant="text" color="secondary" href={`/towers/${row.original.id}`}>
|
||||
<Edit variant="Bulk" color="currentColor" />
|
||||
</IconButton>
|
||||
</>
|
||||
)
|
||||
}
|
||||
];
|
||||
], []);
|
||||
|
||||
useEffect(() => {
|
||||
if (cacheExpired()) {
|
||||
(async () => {
|
||||
await getAll();
|
||||
})();
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Box component="main">
|
||||
|
|
@ -71,7 +73,7 @@ export default function Towers() {
|
|||
<Typography variant="h3">Towers</Typography>
|
||||
</Grid>
|
||||
<Grid size="auto">
|
||||
<Button variant="contained" startIcon={<AddIcon color="inherit" />}>
|
||||
<Button variant="contained" href="/towers/create" startIcon={<AddIcon color="inherit" />}>
|
||||
Add New
|
||||
</Button>
|
||||
</Grid>
|
||||
|
|
@ -85,12 +87,15 @@ export default function Towers() {
|
|||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<Card sx={{ display: "flex" }}>
|
||||
<CardContent sx={{ flex: "1 0 auto" }}>
|
||||
<ReactTable
|
||||
data={data}
|
||||
columns={columns}
|
||||
/>
|
||||
<FlashMessage store={useTowerStore} />
|
||||
|
||||
<Card>
|
||||
<Box sx={{ p: 2 }}>
|
||||
<Typography variant="h5">List Towers</Typography>
|
||||
</Box>
|
||||
<Divider />
|
||||
<CardContent sx={{ flex: "1 0 auto", pt: 2, px: 0 }}>
|
||||
<ReactTable data={data || []} columns={columns} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Box>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,105 @@
|
|||
import Box from "@mui/material/Box";
|
||||
import Breadcrumbs from "@mui/material/Breadcrumbs";
|
||||
import Button from "@mui/material/Button";
|
||||
import Card from "@mui/material/Card";
|
||||
import Divider from "@mui/material/Divider";
|
||||
import CardContent from "@mui/material/CardContent";
|
||||
import Grid from "@mui/material/Grid2";
|
||||
import Link from "@mui/material/Link";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import { Home, Save } from "@mui/icons-material";
|
||||
import useTitle from "../../hooks/useTitle";
|
||||
import IconButton from "../../components/IconButton";
|
||||
import { Back } from "iconsax-react";
|
||||
import { useParams } from "react-router";
|
||||
import Form from "./Form";
|
||||
import { FormEvent, useEffect } from "react";
|
||||
import { useTowerStore } from "../../stores/towerStore";
|
||||
import { TowerRequest } from "../../types/tower.types";
|
||||
import FlashMessage from "../../components/FlashMessage";
|
||||
|
||||
export default function TowerCreateOrEdit() {
|
||||
useTitle("Towers");
|
||||
const { pid } = useParams();
|
||||
const { update, create, isLoading, find, clearTower, tower } = useTowerStore();
|
||||
|
||||
const IS_EDIT_PAGE = pid!!;
|
||||
|
||||
const onSubmit = async (e: FormEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
const formData = new FormData(e.target as HTMLFormElement);
|
||||
|
||||
const data = Object.fromEntries(formData.entries()) as unknown as TowerRequest;
|
||||
|
||||
if (IS_EDIT_PAGE) {
|
||||
await update(pid!!, data);
|
||||
} else {
|
||||
await create(data);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (pid!!) {
|
||||
(async () => {
|
||||
await find(pid!!);
|
||||
})()
|
||||
} else {
|
||||
(async () => {
|
||||
await clearTower()
|
||||
})()
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Box component="main">
|
||||
<Grid container spacing={2} alignItems={"center"} mb={4}>
|
||||
<Grid size="auto">
|
||||
<Box display={"flex"} alignItems={"center"} gap={1}>
|
||||
<IconButton variant="dashed" color="primary" href="/towers">
|
||||
<Back variant="Outline" color="currentColor" />
|
||||
</IconButton>
|
||||
<Typography variant="h3">{IS_EDIT_PAGE ? `Edit Tower - ${pid}` : 'Create Tower'}</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid size={12}>
|
||||
<Breadcrumbs aria-label="breadcrumb">
|
||||
<Link underline="hover" color="inherit" href="/dashboard">
|
||||
<Home />
|
||||
</Link>
|
||||
<Link underline="hover" color="inherit" href="/towers">
|
||||
<Typography sx={{ color: "text.primary" }}>Towers</Typography>
|
||||
</Link>
|
||||
<Typography sx={{ color: "text.primary" }}>{IS_EDIT_PAGE ? pid : 'Create'}</Typography>
|
||||
</Breadcrumbs>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<FlashMessage store={useTowerStore} />
|
||||
|
||||
<Card component="form" onSubmit={onSubmit}>
|
||||
<Box
|
||||
sx={{ px: 3, py: 1 }}
|
||||
display={"flex"}
|
||||
justifyContent={"space-between"}
|
||||
alignItems={"center"}
|
||||
>
|
||||
<Typography variant="h5">Fill the form {tower?.tower_code}</Typography>
|
||||
<Button
|
||||
disabled={isLoading}
|
||||
type="submit"
|
||||
variant="contained"
|
||||
size="small"
|
||||
startIcon={<Save color="inherit" />}
|
||||
>
|
||||
{IS_EDIT_PAGE ? "Save Changes" : "Create"}
|
||||
</Button>
|
||||
</Box>
|
||||
<Divider />
|
||||
<CardContent>
|
||||
<Form />
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
import Box from "@mui/material/Box";
|
||||
import Chip from "@mui/material/Chip";
|
||||
import FormControl from "@mui/material/FormControl";
|
||||
import Grid from "@mui/material/Grid2";
|
||||
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 { useEffect, useState } from "react";
|
||||
import { DeviceStatus } from "../../types/device.types";
|
||||
import { useTowerStore } from "../../stores/towerStore";
|
||||
|
||||
export default function Form() {
|
||||
const { tower, clearTower, deviceSelect, getDeviceSelect } = useTowerStore();
|
||||
const [device, setDevice] = useState<string>("");
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
await getDeviceSelect();
|
||||
})();
|
||||
|
||||
if (tower) {
|
||||
setDevice(tower.device_id as string);
|
||||
}
|
||||
|
||||
const clear = async () => {
|
||||
await clearTower();
|
||||
};
|
||||
|
||||
return () => {
|
||||
clear();
|
||||
};
|
||||
}, [tower]);
|
||||
|
||||
return (
|
||||
<Grid container spacing={2}>
|
||||
<Grid size={{ md: 6, xs: 12 }}>
|
||||
<TextField
|
||||
fullWidth
|
||||
name="tower_code"
|
||||
label="Tower Code"
|
||||
variant="outlined"
|
||||
value={tower?.tower_code}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
<Grid size={{ md: 6, xs: 12 }}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel id="device-label">Device</InputLabel>
|
||||
<Select
|
||||
name="device"
|
||||
labelId="device-label"
|
||||
label="Device"
|
||||
value={device}
|
||||
onChange={(e) => setDevice(e.target.value)}
|
||||
>
|
||||
{deviceSelect?.map((device, key) => (
|
||||
<MenuItem key={key} value={device.value}>
|
||||
{device.label.toUpperCase()}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
|
||||
<Grid size={{ md: 6, xs: 12 }}>
|
||||
<TextField
|
||||
fullWidth
|
||||
name="longitude"
|
||||
label="Longitude"
|
||||
variant="outlined"
|
||||
type="number"
|
||||
value={tower?.longitude}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
<Grid size={{ md: 6, xs: 12 }}>
|
||||
<TextField
|
||||
fullWidth
|
||||
name="latitude"
|
||||
label="Latitude"
|
||||
variant="outlined"
|
||||
type="number"
|
||||
value={tower?.latitude}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
import { createAPI } from "../services/api";
|
||||
import { BackboneRepositoryProps, Backbone, IGetDeviceSelect } from "../types/backbone.types";
|
||||
import { ResponseApi } from "../types/response.types";
|
||||
import { SelectState } from "../types/store.types";
|
||||
|
||||
const api = createAPI();
|
||||
|
||||
export const backboneRepository: BackboneRepositoryProps = {
|
||||
async getAll() {
|
||||
// const response = await api.get<ResponseApi<Backbone[]>>("/api/v1/backbones");
|
||||
|
||||
// return response.data.data as Backbone[];
|
||||
|
||||
return Promise.resolve([
|
||||
{
|
||||
id: "1",
|
||||
backbone_code: "BB-01",
|
||||
device_start: "DEV-S1",
|
||||
device_end: "DEV-E1",
|
||||
address: "Jl. Jend Sudirman No. XX",
|
||||
core_amount: 10,
|
||||
longitude: "1",
|
||||
latitude: "1",
|
||||
total_fishbone: 2,
|
||||
}
|
||||
])
|
||||
},
|
||||
async getBackboneById(id: string) {
|
||||
// const response = await api.get<ResponseApi<Backbone>>(`/api/v1/backbones/${id}`);
|
||||
|
||||
// return response.data.data as Backbone;
|
||||
return Promise.resolve({
|
||||
id: "1",
|
||||
backbone_code: "BB-01",
|
||||
dev_start_id: "1",
|
||||
dev_end_id: "2",
|
||||
core_amount: 10,
|
||||
longitude: "1",
|
||||
latitude: "1",
|
||||
created_at: "2024-01-01",
|
||||
updated_at: "2024-01-01",
|
||||
})
|
||||
},
|
||||
|
||||
async createBackbone(data: Backbone) {
|
||||
const response = await api.post<ResponseApi<null>>("/api/v1/backbones", data);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
async updateBackbone(id: string, data: Backbone) {
|
||||
const response = await api.put<ResponseApi<null>>(`/api/v1/backbones/${id}`, data);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
async getDeviceSelect() {
|
||||
// const response = await api.get<ResponseApi<SelectState[]>>("/api/v1/backbones/backbone");
|
||||
|
||||
// return (response.data.data as IGetBackboneSelect[])?.map((i: IGetBackboneSelect) => ({label: i.backbone_code, value: i.id})) as SelectState[];
|
||||
return Promise.resolve([
|
||||
{
|
||||
label: "DV-EX-001",
|
||||
value: "1",
|
||||
},
|
||||
{
|
||||
label: "DV-EX-002",
|
||||
value: "2",
|
||||
}
|
||||
]);
|
||||
},
|
||||
};
|
||||
|
|
@ -23,6 +23,18 @@ export const deviceRepository: DeviceRepositoryProps = {
|
|||
total_used_port: 5,
|
||||
status: "active",
|
||||
created_at: "2024-01-01",
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
device_code: "DV-EX-002",
|
||||
device_type: "OTB",
|
||||
longitude: "1",
|
||||
latitude: "1",
|
||||
address: "Jl. Jend Sudirman No. XX",
|
||||
port_amount: 10,
|
||||
total_used_port: 5,
|
||||
status: "active",
|
||||
created_at: "2024-01-01",
|
||||
}
|
||||
])
|
||||
},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,63 @@
|
|||
import { createAPI } from "../services/api";
|
||||
import { TowerRepositoryProps, Tower, IGetDeviceSelect } from "../types/tower.types";
|
||||
import { ResponseApi } from "../types/response.types";
|
||||
import { SelectState } from "../types/store.types";
|
||||
|
||||
const api = createAPI();
|
||||
|
||||
export const towerRepository: TowerRepositoryProps = {
|
||||
async getAll() {
|
||||
// const response = await api.get<ResponseApi<Tower[]>>("/api/v1/towers");
|
||||
|
||||
// return response.data.data as Tower[];
|
||||
|
||||
return Promise.resolve( [
|
||||
{
|
||||
id: "1",
|
||||
tower_code: "TW-001",
|
||||
longitude: "1",
|
||||
latitude: "1",
|
||||
device_code: "DV-EX-001",
|
||||
address: "Jl. Jend Sudirman No. XX",
|
||||
created_at: "2024-01-01",
|
||||
}
|
||||
])
|
||||
},
|
||||
|
||||
async getTowerById(id: string) {
|
||||
// const response = await api.get<ResponseApi<Tower>>(`/api/v1/towers/${id}`);
|
||||
|
||||
// return response.data.data as Tower;
|
||||
return Promise.resolve({
|
||||
id: "1",
|
||||
tower_code: "TW-001",
|
||||
longitude: "1",
|
||||
latitude: "1",
|
||||
device_id: "1",
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString()
|
||||
})
|
||||
},
|
||||
|
||||
async createTower(data: Tower) {
|
||||
const response = await api.post<ResponseApi<null>>("/api/v1/towers", data);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
async updateTower(id: string, data: Tower) {
|
||||
const response = await api.put<ResponseApi<null>>(`/api/v1/towers/${id}`, data);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
async getDeviceSelect() {
|
||||
// const response = await api.get<ResponseApi<SelectState[]>>("/api/v1/towers/tower-select");
|
||||
|
||||
// return (response.data.data as IGetDeviceSelect[])?.map((i: IGetDeviceSelect) => ({label: i.tower_code, value: i.id})) as SelectState[];
|
||||
return Promise.resolve([
|
||||
{
|
||||
label: "DV-EX-001",
|
||||
value: "1",
|
||||
}
|
||||
]);
|
||||
}
|
||||
};
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
import { Routes as RRRoutes, Route } from "react-router";
|
||||
import App from "../App";
|
||||
import { Navigate, Routes as RRRoutes, Route } from "react-router";
|
||||
import AuthLayout from "../layouts/AuthLayout";
|
||||
import Login from "../pages/auth/Login";
|
||||
import Dashboard from "../pages/Dashboard";
|
||||
|
|
@ -11,6 +10,8 @@ import Towers from "../pages/Towers";
|
|||
import RequiredAuth from "./RequiredAuth";
|
||||
import DeviceCreateOrEdit from "../pages/Devices/CreateOrEdit";
|
||||
import FishboneCreateOrEdit from "../pages/Fishbone/CreateOrEdit";
|
||||
import TowerCreateOrEdit from "../pages/Towers/CreateOrEdit";
|
||||
import BackboneCreateOrEdit from "../pages/Backbone/CreateOrEdit";
|
||||
|
||||
export default function Routes() {
|
||||
return (
|
||||
|
|
@ -22,7 +23,7 @@ export default function Routes() {
|
|||
</Route>
|
||||
|
||||
<Route element={<RequiredAuth />}>
|
||||
<Route path="/" element={<App />} />
|
||||
<Route path="/" element={<Navigate to={"/dashboard"} />} />
|
||||
|
||||
<Route element={<DashboardLayout />}>
|
||||
<Route path="/dashboard" element={<Dashboard />} />
|
||||
|
|
@ -37,6 +38,12 @@ export default function Routes() {
|
|||
|
||||
<Route path="/fishbone/:pid" element={<FishboneCreateOrEdit />} />
|
||||
<Route path="/fishbone/create" element={<FishboneCreateOrEdit />} />
|
||||
|
||||
<Route path="/towers/:pid" element={<TowerCreateOrEdit />} />
|
||||
<Route path="/towers/create" element={<TowerCreateOrEdit />} />
|
||||
|
||||
<Route path="/backbone/:pid" element={<BackboneCreateOrEdit />} />
|
||||
<Route path="/backbone/create" element={<BackboneCreateOrEdit />} />
|
||||
</Route>
|
||||
|
||||
</Route>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,129 @@
|
|||
import { create } from "zustand";
|
||||
import { persist } from "zustand/middleware";
|
||||
import { backboneRepository } from "../repositories/backboneRepository";
|
||||
import { createJSONStorage } from "zustand/middleware";
|
||||
import { immer } from 'zustand/middleware/immer'
|
||||
import { secureStorage } from "./storage";
|
||||
import { Backbone, BackboneState } from "../types/backbone.types";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
export const useBackboneStore = create<BackboneState>()(
|
||||
persist(
|
||||
immer((set, get) => ({
|
||||
data: [],
|
||||
backbone: null,
|
||||
isLoading: false,
|
||||
error: null,
|
||||
lastUpdate: null,
|
||||
backboneSelect: [],
|
||||
deviceSelect: [],
|
||||
getAll: async () => {
|
||||
set({ isLoading: true });
|
||||
try {
|
||||
const backbones = await backboneRepository.getAll();
|
||||
set({ data: backbones, lastUpdate: dayjs().valueOf() });
|
||||
} catch (error: any) {
|
||||
if (error.status === 401) {
|
||||
set({ error: error.response?.status?.description });
|
||||
} else {
|
||||
set({
|
||||
error: error.response?.status?.description || error.message,
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
set({ isLoading: false, lastUpdate: dayjs().valueOf() });
|
||||
}
|
||||
},
|
||||
find: async (id: string) => {
|
||||
set({ isLoading: true });
|
||||
try {
|
||||
const backbone = await backboneRepository.getBackboneById(id);
|
||||
|
||||
set({ backbone: backbone });
|
||||
} catch (error: any) {
|
||||
if (error.status === 401) {
|
||||
set({ error: error.response.status.description });
|
||||
} else {
|
||||
set({
|
||||
error: error.response?.status?.description || error.message,
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
set({ isLoading: false });
|
||||
}
|
||||
},
|
||||
create: async (
|
||||
backbone: Omit<Backbone, "id" & "created_at" & "updated_at">
|
||||
) => {
|
||||
set({ isLoading: true });
|
||||
try {
|
||||
await backboneRepository.createBackbone(backbone);
|
||||
} 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,
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
set({ isLoading: false });
|
||||
}
|
||||
},
|
||||
update: async (
|
||||
id: string,
|
||||
backbone: Omit<Backbone, "id" & "created_at" & "updated_at">
|
||||
) => {
|
||||
set({ isLoading: true });
|
||||
try {
|
||||
await backboneRepository.updateBackbone(id, backbone);
|
||||
} catch (error: any) {
|
||||
if (error.status === 401) {
|
||||
set({ error: error.response.status.description });
|
||||
} else {
|
||||
set({
|
||||
error: error.response?.status?.description || error.message,
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
set({ isLoading: false });
|
||||
}
|
||||
},
|
||||
getDeviceSelect: async () => {
|
||||
set({ isLoading: true });
|
||||
try {
|
||||
const deviceSelect = await backboneRepository.getDeviceSelect();
|
||||
set({ deviceSelect: deviceSelect });
|
||||
} catch (error: any) {
|
||||
if (error.status === 401) {
|
||||
set({ error: error.response.status.description });
|
||||
} else {
|
||||
set({
|
||||
error: error.response?.status?.description || error.message,
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
set({ isLoading: false });
|
||||
}
|
||||
},
|
||||
clearError: () => set({ error: null }),
|
||||
clearBackbone: () => {
|
||||
set({ backbone: 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: "backbones", storage: createJSONStorage(() => secureStorage) }
|
||||
)
|
||||
);
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
import { create } from "zustand";
|
||||
import { towerRepository } from "../repositories/towerRepository";
|
||||
import { persist } from "zustand/middleware";
|
||||
import { createJSONStorage } from "zustand/middleware";
|
||||
import { immer } from "zustand/middleware/immer";
|
||||
import { secureStorage } from "./storage";
|
||||
import { Tower, TowerState } from "../types/tower.types";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
export const useTowerStore = create<TowerState>()(
|
||||
persist(
|
||||
immer((set, get) => ({
|
||||
data: [],
|
||||
tower: null,
|
||||
isLoading: false,
|
||||
error: null,
|
||||
lastUpdate: null,
|
||||
deviceSelect: [],
|
||||
getAll: async () => {
|
||||
set({ isLoading: true });
|
||||
try {
|
||||
const towers = await towerRepository.getAll();
|
||||
set({ data: towers, lastUpdate: dayjs().valueOf() });
|
||||
} catch (error: any) {
|
||||
if (error.status === 401) {
|
||||
set({ error: error.response?.status?.description });
|
||||
} else {
|
||||
set({
|
||||
error: error.response?.status?.description || error.message,
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
set({ isLoading: false, lastUpdate: dayjs().valueOf() });
|
||||
}
|
||||
},
|
||||
find: async (id: string) => {
|
||||
set({ isLoading: true });
|
||||
try {
|
||||
const tower = await towerRepository.getTowerById(id);
|
||||
|
||||
set({ tower: tower });
|
||||
} catch (error: any) {
|
||||
if (error.status === 401) {
|
||||
set({ error: error.response.status.description });
|
||||
} else {
|
||||
set({
|
||||
error: error.response?.status?.description || error.message,
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
set({ isLoading: false });
|
||||
}
|
||||
},
|
||||
create: async (
|
||||
tower: Omit<Tower, "id" & "created_at" & "updated_at">
|
||||
) => {
|
||||
set({ isLoading: true });
|
||||
try {
|
||||
await towerRepository.createTower(tower);
|
||||
} 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,
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
set({ isLoading: false });
|
||||
}
|
||||
},
|
||||
update: async (
|
||||
id: string,
|
||||
tower: Omit<Tower, "id" & "created_at" & "updated_at">
|
||||
) => {
|
||||
set({ isLoading: true });
|
||||
try {
|
||||
await towerRepository.updateTower(id, tower);
|
||||
} catch (error: any) {
|
||||
if (error.status === 401) {
|
||||
set({ error: error.response.status.description });
|
||||
} else {
|
||||
set({
|
||||
error: error.response?.status?.description || error.message,
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
set({ isLoading: false });
|
||||
}
|
||||
},
|
||||
getDeviceSelect: async () => {
|
||||
set({ isLoading: true });
|
||||
try {
|
||||
const towers = await towerRepository.getDeviceSelect();
|
||||
set({ deviceSelect: towers });
|
||||
} catch (error: any) {
|
||||
if (error.status === 401) {
|
||||
set({ error: error.response?.status?.description });
|
||||
} else {
|
||||
set({
|
||||
error: error.response?.status?.description || error.message,
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
set({ isLoading: false });
|
||||
}
|
||||
},
|
||||
clearError: () => set({ error: null }),
|
||||
clearTower: () => {
|
||||
set({ tower: 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: "towers", storage: createJSONStorage(() => secureStorage) }
|
||||
)
|
||||
);
|
||||
|
|
@ -46,7 +46,7 @@ export default function ThemeCustomization({
|
|||
}
|
||||
}
|
||||
|
||||
const theme = Palette(ThemeMode.LIGHT);
|
||||
const theme = Palette(themeMode);
|
||||
|
||||
const themeTypography: TypographyVariantsOptions =
|
||||
useMemo<TypographyVariantsOptions>(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
import { ResponseApi } from "./response.types";
|
||||
import { CacheState, ErrorState, SelectState } from "./store.types";
|
||||
|
||||
export interface BackboneRepositoryProps {
|
||||
getAll: () => Promise<BackboneDataTable[] | []>;
|
||||
getBackboneById: (id: string) => Promise<Backbone>;
|
||||
createBackbone: (backbone: BackboneRequest) => Promise<Pick<ResponseApi<any>, "status">>;
|
||||
updateBackbone: (id: string, backbone: BackboneRequest) => Promise<Pick<ResponseApi<any>, "status">>;
|
||||
getDeviceSelect: () => Promise<SelectState[] | null>;
|
||||
}
|
||||
|
||||
export interface Backbone {
|
||||
id: string;
|
||||
backbone_code: string;
|
||||
longitude: string | number;
|
||||
latitude: string | number;
|
||||
dev_start_id?: string | number | null;
|
||||
dev_end_id?: string | number | null;
|
||||
core_amount: string | number;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface BackboneRequest extends Omit<Backbone, "id" & "created_at" & "updated_at"> {}
|
||||
|
||||
/**
|
||||
* Backbone State
|
||||
* =============
|
||||
*/
|
||||
export interface BackboneState extends ErrorState, CacheState {
|
||||
data?: BackboneDataTable[];
|
||||
backbone?: Backbone | null;
|
||||
isLoading: boolean;
|
||||
lastUpdate: number | null;
|
||||
deviceSelect: SelectState[] | null;
|
||||
getDeviceSelect: () => Promise<void>;
|
||||
getAll: () => Promise<void>;
|
||||
find: (id: string) => Promise<void>;
|
||||
create: (backbone: BackboneRequest) => Promise<void>;
|
||||
update: (id: string, backbone: BackboneRequest) => Promise<void>;
|
||||
clearBackbone: () => void;
|
||||
}
|
||||
|
||||
export interface BackboneDataTable extends Pick<Backbone, "id" | "backbone_code" | "longitude" | "latitude" | "core_amount"> {
|
||||
device_start: string;
|
||||
device_end: string;
|
||||
total_fishbone: string | number;
|
||||
}
|
||||
|
||||
export interface IGetDeviceSelect {
|
||||
id: string;
|
||||
device_code: string;
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
import { ResponseApi } from "./response.types";
|
||||
import { CacheState, ErrorState, SelectState } from "./store.types";
|
||||
|
||||
export interface TowerRepositoryProps {
|
||||
getAll: () => Promise<TowerDataTable[] | []>;
|
||||
getTowerById: (id: string) => Promise<Tower>;
|
||||
createTower: (tower: TowerRequest) => Promise<Pick<ResponseApi<any>, "status">>;
|
||||
updateTower: (id: string, tower: TowerRequest) => Promise<Pick<ResponseApi<any>, "status">>;
|
||||
getDeviceSelect: () => Promise<SelectState[] | null>;
|
||||
}
|
||||
|
||||
export interface Tower {
|
||||
id: string;
|
||||
tower_code: string;
|
||||
longitude: string | number;
|
||||
latitude: string | number;
|
||||
device_id?: string | number | null
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface TowerRequest extends Omit<Tower, "id" & "created_at" & "updated_at"> {}
|
||||
|
||||
/**
|
||||
* Tower State
|
||||
* =============
|
||||
*/
|
||||
export interface TowerState extends ErrorState, CacheState {
|
||||
data?: TowerDataTable[];
|
||||
tower?: Tower | null;
|
||||
isLoading: boolean;
|
||||
lastUpdate: number | null;
|
||||
deviceSelect: SelectState[] | null;
|
||||
getDeviceSelect: () => Promise<void>;
|
||||
getAll: () => Promise<void>;
|
||||
find: (id: string) => Promise<void>;
|
||||
create: (tower: TowerRequest) => Promise<void>;
|
||||
update: (id: string, tower: TowerRequest) => Promise<void>;
|
||||
clearTower: () => void;
|
||||
}
|
||||
|
||||
export interface TowerDataTable extends Pick<Tower, "id" | "tower_code" | "longitude" | "latitude"> {
|
||||
address: string;
|
||||
device_code: string;
|
||||
}
|
||||
|
||||
export interface IGetDeviceSelect {
|
||||
id: string;
|
||||
device_code: string;
|
||||
}
|
||||
Loading…
Reference in New Issue