import React, { useState, useMemo } from 'react'; import { format } from 'date-fns'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Badge } from '@/components/ui/badge'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table'; import { Clock, CheckCircle, XCircle, Package, Search, Download, User, Calendar, TrendingUp, FileText, Loader2, } from 'lucide-react'; import { AssetRequest, RequestStatus, REQUEST_STATUS_LABELS, } from '@/types/asset-request'; interface AssetRequestDashboardProps { requests: AssetRequest[]; isLoading?: boolean; } interface DashboardStats { total: number; pending: number; approved: number; rejected: number; assigned: number; } const AssetRequestDashboard: React.FC = ({ requests, isLoading = false, }) => { const [searchTerm, setSearchTerm] = useState(''); const [statusFilter, setStatusFilter] = useState('all'); const [dateRangeFilter, setDateRangeFilter] = useState<'all' | '7days' | '30days' | '90days'>( 'all' ); const [employeeFilter, setEmployeeFilter] = useState('all'); // Calculate statistics const stats: DashboardStats = useMemo(() => { return { total: requests.length, pending: requests.filter((r) => r.status === 'pending').length, approved: requests.filter((r) => r.status === 'approved').length, rejected: requests.filter((r) => r.status === 'rejected').length, assigned: requests.filter((r) => r.status === 'assigned').length, }; }, [requests]); // Get unique employees const employees = useMemo(() => { const uniqueEmployees = new Map(); requests.forEach((req) => { uniqueEmployees.set(req.employeeID, req.employeeName); }); return Array.from(uniqueEmployees.entries()).map(([id, name]) => ({ id, name })); }, [requests]); // Filter requests const filteredRequests = useMemo(() => { let filtered = [...requests]; // Status filter if (statusFilter !== 'all') { filtered = filtered.filter((req) => req.status === statusFilter); } // Date range filter if (dateRangeFilter !== 'all') { const now = new Date(); const daysAgo = { '7days': 7, '30days': 30, '90days': 90, }[dateRangeFilter]; const cutoffDate = new Date(now.getTime() - daysAgo * 24 * 60 * 60 * 1000); filtered = filtered.filter( (req) => new Date(req.submittedDate) >= cutoffDate ); } // Employee filter if (employeeFilter !== 'all') { filtered = filtered.filter((req) => req.employeeID.toString() === employeeFilter); } // Search filter if (searchTerm) { const term = searchTerm.toLowerCase(); filtered = filtered.filter( (req) => req.requestID.toString().includes(term) || req.employeeName.toLowerCase().includes(term) || req.justification.toLowerCase().includes(term) || req.requestedAssets.some((asset) => asset.assetType.toLowerCase().includes(term) ) || (req.adminComments && req.adminComments.toLowerCase().includes(term)) ); } // Sort by date (newest first) return filtered.sort( (a, b) => new Date(b.submittedDate).getTime() - new Date(a.submittedDate).getTime() ); }, [requests, searchTerm, statusFilter, dateRangeFilter, employeeFilter]); const handleExport = () => { // Create CSV content const headers = [ 'Request ID', 'Employee', 'Status', 'Submitted Date', 'Assets', 'Justification', 'Reviewer', 'Admin Comments', ]; const rows = filteredRequests.map((req) => [ req.requestID, req.employeeName, req.status, format(new Date(req.submittedDate), 'yyyy-MM-dd'), req.requestedAssets.map((a) => `${a.assetType} (x${a.quantity})`).join('; '), req.justification.replace(/,/g, ';'), req.reviewerName || '', req.adminComments?.replace(/,/g, ';') || '', ]); const csvContent = [ headers.join(','), ...rows.map((row) => row.map((cell) => `"${cell}"`).join(',')), ].join('\n'); // Create and download file const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' }); const link = document.createElement('a'); const url = URL.createObjectURL(blob); link.setAttribute('href', url); link.setAttribute( 'download', `asset-requests-${format(new Date(), 'yyyy-MM-dd')}.csv` ); link.style.visibility = 'hidden'; document.body.appendChild(link); link.click(); document.body.removeChild(link); }; const getStatusIcon = (status: RequestStatus) => { switch (status) { case 'pending': return ; case 'approved': return ; case 'rejected': return ; case 'assigned': return ; } }; const getStatusColor = (status: RequestStatus) => { switch (status) { case 'pending': return 'bg-yellow-100 text-yellow-800 border-yellow-200'; case 'approved': return 'bg-green-100 text-green-800 border-green-200'; case 'rejected': return 'bg-red-100 text-red-800 border-red-200'; case 'assigned': return 'bg-blue-100 text-blue-800 border-blue-200'; } }; if (isLoading) { return ( ); } return (
{/* Statistics Cards */}

Total Requests

{stats.total}

Pending

{stats.pending}

Approved

{stats.approved}

Rejected

{stats.rejected}

Assigned

{stats.assigned}

{/* Filters and Search */}
All Asset Requests
setSearchTerm(e.target.value)} className="pl-10" />
{/* Requests Table */} {filteredRequests.length === 0 ? (

{searchTerm || statusFilter !== 'all' || dateRangeFilter !== 'all' || employeeFilter !== 'all' ? 'No requests match your filters' : 'No asset requests found'}

) : (
Request ID Employee Status Assets Submitted Reviewer {filteredRequests.map((request) => ( #{request.requestID}
{request.employeeName}
{getStatusIcon(request.status)} {REQUEST_STATUS_LABELS[request.status]}
{request.requestedAssets.map((asset, idx) => (
{asset.assetType} (x{asset.quantity})
))}
{format(new Date(request.submittedDate), 'MMM dd, yyyy')}
{request.reviewerName ? (
{request.reviewerName}
) : ( - )}
))}
)} {/* Results Count */} {filteredRequests.length > 0 && (
Showing {filteredRequests.length} of {requests.length} requests
)}
); }; export default AssetRequestDashboard;