feat: imp mock api for user management
This commit is contained in:
parent
ed88b1ed27
commit
ce874def91
|
|
@ -6,9 +6,15 @@ import { AuthState } from "../types/auth.types";
|
||||||
import { FishboneState } from "../types/fishbone.types";
|
import { FishboneState } from "../types/fishbone.types";
|
||||||
import { TowerState } from "../types/tower.types";
|
import { TowerState } from "../types/tower.types";
|
||||||
import { BackboneState } from "../types/backbone.types";
|
import { BackboneState } from "../types/backbone.types";
|
||||||
|
import { UserState } from "../types/users.types";
|
||||||
|
|
||||||
interface FlashMessageProps {
|
interface FlashMessageProps {
|
||||||
store: () => DeviceState | AuthState | FishboneState | TowerState | BackboneState;
|
store: () => DeviceState
|
||||||
|
| AuthState
|
||||||
|
| FishboneState
|
||||||
|
| TowerState
|
||||||
|
| BackboneState
|
||||||
|
| UserState;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function FlashMessage(props: FlashMessageProps) {
|
export default function FlashMessage(props: FlashMessageProps) {
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ function getColorStyle({ variant, theme, color }: IconButtonStyleProps) {
|
||||||
};
|
};
|
||||||
case 'dashed':
|
case 'dashed':
|
||||||
return {
|
return {
|
||||||
backgroundColor: lighter,
|
backgroundColor: theme.palette.mode === 'dark' ? alpha(lighter, 0.85) : lighter,
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
color: dark,
|
color: dark,
|
||||||
borderColor: dark
|
borderColor: dark
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ const MENU = [
|
||||||
menus: [
|
menus: [
|
||||||
{
|
{
|
||||||
title: "User",
|
title: "User",
|
||||||
link: "/user",
|
link: "/users",
|
||||||
icon: Users,
|
icon: Users,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -19,12 +19,6 @@ import { useEffect, useMemo } from "react";
|
||||||
import FlashMessage from "../components/FlashMessage";
|
import FlashMessage from "../components/FlashMessage";
|
||||||
import { TowerDataTable } from "../types/tower.types";
|
import { TowerDataTable } from "../types/tower.types";
|
||||||
|
|
||||||
interface UserData {
|
|
||||||
tower_code: string;
|
|
||||||
device: string;
|
|
||||||
address: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function Towers() {
|
export default function Towers() {
|
||||||
useTitle("Towers");
|
useTitle("Towers");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,97 @@
|
||||||
|
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 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";
|
||||||
|
import AddIcon from "@mui/icons-material/Add";
|
||||||
|
import { Home } from "@mui/icons-material";
|
||||||
|
import ReactTable from "../components/ReactTable";
|
||||||
|
import { ColumnDef } from "@tanstack/react-table";
|
||||||
|
import IconButton from "../components/IconButton";
|
||||||
|
import { Edit } from "iconsax-react";
|
||||||
|
import useTitle from "../hooks/useTitle";
|
||||||
|
import { useUserStore } from "../stores/userStore";
|
||||||
|
import { useEffect, useMemo } from "react";
|
||||||
|
import FlashMessage from "../components/FlashMessage";
|
||||||
|
import { UserDataTable } from "../types/users.types";
|
||||||
|
|
||||||
|
export default function Users() {
|
||||||
|
useTitle("Users");
|
||||||
|
|
||||||
|
const { data, cacheExpired, getAll } = useUserStore();
|
||||||
|
|
||||||
|
// Define columns
|
||||||
|
const columns: ColumnDef<UserDataTable>[] = useMemo(() => [
|
||||||
|
{
|
||||||
|
header: "Name",
|
||||||
|
accessorKey: "name",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: "Username",
|
||||||
|
accessorKey: "username",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: "Role",
|
||||||
|
accessorKey: "role",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: '',
|
||||||
|
accessorKey: "action",
|
||||||
|
enableSorting: false,
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<>
|
||||||
|
<IconButton variant="text" color="secondary" href={`/users/${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}>
|
||||||
|
<Grid size="grow">
|
||||||
|
<Typography variant="h3">Users</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid size="auto">
|
||||||
|
<Button variant="contained" href="/users/create" startIcon={<AddIcon color="inherit" />}>
|
||||||
|
Add New
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
<Grid size={12}>
|
||||||
|
<Breadcrumbs aria-label="breadcrumb">
|
||||||
|
<Link underline="hover" color="inherit" href="/dashboard">
|
||||||
|
<Home />
|
||||||
|
</Link>
|
||||||
|
<Typography sx={{ color: "text.primary" }}>Users</Typography>
|
||||||
|
</Breadcrumbs>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<FlashMessage store={useUserStore} />
|
||||||
|
|
||||||
|
<Card>
|
||||||
|
<Box sx={{ p: 2 }}>
|
||||||
|
<Typography variant="h5">List Users</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 { useUserStore } from "../../stores/userStore";
|
||||||
|
import { UserRequest } from "../../types/users.types";
|
||||||
|
import FlashMessage from "../../components/FlashMessage";
|
||||||
|
|
||||||
|
export default function UserCreateOrEdit() {
|
||||||
|
useTitle("Users");
|
||||||
|
const { pid } = useParams();
|
||||||
|
const { update, create, isLoading, find, clearUser } = useUserStore();
|
||||||
|
|
||||||
|
const IS_EDIT_PAGE: boolean = !!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 UserRequest;
|
||||||
|
|
||||||
|
if (IS_EDIT_PAGE) {
|
||||||
|
await update(pid!, data);
|
||||||
|
} else {
|
||||||
|
await create(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (pid) {
|
||||||
|
(async () => {
|
||||||
|
await find(pid);
|
||||||
|
})()
|
||||||
|
} else {
|
||||||
|
(async () => {
|
||||||
|
await clearUser()
|
||||||
|
})()
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
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="/users">
|
||||||
|
<Back variant="Outline" color="currentColor" />
|
||||||
|
</IconButton>
|
||||||
|
<Typography variant="h3">{IS_EDIT_PAGE ? `Edit User - ${pid}` : 'Create User'}</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="/users">
|
||||||
|
<Typography sx={{ color: "text.primary" }}>Users</Typography>
|
||||||
|
</Link>
|
||||||
|
<Typography sx={{ color: "text.primary" }}>{IS_EDIT_PAGE ? pid : 'Create'}</Typography>
|
||||||
|
</Breadcrumbs>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<FlashMessage store={useUserStore} />
|
||||||
|
|
||||||
|
<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 isEdit={IS_EDIT_PAGE} />
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,115 @@
|
||||||
|
import FormControl from "@mui/material/FormControl";
|
||||||
|
import Grid from "@mui/material/Grid2";
|
||||||
|
import IconButton from "@mui/material/IconButton";
|
||||||
|
import InputAdornment from "@mui/material/InputAdornment";
|
||||||
|
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 { useUserStore } from "../../stores/userStore";
|
||||||
|
import { Visibility, VisibilityOff } from "@mui/icons-material";
|
||||||
|
|
||||||
|
export default function Form({ isEdit }: { isEdit: boolean }) {
|
||||||
|
const { user, clearUser, getRoleSelect, roleSelect } = useUserStore();
|
||||||
|
const [role, setRole] = useState<string>("");
|
||||||
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
|
|
||||||
|
const handleClickShowPassword = () => setShowPassword((show) => !show);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
await getRoleSelect();
|
||||||
|
})();
|
||||||
|
|
||||||
|
if (user) {
|
||||||
|
setRole(user.role_id as string);
|
||||||
|
}
|
||||||
|
|
||||||
|
const clear = async () => {
|
||||||
|
await clearUser();
|
||||||
|
};
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
clear();
|
||||||
|
};
|
||||||
|
}, [user]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Grid container spacing={2}>
|
||||||
|
<Grid size={{ md: isEdit ? 6 : 4, xs: 12 }}>
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
name="name"
|
||||||
|
label="Name"
|
||||||
|
variant="outlined"
|
||||||
|
value={user?.name}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid size={{ md: isEdit ? 6 : 4, xs: 12 }}>
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
name="username"
|
||||||
|
label="Username"
|
||||||
|
variant="outlined"
|
||||||
|
type="text"
|
||||||
|
value={user?.username}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid size={{ md: isEdit ? 12 : 4, xs: 12 }}>
|
||||||
|
<FormControl fullWidth>
|
||||||
|
<InputLabel id="device-label">Role</InputLabel>
|
||||||
|
<Select
|
||||||
|
name="device"
|
||||||
|
labelId="device-label"
|
||||||
|
label="Device"
|
||||||
|
value={role}
|
||||||
|
onChange={(e) => setRole(e.target.value)}
|
||||||
|
>
|
||||||
|
{roleSelect?.map((role, key) => (
|
||||||
|
<MenuItem key={key} value={role.value}>
|
||||||
|
{role.label.toUpperCase()}
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
{!isEdit ? (
|
||||||
|
<Grid size={{ md: 12, xs: 12 }}>
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
name="password"
|
||||||
|
label="Password"
|
||||||
|
variant="outlined"
|
||||||
|
type={showPassword ? 'text' : 'password'}
|
||||||
|
value={user?.password}
|
||||||
|
slotProps={{
|
||||||
|
input: {
|
||||||
|
endAdornment: (
|
||||||
|
<InputAdornment position="start">
|
||||||
|
<IconButton
|
||||||
|
aria-label={
|
||||||
|
showPassword
|
||||||
|
? "hide the password"
|
||||||
|
: "display the password"
|
||||||
|
}
|
||||||
|
onClick={handleClickShowPassword}
|
||||||
|
edge="end"
|
||||||
|
>
|
||||||
|
{showPassword ? <VisibilityOff /> : <Visibility />}
|
||||||
|
</IconButton>
|
||||||
|
</InputAdornment>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
) : (
|
||||||
|
""
|
||||||
|
)}
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
import { createAPI } from "../services/api";
|
||||||
|
import { UserRepositoryProps, User, IGetRoleSelect } from "../types/users.types";
|
||||||
|
import { ResponseApi } from "../types/response.types";
|
||||||
|
import { SelectState } from "../types/store.types";
|
||||||
|
|
||||||
|
const api = createAPI();
|
||||||
|
|
||||||
|
export const userRepository: UserRepositoryProps = {
|
||||||
|
async getAll() {
|
||||||
|
// const response = await api.get<ResponseApi<User[]>>("/api/v1/users");
|
||||||
|
|
||||||
|
// return response.data.data as User[];
|
||||||
|
|
||||||
|
return Promise.resolve([
|
||||||
|
{
|
||||||
|
id: "1",
|
||||||
|
name: "John Doe",
|
||||||
|
username: "johndoe",
|
||||||
|
role: "Admin",
|
||||||
|
created_at: "2024-01-01",
|
||||||
|
updated_at: "2024-01-01",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "2",
|
||||||
|
name: "Jane Doe",
|
||||||
|
username: "janedoe",
|
||||||
|
role: "Admin",
|
||||||
|
created_at: "2024-01-01",
|
||||||
|
updated_at: "2024-01-01",
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
|
||||||
|
async getUserById(id: string) {
|
||||||
|
// const response = await api.get<ResponseApi<User>>(`/api/v1/users/${id}`);
|
||||||
|
|
||||||
|
// return response.data.data as User;
|
||||||
|
return Promise.resolve({
|
||||||
|
id: "1",
|
||||||
|
name: "John Doe",
|
||||||
|
username: "johndoe",
|
||||||
|
role_id: "1",
|
||||||
|
created_at: "2024-01-01",
|
||||||
|
updated_at: "2024-01-01",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
async createUser(user) {
|
||||||
|
const response = await api.post<ResponseApi>("/api/v1/users", user);
|
||||||
|
|
||||||
|
return response.data.status as unknown as Pick<ResponseApi, "status">;
|
||||||
|
},
|
||||||
|
async updateUser(id, user) {
|
||||||
|
const response = await api.put<ResponseApi>(`/api/v1/users/${id}`, user);
|
||||||
|
|
||||||
|
return response.data.status as unknown as Pick<ResponseApi, "status">;
|
||||||
|
},
|
||||||
|
|
||||||
|
async getRoleSelect() {
|
||||||
|
// const response = await api.get<ResponseApi<SelectState[]>>("/api/v1/users/role-select");
|
||||||
|
|
||||||
|
// return (response.data.data as IGetRoleSelect[])?.map((i: IGetRoleSelect) => ({label: i.role_name, value: i.id})) as SelectState[];
|
||||||
|
return Promise.resolve([
|
||||||
|
{
|
||||||
|
label: "Admin",
|
||||||
|
value: "1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Teknisi",
|
||||||
|
value: "2",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Head",
|
||||||
|
value: "3",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -12,6 +12,8 @@ import DeviceCreateOrEdit from "../pages/Devices/CreateOrEdit";
|
||||||
import FishboneCreateOrEdit from "../pages/Fishbone/CreateOrEdit";
|
import FishboneCreateOrEdit from "../pages/Fishbone/CreateOrEdit";
|
||||||
import TowerCreateOrEdit from "../pages/Towers/CreateOrEdit";
|
import TowerCreateOrEdit from "../pages/Towers/CreateOrEdit";
|
||||||
import BackboneCreateOrEdit from "../pages/Backbone/CreateOrEdit";
|
import BackboneCreateOrEdit from "../pages/Backbone/CreateOrEdit";
|
||||||
|
import Users from "../pages/Users";
|
||||||
|
import UserCreateOrEdit from "../pages/Users/CreateOrEdit";
|
||||||
|
|
||||||
export default function Routes() {
|
export default function Routes() {
|
||||||
return (
|
return (
|
||||||
|
|
@ -31,6 +33,7 @@ export default function Routes() {
|
||||||
<Route path="/fishbone" element={<Fishbone />} />
|
<Route path="/fishbone" element={<Fishbone />} />
|
||||||
<Route path="/devices" element={<Devices />} />
|
<Route path="/devices" element={<Devices />} />
|
||||||
<Route path="/towers" element={<Towers />} />
|
<Route path="/towers" element={<Towers />} />
|
||||||
|
<Route path="/users" element={<Users />} />
|
||||||
|
|
||||||
{/* Create Page */}
|
{/* Create Page */}
|
||||||
<Route path="/devices/:pid" element={<DeviceCreateOrEdit />} />
|
<Route path="/devices/:pid" element={<DeviceCreateOrEdit />} />
|
||||||
|
|
@ -44,6 +47,9 @@ export default function Routes() {
|
||||||
|
|
||||||
<Route path="/backbone/:pid" element={<BackboneCreateOrEdit />} />
|
<Route path="/backbone/:pid" element={<BackboneCreateOrEdit />} />
|
||||||
<Route path="/backbone/create" element={<BackboneCreateOrEdit />} />
|
<Route path="/backbone/create" element={<BackboneCreateOrEdit />} />
|
||||||
|
|
||||||
|
<Route path="/users/:pid" element={<UserCreateOrEdit />} />
|
||||||
|
<Route path="/users/create" element={<UserCreateOrEdit />} />
|
||||||
</Route>
|
</Route>
|
||||||
|
|
||||||
</Route>
|
</Route>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,125 @@
|
||||||
|
import { create } from "zustand";
|
||||||
|
import { userRepository } from "../repositories/userRepository";
|
||||||
|
import { persist } from "zustand/middleware";
|
||||||
|
import { createJSONStorage } from "zustand/middleware";
|
||||||
|
import { immer } from "zustand/middleware/immer";
|
||||||
|
import { secureStorage } from "./storage";
|
||||||
|
import { UserRequest, UserState } from "../types/users.types";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
|
export const useUserStore = create<UserState>()(
|
||||||
|
persist(
|
||||||
|
immer<UserState>((set, get) => ({
|
||||||
|
data: [],
|
||||||
|
user: null,
|
||||||
|
isLoading: false,
|
||||||
|
error: null,
|
||||||
|
lastUpdate: null,
|
||||||
|
roleSelect: [],
|
||||||
|
getAll: async () => {
|
||||||
|
set({ isLoading: true });
|
||||||
|
try {
|
||||||
|
const users = await userRepository.getAll();
|
||||||
|
set({ data: users, 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 user = await userRepository.getUserById(id);
|
||||||
|
|
||||||
|
set({ user: user });
|
||||||
|
} 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 (user: UserRequest) => {
|
||||||
|
console.log(user);
|
||||||
|
|
||||||
|
set({ isLoading: true });
|
||||||
|
try {
|
||||||
|
await userRepository.createUser(user);
|
||||||
|
} 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, user: UserRequest) => {
|
||||||
|
set({ isLoading: true });
|
||||||
|
try {
|
||||||
|
await userRepository.updateUser(id, user);
|
||||||
|
} 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 });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getRoleSelect: async () => {
|
||||||
|
set({ isLoading: true });
|
||||||
|
try {
|
||||||
|
const roles = await userRepository.getRoleSelect();
|
||||||
|
set({ roleSelect: roles });
|
||||||
|
} 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 }),
|
||||||
|
clearUser: () => {
|
||||||
|
set({ user: 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: "users", storage: createJSONStorage(() => secureStorage) }
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
@ -32,7 +32,7 @@ export default function Alert(theme: Theme) {
|
||||||
MuiAlert: {
|
MuiAlert: {
|
||||||
styleOverrides: {
|
styleOverrides: {
|
||||||
root: {
|
root: {
|
||||||
color: theme.palette.text.primary,
|
color: theme.palette.mode === 'dark' ? theme.palette.primary.main : theme.palette.text.primary,
|
||||||
fontSize: '0.875rem'
|
fontSize: '0.875rem'
|
||||||
},
|
},
|
||||||
icon: {
|
icon: {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ interface ResponseApiStatus {
|
||||||
description: string;
|
description: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ResponseApi<T> {
|
export interface ResponseApi<T = null> {
|
||||||
status: ResponseApiStatus;
|
status: ResponseApiStatus;
|
||||||
message: string;
|
message: string;
|
||||||
data?: T | [] | null;
|
data?: T | [] | null;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
import { ResponseApi } from "./response.types";
|
||||||
|
import { CacheState, ErrorState, SelectState } from "./store.types";
|
||||||
|
|
||||||
|
export interface UserRepositoryProps {
|
||||||
|
getAll: () => Promise<UserDataTable[] | []>;
|
||||||
|
getUserById: (id: string) => Promise<User>;
|
||||||
|
createUser: (user: UserRequest) => Promise<Pick<ResponseApi, "status">>;
|
||||||
|
updateUser: (id: string, user: UserRequest) => Promise<Pick<ResponseApi, "status">>;
|
||||||
|
getRoleSelect: () => Promise<SelectState[] | null>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface User {
|
||||||
|
id: string;
|
||||||
|
role_id: string | number;
|
||||||
|
name: string;
|
||||||
|
username: string;
|
||||||
|
password?: string;
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UserRequest extends Pick<User, "name" & "username" & "password"> {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User State
|
||||||
|
* =============
|
||||||
|
*/
|
||||||
|
export interface UserState extends ErrorState, CacheState {
|
||||||
|
data?: UserDataTable[];
|
||||||
|
user?: User | null;
|
||||||
|
isLoading: boolean;
|
||||||
|
lastUpdate: number | null;
|
||||||
|
roleSelect: SelectState[] | null;
|
||||||
|
getRoleSelect: () => Promise<void>;
|
||||||
|
getAll: () => Promise<void>;
|
||||||
|
find: (id: string) => Promise<void>;
|
||||||
|
create: (user: UserRequest) => Promise<void>;
|
||||||
|
update: (id: string, user: UserRequest) => Promise<void>;
|
||||||
|
clearUser: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UserDataTable extends Pick<User, "id" | "name" | "username"> {
|
||||||
|
role: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IGetRoleSelect {
|
||||||
|
id: string;
|
||||||
|
role_name: string;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue