done
This commit is contained in:
parent
cc51d06c97
commit
8ef2970c25
|
@ -22,7 +22,8 @@
|
||||||
"react": "^18",
|
"react": "^18",
|
||||||
"react-dom": "^18",
|
"react-dom": "^18",
|
||||||
"react-redux": "^9.1.2",
|
"react-redux": "^9.1.2",
|
||||||
"react-tooltip": "^5.28.0"
|
"react-tooltip": "^5.28.0",
|
||||||
|
"redux-persist": "^6.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^20",
|
"@types/node": "^20",
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 14 MiB |
Binary file not shown.
After Width: | Height: | Size: 27 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
|
@ -2,7 +2,7 @@
|
||||||
import { useAppSelector } from "@/lib/hooks";
|
import { useAppSelector } from "@/lib/hooks";
|
||||||
import { useGetAttendanceRangeQuery, useGetMonthlyAttendanceQuery, useGetOrganizationAttendanceQuery } from "@/services/api";
|
import { useGetAttendanceRangeQuery, useGetMonthlyAttendanceQuery, useGetOrganizationAttendanceQuery } from "@/services/api";
|
||||||
import { ExclamationCircleIcon } from "@heroicons/react/24/outline";
|
import { ExclamationCircleIcon } from "@heroicons/react/24/outline";
|
||||||
import { BarChart, ChartsTooltip, LineChart, pieArcClasses, pieArcLabelClasses, PieChart } from "@mui/x-charts";
|
import { BarChart, ChartsAxisTooltipContent, ChartsTooltip, LineChart, pieArcClasses, pieArcLabelClasses, PieChart } from "@mui/x-charts";
|
||||||
import { ChartsNoDataOverlay } from "@mui/x-charts/ChartsOverlay";
|
import { ChartsNoDataOverlay } from "@mui/x-charts/ChartsOverlay";
|
||||||
import { formatDate } from "date-fns";
|
import { formatDate } from "date-fns";
|
||||||
|
|
||||||
|
@ -74,19 +74,22 @@ export default function AbsensiPage() {
|
||||||
<div className="col-span-8 bg-white py-4 pl-4 rounded-lg max-h-[420px] flex flex-col">
|
<div className="col-span-8 bg-white py-4 pl-4 rounded-lg max-h-[420px] flex flex-col">
|
||||||
<div className="text-xl font-bold">Data Kehadiran Karyawan Setiap Perusahaan</div>
|
<div className="text-xl font-bold">Data Kehadiran Karyawan Setiap Perusahaan</div>
|
||||||
<div className="flex-1 min-h-[300px]">
|
<div className="flex-1 min-h-[300px]">
|
||||||
{attendanceSummary && <BarChart className="w-full" dataset={attendanceSummary} series={[
|
<BarChart className="w-full" dataset={attendanceSummary ?? []} series={[
|
||||||
{dataKey: "count", label: (v) => v === "tooltip" ? "Attendance" : undefined!, color: "#2385DE", stack: "attendance", stackOffset: "expand", valueFormatter: (v) => v + " mandays"},
|
{dataKey: "count", label: (v) => undefined!, color: "#2385DE", stack: "attendance", stackOffset: "expand", valueFormatter: (v) => undefined!},
|
||||||
{dataKey: "absent", label: (v) => v === "tooltip" ? "Absent" : undefined!, color: "#F7B500", stack: "attendance", stackOffset: "expand", valueFormatter: (v) => v + " mandays"},
|
{dataKey: "absent", label: (v) => undefined!, color: "#F7B500", stack: "attendance", stackOffset: "expand", valueFormatter: (v) => undefined!},
|
||||||
]} xAxis={[
|
]} xAxis={[
|
||||||
{dataKey: "organization_code", label: "Nama Perusahaan", scaleType: "band", valueFormatter: (v, context) => context.location === "tooltip" ? attendanceSummary.find(e => e.organization_code === v)?.organization_name : v}
|
{dataKey: "organization_code", label: "Nama Perusahaan", scaleType: "band", valueFormatter: (v, context) => context.location === "tooltip" ? attendanceSummary?.find(e => e.organization_code === v)?.organization_name + ` (${Math.round(attendanceSummary?.find(e => e.organization_code === v)?.count!/attendanceSummary?.find(e => e.organization_code === v)?.workdays! * 100)}%)` : v }
|
||||||
]}
|
]}
|
||||||
yAxis={[
|
yAxis={[
|
||||||
{dataKey: "count",scaleType: "linear", valueFormatter: (v) => (v*100)+"%"}
|
{dataKey: "count",scaleType: "linear", valueFormatter: (v) => (v*100)+"%"}
|
||||||
]}
|
]}
|
||||||
|
tooltip={{
|
||||||
|
trigger: "axis",
|
||||||
|
}}
|
||||||
slots={{
|
slots={{
|
||||||
noDataOverlay: ()=> <ChartsNoDataOverlay message="Data belum tersedia" />,
|
noDataOverlay: ()=> <ChartsNoDataOverlay message="Data belum tersedia" />,
|
||||||
}}
|
}}
|
||||||
/>}
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-span-4 bg-white rounded-lg py-4 pl-4 max-h-[420px] flex flex-col">
|
<div className="col-span-4 bg-white rounded-lg py-4 pl-4 max-h-[420px] flex flex-col">
|
||||||
|
|
|
@ -14,6 +14,7 @@ import { useAppDispatch, useAppSelector } from "@/lib/hooks";
|
||||||
import { setFilter } from "@/lib/slice/filter";
|
import { setFilter } from "@/lib/slice/filter";
|
||||||
import { format } from "date-fns";
|
import { format } from "date-fns";
|
||||||
import { useGetFilterOptionsQuery } from "@/services/api";
|
import { useGetFilterOptionsQuery } from "@/services/api";
|
||||||
|
import { NeedLoginRoute } from "../RouteGuard";
|
||||||
|
|
||||||
|
|
||||||
export default function DashboardLayout({children}:{children: React.ReactNode}) {
|
export default function DashboardLayout({children}:{children: React.ReactNode}) {
|
||||||
|
@ -25,6 +26,7 @@ export default function DashboardLayout({children}:{children: React.ReactNode})
|
||||||
const [region, setRegion] = React.useState("");
|
const [region, setRegion] = React.useState("");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<NeedLoginRoute role="Dashboard">
|
||||||
<div className="flex min-h-screen">
|
<div className="flex min-h-screen">
|
||||||
<div className="w-80 flex flex-col sticky h-screen">
|
<div className="w-80 flex flex-col sticky h-screen">
|
||||||
<div className="w-80">
|
<div className="w-80">
|
||||||
|
@ -63,7 +65,7 @@ export default function DashboardLayout({children}:{children: React.ReactNode})
|
||||||
</svg>
|
</svg>
|
||||||
<span className="font-semibold text-base">HR Cost</span>
|
<span className="font-semibold text-base">HR Cost</span>
|
||||||
</Link>
|
</Link>
|
||||||
<Link className="mt-auto flex text-white gap-4 px-8 py-4 hover:bg-white hover:text-[#664228] items-center " href="/dashboard/hr-cost">
|
<Link className="mt-auto flex text-white gap-4 px-8 py-4 hover:bg-white hover:text-[#664228] items-center " href="/logout">
|
||||||
<svg width="18" height="19" viewBox="0 0 18 19" fill="none" xmlns="http://www.w3.org/2000/svg" className="fill-current">
|
<svg width="18" height="19" viewBox="0 0 18 19" fill="none" xmlns="http://www.w3.org/2000/svg" className="fill-current">
|
||||||
<path d="M2 18.2554C1.45 18.2554 0.979167 18.0595 0.5875 17.6679C0.195833 17.2762 0 16.8054 0 16.2554V2.25537C0 1.70537 0.195833 1.23454 0.5875 0.842871C0.979167 0.451204 1.45 0.255371 2 0.255371H9V2.25537H2V16.2554H9V18.2554H2ZM13 14.2554L11.625 12.8054L14.175 10.2554H6V8.25537H14.175L11.625 5.70537L13 4.25537L18 9.25537L13 14.2554Z"/>
|
<path d="M2 18.2554C1.45 18.2554 0.979167 18.0595 0.5875 17.6679C0.195833 17.2762 0 16.8054 0 16.2554V2.25537C0 1.70537 0.195833 1.23454 0.5875 0.842871C0.979167 0.451204 1.45 0.255371 2 0.255371H9V2.25537H2V16.2554H9V18.2554H2ZM13 14.2554L11.625 12.8054L14.175 10.2554H6V8.25537H14.175L11.625 5.70537L13 4.25537L18 9.25537L13 14.2554Z"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
@ -148,5 +150,6 @@ export default function DashboardLayout({children}:{children: React.ReactNode})
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</NeedLoginRoute>
|
||||||
)
|
)
|
||||||
}
|
}
|
|
@ -1,23 +1,23 @@
|
||||||
"use client";
|
"use client";
|
||||||
import { LocalizationProvider } from "@mui/x-date-pickers";
|
import { LocalizationProvider } from "@mui/x-date-pickers";
|
||||||
import { AdapterDateFns } from "@mui/x-date-pickers/AdapterDateFnsV3";
|
import { AdapterDateFns } from "@mui/x-date-pickers/AdapterDateFnsV3";
|
||||||
import { makeStore, AppStore } from '../lib/store'
|
import { AppStore, persistor, store } from '../lib/store'
|
||||||
import { useRef } from "react";
|
import { useRef } from "react";
|
||||||
import { Provider } from "react-redux";
|
import { Provider } from "react-redux";
|
||||||
import { StyledEngineProvider } from "@mui/material";
|
import { StyledEngineProvider } from "@mui/material";
|
||||||
|
import { PersistGate } from "redux-persist/integration/react";
|
||||||
|
import { GuardedRoute } from "./RouteGuard";
|
||||||
export default function AppProvider({children}:{children: React.ReactNode}) {
|
export default function AppProvider({children}:{children: React.ReactNode}) {
|
||||||
const storeRef = useRef<AppStore>()
|
|
||||||
if (!storeRef.current) {
|
|
||||||
// Create the store instance the first time this renders
|
|
||||||
storeRef.current = makeStore()
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LocalizationProvider dateAdapter={AdapterDateFns}>
|
<LocalizationProvider dateAdapter={AdapterDateFns}>
|
||||||
<Provider store={storeRef.current}>
|
<Provider store={store}>
|
||||||
|
<PersistGate loading={null} persistor={persistor}>
|
||||||
<StyledEngineProvider injectFirst>
|
<StyledEngineProvider injectFirst>
|
||||||
|
<GuardedRoute>
|
||||||
{children}
|
{children}
|
||||||
|
</GuardedRoute>
|
||||||
</StyledEngineProvider>
|
</StyledEngineProvider>
|
||||||
|
</PersistGate>
|
||||||
</Provider>
|
</Provider>
|
||||||
</LocalizationProvider>
|
</LocalizationProvider>
|
||||||
)
|
)
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
"use client";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import { useAppSelector } from "@/lib/hooks";
|
||||||
|
import { usePathname, useRouter } from "next/navigation";
|
||||||
|
|
||||||
|
// set route for disabled user to come, if user has login
|
||||||
|
const IF_LOGIN = ["/login", "/register"];
|
||||||
|
|
||||||
|
export function GuardedRoute({ children }: React.PropsWithChildren) {
|
||||||
|
const router = useRouter();
|
||||||
|
const pathname = usePathname();
|
||||||
|
|
||||||
|
const auth = useAppSelector((state) => state.auth);
|
||||||
|
console.log("GuardedRoute", auth, pathname);
|
||||||
|
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IGuardedRoute {
|
||||||
|
children: React.ReactNode;
|
||||||
|
role: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function NeedLoginRoute({ children, role }: IGuardedRoute) {
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const auth = useAppSelector((state) => state.auth);
|
||||||
|
|
||||||
|
if (auth.token && auth.roles.includes(role)) {
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
console.log("NeedLoginRoute", auth, role);
|
||||||
|
router.push("/login");
|
||||||
|
return;
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
"use client"
|
||||||
|
import Image from "next/image";
|
||||||
|
import background from "../../../public/images/background.jpg";
|
||||||
|
import loginImg from "../../../public/images/logo-login.png";
|
||||||
|
import midsuit from "../../../public/images/midsuit-login.png";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { useLoginMutation } from "@/services/api";
|
||||||
|
import { useAppDispatch } from "@/lib/hooks";
|
||||||
|
import { setRoles, setToken, setUser } from "@/lib/slice/auth";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
|
||||||
|
export default function LoginPage(){
|
||||||
|
const [login, {data: loginData, error: loginError}] = useLoginMutation();
|
||||||
|
const [username, setUsername] = useState("");
|
||||||
|
const [password, setPassword] = useState("");
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if(loginData){
|
||||||
|
dispatch(setUser(loginData.user))
|
||||||
|
dispatch(setToken(loginData.token))
|
||||||
|
dispatch(setRoles(loginData.roles))
|
||||||
|
router.push("/karyawan")
|
||||||
|
}
|
||||||
|
}, [loginData, loginError])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="relative flex flex-col items-center justify-center min-h-screen bg-black">
|
||||||
|
<Image src={background} layout="fill" objectFit="cover" className="absolute z-0 opacity-50" alt=""/>
|
||||||
|
<div className="relative z-10 flex flex-col items-center justify-center w-full h-full p-4 space-y-4">
|
||||||
|
<div className="flex flex-col items-center justify-center w-full max-w-sm px-12 pb-6 space-y-8 bg-white rounded-lg">
|
||||||
|
<div className="w-full flex flex-col space-y-1">
|
||||||
|
<div className="flex items-center justify-center w-full">
|
||||||
|
<Image src={loginImg} width={150} height={150} alt=""/>
|
||||||
|
</div>
|
||||||
|
<div className="text-2xl font-bold text-[#292929] w-full">HRM Dashboard</div>
|
||||||
|
<div className="text-sm text-[#9F9F9F] text-start w-full">Masukan Username & Password</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col space-y-4 w-full">
|
||||||
|
<input type="text" placeholder="Username" className="w-full px-6 py-4 border border-gray-300 rounded-lg" value={username} onChange={(e) => setUsername(e.target.value)}/>
|
||||||
|
<input type="password" placeholder="Password" className="w-full px-6 py-4 border border-gray-300 rounded-lg" value={password} onChange={(e) => setPassword(e.target.value)}/>
|
||||||
|
</div>
|
||||||
|
<div className="w-full flex flex-col space-y-1">
|
||||||
|
{loginError && <div className="text-xs text-red-500">{(loginError as any).data.message}</div>}
|
||||||
|
<button className="w-full px-6 py-4 text-white bg-[#A36A4D] rounded-lg"
|
||||||
|
onClick={async () => {
|
||||||
|
await login({username, password})
|
||||||
|
}}
|
||||||
|
>Log In</button>
|
||||||
|
<div className="flex gap-4 px-8 py-4 mt-4 items-center justify-center">
|
||||||
|
<div className="text-xs text-[#313678]">Powered by</div>
|
||||||
|
<div>
|
||||||
|
<Image src={midsuit} alt="midsuit" width={100} height={40}/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
"use client"
|
||||||
|
import { useAppDispatch } from "@/lib/hooks";
|
||||||
|
import { setRoles, setToken, setUser } from "@/lib/slice/auth";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
|
||||||
|
export default function LogoutPage() {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const router = useRouter();
|
||||||
|
useEffect(() => {
|
||||||
|
dispatch(setUser(null));
|
||||||
|
dispatch(setToken(null));
|
||||||
|
dispatch(setRoles([]));
|
||||||
|
router.push("/login");
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return <div></div>
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
import { User } from "@/services/types";
|
||||||
|
import { createSlice } from "@reduxjs/toolkit/react";
|
||||||
|
|
||||||
|
interface AuthState {
|
||||||
|
user: User | null;
|
||||||
|
token: string | null;
|
||||||
|
roles: string[];
|
||||||
|
isLoading: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialState: AuthState = {
|
||||||
|
user: null,
|
||||||
|
token: null,
|
||||||
|
roles: [],
|
||||||
|
isLoading: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const authSlice = createSlice({
|
||||||
|
name: "auth",
|
||||||
|
initialState,
|
||||||
|
reducers: {
|
||||||
|
setUser: (state, action) => {
|
||||||
|
state.user = action.payload;
|
||||||
|
},
|
||||||
|
setToken: (state, action) => {
|
||||||
|
state.token = action.payload;
|
||||||
|
},
|
||||||
|
setRoles: (state, action) => {
|
||||||
|
state.roles = action.payload;
|
||||||
|
},
|
||||||
|
setIsLoading: (state, action) => {
|
||||||
|
state.isLoading = action.payload;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const { setUser, setToken, setRoles, setIsLoading } = authSlice.actions;
|
|
@ -0,0 +1,23 @@
|
||||||
|
"use client";
|
||||||
|
import createWebStorage from "redux-persist/lib/storage/createWebStorage";
|
||||||
|
|
||||||
|
const createNoopStorage = () => {
|
||||||
|
return {
|
||||||
|
getItem(_key: any) {
|
||||||
|
return Promise.resolve(null);
|
||||||
|
},
|
||||||
|
setItem(_key: any, value: any) {
|
||||||
|
return Promise.resolve(value);
|
||||||
|
},
|
||||||
|
removeItem(_key: any) {
|
||||||
|
return Promise.resolve();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const storage =
|
||||||
|
typeof window !== "undefined"
|
||||||
|
? createWebStorage("local")
|
||||||
|
: createNoopStorage();
|
||||||
|
|
||||||
|
export default storage;
|
|
@ -1,20 +1,33 @@
|
||||||
import { api } from '@/services/api'
|
import { api } from '@/services/api'
|
||||||
import { configureStore } from '@reduxjs/toolkit'
|
import { configureStore } from '@reduxjs/toolkit'
|
||||||
import filterReducer from '@/lib/slice/filter'
|
import filterReducer from '@/lib/slice/filter'
|
||||||
|
import { FLUSH, PAUSE, PERSIST, persistReducer, persistStore, PURGE, REGISTER, REHYDRATE } from "redux-persist";
|
||||||
|
import { authSlice } from './slice/auth';
|
||||||
|
import storage from './storage';
|
||||||
|
|
||||||
export const makeStore = () => {
|
const persistAuth = persistReducer({
|
||||||
return configureStore({
|
key: 'auth',
|
||||||
|
storage: storage,
|
||||||
|
}, authSlice.reducer)
|
||||||
|
|
||||||
|
export const store = configureStore({
|
||||||
reducer: {
|
reducer: {
|
||||||
[api.reducerPath]: api.reducer,
|
[api.reducerPath]: api.reducer,
|
||||||
|
auth: persistAuth,
|
||||||
filter: filterReducer,
|
filter: filterReducer,
|
||||||
},
|
},
|
||||||
middleware: (getDefaultMiddleware) =>
|
middleware: (getDefaultMiddleware) =>
|
||||||
getDefaultMiddleware().concat(api.middleware),
|
getDefaultMiddleware({
|
||||||
|
serializableCheck: {
|
||||||
|
ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
|
||||||
|
},
|
||||||
|
}).concat(api.middleware),
|
||||||
})
|
})
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
export const persistor = persistStore(store)
|
||||||
// Infer the type of makeStore
|
// Infer the type of makeStore
|
||||||
export type AppStore = ReturnType<typeof makeStore>
|
export type AppStore = typeof store
|
||||||
// Infer the `RootState` and `AppDispatch` types from the store itself
|
// Infer the `RootState` and `AppDispatch` types from the store itself
|
||||||
export type RootState = ReturnType<AppStore['getState']>
|
export type RootState = ReturnType<AppStore['getState']>
|
||||||
export type AppDispatch = AppStore['dispatch']
|
export type AppDispatch = AppStore['dispatch']
|
|
@ -1,10 +1,38 @@
|
||||||
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
|
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
|
||||||
import { AttendanceRange, AttendanceSummary, EmployeeSummary, FilterOptions, MonthlyAttendance, MonthlyEmployee, ResignationCategory, ResignationReason, ResignationType as ResignationType, ResignSummary } from './types'
|
import { AttendanceRange, AttendanceSummary, EmployeeSummary, FilterOptions, MonthlyAttendance, MonthlyEmployee, ResignationCategory, ResignationReason, ResignationType as ResignationType, ResignSummary, User } from './types'
|
||||||
import { Response , Filter} from './types'
|
import { Response , Filter} from './types'
|
||||||
|
|
||||||
export const api = createApi({
|
export const api = createApi({
|
||||||
baseQuery: fetchBaseQuery({ baseUrl: 'https://erp.julongindonesia.com:8443/api' }),
|
baseQuery: fetchBaseQuery({ baseUrl: 'http://localhost:8080' }),
|
||||||
endpoints: (builder) => ({
|
endpoints: (builder) => ({
|
||||||
|
login: builder.mutation<{
|
||||||
|
user: User;
|
||||||
|
token: string;
|
||||||
|
roles: string[];
|
||||||
|
}, { username: string, password: string }>({
|
||||||
|
query: ({ username, password }) => ({
|
||||||
|
url: '/login',
|
||||||
|
method: 'POST',
|
||||||
|
body: { username, password },
|
||||||
|
}),
|
||||||
|
transformResponse: (response: Response) => {
|
||||||
|
if (response.status === "success") {
|
||||||
|
return response.data!;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
authCheck: builder.query<{
|
||||||
|
user: User;
|
||||||
|
token: string;
|
||||||
|
roles: string[];
|
||||||
|
}, void>({
|
||||||
|
query: () => ({ url: '/auth-check' }),
|
||||||
|
transformResponse: (response: Response) => {
|
||||||
|
if (response.status === "success") {
|
||||||
|
return response.data!;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}),
|
||||||
getFilterOptions: builder.query<FilterOptions, Filter>({
|
getFilterOptions: builder.query<FilterOptions, Filter>({
|
||||||
query: (params) => ({ url: '/dashboard/filter-options', params }),
|
query: (params) => ({ url: '/dashboard/filter-options', params }),
|
||||||
transformResponse: (response: Response) => {
|
transformResponse: (response: Response) => {
|
||||||
|
@ -90,4 +118,5 @@ export const api = createApi({
|
||||||
|
|
||||||
export const { useGetFilterOptionsQuery, useGetEmployeeSummaryQuery, useGetMonthlyEmployeeQuery, useGetMonthlyAttendanceQuery,
|
export const { useGetFilterOptionsQuery, useGetEmployeeSummaryQuery, useGetMonthlyEmployeeQuery, useGetMonthlyAttendanceQuery,
|
||||||
useGetOrganizationAttendanceQuery, useGetAttendanceRangeQuery, useGetResignSummaryQuery, useGetResignTypeQuery,
|
useGetOrganizationAttendanceQuery, useGetAttendanceRangeQuery, useGetResignSummaryQuery, useGetResignTypeQuery,
|
||||||
|
useLoginMutation, useAuthCheckQuery,
|
||||||
useGetResignCategoryQuery, useGetResignReasonQuery } = api
|
useGetResignCategoryQuery, useGetResignReasonQuery } = api
|
|
@ -87,3 +87,11 @@ export type ResignationReason = {
|
||||||
reason: string;
|
reason: string;
|
||||||
count: number;
|
count: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type User = {
|
||||||
|
ad_user_id: number;
|
||||||
|
name: string;
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
hc_employee_id: number;
|
||||||
|
}
|
|
@ -1508,6 +1508,11 @@ readdirp@~3.6.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
picomatch "^2.2.1"
|
picomatch "^2.2.1"
|
||||||
|
|
||||||
|
redux-persist@^6.0.0:
|
||||||
|
version "6.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/redux-persist/-/redux-persist-6.0.0.tgz#b4d2972f9859597c130d40d4b146fecdab51b3a8"
|
||||||
|
integrity sha512-71LLMbUq2r02ng2We9S215LtPu3fY0KgaGE0k8WRgl6RkqxtGfl7HUozz1Dftwsb0D/5mZ8dwAaPbtnzfvbEwQ==
|
||||||
|
|
||||||
redux-thunk@^3.1.0:
|
redux-thunk@^3.1.0:
|
||||||
version "3.1.0"
|
version "3.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-3.1.0.tgz#94aa6e04977c30e14e892eae84978c1af6058ff3"
|
resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-3.1.0.tgz#94aa6e04977c30e14e892eae84978c1af6058ff3"
|
||||||
|
|
Loading…
Reference in New Issue