Spaces:
Sleeping
Sleeping
| import React from "react"; | |
| import { format, differenceInCalendarDays } from "date-fns"; | |
| import { | |
| Dialog, | |
| DialogContent, | |
| DialogDescription, | |
| DialogHeader, | |
| DialogTitle, | |
| DialogFooter, | |
| } from "@/components/ui/dialog"; | |
| import { Button } from "@/components/ui/button"; | |
| import { Badge } from "@/components/ui/badge"; | |
| import { Leave, LeaveStatus } from "@/types"; | |
| import { Employee } from "@/services/employeeApi"; | |
| import { | |
| Calendar, | |
| Clock, | |
| FileText, | |
| User, | |
| CheckCircle, | |
| XCircle, | |
| AlertCircle, | |
| File, | |
| Download, | |
| } from "lucide-react"; | |
| // Helper function to get badge variant based on leave status | |
| const getStatusBadgeVariant = (status: LeaveStatus) => { | |
| switch (status) { | |
| case "Approved": | |
| return "success"; | |
| case "Rejected": | |
| return "destructive"; | |
| case "Cancelled": | |
| return "outline"; | |
| case "Pending": | |
| default: | |
| return "warning"; | |
| } | |
| }; | |
| // Helper function to get status icon based on leave status | |
| const getStatusIcon = (status: LeaveStatus) => { | |
| switch (status) { | |
| case "Approved": | |
| return <CheckCircle className="h-4 w-4" />; | |
| case "Rejected": | |
| return <XCircle className="h-4 w-4" />; | |
| case "Cancelled": | |
| return <AlertCircle className="h-4 w-4" />; | |
| case "Pending": | |
| default: | |
| return <Clock className="h-4 w-4" />; | |
| } | |
| }; | |
| interface LeaveDetailProps { | |
| leave: Leave | null; | |
| employee?: Employee | null; | |
| approver?: Employee | null; | |
| isOpen: boolean; | |
| onOpenChange: (open: boolean) => void; | |
| onDownloadDocument?: (leave: Leave) => void; | |
| } | |
| const LeaveDetail: React.FC<LeaveDetailProps> = ({ | |
| leave, | |
| employee, | |
| approver, | |
| isOpen, | |
| onOpenChange, | |
| onDownloadDocument, | |
| }) => { | |
| if (!leave) return null; | |
| const startDate = new Date(leave.startDate); | |
| const endDate = new Date(leave.endDate); | |
| const durationDays = differenceInCalendarDays(endDate, startDate) + 1; | |
| return ( | |
| <Dialog open={isOpen} onOpenChange={onOpenChange}> | |
| <DialogContent className="sm:max-w-[550px]"> | |
| <DialogHeader> | |
| <DialogTitle className="flex items-center justify-between"> | |
| <span>{leave.type} Leave Request</span> | |
| <Badge | |
| variant={getStatusBadgeVariant(leave.status) as any} | |
| className="flex items-center gap-1" | |
| > | |
| {getStatusIcon(leave.status)} | |
| <span>{leave.status}</span> | |
| </Badge> | |
| </DialogTitle> | |
| <DialogDescription> | |
| Leave request details and status | |
| </DialogDescription> | |
| </DialogHeader> | |
| <div className="grid gap-4 py-4"> | |
| {employee && ( | |
| <div className="grid grid-cols-4 items-center gap-4"> | |
| <div className="font-medium">Employee:</div> | |
| <div className="col-span-3 flex items-center gap-2"> | |
| <User className="h-4 w-4" /> | |
| <span> | |
| {employee.firstName} {employee.lastName} (ID: {employee.id}) | |
| </span> | |
| </div> | |
| </div> | |
| )} | |
| <div className="grid grid-cols-4 items-center gap-4"> | |
| <div className="font-medium">Duration:</div> | |
| <div className="col-span-3 flex items-center gap-2"> | |
| <Calendar className="h-4 w-4" /> | |
| <span> | |
| {format(startDate, "PPP")} to {format(endDate, "PPP")} | |
| <span className="ml-1 text-xs text-muted-foreground"> | |
| ({durationDays} {durationDays === 1 ? "day" : "days"}) | |
| </span> | |
| </span> | |
| </div> | |
| </div> | |
| <div className="grid grid-cols-4 items-center gap-4"> | |
| <div className="font-medium">Applied On:</div> | |
| <div className="col-span-3 flex items-center gap-2"> | |
| <Clock className="h-4 w-4" /> | |
| <span>{format(new Date(leave.appliedOn), "PPP")}</span> | |
| </div> | |
| </div> | |
| <div className="grid grid-cols-4 items-start gap-4"> | |
| <div className="font-medium">Reason:</div> | |
| <div className="col-span-3 flex items-start gap-2"> | |
| <FileText className="h-4 w-4 mt-0.5" /> | |
| <span>{leave.reason}</span> | |
| </div> | |
| </div> | |
| {(leave.status === "Approved" || leave.status === "Rejected") && ( | |
| <> | |
| {approver && ( | |
| <div className="grid grid-cols-4 items-center gap-4"> | |
| <div className="font-medium">Actioned By:</div> | |
| <div className="col-span-3 flex items-center gap-2"> | |
| <User className="h-4 w-4" /> | |
| <span> | |
| {approver.firstName} {approver.lastName} | |
| </span> | |
| </div> | |
| </div> | |
| )} | |
| {leave.actionDate && ( | |
| <div className="grid grid-cols-4 items-center gap-4"> | |
| <div className="font-medium">Action Date:</div> | |
| <div className="col-span-3 flex items-center gap-2"> | |
| <Clock className="h-4 w-4" /> | |
| <span>{format(new Date(leave.actionDate), "PPP")}</span> | |
| </div> | |
| </div> | |
| )} | |
| {leave.comments && ( | |
| <div className="grid grid-cols-4 items-start gap-4"> | |
| <div className="font-medium">Comments:</div> | |
| <div className="col-span-3 flex items-start gap-2"> | |
| <FileText className="h-4 w-4 mt-0.5" /> | |
| <span>{leave.comments}</span> | |
| </div> | |
| </div> | |
| )} | |
| </> | |
| )} | |
| {leave.documents && ( | |
| <div className="grid grid-cols-4 items-center gap-4"> | |
| <div className="font-medium">Documents:</div> | |
| <div className="col-span-3"> | |
| <Button | |
| variant="outline" | |
| size="sm" | |
| className="flex items-center gap-2" | |
| onClick={() => onDownloadDocument && onDownloadDocument(leave)} | |
| > | |
| <File className="h-4 w-4" /> | |
| <span>View Document</span> | |
| <Download className="h-4 w-4 ml-1" /> | |
| </Button> | |
| </div> | |
| </div> | |
| )} | |
| </div> | |
| <DialogFooter> | |
| <Button type="button" onClick={() => onOpenChange(false)}> | |
| Close | |
| </Button> | |
| </DialogFooter> | |
| </DialogContent> | |
| </Dialog> | |
| ); | |
| }; | |
| export default LeaveDetail; |