import { useEffect, useState, useCallback } from 'react'; import { Card, CardContent } from '@/components/ui/card'; import { Input } from '@/components/ui/input'; import { Badge } from '@/components/ui/badge'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { FileText, Search, Clock, CheckCircle, XCircle, User, ArrowRight, TrendingUp, Edit, Flame, Target, AlertCircle } from 'lucide-react'; import { motion } from 'framer-motion'; import workflowApi from '@/services/workflowApi'; import { PageHeader } from '@/components/common/PageHeader'; import { StatsCard } from '@/components/dashboard/StatsCard'; import { Pagination } from '@/components/common/Pagination'; interface MyRequestsProps { onViewRequest: (requestId: string, requestTitle?: string, status?: string) => void; dynamicRequests?: any[]; } 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 '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: XCircle, iconColor: 'text-red-600' }; case 'pending': return { color: 'bg-yellow-100 text-yellow-800 border-yellow-200', icon: Clock, iconColor: 'text-yellow-600' }; case 'closed': return { color: 'bg-gray-100 text-gray-800 border-gray-200', icon: CheckCircle, iconColor: 'text-gray-600' }; case 'draft': return { color: 'bg-gray-100 text-gray-800 border-gray-200', icon: Edit, iconColor: 'text-gray-600' }; default: return { color: 'bg-gray-100 text-gray-800 border-gray-200', icon: AlertCircle, iconColor: 'text-gray-600' }; } }; export function MyRequests({ onViewRequest, dynamicRequests = [] }: MyRequestsProps) { const [searchTerm, setSearchTerm] = useState(''); const [statusFilter, setStatusFilter] = useState('all'); const [priorityFilter, setPriorityFilter] = useState('all'); const [apiRequests, setApiRequests] = useState([]); const [loading, setLoading] = useState(false); const [hasFetchedFromApi, setHasFetchedFromApi] = useState(false); // Pagination states const [currentPage, setCurrentPage] = useState(1); const [totalPages, setTotalPages] = useState(1); const [totalRecords, setTotalRecords] = useState(0); const [itemsPerPage] = useState(10); const fetchMyRequests = useCallback(async (page: number = 1, filters?: { search?: string; status?: string; priority?: string }) => { try { if (page === 1) { setLoading(true); setApiRequests([]); } const result = await workflowApi.listMyWorkflows({ page, limit: itemsPerPage, search: filters?.search, status: filters?.status, priority: filters?.priority }); console.log('[MyRequests] API Response:', result); // Debug log // Extract data - workflowApi now returns { data: [], pagination: {} } const items = Array.isArray((result as any)?.data) ? (result as any).data : []; console.log('[MyRequests] Parsed items:', items); // Debug log setApiRequests(items); setHasFetchedFromApi(true); // Set pagination data const pagination = (result as any)?.pagination; if (pagination) { setCurrentPage(pagination.page || 1); setTotalPages(pagination.totalPages || 1); setTotalRecords(pagination.total || 0); } } catch (error) { console.error('[MyRequests] Error fetching requests:', error); setApiRequests([]); setHasFetchedFromApi(true); } finally { setLoading(false); } }, [itemsPerPage]); const handlePageChange = (newPage: number) => { if (newPage >= 1 && newPage <= totalPages) { setCurrentPage(newPage); fetchMyRequests(newPage, { search: searchTerm || undefined, status: statusFilter !== 'all' ? statusFilter : undefined, priority: priorityFilter !== 'all' ? priorityFilter : undefined }); } }; // Initial fetch on mount useEffect(() => { fetchMyRequests(1, { search: searchTerm || undefined, status: statusFilter !== 'all' ? statusFilter : undefined, priority: priorityFilter !== 'all' ? priorityFilter : undefined }); }, [fetchMyRequests]); // Fetch when filters change (with debouncing for search) useEffect(() => { // Debounce search: wait 500ms after user stops typing const timeoutId = setTimeout(() => { if (hasFetchedFromApi) { // Only refetch if we've already loaded data once setCurrentPage(1); // Reset to page 1 when filters change fetchMyRequests(1, { search: searchTerm || undefined, status: statusFilter !== 'all' ? statusFilter : undefined, priority: priorityFilter !== 'all' ? priorityFilter : undefined }); } }, searchTerm ? 500 : 0); // Debounce only for search, instant for dropdowns return () => clearTimeout(timeoutId); }, [searchTerm, statusFilter, priorityFilter, hasFetchedFromApi, fetchMyRequests]); // Convert API/dynamic requests to the format expected by this component // Once API has fetched (even if empty), always use API data, never fall back to props const sourceRequests = hasFetchedFromApi ? apiRequests : dynamicRequests; const convertedDynamicRequests = Array.isArray(sourceRequests) ? sourceRequests.map((req: any) => { const createdAt = req.submittedAt || req.submitted_at || req.createdAt || req.created_at; const priority = (req.priority || '').toString().toLowerCase(); return { id: req.requestNumber || req.request_number || req.requestId || req.id || req.request_id, requestId: req.requestId || req.id || req.request_id, displayId: req.requestNumber || req.request_number || req.id, title: req.title, description: req.description, status: (req.status || '').toString().toLowerCase().replace('_','-'), priority: priority, department: req.department, submittedDate: req.submittedAt || (req.createdAt ? new Date(req.createdAt).toISOString().split('T')[0] : undefined), createdAt: createdAt, currentApprover: req.currentApprover?.name || req.currentApprover?.email || '—', approverLevel: req.currentLevel && req.totalLevels ? `${req.currentLevel} of ${req.totalLevels}` : (req.currentStep && req.totalSteps ? `${req.currentStep} of ${req.totalSteps}` : '—'), templateType: req.templateType, templateName: req.templateName }; }) : []; // Use only API/dynamic requests - backend already filtered const allRequests = convertedDynamicRequests; // No frontend filtering - backend handles all filtering const filteredRequests = allRequests; // Stats calculation - using total from pagination for total count const stats = { total: totalRecords || allRequests.length, pending: allRequests.filter(r => r.status === 'pending').length, approved: allRequests.filter(r => r.status === 'approved').length, rejected: allRequests.filter(r => r.status === 'rejected').length, draft: allRequests.filter(r => r.status === 'draft').length, closed: allRequests.filter(r => r.status === 'closed').length }; return (
{/* Page Header */} {/* Stats Overview */}
{/* Filters and Search */}
setSearchTerm(e.target.value)} className="pl-9 text-sm sm:text-base bg-white border-gray-300 hover:border-gray-400 focus:border-blue-400 focus:ring-1 focus:ring-blue-200 h-9 sm:h-10" data-testid="search-input" />
{/* Requests List */}
{loading ? ( Loading your requests… ) : filteredRequests.length === 0 ? (

No requests found

{searchTerm || statusFilter !== 'all' || priorityFilter !== 'all' ? 'Try adjusting your search or filters' : 'You haven\'t created any requests yet'}

) : ( filteredRequests.map((request, index) => ( onViewRequest(request.id, request.title, request.status)} data-testid={`request-card-${request.id}`} >
{/* Header with Title and Status Badges */}

{request.title}

{(() => { const IconComponent = getStatusConfig(request.status).icon; return ; })()} {request.status} {(() => { const IconComponent = getPriorityConfig(request.priority).icon; return ; })()} {request.priority} {(request as any).templateType && ( Template: {(request as any).templateName} )}

{request.description}

ID: {(request as any).displayId || request.id} Submitted: {request.submittedDate ? new Date(request.submittedDate).toLocaleDateString() : 'N/A'}
{/* Current Approver and Level Info */}
Current Approver: {request.currentApprover}
Approval Level: {request.approverLevel}
Submitted: {request.submittedDate ? new Date(request.submittedDate).toLocaleDateString() : 'N/A'}
)) )}
{/* Pagination */}
); }