import { toast } from 'sonner'; import { AlertCircle, Check, CheckCircle, CheckCircle2, ChevronDown, ChevronRight, Clock, ClipboardList, Download, FileText, GitBranch, Lock, RefreshCw, ShieldCheck, Upload, User, } from 'lucide-react'; import { cn, formatDateTime } from '@/components/ui/utils'; import QuestionnaireResponseView from '../QuestionnaireResponseView'; import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardHeader } from '@/components/ui/card'; import { Checkbox } from '@/components/ui/checkbox'; import { Progress } from '@/components/ui/progress'; import { ScrollArea } from '@/components/ui/scroll-area'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; interface ApplicationDetailsTabsProps { application: any; activeTab: string; setActiveTab: (value: string) => void; processStages: any[]; documents: any[]; interviews: any[]; expandedBranches: Record; setExpandedBranches: React.Dispatch>>; setSelectedStage: (value: string | null) => void; setShowDocumentsModal: (value: boolean) => void; setShowUploadForm: (value: boolean) => void; handleRetriggerEvaluators: () => void; handleCancelInterview: (interviewId: any) => void; setSelectedEvaluationForView: (value: any) => void; setShowFeedbackDetailsModal: (value: boolean) => void; renderFddAuditContent: () => React.ReactNode; eorProgress: number; eorData: any; eorChecklist: any[]; setUploadDocType: (value: string) => void; isAdmin: boolean; fetchApplication: () => void; fetchEorData: () => void; deposits: any[]; getDeposit: (type: string) => any; paymentConfigs: any; setPreviewDoc: (value: any) => void; setShowPreviewModal: (value: boolean) => void; auditLoading: boolean; auditLogs: any[]; auditLogActionBadgeClass: (action: string) => string; } export function ApplicationDetailsTabs(props: ApplicationDetailsTabsProps) { const { application, activeTab, setActiveTab, processStages, documents, interviews, expandedBranches, setExpandedBranches, setSelectedStage, setShowDocumentsModal, setShowUploadForm, handleRetriggerEvaluators, handleCancelInterview, setSelectedEvaluationForView, setShowFeedbackDetailsModal, renderFddAuditContent, eorProgress, eorData, eorChecklist, setUploadDocType, isAdmin, fetchApplication, fetchEorData, deposits, getDeposit, paymentConfigs, setPreviewDoc, setShowPreviewModal, auditLoading, auditLogs, auditLogActionBadgeClass, } = props; const normalizeRole = (value: unknown): string => String(value || '') .trim() .toLowerCase() .replace(/[_\s-]+/g, ' '); const participantHasAnyRole = (participant: any, expectedRoles: string[]) => { const participantRoles = [ participant?.user?.role, participant?.user?.roleCode, participant?.metadata?.role, ].map(normalizeRole); const normalizedExpected = expectedRoles.map(normalizeRole); return participantRoles.some((role) => normalizedExpected.includes(role)); }; return (
Questionnaire Progress Documents Interviews FDD Audit EOR Checklist Payments Audit Trail

Application Journey

{application.progress}% Complete
{(() => { const interviewRoleMap: Record = { 1: ['DD-ZM', 'RBM'], 2: ['DD Lead', 'ZBH'], 3: ['NBH', 'DD Head'], }; const stageRoleMap: Record = { LOI_APPROVAL: ['DD Head', 'NBH'], LOA_APPROVAL: ['DD Head', 'NBH'], }; const getApproverStatus = (stageCode: string | number) => { const stageParticipants = (application.participants || []).filter((p: any) => { const metadataMatch = p.metadata?.stageCode === stageCode || p.metadata?.allAssignments?.includes(stageCode) || (typeof stageCode === 'number' && (p.metadata?.interviewLevel === stageCode || p.metadata?.interviewLevel === String(stageCode) || p.metadata?.allAssignments?.includes(stageCode) || p.metadata?.allAssignments?.includes(String(stageCode)))) || (typeof stageCode === 'string' && !isNaN(Number(stageCode)) && (p.metadata?.interviewLevel === Number(stageCode) || p.metadata?.allAssignments?.includes(Number(stageCode)))); if (metadataMatch) return true; if (typeof stageCode === 'number') { return participantHasAnyRole(p, interviewRoleMap[stageCode] || []); } return participantHasAnyRole(p, stageRoleMap[stageCode] || []); }); return stageParticipants.map((p: any) => { const saCode = typeof stageCode === 'number' ? `INTERVIEW_LEVEL_${stageCode}` : stageCode; const approval = (application.stageApprovals || []).find((sa: any) => sa.stageCode === saCode && String(sa.actorUserId) === String(p.userId) ); return { name: p.user?.name || p.user?.fullName || 'Unknown', role: p.user?.role || p.user?.roleCode || p.metadata?.role || 'Reviewer', status: approval ? (approval.decision === 'Approved' ? 'approved' : 'rejected') : 'pending' }; }); }; const renderApprovers = (stageName: string, stageIndex: number) => { const stageMapping: Record = { '1st Level Interview': 1, '2nd Level Interview': 2, '3rd Level Interview': 3, 'LOI Approval': 'LOI_APPROVAL', 'LOA': 'LOA_APPROVAL' }; const stageCode = stageMapping[stageName]; if (!stageCode) return null; const approvers = getApproverStatus(stageCode); if (approvers.length === 0) return null; return (
{approvers.map((approver: any, i: number) => (
{approver.name.split(' ').map((n: string) => n[0]).join('').substring(0, 2).toUpperCase()}
{approver.name} {approver.role}
{approver.role}: {approver.status.toUpperCase()}
))}
); }; return processStages.map((stage, index) => (
{stage.isParallel ? ( ) : stage.isLocked ? (
Stage Locked {stage.lockMessage}
) : ( <> {stage.status === 'completed' ? ( ) : stage.status === 'active' ? ( ) : (
)} )}
{index < processStages.length - 1 && !stage.isParallel && (
)}

{stage.name}

{stage.description && (

{stage.description}

)} {renderApprovers(stage.name as string, index)} {stage.evaluators && stage.evaluators.length > 0 && !['LOI Approval', 'LOA', '1st Level Interview', '2nd Level Interview', '3rd Level Interview'].includes(stage.name as string) && (

Evaluators: {stage.evaluators.join(' + ')}

)} {(() => { const expectedMap: Record = { 3: 2, 4: 2, 5: 2, 6: 2, 8: 2, 12: 2 }; const stageId = Number(stage.id); const expectedCount = expectedMap[stageId]; const stageCodeById: Record = { 3: 1, // shortlist depends on L1 evaluators 4: 1, 5: 2, 6: 3, 8: 'LOI_APPROVAL', 12: 'LOA_APPROVAL', }; const mappedStageCode = stageCodeById[stageId]; const actualCount = mappedStageCode ? getApproverStatus(mappedStageCode).length : (stage.evaluators?.length || 0); const isEligibleForWarning = stageId === 3 ? (stage.status === 'completed') : (stage.status !== 'pending'); if (expectedCount && actualCount < expectedCount && application.status !== 'Rejected' && isEligibleForWarning) { return (
Missing Evaluators {actualCount === 0 ? "Respective role users were not found for this location." : `Some roles (${actualCount}/${expectedCount}) are missing for this location.` }
); } return null; })()} {(() => { const stageDocsCount = documents.filter(doc => doc.stage === stage.name || (!doc.stage && doc.documentType?.toLowerCase().includes(stage.name.toLowerCase().split(' ')[0])) ).length; return (
); })()}

{stage.status === 'completed' && stage.date && `Completed: ${formatDateTime(stage.date)}`} {stage.status === 'active' && 'In Progress'} {stage.status === 'pending' && 'Pending'}

{stage.isParallel && stage.branches && (
{stage.branches.map((branch: any, branchIndex: number) => { const branchKey = branch.name.toLowerCase().replace(/\s+/g, '-'); const isExpanded = expandedBranches[branchKey]; const branchColor = branch.color === 'blue' ? 'blue' : 'green'; return (
{isExpanded && (
{branch.stages.map((branchStage: any, bsIdx: number) => (
{(() => { const stageDocs = documents.filter((doc: any) => 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.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'}

); })()}
))}
)}
); })}
)}
)) })()}

Uploaded Documents

File Name Type Upload Date Uploader Actions {documents.length === 0 ? ( No documents uploaded yet ) : ( documents.map((doc, idx) => ( {doc.fileName} {doc.documentType} {formatDateTime(doc.createdAt)} {doc.uploader?.fullName || (doc.uploadedBy ? 'Unknown User' : 'Applicant')}
)))}

Scheduled Interviews

Level Date & Time Type Location/Link Status Scheduled By Actions {(!interviews || interviews.length === 0) ? ( No interviews scheduled yet ) : ( (Array.isArray(interviews) ? interviews : []).map((interview, idx) => ( Level {interview.level} {interview.scheduleDate ? new Date(interview.scheduleDate).toLocaleString() : 'N/A'} {interview.interviewType} {interview.interviewType?.toLowerCase().includes('virtual') ? ( Join Meeting ) : ( {interview.linkOrLocation} )} {interview.status} {interview.scheduler?.fullName || interview.scheduledBy || 'N/A'} {(interview.status === 'Scheduled' || interview.status === 'scheduled') && ( )} )) )}

Interview Feedback

{(!interviews || interviews.length === 0) ? (

No interviews scheduled.

) : ( (Array.isArray(interviews) ? interviews : []).map((interview, iIdx) => (

Level {interview.level} Interview ({formatDateTime(interview.scheduleDate)} - {interview.interviewType})

{interview.evaluations && interview.evaluations.length > 0 ? ( Interviewer Role {interview.level === 1 ? 'Score (KT Matrix)' : 'Overall Score'} Remarks Recommendation {interview.evaluations.map((evalItem: any, eIdx: number) => ( {evalItem.evaluator?.fullName} {evalItem.evaluator?.role?.roleName || 'N/A'} {evalItem.ktMatrixScore ? ( = 50 ? 'outline' : 'destructive') : (Number(evalItem.ktMatrixScore) >= 5 ? 'outline' : 'destructive') } data-testid={`onboarding-interview-evaluation-score-${iIdx}-${eIdx}`}> {evalItem.ktMatrixScore}/{interview.level === 1 ? '100' : '10'} ) : 'N/A'} {evalItem.remarks ? (
{evalItem.remarks} {evalItem.feedbackDetails && evalItem.feedbackDetails.length > 0 && ( )}
) : evalItem.feedbackDetails && evalItem.feedbackDetails.length > 0 ? ( ) : ( evalItem.qualitativeFeedback || '-' )}
{evalItem.recommendation || '-'}
))}
) : (

No feedback recorded yet.

)}
)) )}
{['Level 2 Approved', 'Level 3 Interview Pending', 'Approved', 'Onboarded'].includes(application.status) && (

Level 2 Interview Summary

Decision: Approved by both ZBH and DD Lead

Overall Assessment: Strong candidate with excellent business plan

)}
{renderFddAuditContent()}

