Spaces:
Sleeping
Sleeping
| import React from 'react'; | |
| import { format } from 'date-fns'; | |
| import { Button } from '@/components/ui/button'; | |
| import { Card, CardContent } from '@/components/ui/card'; | |
| import { Badge } from '@/components/ui/badge'; | |
| import { User, Calendar, CheckCircle, XCircle, AlertTriangle, MessageSquare } from 'lucide-react'; | |
| import { AssetRequestWithAvailability } from '@/types/asset-request'; | |
| interface ViewDetailsDialogProps { | |
| isOpen: boolean; | |
| onClose: () => void; | |
| request: AssetRequestWithAvailability | null; | |
| onApprove?: (request: AssetRequestWithAvailability) => void; | |
| onReject?: (request: AssetRequestWithAvailability) => void; | |
| onAddComment?: (request: AssetRequestWithAvailability) => void; | |
| } | |
| const ViewDetailsDialog: React.FC<ViewDetailsDialogProps> = ({ | |
| isOpen, | |
| onClose, | |
| request, | |
| onApprove, | |
| onReject, | |
| onAddComment, | |
| }) => { | |
| const handleOverlayClick = (e: React.MouseEvent) => { | |
| if (e.target === e.currentTarget) { | |
| onClose(); | |
| } | |
| }; | |
| const getAvailabilityBadge = (available: number, requested: number) => { | |
| if (available >= requested) { | |
| return ( | |
| <Badge className="bg-green-100 text-green-800 border-green-200"> | |
| Available ({available}) | |
| </Badge> | |
| ); | |
| } else if (available > 0) { | |
| return ( | |
| <Badge className="bg-yellow-100 text-yellow-800 border-yellow-200"> | |
| <AlertTriangle className="h-3 w-3 mr-1 inline" /> | |
| Partial ({available}/{requested}) | |
| </Badge> | |
| ); | |
| } else { | |
| return ( | |
| <Badge className="bg-red-100 text-red-800 border-red-200"> | |
| <XCircle className="h-3 w-3 mr-1 inline" /> | |
| Unavailable | |
| </Badge> | |
| ); | |
| } | |
| }; | |
| if (!isOpen || !request) return null; | |
| return ( | |
| <div | |
| className="fixed inset-0 z-[999] flex items-center justify-center p-4 overflow-y-auto" | |
| role="dialog" | |
| aria-modal="true" | |
| aria-labelledby="dialog-title" | |
| > | |
| {/* Overlay */} | |
| <div | |
| className="fixed inset-0 bg-black/80 backdrop-blur-sm transition-opacity" | |
| onClick={handleOverlayClick} | |
| aria-hidden="true" | |
| /> | |
| {/* Modal Content */} | |
| <div | |
| className="relative z-[1000] w-full max-w-2xl rounded-lg border bg-background shadow-lg animate-in fade-in-0 zoom-in-95 duration-200 max-h-[90vh] overflow-hidden flex flex-col" | |
| onClick={(e) => e.stopPropagation()} | |
| > | |
| {/* Header */} | |
| <div className="p-6 border-b flex-shrink-0"> | |
| <h2 | |
| id="dialog-title" | |
| className="text-lg font-semibold text-foreground" | |
| > | |
| Request #{request.requestID} Details | |
| </h2> | |
| <p className="text-sm text-muted-foreground mt-1"> | |
| Review the complete details of this asset request | |
| </p> | |
| </div> | |
| {/* Content - Scrollable */} | |
| <div className="p-6 space-y-4 overflow-y-auto flex-1"> | |
| {/* Employee and Date Info */} | |
| <div className="grid grid-cols-2 gap-4"> | |
| <div> | |
| <div className="text-sm font-medium text-muted-foreground mb-1"> | |
| Employee | |
| </div> | |
| <div className="flex items-center gap-2"> | |
| <User className="h-4 w-4" /> | |
| <span>{request.employeeName}</span> | |
| </div> | |
| </div> | |
| <div> | |
| <div className="text-sm font-medium text-muted-foreground mb-1"> | |
| Submitted Date | |
| </div> | |
| <div className="flex items-center gap-2"> | |
| <Calendar className="h-4 w-4" /> | |
| <span> | |
| {format(new Date(request.submittedDate), 'PPP')} | |
| </span> | |
| </div> | |
| </div> | |
| </div> | |
| {/* Requested Assets */} | |
| <div> | |
| <div className="text-sm font-medium text-muted-foreground mb-2"> | |
| Requested Assets | |
| </div> | |
| <div className="space-y-2"> | |
| {request.requestedAssets.map((asset, idx) => ( | |
| <Card key={idx} className="border-2"> | |
| <CardContent className="p-3"> | |
| <div className="flex items-start justify-between gap-3"> | |
| <div className="flex-1 min-w-0"> | |
| <div className="font-medium">{asset.assetType}</div> | |
| <div className="text-sm text-muted-foreground"> | |
| Quantity: {asset.quantity} | |
| </div> | |
| {asset.specifications && ( | |
| <div className="text-sm text-muted-foreground mt-1"> | |
| Specs: {asset.specifications} | |
| </div> | |
| )} | |
| </div> | |
| <div className="flex-shrink-0"> | |
| {request.availabilityStatus[idx] && | |
| getAvailabilityBadge( | |
| request.availabilityStatus[idx].available, | |
| request.availabilityStatus[idx].requested | |
| )} | |
| </div> | |
| </div> | |
| </CardContent> | |
| </Card> | |
| ))} | |
| </div> | |
| </div> | |
| {/* Justification */} | |
| <div> | |
| <div className="text-sm font-medium text-muted-foreground mb-2"> | |
| Justification | |
| </div> | |
| <div className="bg-muted/50 rounded-md p-3 text-sm"> | |
| {request.justification} | |
| </div> | |
| </div> | |
| </div> | |
| {/* Footer - Fixed at bottom */} | |
| <div className="p-6 border-t bg-muted/30 flex-shrink-0"> | |
| <div className="flex justify-between items-center gap-3"> | |
| <div> | |
| {onAddComment && ( | |
| <Button | |
| type="button" | |
| variant="outline" | |
| onClick={() => { | |
| onClose(); | |
| onAddComment(request); | |
| }} | |
| className="border-blue-600 text-blue-600 hover:bg-blue-50" | |
| > | |
| <MessageSquare className="h-4 w-4 mr-2" /> | |
| Add Comment | |
| </Button> | |
| )} | |
| </div> | |
| <div className="flex gap-3"> | |
| <Button | |
| type="button" | |
| variant="outline" | |
| onClick={onClose} | |
| > | |
| Close | |
| </Button> | |
| {onApprove && ( | |
| <Button | |
| type="button" | |
| variant="default" | |
| onClick={() => { | |
| onClose(); | |
| onApprove(request); | |
| }} | |
| className="bg-green-600 hover:bg-green-700 text-white" | |
| > | |
| <CheckCircle className="h-4 w-4 mr-2" /> | |
| Approve | |
| </Button> | |
| )} | |
| {onReject && ( | |
| <Button | |
| type="button" | |
| variant="destructive" | |
| onClick={() => { | |
| onClose(); | |
| onReject(request); | |
| }} | |
| > | |
| <XCircle className="h-4 w-4 mr-2" /> | |
| Reject | |
| </Button> | |
| )} | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| }; | |
| export default ViewDetailsDialog; | |