From 078afa3590f292b1f1ba9dd6a0635e977ba74dfe Mon Sep 17 00:00:00 2001 From: laxman h Date: Wed, 8 Apr 2026 19:02:47 +0530 Subject: [PATCH] fdd againseparated from normal dashboard and fdd approval moved to admin authority --- src/App.tsx | 3 + src/api/API.ts | 5 + .../applications/ApplicationDetails.tsx | 654 ++++++++++++------ .../applications/FDDApplicationDetails.tsx | 143 ++-- .../applications/FinanceFnFDetailsPage.tsx | 6 +- .../applications/FinanceOnboardingPage.tsx | 119 +--- .../FinancePaymentDetailsPage.tsx | 31 +- src/components/applications/FnFPage.tsx | 24 +- .../ProspectiveApplicationDetails.tsx | 4 +- src/components/dashboard/FDDDashboardPage.tsx | 2 +- src/components/dashboard/FinanceDashboard.tsx | 49 +- src/components/ui/DocumentPreviewModal.tsx | 2 +- src/services/onboarding.service.ts | 15 + 13 files changed, 650 insertions(+), 407 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 0578268..7f7e5df 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -15,6 +15,7 @@ import { FinanceDashboard } from './components/dashboard/FinanceDashboard'; import { DealerDashboard } from './components/dashboard/DealerDashboard'; import { ProspectiveDashboardPage } from './components/dashboard/ProspectiveDashboardPage'; import { FDDDashboardPage } from './components/dashboard/FDDDashboardPage'; +import { FDDApplicationDetails } from './components/applications/FDDApplicationDetails'; import { ApplicationsPage } from './components/applications/ApplicationsPage'; import { AllApplicationsPage } from './components/applications/AllApplicationsPage'; import { OpportunityRequestsPage } from './components/applications/OpportunityRequestsPage'; @@ -149,6 +150,7 @@ export default function App() { '/questionnaire-builder': 'Questionnaire Builder', '/approval-policies': 'Approval Policies', '/fdd-dashboard': 'FDD Dashboard', + '/fdd-details': 'Audit Workspace', }; return titles[pathname] || 'Dashboard'; }; @@ -228,6 +230,7 @@ export default function App() { {/* FDD Routes - Integrated into Layout */} } /> + } /> {/* Admin/Lead Routes */} navigate(`/applications/${id}`)} />} /> diff --git a/src/api/API.ts b/src/api/API.ts index 5c24a21..2fe5a79 100644 --- a/src/api/API.ts +++ b/src/api/API.ts @@ -180,6 +180,11 @@ export const API = { getEorChecklistForRelocation: (relocationId: string) => client.get(`/eor/relocation/${relocationId}`), updateEorChecklistItem: (checklistId: string, data: any) => client.post(`/eor/item/${checklistId}`, data), submitEorAudit: (checklistId: string, data: any) => client.post(`/eor/audit/${checklistId}`, data), + + // FDD module additional routes + submitFddReport: (data: any) => client.post('/fdd/report', data), + getFddAssignment: (applicationId: string) => client.get(`/fdd/${applicationId}`), + assignFddAgency: (data: any) => client.post('/fdd/assign', data), }; export default API; diff --git a/src/components/applications/ApplicationDetails.tsx b/src/components/applications/ApplicationDetails.tsx index b726601..3fdeddd 100644 --- a/src/components/applications/ApplicationDetails.tsx +++ b/src/components/applications/ApplicationDetails.tsx @@ -261,10 +261,20 @@ export const ApplicationDetails = () => { // const application = mockApplications.find(app => app.id === applicationId); const [application, setApplication] = useState(null); const [loading, setLoading] = useState(true); + const [documents, setDocuments] = useState([]); - const fetchApplication = async () => { + const refreshDocuments = async () => { try { - setLoading(true); + const docs = await onboardingService.getDocuments(applicationId!); + setDocuments(docs || []); + } catch (error) { + console.error('Failed to refresh documents:', error); + } + }; + + const fetchApplication = async (silent = false) => { + try { + if (!silent) setLoading(true); const data = await onboardingService.getApplicationById(applicationId!); // Helper to find stage date @@ -345,6 +355,9 @@ export const ApplicationDetails = () => { constitutionType: data.constitutionType, }; setApplication(mappedApp); + if (data.uploadedDocuments) { + setDocuments(data.uploadedDocuments || []); + } } catch (error) { console.error('Failed to fetch application details', error); } finally { @@ -355,6 +368,7 @@ export const ApplicationDetails = () => { useEffect(() => { if (applicationId) { fetchApplication(); + refreshDocuments(); } }, [applicationId]); @@ -374,7 +388,9 @@ export const ApplicationDetails = () => { }; useEffect(() => { - if (['EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application?.status || '')) { + // Always fetch EOR data regardless of status — documents can be uploaded + // before 'EOR In Progress' and must show as linked in the checklist. + if (applicationId) { fetchEorData(); } }, [applicationId, application?.status]); @@ -404,11 +420,11 @@ export const ApplicationDetails = () => { // Fetch audit logs when application loads useEffect(() => { - if (applicationId) { + if (application?.id) { const fetchAuditLogs = async () => { setAuditLoading(true); try { - const logs = await auditService.getAuditLogs('application', applicationId); + const logs = await auditService.getAuditLogs('application', application.id); setAuditLogs(Array.isArray(logs) ? logs : []); } catch (error) { console.error('Failed to fetch audit logs', error); @@ -419,7 +435,7 @@ export const ApplicationDetails = () => { }; fetchAuditLogs(); } - }, [applicationId]); + }, [application?.id]); const routerLocation = useLocation(); const [activeTab, setActiveTab] = useState(routerLocation.state?.activeTab || 'questionnaire'); @@ -450,7 +466,6 @@ export const ApplicationDetails = () => { const [interviewType, setInterviewType] = useState('level1'); const [meetingLink, setMeetingLink] = useState(''); const [location, setLocation] = useState(''); - const [documents, setDocuments] = useState([]); const [showUploadForm, setShowUploadForm] = useState(false); // Toggle for upload view const [uploadFile, setUploadFile] = useState(null); const [uploadDocType, setUploadDocType] = useState(''); @@ -473,6 +488,38 @@ export const ApplicationDetails = () => { // Fetch document configurations + const [fddAgencies, setFddAgencies] = useState([]); + const [selectedAgencyId, setSelectedAgencyId] = useState(''); + const [isAssigningAgency, setIsAssigningAgency] = useState(false); + + const fetchFddAgencies = async () => { + try { + const agencies = await onboardingService.getUsers({ roleCode: 'FDD' }); + setFddAgencies(Array.isArray(agencies) ? agencies : []); + } catch (error) { + console.error('Failed to fetch FDD agencies', error); + } + }; + + const handleAssignAgency = async () => { + if (!selectedAgencyId) { + toast.warning('Please select an agency'); + return; + } + try { + setIsAssigningAgency(true); + await onboardingService.assignFddAgency({ + applicationId: application?.id || applicationId, + assignedToAgency: selectedAgencyId + }); + toast.success('FDD Agency assigned successfully'); + fetchApplication(); + } catch (error) { + toast.error('Failed to assign agency'); + } finally { + setIsAssigningAgency(false); + } + }; // Fetch document configurations useEffect(() => { const fetchConfigs = async () => { @@ -513,7 +560,7 @@ export const ApplicationDetails = () => { // FDD Partner States const [showFddFinalizeModal, setShowFddFinalizeModal] = useState(false); const [showFddFlagModal, setShowFddFlagModal] = useState(false); - const [fddAuditRecommendation, setFddAuditRecommendation] = useState('Green'); + const [fddAuditRecommendation, setFddAuditRecommendation] = useState('Recommended'); const [fddAuditFindings, setFddAuditFindings] = useState(''); const [isFinalizingFdd, setIsFinalizingFdd] = useState(false); const [isFddFlagging, setIsFddFlagging] = useState(false); @@ -798,16 +845,11 @@ export const ApplicationDetails = () => { }; useEffect(() => { - if ((activeTab === 'documents' || activeTab === 'progress') && applicationId) { - const fetchDocuments = async () => { - try { - const docs = await onboardingService.getDocuments(applicationId); - setDocuments(docs || []); - } catch (error) { - console.error('Failed to fetch documents', error); - } - }; - fetchDocuments(); + if (['documents', 'progress', 'fdd', 'eor'].includes(activeTab) && applicationId) { + refreshDocuments(); + } + if (activeTab === 'fdd' && (currentUser?.role === 'DD Admin' || currentUser?.role === 'Super Admin')) { + fetchFddAgencies(); } }, [activeTab, applicationId]); @@ -978,8 +1020,11 @@ export const ApplicationDetails = () => { const getStageStatus = (stageName: string, fallbackLogic: () => ProcessStage['status']): ProcessStage['status'] => { const backendStage = (application.progressTracking || []).find((ps: any) => ps.stageName === stageName); - if (backendStage && (backendStage.status === 'completed' || backendStage.status === 'active')) { - return backendStage.status as any; + if (backendStage) { + if (backendStage.status === 'completed' || backendStage.status === 'active') { + return backendStage.status as any; + } + // If backend says 'pending' fall through to fallback — it may have richer context } return fallbackLogic(); }; @@ -997,7 +1042,7 @@ export const ApplicationDetails = () => { id: 2, name: 'Questionnaire', status: getStageStatus('Questionnaire', () => - ['Questionnaire Completed', 'Shortlisted', 'Level 1 Interview Pending', 'Level 1 Approved', 'Level 2 Interview Pending', 'Level 2 Approved', 'Level 2 Recommended', 'Level 3 Interview Pending', 'Level 3 Approved', 'FDD Verification', 'LOI In Progress', 'Payment Pending', 'LOI Issued', 'Statutory LOI Ack', 'Dealer Code Generation', 'Architecture Team Assigned', 'Architecture Document Upload', 'Architecture Team Completion', 'LOA Pending', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) ? 'completed' : + ['Questionnaire Completed', 'Shortlisted', 'Level 1 Interview Pending', 'Level 1 Approved', 'Level 2 Interview Pending', 'Level 2 Approved', 'Level 2 Recommended', 'Level 3 Interview Pending', 'Level 3 Approved', 'FDD Verification', 'LOI In Progress', 'Payment Pending', 'LOI Issued', 'Statutory LOI Ack', 'Dealer Code Generation', 'Architecture Team Assigned', 'Architecture Document Upload', 'Architecture Team Completion', 'LOA Pending', 'LOA Issued', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) ? 'completed' : application.status === 'Questionnaire Pending' ? 'active' : 'pending' ), date: application.questionnaireDate, @@ -1007,7 +1052,7 @@ export const ApplicationDetails = () => { { id: 3, name: 'Shortlist', - status: getStageStatus('Shortlist', () => ['Shortlisted', 'Level 1 Interview Pending', 'Level 1 Approved', 'Level 2 Interview Pending', 'Level 2 Approved', 'Level 2 Recommended', 'Level 3 Interview Pending', 'Level 3 Approved', 'FDD Verification', 'LOI In Progress', 'Payment Pending', 'LOI Issued', 'Statutory LOI Ack', 'Dealer Code Generation', 'Architecture Team Assigned', 'Architecture Document Upload', 'Architecture Team Completion', 'LOA Pending', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Rejected', 'Onboarded'].includes(application.status) ? 'completed' : 'pending'), + status: getStageStatus('Shortlist', () => ['Shortlisted', 'Level 1 Interview Pending', 'Level 1 Approved', 'Level 2 Interview Pending', 'Level 2 Approved', 'Level 2 Recommended', 'Level 3 Interview Pending', 'Level 3 Approved', 'FDD Verification', 'LOI In Progress', 'Payment Pending', 'LOI Issued', 'Statutory LOI Ack', 'Dealer Code Generation', 'Architecture Team Assigned', 'Architecture Document Upload', 'Architecture Team Completion', 'LOA Pending', 'LOA Issued', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Rejected', 'Onboarded'].includes(application.status) ? 'completed' : 'pending'), date: application.shortlistDate, description: 'Application shortlisted by DD', evaluators: Array.from(new Set((application.participants || []) @@ -1019,7 +1064,7 @@ export const ApplicationDetails = () => { { id: 4, name: '1st Level Interview', - status: getStageStatus('1st Level Interview', () => ['Level 1 Approved', 'Level 2 Interview Pending', 'Level 2 Approved', 'Level 2 Recommended', 'Level 3 Interview Pending', 'Level 3 Approved', 'FDD Verification', 'LOI In Progress', 'Payment Pending', 'LOI Issued', 'Statutory LOI Ack', 'Dealer Code Generation', 'Architecture Team Assigned', 'Architecture Document Upload', 'Architecture Team Completion', 'LOA Pending', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) ? 'completed' : application.status === 'Level 1 Interview Pending' ? 'active' : application.status === 'Rejected' && application.progress >= 40 ? 'completed' : 'pending'), + status: getStageStatus('1st Level Interview', () => ['Level 1 Approved', 'Level 2 Interview Pending', 'Level 2 Approved', 'Level 2 Recommended', 'Level 3 Interview Pending', 'Level 3 Approved', 'FDD Verification', 'LOI In Progress', 'Payment Pending', 'LOI Issued', 'Statutory LOI Ack', 'Dealer Code Generation', 'Architecture Team Assigned', 'Architecture Document Upload', 'Architecture Team Completion', 'LOA Pending', 'LOA Issued', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) ? 'completed' : application.status === 'Level 1 Interview Pending' ? 'active' : application.status === 'Rejected' && application.progress >= 40 ? 'completed' : 'pending'), date: application.level1InterviewDate, description: 'DD-ZM + RBM evaluation', evaluators: Array.from(new Set((application.participants || []) @@ -1036,7 +1081,7 @@ export const ApplicationDetails = () => { { id: 5, name: '2nd Level Interview', - status: getStageStatus('2nd Level Interview', () => ['Level 2 Approved', 'Level 2 Recommended', 'Level 3 Interview Pending', 'Level 3 Approved', 'FDD Verification', 'LOI In Progress', 'Payment Pending', 'LOI Issued', 'Statutory LOI Ack', 'Dealer Code Generation', 'Architecture Team Assigned', 'Architecture Document Upload', 'Architecture Team Completion', 'LOA Pending', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) ? 'completed' : application.status === 'Level 2 Interview Pending' ? 'active' : application.status === 'Rejected' && application.progress >= 55 ? 'completed' : 'pending'), + status: getStageStatus('2nd Level Interview', () => ['Level 2 Approved', 'Level 2 Recommended', 'Level 3 Interview Pending', 'Level 3 Approved', 'FDD Verification', 'LOI In Progress', 'Payment Pending', 'LOI Issued', 'Statutory LOI Ack', 'Dealer Code Generation', 'Architecture Team Assigned', 'Architecture Document Upload', 'Architecture Team Completion', 'LOA Pending', 'LOA Issued', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) ? 'completed' : application.status === 'Level 2 Interview Pending' ? 'active' : application.status === 'Rejected' && application.progress >= 55 ? 'completed' : 'pending'), date: application.level2InterviewDate, description: 'DD Lead + ZBH evaluation', evaluators: Array.from(new Set((application.participants || []) @@ -1053,7 +1098,7 @@ export const ApplicationDetails = () => { { id: 6, name: '3rd Level Interview', - status: getStageStatus('3rd Level Interview', () => ['Level 3 Approved', 'FDD Verification', 'LOI In Progress', 'Payment Pending', 'LOI Issued', 'Statutory LOI Ack', 'Dealer Code Generation', 'Architecture Team Assigned', 'Architecture Document Upload', 'Architecture Team Completion', 'LOA Pending', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) ? 'completed' : application.status === 'Level 3 Interview Pending' ? 'active' : application.status === 'Rejected' && application.progress >= 70 ? 'completed' : 'pending'), + status: getStageStatus('3rd Level Interview', () => ['Level 3 Approved', 'FDD Verification', 'LOI In Progress', 'Payment Pending', 'LOI Issued', 'Statutory LOI Ack', 'Dealer Code Generation', 'Architecture Team Assigned', 'Architecture Document Upload', 'Architecture Team Completion', 'LOA Pending', 'LOA Issued', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) ? 'completed' : application.status === 'Level 3 Interview Pending' ? 'active' : application.status === 'Rejected' && application.progress >= 70 ? 'completed' : 'pending'), date: application.level3InterviewDate, description: 'NBH + DD Head evaluation', evaluators: Array.from(new Set((application.participants || []) @@ -1070,7 +1115,7 @@ export const ApplicationDetails = () => { { id: 7, name: 'FDD', - status: getStageStatus('FDD', () => ['LOI In Progress', 'Payment Pending', 'LOI Issued', 'Statutory LOI Ack', 'Dealer Code Generation', 'Architecture Team Assigned', 'Architecture Document Upload', 'Architecture Team Completion', 'LOA Pending', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) ? 'completed' : application.status === 'FDD Verification' ? 'active' : 'pending'), + status: getStageStatus('FDD', () => ['LOI In Progress', 'Payment Pending', 'LOI Issued', 'Statutory LOI Ack', 'Dealer Code Generation', 'Architecture Team Assigned', 'Architecture Document Upload', 'Architecture Team Completion', 'LOA Pending', 'LOA Issued', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) ? 'completed' : application.status === 'FDD Verification' ? 'active' : 'pending'), date: application.fddDate, description: 'Financial Due Diligence', documentsUploaded: 5 @@ -1078,7 +1123,7 @@ export const ApplicationDetails = () => { { id: 8, name: 'LOI Approval', - status: getStageStatus('LOI Approval', () => ['Payment Pending', 'LOI Issued', 'Statutory LOI Ack', 'Dealer Code Generation', 'Architecture Team Assigned', 'Architecture Document Upload', 'Architecture Team Completion', 'LOA Pending', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) ? 'completed' : application.status === 'LOI In Progress' ? 'active' : 'pending'), + status: getStageStatus('LOI Approval', () => ['Payment Pending', 'LOI Issued', 'Statutory LOI Ack', 'Dealer Code Generation', 'Architecture Team Assigned', 'Architecture Document Upload', 'Architecture Team Completion', 'LOA Pending', 'LOA Issued', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) ? 'completed' : application.status === 'LOI In Progress' ? 'active' : 'pending'), date: application.loiApprovalDate, description: 'Letter of Intent approval', evaluators: Array.from(new Set((application.participants || []) @@ -1093,7 +1138,7 @@ export const ApplicationDetails = () => { { id: 9, name: 'Security Details', - status: getStageStatus('Security Details', () => ['LOI Issued', 'Statutory LOI Ack', 'Dealer Code Generation', 'Architecture Team Assigned', 'Architecture Document Upload', 'Architecture Team Completion', 'LOA Pending', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) ? 'completed' : application.status === 'Payment Pending' ? 'active' : 'pending'), + status: getStageStatus('Security Details', () => ['LOI Issued', 'Statutory LOI Ack', 'Dealer Code Generation', 'Architecture Team Assigned', 'Architecture Document Upload', 'Architecture Team Completion', 'LOA Pending', 'LOA Issued', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) ? 'completed' : application.status === 'Payment Pending' ? 'active' : 'pending'), date: application.securityDetailsDate, description: 'Security verification', documentsUploaded: 3 @@ -1101,7 +1146,7 @@ export const ApplicationDetails = () => { { id: 10, name: 'LOI Issue', - status: getStageStatus('LOI Issue', () => ['Statutory LOI Ack', 'Dealer Code Generation', 'Architecture Team Assigned', 'Architecture Document Upload', 'Architecture Team Completion', 'LOA Pending', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) ? 'completed' : application.status === 'LOI Issued' ? 'active' : 'pending'), + status: getStageStatus('LOI Issue', () => ['LOI Issued', 'Statutory LOI Ack', 'Dealer Code Generation', 'Architecture Team Assigned', 'Architecture Document Upload', 'Architecture Team Completion', 'LOA Pending', 'LOA Issued', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) ? 'completed' : 'pending'), date: application.loiIssueDate, description: 'Letter of Intent issued', documentsUploaded: 1 @@ -1109,7 +1154,7 @@ export const ApplicationDetails = () => { { id: 11, name: 'Dealer Code Generation', - status: getStageStatus('Dealer Code Generation', () => (application.dealerCode || ['Architecture Team Assigned', 'Architecture Document Upload', 'Architecture Team Completion', 'Statutory GST', 'Statutory PAN', 'Statutory NODAL', 'Statutory NODAL', 'Statutory Check', 'Statutory Partnership', 'Statutory Firm Reg', 'Statutory Rental', 'Statutory Virtual Code', 'Statutory Domain', 'Statutory MSD', 'Statutory LOI Ack', 'LOA Pending', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status)) ? 'completed' : application.status === 'Dealer Code Generation' ? 'active' : 'pending'), + status: getStageStatus('Dealer Code Generation', () => (application.dealerCode || ['Dealer Code Generation', 'Architecture Team Assigned', 'Architecture Document Upload', 'Architecture Team Completion', 'Statutory GST', 'Statutory PAN', 'Statutory NODAL', 'Statutory NODAL', 'Statutory Check', 'Statutory Partnership', 'Statutory Firm Reg', 'Statutory Rental', 'Statutory Virtual Code', 'Statutory Domain', 'Statutory MSD', 'Statutory LOI Ack', 'LOA Pending', 'LOA Issued', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status)) ? 'completed' : 'pending'), date: application.dealerCodeDate, description: 'Dealer code generated and assigned', documentsUploaded: 0, @@ -1122,7 +1167,7 @@ export const ApplicationDetails = () => { { id: '11a-1', name: 'Assigned to Architecture Team', - status: application.architectureAssignedTo || ['Architecture Document Upload', 'Architecture Team Completion', 'Statutory GST', 'Statutory PAN', 'Statutory Nodal', 'Statutory Check', 'Statutory Partnership', 'Statutory Firm Reg', 'Statutory Rental', 'Statutory Virtual Code', 'Statutory Domain', 'Statutory MSD', 'Statutory LOI Ack', 'LOA Pending', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) || application.architectureStatus === 'COMPLETED' || isDocumentUploaded('Architecture Assignment Document') ? 'completed' : application.status === 'Architecture Team Assigned' ? 'active' : 'pending', + status: application.architectureAssignedTo || ['Architecture Document Upload', 'Architecture Team Completion', 'Statutory GST', 'Statutory PAN', 'Statutory Nodal', 'Statutory Check', 'Statutory Partnership', 'Statutory Firm Reg', 'Statutory Rental', 'Statutory Virtual Code', 'Statutory Domain', 'Statutory MSD', 'Statutory LOI Ack', 'LOA Pending', 'LOA Issued', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) || application.architectureStatus === 'COMPLETED' || isDocumentUploaded('Architecture Assignment Document') ? 'completed' : application.status === 'Architecture Team Assigned' ? 'active' : 'pending', date: application.architectureAssignedDate, description: 'Assigned to architecture team for site planning', documentsUploaded: 0 @@ -1130,7 +1175,7 @@ export const ApplicationDetails = () => { { id: '11a-2', name: 'Architectural Document Upload', - status: isDocumentUploaded('Architecture Blueprint') || isDocumentUploaded('Site Plan') || ['Architecture Team Completion', 'LOA Pending', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) || application.architectureStatus === 'COMPLETED' ? 'completed' : (application.architectureAssignedTo || application.status === 'Architecture Document Upload' || application.architectureStatus === 'IN_PROGRESS') ? 'active' : 'pending', + status: isDocumentUploaded('Architecture Blueprint') || isDocumentUploaded('Site Plan') || ['Architecture Team Completion', 'LOA Pending', 'LOA Issued', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) || application.architectureStatus === 'COMPLETED' ? 'completed' : (application.architectureAssignedTo || application.status === 'Architecture Document Upload' || application.architectureStatus === 'IN_PROGRESS') ? 'active' : 'pending', date: application.architectureDocumentDate, description: 'Architectural documents and blueprints uploaded', documentsUploaded: (documents || []).filter(d => ['Architecture Blueprint', 'Site Plan'].includes(d.documentType)).length @@ -1138,7 +1183,7 @@ export const ApplicationDetails = () => { { id: '11a-3', name: 'Architecture Team Completion', - status: ['LOA Pending', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) || application.architectureStatus === 'COMPLETED' || isDocumentUploaded('Architecture Completion Certificate') ? 'completed' : application.status === 'Architecture Team Completion' ? 'active' : 'pending', + status: ['LOA Pending', 'LOA Issued', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) || application.architectureStatus === 'COMPLETED' || isDocumentUploaded('Architecture Completion Certificate') ? 'completed' : application.status === 'Architecture Team Completion' ? 'active' : 'pending', date: application.architectureCompletionDate, description: 'Architecture team work completed', documentsUploaded: 0 @@ -1152,77 +1197,77 @@ export const ApplicationDetails = () => { { id: '11b-1', name: 'GST', - status: isDocumentUploaded('GST Certificate') || ['Statutory PAN', 'Statutory Nodal', 'Statutory Check', 'Statutory Partnership', 'Statutory Firm Reg', 'Statutory Rental', 'Statutory Virtual Code', 'Statutory Domain', 'Statutory MSD', 'Statutory LOI Ack', 'LOA Pending', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) ? 'completed' : application.status === 'Statutory GST' ? 'active' : 'pending', + status: isDocumentUploaded('GST Certificate') || ['Statutory PAN', 'Statutory Nodal', 'Statutory Check', 'Statutory Partnership', 'Statutory Firm Reg', 'Statutory Rental', 'Statutory Virtual Code', 'Statutory Domain', 'Statutory MSD', 'Statutory LOI Ack', 'LOA Pending', 'LOA Issued', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) ? 'completed' : application.status === 'Statutory GST' ? 'active' : 'pending', description: 'GST certificate', documentsUploaded: (documents || []).filter(d => d.documentType === 'GST Certificate').length }, { id: '11b-2', name: 'PAN', - status: isDocumentUploaded('PAN Card') || ['Statutory Nodal', 'Statutory Check', 'Statutory Partnership', 'Statutory Firm Reg', 'Statutory Rental', 'Statutory Virtual Code', 'Statutory Domain', 'Statutory MSD', 'Statutory LOI Ack', 'LOA Pending', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) ? 'completed' : application.status === 'Statutory PAN' ? 'active' : 'pending', + status: isDocumentUploaded('PAN Card') || ['Statutory Nodal', 'Statutory Check', 'Statutory Partnership', 'Statutory Firm Reg', 'Statutory Rental', 'Statutory Virtual Code', 'Statutory Domain', 'Statutory MSD', 'Statutory LOI Ack', 'LOA Pending', 'LOA Issued', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) ? 'completed' : application.status === 'Statutory PAN' ? 'active' : 'pending', description: 'PAN card', documentsUploaded: (documents || []).filter(d => d.documentType === 'PAN Card').length }, { id: '11b-3', name: 'Nodal Agreement', - status: isDocumentUploaded('Nodal Agreement') || ['Statutory Check', 'Statutory Partnership', 'Statutory Firm Reg', 'Statutory Rental', 'Statutory Virtual Code', 'Statutory Domain', 'Statutory MSD', 'Statutory LOI Ack', 'LOA Pending', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) ? 'completed' : application.status === 'Statutory Nodal' ? 'active' : 'pending', + status: isDocumentUploaded('Nodal Agreement') || ['Statutory Check', 'Statutory Partnership', 'Statutory Firm Reg', 'Statutory Rental', 'Statutory Virtual Code', 'Statutory Domain', 'Statutory MSD', 'Statutory LOI Ack', 'LOA Pending', 'LOA Issued', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) ? 'completed' : application.status === 'Statutory Nodal' ? 'active' : 'pending', description: 'Nodal agreement document', documentsUploaded: (documents || []).filter(d => d.documentType === 'Nodal Agreement').length }, { id: '11b-4', name: 'Cancelled Check', - status: isDocumentUploaded('Cancelled Check') || ['Statutory Partnership', 'Statutory Firm Reg', 'Statutory Rental', 'Statutory Virtual Code', 'Statutory Domain', 'Statutory MSD', 'Statutory LOI Ack', 'LOA Pending', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) ? 'completed' : application.status === 'Statutory Check' ? 'active' : 'pending', + status: isDocumentUploaded('Cancelled Check') || ['Statutory Partnership', 'Statutory Firm Reg', 'Statutory Rental', 'Statutory Virtual Code', 'Statutory Domain', 'Statutory MSD', 'Statutory LOI Ack', 'LOA Pending', 'LOA Issued', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) ? 'completed' : application.status === 'Statutory Check' ? 'active' : 'pending', description: 'Cancelled check copy', documentsUploaded: (documents || []).filter(d => d.documentType === 'Cancelled Check').length }, { id: '11b-5', name: 'Partnership Deed/LLP/MOA/AOA/COI', - status: isDocumentUploaded('Partnership Deed') || isDocumentUploaded('LLP Agreement') || isDocumentUploaded('Certificate of Incorporation') || isDocumentUploaded('MOA') || isDocumentUploaded('AOA') || ['Statutory Firm Reg', 'Statutory Rental', 'Statutory Virtual Code', 'Statutory Domain', 'Statutory MSD', 'Statutory LOI Ack', 'LOA Pending', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) ? 'completed' : application.status === 'Statutory Partnership' ? 'active' : 'pending', + status: isDocumentUploaded('Partnership Deed') || isDocumentUploaded('LLP Agreement') || isDocumentUploaded('Certificate of Incorporation') || isDocumentUploaded('MOA') || isDocumentUploaded('AOA') || ['Statutory Firm Reg', 'Statutory Rental', 'Statutory Virtual Code', 'Statutory Domain', 'Statutory MSD', 'Statutory LOI Ack', 'LOA Pending', 'LOA Issued', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) ? 'completed' : application.status === 'Statutory Partnership' ? 'active' : 'pending', description: 'Business entity documents', documentsUploaded: (documents || []).filter(d => ['Partnership Deed', 'LLP Agreement', 'Certificate of Incorporation', 'MOA', 'AOA'].includes(d.documentType)).length }, { id: '11b-6', name: 'Firm Registration Certificate', - status: isDocumentUploaded('Firm Registration') || ['Statutory Rental', 'Statutory Virtual Code', 'Statutory Domain', 'Statutory MSD', 'Statutory LOI Ack', 'LOA Pending', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) ? 'completed' : application.status === 'Statutory Firm Reg' ? 'active' : 'pending', + status: isDocumentUploaded('Firm Registration') || ['Statutory Rental', 'Statutory Virtual Code', 'Statutory Domain', 'Statutory MSD', 'Statutory LOI Ack', 'LOA Pending', 'LOA Issued', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) ? 'completed' : application.status === 'Statutory Firm Reg' ? 'active' : 'pending', description: 'Firm registration certificate', documentsUploaded: (documents || []).filter(d => d.documentType === 'Firm Registration').length }, { id: '11b-7', name: 'Rental agreement/ Lease agreement / Own/ Land agreement', - status: isDocumentUploaded('Rental Agreement') || isDocumentUploaded('Property Documents') || ['Statutory Virtual Code', 'Statutory Domain', 'Statutory MSD', 'Statutory LOI Ack', 'LOA Pending', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) ? 'completed' : application.status === 'Statutory Rental' ? 'active' : 'pending', + status: isDocumentUploaded('Rental Agreement') || isDocumentUploaded('Property Documents') || ['Statutory Virtual Code', 'Statutory Domain', 'Statutory MSD', 'Statutory LOI Ack', 'LOA Pending', 'LOA Issued', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) ? 'completed' : application.status === 'Statutory Rental' ? 'active' : 'pending', description: 'Property agreement document', documentsUploaded: (documents || []).filter(d => ['Rental Agreement', 'Property Documents'].includes(d.documentType)).length }, { id: '11b-8', name: 'Virtual Code', - status: isDocumentUploaded('Virtual Code Confirmation') || ['Statutory Domain', 'Statutory MSD', 'Statutory LOI Ack', 'LOA Pending', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) ? 'completed' : application.status === 'Statutory Virtual Code' ? 'active' : 'pending', + status: isDocumentUploaded('Virtual Code Confirmation') || ['Statutory Domain', 'Statutory MSD', 'Statutory LOI Ack', 'LOA Pending', 'LOA Issued', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) ? 'completed' : application.status === 'Statutory Virtual Code' ? 'active' : 'pending', description: 'Virtual code availability', documentsUploaded: (documents || []).filter(d => d.documentType === 'Virtual Code Confirmation').length }, { id: '11b-9', name: 'Domain ID', - status: isDocumentUploaded('Domain ID Setup') || ['Statutory MSD', 'Statutory LOI Ack', 'LOA Pending', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) ? 'completed' : application.status === 'Statutory Domain' ? 'active' : 'pending', + status: isDocumentUploaded('Domain ID Setup') || ['Statutory MSD', 'Statutory LOI Ack', 'LOA Pending', 'LOA Issued', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) ? 'completed' : application.status === 'Statutory Domain' ? 'active' : 'pending', description: 'Domain ID setup', documentsUploaded: (documents || []).filter(d => d.documentType === 'Domain ID Setup').length }, { id: '11b-10', name: 'MSD Configuration', - status: isDocumentUploaded('MSD Configuration') || ['Statutory LOI Ack', 'LOA Pending', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) ? 'completed' : application.status === 'Statutory MSD' ? 'active' : 'pending', + status: isDocumentUploaded('MSD Configuration') || ['Statutory LOI Ack', 'LOA Pending', 'LOA Issued', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) ? 'completed' : application.status === 'Statutory MSD' ? 'active' : 'pending', description: 'Microsoft Dynamics configuration', documentsUploaded: (documents || []).filter(d => d.documentType === 'MSD Configuration').length }, { id: '11b-11', name: 'LOI Acknowledgement Copy', - status: isDocumentUploaded('LOI Acknowledgement') || ['LOA Pending', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) ? 'completed' : application.status === 'Statutory LOI Ack' ? 'active' : 'pending', + status: isDocumentUploaded('LOI Acknowledgement') || ['LOA Pending', 'LOA Issued', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) ? 'completed' : application.status === 'Statutory LOI Ack' ? 'active' : 'pending', description: 'LOI acknowledgement copy', documentsUploaded: (documents || []).filter(d => d.documentType === 'LOI Acknowledgement').length } @@ -1236,8 +1281,8 @@ export const ApplicationDetails = () => { status: getStageStatus('LOA', () => ['EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) ? 'completed' : application.status === 'LOA Pending' ? 'active' : 'pending'), isLocked: application.status === 'LOA Pending' && getDeposit('FINAL')?.status !== 'Verified' && - !documents.some(d => (d.documentType?.toLowerCase().includes('final') && d.documentType?.toLowerCase().includes('deposit')) && d.status === 'Approved'), - lockMessage: 'Final Security Deposit (₹15L) must be verified by Finance before LOA Approval.', + !documents.some(d => (d.documentType?.toLowerCase().includes('first') && d.documentType?.toLowerCase().includes('fill')) && d.status === 'Approved'), + lockMessage: 'First Fill (₹15L) must be verified by Finance before LOA Approval.', date: application.loaDate, description: 'Letter of Authorization', evaluators: Array.from(new Set((application.participants || []) @@ -1252,7 +1297,9 @@ export const ApplicationDetails = () => { { id: 13, name: 'EOR Complete', - status: getStageStatus('EOR Complete', () => ['Inauguration', 'Approved', 'Onboarded'].includes(application.status) ? 'completed' : application.status === 'EOR Complete' ? 'active' : 'pending'), + status: getStageStatus('EOR Complete', () => + ['Inauguration', 'Approved', 'Onboarded'].includes(application.status) ? 'completed' : + ['EOR In Progress', 'EOR Complete'].includes(application.status) ? 'active' : 'pending'), date: application.eorCompleteDate, description: 'Essential Operating Requirements completed', documentsUploaded: 6 @@ -1645,8 +1692,12 @@ export const ApplicationDetails = () => { } }; - if (loading) { - return
Loading...
; + if (loading && !application) { + return ( +
+ +
+ ); } if (!application) { @@ -1711,7 +1762,7 @@ export const ApplicationDetails = () => { ['Approved', 'Rejected', 'Selected'].includes(currentUserEvaluation?.recommendation || ''); // Final visibility flags - const isAdmin = currentUser && ['DD Admin', 'Super Admin', 'NBH', 'DD Lead', 'DD Head', 'Finance', 'Finance Admin', 'FDD'].includes(currentUser.role); + const isAdmin = currentUser && ['DD Admin', 'Super Admin', 'NBH', 'DD Lead', 'DD Head', 'Finance', 'Finance Admin', 'FDD', 'ZBH', 'RBM'].includes(currentUser.role); const isAdministrativeStage = [ 'Level 3 Approved', 'FDD Verification', 'LOI In Progress', 'LOI Issued', 'Statutory LOI Ack', @@ -1719,19 +1770,16 @@ export const ApplicationDetails = () => { 'Statutory GST', 'Statutory PAN', 'Statutory Nodal', 'Statutory Check', 'Statutory Partnership', 'Statutory Firm Reg', 'Statutory Rental', 'Statutory Virtual Code', 'Statutory Domain', 'Statutory MSD', - 'LOA Pending', 'EOR Complete', 'Inauguration' + 'LOA Pending', 'EOR In Progress', 'EOR Complete', 'Inauguration' ].includes(application.status); const finalDepositVerified = getDeposit('FINAL')?.status === 'Verified'; const isLoaLocked = application.status === 'LOA Pending' && !finalDepositVerified; // Sequential Enforcement for LOI APPROVAL (SRS 6.16.3.5) - // Approval Chain: Finance -> DD-Head -> NBH + // Approval Chain: DD-Head -> NBH // Sequential Enforcement for LOI APPROVAL (SRS 6.16.3.5) - // Approval Chain: Finance -> DD-Head -> NBH - const financeApproved = application.stageApprovals?.some( - (a: any) => a.stageCode === 'LOI_APPROVAL' && a.actorRole === 'Finance' && a.decision === 'Approved' - ); + // Approval Chain: DD-Head -> NBH const ddHeadApproved = application.stageApprovals?.some( (a: any) => a.stageCode === 'LOI_APPROVAL' && a.actorRole === 'DD Head' && a.decision === 'Approved' ); @@ -1744,14 +1792,18 @@ export const ApplicationDetails = () => { let sequenceMet = true; if (!['Super Admin', 'DD Admin'].includes(currentUser?.role || '')) { + // FDD Stage Restriction: Only DD Admin or Super Admin can approve + if (application.status === 'FDD Verification' || application.status === 'Level 3 Approved') { + sequenceMet = false; + } + // LOI Sequence Enforcement if (application.status === 'LOI In Progress') { - if (currentUser?.role === 'DD Head') sequenceMet = !!financeApproved; if (currentUser?.role === 'NBH') { sequenceMet = !!ddHeadApproved; // NBH can only approve after DD Head } - // Roles not in the sequence (like DD Lead or FDD) should not see the buttons for LOI issuance decision - if (!['Finance', 'DD Head', 'NBH'].includes(currentUser?.role || '')) { + // Roles not in the sequence (like Admin or Finance) should not see the buttons for LOI issuance decision + if (!['DD Head', 'NBH'].includes(currentUser?.role || '')) { sequenceMet = false; } } @@ -1784,43 +1836,107 @@ export const ApplicationDetails = () => { const renderFddAuditContent = () => { const assignments = application?.fddAssignments || []; + const fddParticipants = application?.participants?.filter((p: any) => + p.user?.role === 'FDD' || + p.user?.roleCode === 'FDD' || + p.user?.allRoles?.includes('FDD') + ) || []; + + const hasAssignment = assignments.length > 0 || fddParticipants.length > 0; + const primaryFddUser = fddParticipants[0]?.user; const MANDATORY_FINANCIAL_DOCS = [ { type: 'Bank Statement', label: 'Bank Statements' }, { type: 'Income Tax Returns (ITR)', label: 'ITR (Last 3 Years)' }, - { type: 'Credit Reports', label: 'CIBIL / Credit Reports' }, + { type: 'CIBIL Report', label: 'CIBIL / Credit Reports' }, { type: 'Property Documents', label: 'Property Documents' }, { type: 'Business Valuation Report', label: 'Valuation Reports' } ]; const getDocByTypeName = (typeName: string) => { - // Look in documents array (which is state) - return (documents || []).find((d: any) => d.documentType === typeName); + if (!documents) return null; + const target = typeName.toLowerCase(); + return documents.find((d: any) => { + const docType = (d.documentType || '').toLowerCase(); + const fileName = (d.fileName || '').toLowerCase(); + + if (docType === target) return true; + + if (target.includes('itr') && (docType.includes('itr') || fileName.includes('itr'))) return true; + if (target.includes('bank statement') && (docType.includes('bank') || fileName.includes('bank'))) return true; + if (target.includes('cibil') && (docType.includes('cibil') || fileName.includes('cibil') || docType.includes('credit'))) return true; + + return false; + }); }; - if (assignments.length === 0 && (application.status !== 'FDD Verification' && application.status !== 'LOI In Progress')) { + if (!hasAssignment && !['FDD Verification', 'LOI In Progress', 'Payment Pending'].includes(application.status)) { return ( -
- -

No FDD Assignment

-

- The Financial Due Diligence process has not been initiated for this application yet. -

+
+
+ +

No FDD Assignment

+

+ The Financial Due Diligence process has not been initiated for this application yet. +

+
+ + {(currentUser?.role === 'DD Admin' || currentUser?.role === 'Super Admin') && ( + + + + + Initiate FDD Audit + + + +
+
+ + +
+
+ +
+
+
+
+ )}
); } return (
- {/* FDD Partner Actions (Only visible to FDD role in Active FDD stage) */} - {currentUser?.role === 'FDD' && application.status === 'FDD Verification' && ( + {/* FDD/Finance Audit Workspace */} + {((currentUser?.role === 'FDD' || currentUser?.role === 'DD Admin' || currentUser?.role === 'Super Admin' || hasAssignment) && + (['FDD Verification', 'Level 3 Approved', 'LOI In Progress'].includes(application.status))) && (
-

- - Audit Workspace +

+
+ +
+ Audit Management Workspace {primaryFddUser && Assigned to: {primaryFddUser.name}}

-

Capture findings and upload the final audit report to progress the LOI issuance.

+

Capture financial findings, upload reports, and provide your formal recommendation to progress the application.

{ setIsUploading(true); const formData = new FormData(); formData.append('file', file); - formData.append('documentType', 'FDD Audit Report'); + formData.append('documentType', 'FDD Final Audit Report'); + formData.append('stage', 'FDD'); formData.append('applicationId', application.id); await onboardingService.uploadDocument(application.id, formData); - toast.success('FDD Audit Report uploaded successfully'); - fetchApplication(); + toast.success('FDD Final Audit Report uploaded successfully'); + refreshDocuments(); } catch (err) { toast.error('Upload failed'); } finally { @@ -1854,19 +1971,23 @@ export const ApplicationDetails = () => { {isUploading ? 'Uploading...' : 'Upload Report'} - - + {(currentUser?.role === 'DD Admin' || currentUser?.role === 'Super Admin') && ( + <> + + + + )}
)} @@ -1895,21 +2016,55 @@ export const ApplicationDetails = () => {

{docType.label}

- {doc ? `Uploaded: ${new Date(doc.createdAt).toLocaleDateString()}` : 'Missing in Documentation'} + {doc ? `Uploaded: ${formatDateTime(doc.createdAt)}` : 'Missing in Documentation'}

- {doc && ( + {doc ? (
-
+ ) : ( + )}
); @@ -1964,13 +2119,13 @@ export const ApplicationDetails = () => {
{report.recommendation?.toUpperCase()}
@@ -2051,6 +2206,86 @@ export const ApplicationDetails = () => { ))} + + {/* FDD Supporting Documents Section */} +
+
+

Supporting Audit Documents

+ + {documents.filter(d => { + const type = (d.documentType || '').toLowerCase(); + const stage = (d.stage || '').toLowerCase(); + return stage === 'fdd' || + type.includes('report') || + type.includes('itr') || + type.includes('bank') || + type.includes('cibil') || + type.includes('valuation'); + }).length} Document(s) + +
+ +
+ {documents.filter(d => { + const type = (d.documentType || '').toLowerCase(); + const stage = (d.stage || '').toLowerCase(); + return stage === 'fdd' || + type.includes('report') || + type.includes('itr') || + type.includes('bank') || + type.includes('cibil') || + type.includes('valuation'); + }).map((doc) => ( +
+
+
+ +
+
+

{doc.fileName}

+

{doc.documentType}

+
+
+
+ + +
+
+ ))} + + {documents.filter(d => { + const type = (d.documentType || '').toLowerCase(); + const stage = (d.stage || '').toLowerCase(); + return stage === 'fdd' || + type.includes('report') || + type.includes('itr') || + type.includes('bank') || + type.includes('cibil') || + type.includes('valuation'); + }).length === 0 && ( +
+

No supporting audit documents uploaded yet.

+
+ )} +
+
); @@ -2425,7 +2660,7 @@ export const ApplicationDetails = () => { 4: 2, // L1 Interview (ZM + RBM) 5: 2, // L2 Interview (ZBH + DD Lead) 6: 2, // L3 Interview (NBH + DD Head) - 8: 3, // LOI Approval (Finance + DD Head + NBH) + 8: 2, // LOI Approval (DD Head + NBH) 12: 2 // LOA Approval (DD Head + NBH) }; const stageId = Number(stage.id); @@ -2544,56 +2779,60 @@ export const ApplicationDetails = () => { {branch.stages.map((branchStage) => (
-
-
- {branchStage.status === 'completed' ? ( - - ) : branchStage.status === 'active' ? ( - - ) : ( -
- )} -
-
-
-

{branchStage.name}

- {branchStage.description && ( -

{branchStage.description}

- )} - - {(() => { - const branchDocsCount = documents.filter(doc => - doc.documentType?.toLowerCase().includes(branchStage.name.toLowerCase().split(' ')[0]) || - doc.stage === branchStage.name - ).length; - - return ( -
- + {(() => { + const stageDocs = documents.filter(doc => + doc.documentType?.toLowerCase().includes(branchStage.name.toLowerCase().split(' ')[0]) || + doc.stage === branchStage.name + ); + const isDone = branchStage.status === 'completed' || stageDocs.length > 0; + + return ( + <> +
+
+ {isDone ? ( + + ) : branchStage.status === 'active' ? ( + + ) : ( +
+ )} +
- ); - })()} -

- {branchStage.status === 'completed' && branchStage.date && `Done: ${formatDateTime(branchStage.date)}`} - {branchStage.status === 'active' && 'Evaluating'} - {branchStage.status === 'pending' && 'Pending'} -

-
+
+

{branchStage.name}

+ {branchStage.description && ( +

{branchStage.description}

+ )} + +
+ +
+

+ {isDone && branchStage.date ? `Done: ${formatDateTime(branchStage.date)}` : isDone && stageDocs.length > 0 ? `Uploaded: ${formatDateTime(stageDocs[0].updatedAt || stageDocs[0].createdAt)}` : branchStage.status === 'active' ? 'Evaluating' : 'Pending'} +

+
+ + ); + })()}
))} @@ -2990,7 +3229,7 @@ export const ApplicationDetails = () => { {/* Initial Security Deposit */} {(() => { const deposit = getDeposit('INITIAL'); - const config = paymentConfigs.INITIAL_SECURITY_DEPOSIT; + const config = paymentConfigs.SECURITY_DEPOSIT; const expectedAmount = config?.amount || 500000; return ( @@ -3005,7 +3244,7 @@ export const ApplicationDetails = () => {
- Advance Payment + Security Deposit
{ "{deposit.remarks}" )} + + {/* Respective Documents */} +
+

Verification Documents

+
+ {documents.filter((d: any) => d.documentType?.toLowerCase().includes('security') && d.documentType?.toLowerCase().includes('deposit')).map((doc: any, idx: number) => ( +
+
+ + {doc.fileName || doc.name} +
+ +
+ ))} + {documents.filter((d: any) => d.documentType?.toLowerCase().includes('security') && d.documentType?.toLowerCase().includes('deposit')).length === 0 && ( +

No proof uploaded

+ )} +
+
@@ -3047,7 +3312,7 @@ export const ApplicationDetails = () => { {/* Final Security Deposit */} {(() => { const deposit = getDeposit('FINAL'); - const config = paymentConfigs.FINAL_SECURITY_DEPOSIT; + const config = paymentConfigs.FIRST_FILL; const expectedAmount = config?.amount || 1500000; return ( @@ -3062,7 +3327,7 @@ export const ApplicationDetails = () => {
- Final Security Deposit + First Fill { "{deposit.remarks}" )} + + {/* Respective Documents */} +
+

Verification Documents

+
+ {documents.filter((d: any) => d.documentType?.toLowerCase().includes('first') && d.documentType?.toLowerCase().includes('fill')).map((doc: any, idx: number) => ( +
+
+ + {doc.fileName || doc.name} +
+ +
+ ))} + {documents.filter((d: any) => d.documentType?.toLowerCase().includes('first') && d.documentType?.toLowerCase().includes('fill')).length === 0 && ( +

No proof uploaded

+ )} +
+
); })()} - - {/* Payment Proof Documents */} - - - - - Verification Documents - - - - {documents.filter((d: any) => d.documentType?.toLowerCase().includes('deposit')).length > 0 ? ( -
- {documents.filter((d: any) => d.documentType?.toLowerCase().includes('deposit')).map((doc: any, index: number) => ( -
-
-
- -
-
-

{doc.fileName || doc.name}

-

{doc.documentType}

-
-
- -
- ))} -
- ) : ( -
-

No payment proofs uploaded yet.

-
- )} -
-
@@ -3256,7 +3503,7 @@ export const ApplicationDetails = () => { Stage Locked - Final Security Deposit (₹15L) must be verified by Finance before LOA Approval can proceed. + First Fill (₹15L) must be verified by Finance before LOA Approval can proceed. )} @@ -4671,15 +4918,15 @@ export const ApplicationDetails = () => {
- {['Green', 'Amber', 'Red'].map((rec) => ( + {['Recommended', 'Qualified with Observations', 'Not Recommended'].map((rec) => ( @@ -309,11 +326,12 @@ export function FDDApplicationDetails() { className="w-full px-3 py-2 bg-slate-50 border border-slate-200 rounded text-sm font-medium text-slate-700 outline-none focus:ring-1 focus:ring-blue-500 transition-all" > - - - + + + + - +
@@ -365,7 +383,7 @@ export function FDDApplicationDetails() { APPLICANT

- {doc.documentType} • {new Date(doc.createdAt).toLocaleDateString()} + {doc.documentType} • {formatDateTime(doc.createdAt)} {doc.uploader?.fullName && ` • by ${doc.uploader.fullName}`}

@@ -378,9 +396,6 @@ export function FDDApplicationDetails() { > - - -
))} @@ -409,7 +424,7 @@ export function FDDApplicationDetails() { YOUR AUDIT REPORT

- {doc.documentType} • {new Date(doc.createdAt).toLocaleDateString()} + {doc.documentType} • {formatDateTime(doc.createdAt)} {doc.uploader?.fullName && ` • by ${doc.uploader.fullName}`}

@@ -422,9 +437,6 @@ export function FDDApplicationDetails() { > - - - ))} @@ -476,10 +488,6 @@ export function FDDApplicationDetails() {

{application.email}

{application.phone}

-
-

FDD Due Date

-

April 25, 2026

-
@@ -516,19 +524,52 @@ export function FDDApplicationDetails() {
- Finalize Audit Report + Submit Audit Report - You are about to submit your final findings. This action will lock the report and move the application to the next stage. + You are about to submit your final findings. This action will notify the Admin for review and approval.

- Ensure all required financial documents are uploaded and verified before proceeding. + Once submitted, you cannot edit the findings. Ensure all documents are uploaded.

+
+
+ +
+ {['Recommended', 'Qualified with Observations', 'Not Recommended'].map((rec) => ( + + ))} +
+
+ +
+ +