Essential Operating Requirements

{Math.round(eorProgress)}% Complete
{(eorData?.items || eorChecklist).map((item: any, idx: number) => { const docType = item.description || item.item; const hasDocument = !!item.proofDocument; return (
{ setSelectedStage(`EOR: ${docType}`); setUploadDocType(docType); setShowDocumentsModal(true); if (!hasDocument) setShowUploadForm(true); else setShowUploadForm(false); }} >
{docType} {hasDocument && !item.isCompliant && ( Needs Verification )}
{hasDocument && (
{item.proofDocument.fileName}
)} {!hasDocument && ( Click to upload proof )}
{hasDocument && !item.isCompliant && isAdmin && (
)} {(item.isCompliant || item.completed) && (
)} {!hasDocument && (
)}
); })}
{eorProgress === 100 && isAdmin && (application.status === 'EOR In Progress' || application.status === 'LOA Pending') && (

EOR Checklist Complete

All 12 mandatory requirements have been verified. You can now complete the audit and move to final inauguration.

)}

Security Deposits

{deposits.length} Payment Record(s)
{(() => { const deposit = getDeposit('SECURITY_DEPOSIT'); const config = paymentConfigs.SECURITY_DEPOSIT; const expectedAmount = config?.amount || 500000; return (
Security Deposit
{deposit?.status || 'Awaiting'}
Amount Received ₹{Number(deposit?.amount || 0).toLocaleString()}
Expected Total ₹{expectedAmount.toLocaleString()}
{deposit?.paymentReference && (
Ref: {deposit.paymentReference} {deposit.verifiedAt && {formatDateTime(deposit.verifiedAt)}}
)} {deposit?.remarks && (
"{deposit.remarks}"
)}

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

)}
); })()} {(() => { const deposit = getDeposit('FIRST_FILL'); const config = paymentConfigs.FIRST_FILL; const expectedAmount = config?.amount || 1500000; return (
First Fill
{deposit?.status || 'Awaiting'}
Amount Received ₹{Number(deposit?.amount || 0).toLocaleString()}
Expected Total ₹{expectedAmount.toLocaleString()}
{deposit?.paymentReference && (
Ref: {deposit.paymentReference} {deposit.verifiedAt && {formatDateTime(deposit.verifiedAt)}}
)} {deposit?.remarks && (
"{deposit.remarks}"
)}

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

)}
); })()}
{auditLoading ? (
Loading audit trail…
) : auditLogs.length === 0 ? (
No audit logs recorded yet for this application.
) : ( auditLogs.map((log: any, idx: number) => (
{String(log.action || 'EVENT').replace(/_/g, ' ')} {log.stage ? ( {log.stage} ) : null}

{log.description || '—'}

{log.userName || 'System'} {log.userEmail ? ( · {log.userEmail} ) : null}
)) )}
); }