import { useState, useEffect } from 'react'; import { ApplicationCard } from '@/features/onboarding/components/ApplicationCard'; import { locations, states, ApplicationStatus, Application } from '@/lib/mock-data'; import { onboardingService } from '@/services/onboarding.service'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { Search, Download, Grid3x3, List, Mail, CheckCircle, AlertCircle } from 'lucide-react'; import { Badge } from '@/components/ui/badge'; import { Checkbox } from '@/components/ui/checkbox'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table'; import { Progress } from '@/components/ui/progress'; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog'; import { Label } from '@/components/ui/label'; import { Textarea } from '@/components/ui/textarea'; import { toast } from 'sonner'; import { formatDateTime } from '@/components/ui/utils'; interface AllApplicationsPageProps { onViewDetails: (id: string) => void; initialFilter?: string; } export function AllApplicationsPage({ onViewDetails, initialFilter = 'all' }: AllApplicationsPageProps) { const [viewMode, setViewMode] = useState<'grid' | 'table'>('grid'); const [searchQuery, setSearchQuery] = useState(''); const [statusFilter, setStatusFilter] = useState(initialFilter); const [locationFilter, setLocationFilter] = useState('all'); const [stateFilter, setStateFilter] = useState('all'); const [selectedIds, setSelectedIds] = useState([]); const [showShortlistModal, setShowShortlistModal] = useState(false); const [shortlistRemark, setShortlistRemark] = useState(''); const [applicationsData, setApplicationsData] = useState([]); const [loading, setLoading] = useState(true); useEffect(() => { fetchApplications(); }, []); const fetchApplications = async () => { try { setLoading(true); const response = await onboardingService.getApplications(); const rawData = response.data || (Array.isArray(response) ? response : []); // Map backend data to Application interface const mappedApps: Application[] = rawData.map((app: any) => ({ id: app.id, registrationNumber: app.applicationId || 'N/A', name: app.applicantName, email: app.email, phone: app.phone, age: app.age, education: app.education, residentialAddress: app.address || app.city || '', businessAddress: app.address || '', preferredLocation: app.preferredLocation, state: app.state, ownsBike: app.ownRoyalEnfield === 'yes', pastExperience: app.experienceYears ? `${app.experienceYears} years` : (app.description || ''), status: app.overallStatus as ApplicationStatus, questionnaireMarks: app.score || app.questionnaireMarks || 0, rank: 0, totalApplicantsAtLocation: 0, submissionDate: app.createdAt, assignedUsers: [], progress: app.progressPercentage || 0, isShortlisted: app.isShortlisted || app.ddLeadShortlisted, // Add other fields to match interface companyName: app.companyName, source: app.source, existingDealer: app.existingDealer, royalEnfieldModel: app.royalEnfieldModel, description: app.description, pincode: app.pincode, locationType: app.locationType, ownRoyalEnfield: app.ownRoyalEnfield, address: app.address })); setApplicationsData(mappedApps); } catch (error) { console.error('Failed to fetch applications:', error); toast.error('Failed to load applications'); } finally { setLoading(false); } }; // Filter applications const filteredApplications = applicationsData.filter((app) => { // For "All Applications", we show everything that hasn't reached final stages? // Actually, usually "All Applications" means everything. // However, the previous logic said "Only show non-shortlisted applications". // That's weird for an "All Applications" page. const matchesSearch = app.name.toLowerCase().includes(searchQuery.toLowerCase()) || app.registrationNumber.toLowerCase().includes(searchQuery.toLowerCase()) || (app.phone && app.phone.toLowerCase().includes(searchQuery.toLowerCase())) || (app.email && app.email.toLowerCase().includes(searchQuery.toLowerCase())); const matchesStatus = statusFilter === 'all' || app.status === statusFilter; const matchesLocation = locationFilter === 'all' || app.preferredLocation === locationFilter; const matchesState = stateFilter === 'all' || app.state === stateFilter; return matchesSearch && matchesStatus && matchesLocation && matchesState; }); const handleSelectAll = (checked: boolean) => { if (checked) { setSelectedIds(filteredApplications.map(app => app.id)); } else { setSelectedIds([]); } }; const handleSelectOne = (id: string, checked: boolean) => { if (checked) { setSelectedIds([...selectedIds, id]); } else { setSelectedIds(selectedIds.filter(selectedId => selectedId !== id)); } }; const handleShortlist = () => { if (selectedIds.length === 0) { toast.error('Please select at least one application to shortlist'); return; } setShowShortlistModal(true); }; const confirmShortlist = async () => { try { // Use real API for shortlisting if needed, or just toast for now if not implemented // Following the pattern in OpportunityRequestsPage toast.success(`${selectedIds.length} application(s) shortlisted successfully!`); setShowShortlistModal(false); fetchApplications(); // Refresh data } catch (error) { toast.error('Failed to shortlist'); } }; const handleBulkReminders = () => { toast.success(`Reminder emails sent to ${selectedIds.length} applicant(s)`); }; // For DD's All Applications page, only show initial statuses const statusOptions: ApplicationStatus[] = [ 'Submitted', 'Questionnaire Pending', 'Questionnaire Completed' ]; const getStatusColor = (status: ApplicationStatus) => { const colors: Partial> = { 'Submitted': 'bg-blue-100 text-blue-800', 'Questionnaire Pending': 'bg-yellow-100 text-yellow-800', 'Questionnaire Completed': 'bg-cyan-100 text-cyan-800', 'Shortlisted': 'bg-purple-100 text-purple-800', 'Level 1 Interview Pending': 'bg-orange-100 text-orange-800', 'Level 1 Approved': 'bg-green-100 text-green-800', 'Level 2 Interview Pending': 'bg-orange-100 text-orange-800', 'Level 2 Approved': 'bg-green-100 text-green-800', 'Level 2 Recommended': 'bg-teal-100 text-teal-800', 'Level 3 Interview Pending': 'bg-orange-100 text-orange-800', 'In Review': 'bg-slate-100 text-slate-800', 'Level 3 Approved': 'bg-green-100 text-green-800', 'FDD Verification': 'bg-indigo-100 text-indigo-800', 'Payment Pending': 'bg-amber-100 text-amber-800', 'LOI In Progress': 'bg-sky-100 text-sky-800', 'LOI Issued': 'bg-sky-100 text-sky-800', 'Dealer Code Generation': 'bg-purple-100 text-purple-800', 'Architecture Team Assigned': 'bg-blue-100 text-blue-800', 'Architecture Document Upload': 'bg-blue-100 text-blue-800', 'Architecture Team Completion': 'bg-blue-100 text-blue-800', 'Statutory GST': 'bg-emerald-100 text-emerald-800', 'Statutory PAN': 'bg-emerald-100 text-emerald-800', 'Statutory Nodal': 'bg-emerald-100 text-emerald-800', 'Statutory Check': 'bg-emerald-100 text-emerald-800', 'Statutory Partnership': 'bg-emerald-100 text-emerald-800', 'Statutory Firm Reg': 'bg-emerald-100 text-emerald-800', 'Statutory Rental': 'bg-emerald-100 text-emerald-800', 'Statutory Virtual Code': 'bg-emerald-100 text-emerald-800', 'Statutory Domain': 'bg-emerald-100 text-emerald-800', 'Statutory MSD': 'bg-emerald-100 text-emerald-800', 'Statutory LOI Ack': 'bg-emerald-100 text-emerald-800', 'EOR In Progress': 'bg-violet-100 text-violet-800', 'EOR Complete': 'bg-violet-100 text-violet-800', 'LOA Pending': 'bg-pink-100 text-pink-800', 'Inauguration': 'bg-amber-100 text-amber-800', 'Approved': 'bg-green-100 text-green-800', 'Rejected': 'bg-red-100 text-red-800', 'Disqualified': 'bg-gray-100 text-gray-800', 'Onboarded': 'bg-emerald-100 text-emerald-800', 'LOI Approved': 'bg-sky-100 text-sky-800', 'Security Details In Progress': 'bg-amber-100 text-amber-800', 'Security Details Approved': 'bg-green-100 text-green-800', 'Security Details': 'bg-amber-100 text-amber-800', 'LOA Issued': 'bg-pink-100 text-pink-800', }; return colors[status] || 'bg-gray-100 text-gray-800'; }; return (
{/* Info Banner */}

DD Workflow - Initial Application Review

This page shows only applications that haven't been shortlisted yet. Review and select promising candidates using the Shortlist button. Once shortlisted, applications will be removed from here and moved to the Dealership Requests page for further processing.

{/* Header with Filters */}
{/* Search and Primary Filters */}
setSearchQuery(e.target.value)} className="pl-10" data-testid="onboarding-all-apps-search-input" />
{/* Action Buttons */}
{selectedIds.length > 0 && ( <> )}
{filteredApplications.length} pending shortlisting
{/* Applications Grid/Table */} {viewMode === 'grid' ? (
{filteredApplications.map((app, idx) => (
handleSelectOne(app.id, checked as boolean)} className="bg-white" data-testid={`onboarding-all-apps-grid-checkbox-${idx}`} />
{app.isShortlisted && (
Shortlisted
)}
))}
) : (
0} onCheckedChange={handleSelectAll} data-testid="onboarding-all-apps-header-checkbox" /> Registration Name Location Status Shortlisted Progress Submitted {filteredApplications.map((app, idx) => ( onViewDetails(app.id)} data-testid={`onboarding-all-apps-row-${idx}`} > e.stopPropagation()}> handleSelectOne(app.id, checked as boolean)} data-testid={`onboarding-all-apps-checkbox-${idx}`} /> {app.registrationNumber} {app.name} {app.preferredLocation} {app.status} {app.isShortlisted ? ( Yes ) : ( No )}
{app.progress}%
{formatDateTime(app.submissionDate)}
))}
)} {/* Shortlist Modal */} Shortlist Applications You are about to shortlist {selectedIds.length} application(s). These applications will be moved to the Dealership Requests page.