import React, { useState, useMemo } from 'react'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from './ui/card'; import { Badge } from './ui/badge'; import { Button } from './ui/button'; import { Input } from './ui/input'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './ui/select'; import { Avatar, AvatarFallback, AvatarImage } from './ui/avatar'; import { Progress } from './ui/progress'; import { Calendar, Clock, Filter, Search, User, FileText, AlertCircle, CheckCircle, ArrowRight, SortAsc, SortDesc, MoreHorizontal, Flame, Target, Eye, Users, TrendingUp, RefreshCw, Download, Settings2, X } from 'lucide-react'; interface Request { id: string; title: string; description: string; status: 'pending' | 'approved' | 'rejected' | 'in-review'; priority: 'express' | 'standard'; initiator: { name: string; avatar: string; }; currentApprover?: { name: string; avatar: string; }; slaProgress: number; slaRemaining: string; createdAt: string; dueDate: string; approvalStep: string; reason?: string; department?: string; } interface RequestsListProps { type: 'open' | 'closed'; onViewRequest?: (requestId: string, requestTitle?: string) => void; } // Static data to prevent re-creation on each render const MOCK_REQUESTS: Request[] = [ { id: 'RE-REQ-CM-001', title: 'Dealer Marketing Activity Claim - Diwali Festival Campaign', description: 'Claim request for dealer-led Diwali festival marketing campaign using Claim Management template workflow.', status: 'pending', priority: 'standard', initiator: { name: 'Sneha Patil', avatar: 'SP' }, currentApprover: { name: 'Sneha Patil (Initiator)', avatar: 'SP' }, slaProgress: 35, slaRemaining: '4 days 12 hours', createdAt: '2024-10-07', dueDate: '2024-10-16', approvalStep: 'Initiator Review & Confirmation', department: 'Marketing - West Zone' } as any, { id: 'RE-REQ-001', title: 'Marketing Campaign Budget Approval', description: 'Request for Q4 marketing campaign budget allocation of $50,000 for digital advertising across social media platforms and content creation.', status: 'pending', priority: 'express', initiator: { name: 'Sarah Chen', avatar: 'SC' }, currentApprover: { name: 'Mike Johnson', avatar: 'MJ' }, slaProgress: 85, slaRemaining: '2 hours', createdAt: '2024-10-07', dueDate: '2024-10-09', approvalStep: 'Awaiting Finance Approval', department: 'Marketing' }, { id: 'RE-REQ-002', title: 'IT Equipment Purchase', description: 'Purchase of 10 new laptops for the development team including software licenses and accessories for enhanced productivity.', status: 'in-review', priority: 'standard', initiator: { name: 'David Kumar', avatar: 'DK' }, currentApprover: { name: 'Lisa Wong', avatar: 'LW' }, slaProgress: 45, slaRemaining: '1 day', createdAt: '2024-10-06', dueDate: '2024-10-12', approvalStep: 'IT Department Review' }, { id: 'RE-REQ-003', title: 'Vendor Contract Renewal', description: 'Annual renewal of cleaning services contract with updated terms and pricing structure for office maintenance.', status: 'pending', priority: 'standard', initiator: { name: 'John Doe', avatar: 'JD' }, currentApprover: { name: 'Anna Smith', avatar: 'AS' }, slaProgress: 90, slaRemaining: '30 minutes', createdAt: '2024-10-05', dueDate: '2024-10-08', approvalStep: 'Final Management Approval' }, { id: 'RE-REQ-004', title: 'Office Space Expansion', description: 'Lease additional office space for growing team, 2000 sq ft in the same building with modern amenities.', status: 'in-review', priority: 'express', initiator: { name: 'Lisa Wong', avatar: 'LW' }, currentApprover: { name: 'David Kumar', avatar: 'DK' }, slaProgress: 30, slaRemaining: '3 days', createdAt: '2024-10-04', dueDate: '2024-10-15', approvalStep: 'Legal Review' }, { id: 'RE-REQ-005', title: 'Employee Training Program', description: 'Approval for new employee onboarding and skill development training program with external consultants.', status: 'pending', priority: 'standard', initiator: { name: 'Anna Smith', avatar: 'AS' }, currentApprover: { name: 'Sarah Chen', avatar: 'SC' }, slaProgress: 60, slaRemaining: '12 hours', createdAt: '2024-10-03', dueDate: '2024-10-11', approvalStep: 'HR Approval' } ]; const CLOSED_REQUESTS: Request[] = [ { ...MOCK_REQUESTS[0], status: 'approved', reason: 'Budget approved with quarterly review conditions' }, { ...MOCK_REQUESTS[1], status: 'approved', reason: 'All equipment approved and ordered through preferred vendor' }, { ...MOCK_REQUESTS[2], status: 'rejected', reason: 'Pricing not competitive, seek alternative vendors' }, { ...MOCK_REQUESTS[3], status: 'approved', reason: 'Lease terms negotiated and approved by legal team' }, { ...MOCK_REQUESTS[4], status: 'approved', reason: 'Program approved with budget adjustments' } ]; // Utility functions const getPriorityConfig = (priority: string) => { switch (priority) { case 'express': return { color: 'bg-red-100 text-red-800 border-red-200', icon: Flame, iconColor: 'text-red-600' }; case 'standard': return { color: 'bg-blue-100 text-blue-800 border-blue-200', icon: Target, iconColor: 'text-blue-600' }; default: return { color: 'bg-gray-100 text-gray-800 border-gray-200', icon: Target, iconColor: 'text-gray-600' }; } }; const getStatusConfig = (status: string) => { switch (status) { case 'pending': return { color: 'bg-yellow-100 text-yellow-800 border-yellow-200', icon: Clock, iconColor: 'text-yellow-600' }; case 'in-review': return { color: 'bg-blue-100 text-blue-800 border-blue-200', icon: Eye, iconColor: 'text-blue-600' }; case 'approved': return { color: 'bg-green-100 text-green-800 border-green-200', icon: CheckCircle, iconColor: 'text-green-600' }; case 'rejected': return { color: 'bg-red-100 text-red-800 border-red-200', icon: AlertCircle, iconColor: 'text-red-600' }; default: return { color: 'bg-gray-100 text-gray-800 border-gray-200', icon: AlertCircle, iconColor: 'text-gray-600' }; } }; const getSLAUrgency = (progress: number) => { if (progress >= 80) return { color: 'bg-red-500', textColor: 'text-red-600', urgency: 'critical' }; if (progress >= 60) return { color: 'bg-orange-500', textColor: 'text-orange-600', urgency: 'warning' }; return { color: 'bg-green-500', textColor: 'text-green-600', urgency: 'normal' }; }; export function RequestsList({ type, onViewRequest }: RequestsListProps) { const [searchTerm, setSearchTerm] = useState(''); const [priorityFilter, setPriorityFilter] = useState('all'); const [statusFilter, setStatusFilter] = useState('all'); const [sortBy, setSortBy] = useState<'created' | 'due' | 'priority' | 'sla'>('due'); const [sortOrder, setSortOrder] = useState<'asc' | 'desc'>('desc'); const [showAdvancedFilters, setShowAdvancedFilters] = useState(false); const sourceRequests = type === 'open' ? MOCK_REQUESTS : CLOSED_REQUESTS; const filteredAndSortedRequests = useMemo(() => { let filtered = sourceRequests.filter(request => { const matchesSearch = request.title.toLowerCase().includes(searchTerm.toLowerCase()) || request.id.toLowerCase().includes(searchTerm.toLowerCase()) || request.initiator.name.toLowerCase().includes(searchTerm.toLowerCase()); const matchesPriority = priorityFilter === 'all' || request.priority === priorityFilter; const matchesStatus = statusFilter === 'all' || request.status === statusFilter; return matchesSearch && matchesPriority && matchesStatus; }); // Sort requests filtered.sort((a, b) => { let aValue: any, bValue: any; switch (sortBy) { case 'created': aValue = new Date(a.createdAt); bValue = new Date(b.createdAt); break; case 'due': aValue = new Date(a.dueDate); bValue = new Date(b.dueDate); break; case 'priority': const priorityOrder = { express: 2, standard: 1 }; aValue = priorityOrder[a.priority as keyof typeof priorityOrder]; bValue = priorityOrder[b.priority as keyof typeof priorityOrder]; break; case 'sla': aValue = a.slaProgress; bValue = b.slaProgress; break; default: return 0; } if (sortOrder === 'asc') { return aValue > bValue ? 1 : -1; } else { return aValue < bValue ? 1 : -1; } }); return filtered; }, [sourceRequests, searchTerm, priorityFilter, statusFilter, sortBy, sortOrder]); const clearFilters = () => { setSearchTerm(''); setPriorityFilter('all'); setStatusFilter('all'); }; const activeFiltersCount = [ searchTerm, priorityFilter !== 'all' ? priorityFilter : null, statusFilter !== 'all' ? statusFilter : null ].filter(Boolean).length; return (
{type === 'open' ? 'Manage and track active approval requests' : 'Review completed and archived requests' }
{request.description}
{request.initiator.name}
Initiator
{request.currentApprover.name}
Current Approver
{searchTerm || activeFiltersCount > 0 ? 'Try adjusting your filters or search terms to see more results.' : `No ${type} requests available at the moment.` }
{activeFiltersCount > 0 && ( )}