diff --git a/src/assets/backbone.svg b/src/assets/backbone.svg
new file mode 100644
index 0000000..44ed429
--- /dev/null
+++ b/src/assets/backbone.svg
@@ -0,0 +1,6 @@
+
\ No newline at end of file
diff --git a/src/assets/device.svg b/src/assets/device.svg
new file mode 100644
index 0000000..b7e0a91
--- /dev/null
+++ b/src/assets/device.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/fishbone.svg b/src/assets/fishbone.svg
new file mode 100644
index 0000000..cbffa8d
--- /dev/null
+++ b/src/assets/fishbone.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/tower.svg b/src/assets/tower.svg
new file mode 100644
index 0000000..8277339
--- /dev/null
+++ b/src/assets/tower.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/users.svg b/src/assets/users.svg
new file mode 100644
index 0000000..2dfc863
--- /dev/null
+++ b/src/assets/users.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/components/FlashMessage.tsx b/src/components/FlashMessage.tsx
index 8a8187d..19b221c 100644
--- a/src/components/FlashMessage.tsx
+++ b/src/components/FlashMessage.tsx
@@ -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) {
diff --git a/src/layouts/Dashboard/Sidebar.tsx b/src/layouts/Dashboard/Sidebar.tsx
index ff327b6..7beede1 100644
--- a/src/layouts/Dashboard/Sidebar.tsx
+++ b/src/layouts/Dashboard/Sidebar.tsx
@@ -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,
},
],
},
diff --git a/src/pages/Backbone.tsx b/src/pages/Backbone.tsx
index c1ec796..67690f5 100644
--- a/src/pages/Backbone.tsx
+++ b/src/pages/Backbone.tsx
@@ -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[] = [
+ const columns: ColumnDef[] = 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 }) => (
<>
-
+
>
),
},
- ];
+ ], []);
+
+ useEffect(() => {
+ if (cacheExpired()) {
+ (async () => {
+ await getAll();
+ })();
+ }
+ }, []);
return (
@@ -73,7 +83,7 @@ export default function Backbone() {
Backbone
- }>
+ } href="/backbone/create">
Add New
@@ -87,12 +97,15 @@ export default function Backbone() {
-
-
-
+ {/* */}
+
+
+
+ List Backbone
+
+
+
+
diff --git a/src/pages/Backbone/CreateOrEdit.tsx b/src/pages/Backbone/CreateOrEdit.tsx
new file mode 100644
index 0000000..527ee8b
--- /dev/null
+++ b/src/pages/Backbone/CreateOrEdit.tsx
@@ -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 (
+
+
+
+
+
+
+
+ {IS_EDIT_PAGE ? `Edit Backbone - ${pid}` : 'Create Backbone'}
+
+
+
+
+
+
+
+
+ Backbone
+
+ {IS_EDIT_PAGE ? pid : 'Create'}
+
+
+
+
+
+
+
+
+ Fill the form
+ }
+ >
+ {IS_EDIT_PAGE ? "Save Changes" : "Create"}
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/pages/Backbone/Form.tsx b/src/pages/Backbone/Form.tsx
new file mode 100644
index 0000000..12c029e
--- /dev/null
+++ b/src/pages/Backbone/Form.tsx
@@ -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("");
+ const [deviceEnd, setDeviceEnd] = useState("");
+
+ 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 (
+
+
+
+
+
+
+
+
+
+ Device Start
+
+
+
+
+
+ Device End
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/pages/Towers.tsx b/src/pages/Towers.tsx
index 78eb1ed..43c7826 100644
--- a/src/pages/Towers.tsx
+++ b/src/pages/Towers.tsx
@@ -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[] = [
+ const columns: ColumnDef[] = 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 }) => (
<>
-
+
>
)
}
- ];
+ ], []);
+
+ useEffect(() => {
+ if (cacheExpired()) {
+ (async () => {
+ await getAll();
+ })();
+ }
+ }, []);
return (
@@ -71,7 +73,7 @@ export default function Towers() {
Towers
- }>
+ }>
Add New
@@ -85,12 +87,15 @@ export default function Towers() {
-
-
-
+
+
+
+
+ List Towers
+
+
+
+
diff --git a/src/pages/Towers/CreateOrEdit.tsx b/src/pages/Towers/CreateOrEdit.tsx
new file mode 100644
index 0000000..81c56c8
--- /dev/null
+++ b/src/pages/Towers/CreateOrEdit.tsx
@@ -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 (
+
+
+
+
+
+
+
+ {IS_EDIT_PAGE ? `Edit Tower - ${pid}` : 'Create Tower'}
+
+
+
+
+
+
+
+
+ Towers
+
+ {IS_EDIT_PAGE ? pid : 'Create'}
+
+
+
+
+
+
+
+
+ Fill the form {tower?.tower_code}
+ }
+ >
+ {IS_EDIT_PAGE ? "Save Changes" : "Create"}
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/pages/Towers/Form.tsx b/src/pages/Towers/Form.tsx
new file mode 100644
index 0000000..d99087f
--- /dev/null
+++ b/src/pages/Towers/Form.tsx
@@ -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("");
+
+ useEffect(() => {
+ (async () => {
+ await getDeviceSelect();
+ })();
+
+ if (tower) {
+ setDevice(tower.device_id as string);
+ }
+
+ const clear = async () => {
+ await clearTower();
+ };
+
+ return () => {
+ clear();
+ };
+ }, [tower]);
+
+ return (
+
+
+
+
+
+
+
+ Device
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/repositories/backboneRepository.ts b/src/repositories/backboneRepository.ts
new file mode 100644
index 0000000..6e96deb
--- /dev/null
+++ b/src/repositories/backboneRepository.ts
@@ -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>("/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>(`/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>("/api/v1/backbones", data);
+ return response.data;
+ },
+
+ async updateBackbone(id: string, data: Backbone) {
+ const response = await api.put>(`/api/v1/backbones/${id}`, data);
+ return response.data;
+ },
+
+ async getDeviceSelect() {
+ // const response = await api.get>("/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",
+ }
+ ]);
+ },
+};
diff --git a/src/repositories/deviceRepository.ts b/src/repositories/deviceRepository.ts
index a8454fa..cf6ce76 100644
--- a/src/repositories/deviceRepository.ts
+++ b/src/repositories/deviceRepository.ts
@@ -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",
}
])
},
diff --git a/src/repositories/towerRepository.ts b/src/repositories/towerRepository.ts
new file mode 100644
index 0000000..8d93f9f
--- /dev/null
+++ b/src/repositories/towerRepository.ts
@@ -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>("/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>(`/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>("/api/v1/towers", data);
+ return response.data;
+ },
+
+ async updateTower(id: string, data: Tower) {
+ const response = await api.put>(`/api/v1/towers/${id}`, data);
+ return response.data;
+ },
+
+ async getDeviceSelect() {
+ // const response = await api.get>("/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",
+ }
+ ]);
+ }
+};
diff --git a/src/routes/index.tsx b/src/routes/index.tsx
index 1edbe3f..944edbd 100644
--- a/src/routes/index.tsx
+++ b/src/routes/index.tsx
@@ -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() {
}>
- } />
+ } />
}>
} />
@@ -37,6 +38,12 @@ export default function Routes() {
} />
} />
+
+ } />
+ } />
+
+ } />
+ } />
diff --git a/src/stores/backboneStore.ts b/src/stores/backboneStore.ts
new file mode 100644
index 0000000..55a3631
--- /dev/null
+++ b/src/stores/backboneStore.ts
@@ -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()(
+ 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
+ ) => {
+ 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
+ ) => {
+ 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) }
+ )
+);
diff --git a/src/stores/towerStore.ts b/src/stores/towerStore.ts
new file mode 100644
index 0000000..84afedf
--- /dev/null
+++ b/src/stores/towerStore.ts
@@ -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()(
+ 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
+ ) => {
+ 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
+ ) => {
+ 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) }
+ )
+);
diff --git a/src/theme/index.tsx b/src/theme/index.tsx
index 394f373..d4701df 100644
--- a/src/theme/index.tsx
+++ b/src/theme/index.tsx
@@ -46,7 +46,7 @@ export default function ThemeCustomization({
}
}
- const theme = Palette(ThemeMode.LIGHT);
+ const theme = Palette(themeMode);
const themeTypography: TypographyVariantsOptions =
useMemo(
diff --git a/src/types/backbone.types.ts b/src/types/backbone.types.ts
new file mode 100644
index 0000000..efcdc3c
--- /dev/null
+++ b/src/types/backbone.types.ts
@@ -0,0 +1,53 @@
+import { ResponseApi } from "./response.types";
+import { CacheState, ErrorState, SelectState } from "./store.types";
+
+export interface BackboneRepositoryProps {
+ getAll: () => Promise;
+ getBackboneById: (id: string) => Promise;
+ createBackbone: (backbone: BackboneRequest) => Promise, "status">>;
+ updateBackbone: (id: string, backbone: BackboneRequest) => Promise, "status">>;
+ getDeviceSelect: () => Promise;
+}
+
+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 State
+ * =============
+ */
+export interface BackboneState extends ErrorState, CacheState {
+ data?: BackboneDataTable[];
+ backbone?: Backbone | null;
+ isLoading: boolean;
+ lastUpdate: number | null;
+ deviceSelect: SelectState[] | null;
+ getDeviceSelect: () => Promise;
+ getAll: () => Promise;
+ find: (id: string) => Promise;
+ create: (backbone: BackboneRequest) => Promise;
+ update: (id: string, backbone: BackboneRequest) => Promise;
+ clearBackbone: () => void;
+}
+
+export interface BackboneDataTable extends Pick {
+ device_start: string;
+ device_end: string;
+ total_fishbone: string | number;
+}
+
+export interface IGetDeviceSelect {
+ id: string;
+ device_code: string;
+}
\ No newline at end of file
diff --git a/src/types/tower.types.ts b/src/types/tower.types.ts
new file mode 100644
index 0000000..b7108c8
--- /dev/null
+++ b/src/types/tower.types.ts
@@ -0,0 +1,50 @@
+import { ResponseApi } from "./response.types";
+import { CacheState, ErrorState, SelectState } from "./store.types";
+
+export interface TowerRepositoryProps {
+ getAll: () => Promise;
+ getTowerById: (id: string) => Promise;
+ createTower: (tower: TowerRequest) => Promise, "status">>;
+ updateTower: (id: string, tower: TowerRequest) => Promise, "status">>;
+ getDeviceSelect: () => Promise;
+}
+
+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 State
+ * =============
+ */
+export interface TowerState extends ErrorState, CacheState {
+ data?: TowerDataTable[];
+ tower?: Tower | null;
+ isLoading: boolean;
+ lastUpdate: number | null;
+ deviceSelect: SelectState[] | null;
+ getDeviceSelect: () => Promise;
+ getAll: () => Promise;
+ find: (id: string) => Promise;
+ create: (tower: TowerRequest) => Promise;
+ update: (id: string, tower: TowerRequest) => Promise;
+ clearTower: () => void;
+}
+
+export interface TowerDataTable extends Pick {
+ address: string;
+ device_code: string;
+}
+
+export interface IGetDeviceSelect {
+ id: string;
+ device_code: string;
+}
\ No newline at end of file