sori-client/src/app/LeaveApplicationRecordForAdmin/AdminLeaveApplicationScene.tsx

215 lines
7.5 KiB
TypeScript

import { ScrollArea } from '@mantine/core';
import { FC, useEffect, useState } from 'react';
import { Button, Stack } from '../../components';
import { Dialog } from '../../components/Dialog';
import { Pagination } from '../../components/Pagination';
import { LeaveApplication, soriAPIClient } from '../../lib/sori';
import { ApproveLeaveForm } from './ApproveLeaveForm';
import { CreateLeaveApplicationForm } from './CreateLeaveApplicationForm';
import { RejectLeaveForm } from './RejectLeaveForm';
export const AdminLeaveApplicationScene: FC = () => {
const [createOpen, setCreateOpen] = useState(false);
const [approveOpen, setApproveOpen] = useState(false);
const [rejectOpen, setRejectOpen] = useState(false);
const [leaveApplications, setLeaveApplications] = useState<
LeaveApplication[]
>([]);
const [selectedLeaveApplication, setSelectedLeaveApplication] =
useState<LeaveApplication | null>(null);
const [currentPage, setCurrentPage] = useState(1);
const pageSize = 20;
const paginatedstaffs = leaveApplications.slice(
(currentPage - 1) * pageSize,
currentPage * pageSize,
);
const loadLeaveApplications = async () => {
const leaveApplicationsResult =
await soriAPIClient.leaveApplications.actions.getLeaveApplications();
const leaveApplications = leaveApplicationsResult.unwrap();
setLeaveApplications(leaveApplications);
};
useEffect(() => {
loadLeaveApplications();
}, []);
// const onSubmit = (project: Partial<Project>) => {
// setProjects([...projects, project as Project]);
// setOpen(false);
// };
return (
<>
<Dialog open={createOpen} onRequestClose={() => setCreateOpen(false)}>
<CreateLeaveApplicationForm
onClose={() => {
setCreateOpen(false);
loadLeaveApplications();
}}
open={createOpen}
/>
</Dialog>
<Dialog open={approveOpen} onRequestClose={() => setApproveOpen(false)}>
{selectedLeaveApplication && (
<ApproveLeaveForm
leaveApplication={selectedLeaveApplication}
onClose={() => {
setApproveOpen(false);
setSelectedLeaveApplication(null);
loadLeaveApplications(); // refresh list after delete
}}
/>
)}
</Dialog>
<Dialog open={rejectOpen} onRequestClose={() => setRejectOpen(false)}>
{selectedLeaveApplication && (
<RejectLeaveForm
leaveApplication={selectedLeaveApplication}
onClose={() => {
setRejectOpen(false);
setSelectedLeaveApplication(null);
loadLeaveApplications(); // refresh list after delete
}}
/>
)}
</Dialog>
<Stack className="border-black-2 relative mx-auto mt-20px h-[90%] w-[90%] overflow-hidden border rounded-lg shadow-[0_2px_5px_2px_rgba(0,0,0,0.1)] xl:px-20">
<div className="h-20 min-h-12 flex flex-col justify-center gap-1 px-8 text-lg text-xl text-black/90 font-bold font-medium dark:text-white/90">
<div className="flex items-center justify-between">
<span>Leave Application Management (Admin)</span>
</div>
</div>
<Stack className="relative h-full w-full overflow-hidden">
<LeaveAppTable
leaveApplications={paginatedstaffs}
onApproveClick={(leaveApplication) => {
setSelectedLeaveApplication(leaveApplication);
setApproveOpen(true);
}}
onRejectClick={(leaveApplication) => {
setSelectedLeaveApplication(leaveApplication);
setRejectOpen(true);
}}
/>
<Pagination
count={leaveApplications.length}
pageSize={pageSize}
siblingCount={1}
currentPage={currentPage}
onPageChange={setCurrentPage}
/>
</Stack>
</Stack>
</>
);
};
const LeaveAppTable: FC<{
leaveApplications: LeaveApplication[];
onApproveClick: (leaveApplication: LeaveApplication) => void;
onRejectClick: (leaveApplication: LeaveApplication) => void;
}> = ({ leaveApplications, onApproveClick, onRejectClick }) => {
return (
<ScrollArea className="mb-1 h-full w-full overflow-hidden border border-gray-300 rounded-lg shadow-[2px_2px_5px_2px_rgba(0,0,0,0.1)]">
<table className="min-w-full">
<thead>
<tr className="text-gray-700">
<th className="border-b px-4 py-2">Leave Type</th>
<th className="border-b px-4 py-2">From Date</th>
<th className="border-b px-4 py-2">To Date</th>
<th className="border-b px-4 py-2">Duration (Days)</th>
<th className="border-b px-4 py-2">Full/Half</th>
<th className="border-b px-4 py-2">Last Updated</th>
<th className="border-b px-4 py-2">Status</th>
<th className="border-b px-4 py-2"></th>
<th className="border-b px-4 py-2"></th>
</tr>
</thead>
<tbody>
{leaveApplications.map((leaveApplication) => (
<LeaveAppTr
key={leaveApplication._id.toString()}
LeaveApplication={leaveApplication}
onApproveClick={onApproveClick}
onRejectClick={onRejectClick}
/>
))}
</tbody>
</table>
</ScrollArea>
);
};
const LeaveAppTr: FC<{
LeaveApplication: LeaveApplication;
onApproveClick: (leaveApplication: LeaveApplication) => void;
onRejectClick: (leaveApplication: LeaveApplication) => void;
}> = ({ LeaveApplication, onApproveClick, onRejectClick }) => {
const getLastUpdatedTime = (updatedAt: Date) => {
const lastUpdated = new Date(updatedAt);
const today = new Date();
const diffMs = lastUpdated.getTime() - today.getTime();
const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
if (-diffDays >= 30) {
return `> ${Math.trunc(-diffDays / 30)} 個月`;
}
return `${-diffDays} day(s)`;
};
const workDayType = LeaveApplication.leaves[0].workDay.type;
let leaveDuration = 0;
if (workDayType === 'full') {
leaveDuration = LeaveApplication.leaves.length;
} else {
leaveDuration = 0.5;
}
return (
<tr
key={LeaveApplication._id.toString()}
className="text-center text-gray-600"
>
<td className="border-b px-4 py-2">
{LeaveApplication.leaves[0].type[0].toUpperCase() +
LeaveApplication.leaves[0].type.slice(1)}
</td>
<td className="border-b px-4 py-2">
{LeaveApplication.leaves[0].workDay.date}
</td>
<td className="border-b px-4 py-2">
{
LeaveApplication.leaves[LeaveApplication.leaves.length - 1].workDay
.date
}
</td>
<td className="border-b px-4 py-2">{leaveDuration}</td>
<td className="border-b px-4 py-2">
{LeaveApplication.leaves[0].workDay.type}
</td>
<td className="border-b px-4 py-2">{`${getLastUpdatedTime(LeaveApplication.updatedAt)}`}</td>
<td className="border-b px-4 py-2">{LeaveApplication.status}</td>
<td className="border-b px-4 py-2">
<Button
className="mx-auto border-gray-300 rounded-lg bg-transparent"
onClick={() => onApproveClick(LeaveApplication)}
>
Approve
</Button>
</td>
<td className="border-b px-4 py-2">
<Button
className="mx-auto border-gray-300 rounded-lg bg-transparent"
onClick={() => onRejectClick(LeaveApplication)}
>
Reject
</Button>
</td>
</tr>
);
};