Dealer_Onboard_Frontend/src/features/onboarding/components/application-details/ApplicationDetailsExtendedModals.tsx

556 lines
39 KiB
TypeScript

import { AlertCircle, Building2, Download, Eye, FileText, Info, Loader2, ShieldAlert, ShieldCheck, Upload } from 'lucide-react';
import { toast } from 'sonner';
import { onboardingService } from '@/services/onboarding.service';
import { cn, formatDateTime } from '@/components/ui/utils';
import { DocumentPreviewModal } from '@/components/ui/DocumentPreviewModal';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { Separator } from '@/components/ui/separator';
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from '@/components/ui/table';
import { Textarea } from '@/components/ui/textarea';
interface ApplicationDetailsExtendedModalsProps {
[key: string]: any;
}
export function ApplicationDetailsExtendedModals(props: ApplicationDetailsExtendedModalsProps) {
const {
application,
ktCriteria,
l2Fields,
l3Fields,
showKTMatrixModal,
setShowKTMatrixModal,
ktMatrixSelectedValues,
handleKTMatrixChange,
ktMatrixRemarks,
setKtMatrixRemarks,
calculateKTScore,
handleSubmitKTMatrix,
isSubmittingKT,
showLevel2FeedbackModal,
setShowLevel2FeedbackModal,
level2Feedback,
handleLevel2Change,
handleSubmitLevel2Feedback,
isSubmittingLevel2,
showFeedbackDetailsModal,
setShowFeedbackDetailsModal,
selectedEvaluationForView,
showLevel3FeedbackModal,
setShowLevel3FeedbackModal,
level3Feedback,
handleLevel3Change,
handleSubmitLevel3Feedback,
isSubmittingLevel3,
showDocumentsModal,
setShowDocumentsModal,
showUploadForm,
setShowUploadForm,
selectedStage,
getDocumentsForStage,
setPreviewDoc,
setShowPreviewModal,
flattenedStages,
setSelectedStage,
uploadDocType,
setUploadDocType,
setUploadFile,
isUploading,
handleUpload,
uploadFile,
documentConfigs,
showPreviewModal,
previewDoc,
showFddFinalizeModal,
setShowFddFinalizeModal,
currentUser,
fddAuditRecommendation,
setFddAuditRecommendation,
fddAuditFindings,
setFddAuditFindings,
isFinalizingFdd,
setIsFinalizingFdd,
fetchApplication,
showFddFlagModal,
setShowFddFlagModal,
isFddFlagging,
setIsFddFlagging,
showFirmTypeModal,
setShowFirmTypeModal,
tempFirmType,
setTempFirmType,
updatingFirmType,
handleUpdateFirmType,
} = props;
return (
<>
<Dialog open={showKTMatrixModal} onOpenChange={setShowKTMatrixModal}>
<DialogContent
className="flex min-h-0 max-h-[90vh] w-[calc(100%-2rem)] max-w-lg flex-col gap-0 overflow-hidden p-0 sm:max-w-lg"
data-testid="onboarding-kt-matrix-modal"
>
<DialogHeader className="shrink-0 space-y-2 border-b px-5 py-4 text-left">
<DialogTitle className="text-base">KT matrix</DialogTitle>
<DialogDescription className="text-sm leading-relaxed">
Level 1 interview · {application.name}
<span className="mt-1 block text-xs text-muted-foreground">
{Object.keys(ktMatrixSelectedValues).length} of {ktCriteria.length} criteria answered
</span>
</DialogDescription>
</DialogHeader>
<div className="custom-scrollbar-slim min-h-0 flex-1 overflow-y-auto px-5 py-5">
<div className="space-y-6">
{ktCriteria.map((criterion: any, idx: number) => (
<div key={criterion.name} className="space-y-2">
<Label htmlFor={`kt-matrix-${idx}`} className="block text-sm font-medium leading-relaxed text-foreground">
<span className="text-muted-foreground">{idx + 1}.</span> {criterion.name} <span className="text-red-500">*</span>{' '}
<span className="font-normal text-muted-foreground">({criterion.weight}%)</span>
</Label>
<Select
value={ktMatrixSelectedValues[criterion.name] ?? undefined}
onValueChange={(value) => {
const option = criterion.options.find((o: any) => o.value === value);
if (option) handleKTMatrixChange(criterion.name, option.value, option.score);
}}
>
<SelectTrigger id={`kt-matrix-${idx}`} className="h-10 w-full text-left text-sm font-normal" data-testid={`onboarding-kt-matrix-select-${idx}`}>
<SelectValue placeholder="Choose an option…" />
</SelectTrigger>
<SelectContent position="popper" className="max-h-72 w-[var(--radix-select-trigger-width)]">
{criterion.options.map((option: any) => (
<SelectItem key={option.value} value={option.value} className="py-2.5 text-sm leading-snug" data-testid={`onboarding-kt-matrix-option-${idx}-${option.value}`}>
{option.label} <span className="text-muted-foreground">({option.score})</span>
</SelectItem>
))}
</SelectContent>
</Select>
</div>
))}
<div className="space-y-2 border-t border-border pt-6">
<Label htmlFor="kt-matrix-remarks" className="text-sm font-medium">Notes <span className="font-normal text-muted-foreground">(optional)</span></Label>
<Textarea
id="kt-matrix-remarks"
placeholder="Optional remarks…"
className="min-h-[96px] resize-y text-sm leading-relaxed"
value={ktMatrixRemarks}
onChange={(e) => setKtMatrixRemarks(e.target.value)}
data-testid="onboarding-kt-matrix-remarks-textarea"
/>
</div>
</div>
</div>
<div className="flex shrink-0 flex-col gap-4 border-t px-5 py-4 sm:flex-row sm:items-center sm:justify-between">
<p className="text-sm text-muted-foreground">Weighted total <span className="font-semibold tabular-nums text-foreground" data-testid="onboarding-kt-matrix-total-score">{calculateKTScore()}</span><span className="text-muted-foreground"> / 100</span></p>
<div className="flex gap-2 sm:shrink-0">
<Button variant="outline" onClick={() => setShowKTMatrixModal(false)} data-testid="onboarding-kt-matrix-cancel">Cancel</Button>
<Button onClick={handleSubmitKTMatrix} disabled={isSubmittingKT || Object.keys(ktMatrixSelectedValues).length < ktCriteria.length} data-testid="onboarding-kt-matrix-submit">{isSubmittingKT ? 'Saving…' : 'Submit'}</Button>
</div>
</div>
</DialogContent>
</Dialog>
<Dialog open={showLevel2FeedbackModal} onOpenChange={setShowLevel2FeedbackModal}>
<DialogContent className="max-w-2xl max-h-[90vh] overflow-y-auto" data-testid="onboarding-level2-feedback-modal">
<DialogHeader>
<DialogTitle>Level 2 Interview Feedback</DialogTitle>
<DialogDescription>Provide detailed feedback from the Level 2 interview (DD Lead + ZBH evaluation).</DialogDescription>
</DialogHeader>
<div className="space-y-4">
<div><Label>Interview Date <span className="text-red-500">*</span></Label><Input type="date" className="mt-2" value={level2Feedback.interviewDate} disabled /></div>
<div><Label>Interviewer Name <span className="text-red-500">*</span></Label><Input placeholder="Enter your name" className="mt-2" value={level2Feedback.interviewerName} disabled /></div>
<div>
<Label>Overall Performance Score <span className="text-red-500">*</span></Label>
<Select value={level2Feedback.overallScore} onValueChange={(value) => handleLevel2Change('overallScore', value)}>
<SelectTrigger className="mt-2" data-testid="onboarding-level2-overall-score-select"><SelectValue placeholder="Select score" /></SelectTrigger>
<SelectContent><SelectItem value="10">Outstanding (9-10)</SelectItem><SelectItem value="8">Excellent (7-8)</SelectItem><SelectItem value="6">Good (5-6)</SelectItem><SelectItem value="4">Average (3-4)</SelectItem><SelectItem value="2">Below Average (1-2)</SelectItem></SelectContent>
</Select>
</div>
<Separator />
{(l2Fields || []).map((field: any, idx: number) => (
<div key={field.itemKey || idx}>
<Label>
{field.label}
{field.isRequired && <span className="text-red-500">*</span>}
</Label>
{field.type === 'select' ? (
<Select value={level2Feedback[field.itemKey] || ''} onValueChange={(value) => handleLevel2Change(field.itemKey, value)}>
<SelectTrigger className="mt-2"><SelectValue placeholder={`Select ${field.label}...`} /></SelectTrigger>
<SelectContent>
{(field.options || []).map((opt: any, oIdx: number) => (
<SelectItem key={oIdx} value={opt.optionValue || opt.value}>{opt.optionLabel || opt.label}</SelectItem>
))}
</SelectContent>
</Select>
) : field.type === 'number' ? (
<Input type="number" className="mt-2" value={level2Feedback[field.itemKey] || ''} onChange={(e) => handleLevel2Change(field.itemKey, e.target.value)} />
) : (
<Textarea
placeholder={`Enter ${field.label.toLowerCase()}...`}
className="mt-2"
rows={3}
value={level2Feedback[field.itemKey] || ''}
onChange={(e) => handleLevel2Change(field.itemKey, e.target.value)}
/>
)}
</div>
))}
<div className="flex gap-3">
<Button variant="outline" className="flex-1" onClick={() => setShowLevel2FeedbackModal(false)} data-testid="onboarding-level2-feedback-cancel">Cancel</Button>
<Button className="flex-1 bg-black hover:bg-zinc-800 text-white" onClick={handleSubmitLevel2Feedback} disabled={isSubmittingLevel2} data-testid="onboarding-level2-feedback-submit">{isSubmittingLevel2 ? 'Submitting...' : 'Submit Feedback'}</Button>
</div>
</div>
</DialogContent>
</Dialog>
<Dialog open={showFeedbackDetailsModal} onOpenChange={setShowFeedbackDetailsModal}>
<DialogContent className="max-w-2xl max-h-[90vh] overflow-y-auto" data-testid="onboarding-feedback-details-modal">
<DialogHeader><DialogTitle>Interview Feedback Details</DialogTitle></DialogHeader>
{selectedEvaluationForView && (
<div className="space-y-4">
<div className="grid grid-cols-2 gap-4 bg-slate-50 p-4 rounded-lg">
<div><p className="text-sm font-medium text-slate-500">Interviewer</p><p className="font-semibold" data-testid="onboarding-feedback-details-interviewer">{selectedEvaluationForView.evaluator?.fullName}</p></div>
<div><p className="text-sm font-medium text-slate-500">Role</p><p data-testid="onboarding-feedback-details-role">{selectedEvaluationForView.evaluator?.role?.roleName || 'N/A'}</p></div>
<div><p className="text-sm font-medium text-slate-500">{selectedEvaluationForView.interview?.level === 1 ? 'Score (KT Matrix)' : 'Overall Score'}</p><p className="font-bold text-lg" data-testid="onboarding-feedback-details-score">{selectedEvaluationForView.ktMatrixScore ? `${selectedEvaluationForView.ktMatrixScore}/${selectedEvaluationForView.interview?.level === 1 ? '100' : '10'}` : 'N/A'}</p></div>
<div><p className="text-sm font-medium text-slate-500">Recommendation</p><Badge variant={selectedEvaluationForView.recommendation?.toLowerCase().includes('reject') ? 'destructive' : selectedEvaluationForView.recommendation?.toLowerCase().includes('hold') ? 'secondary' : 'default'} data-testid="onboarding-feedback-details-recommendation">{selectedEvaluationForView.recommendation || 'N/A'}</Badge></div>
</div>
<Separator />
<div>
<h4 className="font-semibold mb-3">Detailed Feedback</h4>
{selectedEvaluationForView.feedbackDetails?.length > 0 ? (
<div className="space-y-4">
{selectedEvaluationForView.feedbackDetails.map((detail: any, index: number) => (
<div key={index} className="border-b last:border-0 pb-3 last:pb-0" data-testid={`onboarding-feedback-detail-item-${index}`}>
<p className="font-medium text-slate-900">{detail.feedbackType}</p>
<p className="text-slate-700 mt-1 whitespace-pre-wrap text-sm">{detail.comments}</p>
</div>
))}
</div>
) : (
<p className="text-slate-500 italic">No detailed feedback available.</p>
)}
</div>
</div>
)}
</DialogContent>
</Dialog>
<Dialog open={showLevel3FeedbackModal} onOpenChange={setShowLevel3FeedbackModal}>
<DialogContent className="max-w-2xl max-h-[90vh] overflow-y-auto" data-testid="onboarding-level3-feedback-modal">
<DialogHeader>
<DialogTitle>Level 3 Interview Feedback</DialogTitle>
<DialogDescription>Provide detailed feedback from the Level 3 interview (NBH + DD-Head evaluation).</DialogDescription>
</DialogHeader>
<div className="space-y-4">
<div><Label>Interview Date <span className="text-red-500">*</span></Label><Input type="date" className="mt-2" value={level3Feedback.interviewDate} disabled /></div>
<div><Label>Interviewer Name <span className="text-red-500">*</span></Label><Input placeholder="Enter your name" className="mt-2" value={level3Feedback.interviewerName} disabled /></div>
<div>
<Label>Overall Performance Score <span className="text-red-500">*</span></Label>
<Select value={level3Feedback.overallScore} onValueChange={(value) => handleLevel3Change('overallScore', value)}>
<SelectTrigger className="mt-2" data-testid="onboarding-level3-overall-score-select"><SelectValue placeholder="Select score" /></SelectTrigger>
<SelectContent><SelectItem value="10">Outstanding (9-10)</SelectItem><SelectItem value="8">Excellent (7-8)</SelectItem><SelectItem value="6">Good (5-6)</SelectItem><SelectItem value="4">Average (3-4)</SelectItem><SelectItem value="2">Below Average (1-2)</SelectItem></SelectContent>
</Select>
</div>
<Separator />
{(l3Fields || []).map((field: any, idx: number) => (
<div key={field.itemKey || idx}>
<Label>
{field.label}
{field.isRequired && <span className="text-red-500">*</span>}
</Label>
{field.type === 'select' ? (
<Select value={level3Feedback[field.itemKey] || ''} onValueChange={(value) => handleLevel3Change(field.itemKey, value)}>
<SelectTrigger className="mt-2"><SelectValue placeholder={`Select ${field.label}...`} /></SelectTrigger>
<SelectContent>
{(field.options || []).map((opt: any, oIdx: number) => (
<SelectItem key={oIdx} value={opt.optionValue || opt.value}>{opt.optionLabel || opt.label}</SelectItem>
))}
</SelectContent>
</Select>
) : field.type === 'number' ? (
<Input type="number" className="mt-2" value={level3Feedback[field.itemKey] || ''} onChange={(e) => handleLevel3Change(field.itemKey, e.target.value)} />
) : (
<Textarea
placeholder={`Enter ${field.label.toLowerCase()}...`}
className="mt-2"
rows={3}
value={level3Feedback[field.itemKey] || ''}
onChange={(e) => handleLevel3Change(field.itemKey, e.target.value)}
/>
)}
</div>
))}
<div className="flex gap-3">
<Button variant="outline" className="flex-1" onClick={() => setShowLevel3FeedbackModal(false)} data-testid="onboarding-level3-feedback-cancel">Cancel</Button>
<Button className="flex-1 bg-black hover:bg-zinc-800 text-white" onClick={handleSubmitLevel3Feedback} disabled={isSubmittingLevel3} data-testid="onboarding-level3-feedback-submit">{isSubmittingLevel3 ? 'Submitting...' : 'Submit Feedback'}</Button>
</div>
</div>
</DialogContent>
</Dialog>
<Dialog open={showDocumentsModal} onOpenChange={(open) => { setShowDocumentsModal(open); if (!open) setShowUploadForm(false); }}>
<DialogContent className="max-w-[95vw] sm:max-w-2xl md:max-w-3xl lg:max-w-4xl max-h-[90vh] overflow-hidden flex flex-col p-4 sm:p-6" data-testid="onboarding-documents-modal">
<DialogHeader className="pb-4">
<DialogTitle className="text-xl font-bold flex items-center gap-2"><FileText className="w-5 h-5 text-amber-600" />Documents - {selectedStage || 'General'}</DialogTitle>
<DialogDescription className="text-slate-500">View and manage documents uploaded for this stage.</DialogDescription>
</DialogHeader>
{!showUploadForm ? (
<div className="flex-1 flex flex-col min-h-0 space-y-4">
{getDocumentsForStage(selectedStage || '').length > 0 ? (
<div className="flex-1 overflow-auto border rounded-lg border-slate-200" data-testid="onboarding-documents-table-container">
<Table className="w-full table-auto">
<TableHeader className="bg-slate-50/80 sticky top-0 z-10">
<TableRow className="hover:bg-transparent border-b">
<TableHead className="w-[45%] min-w-[150px] font-semibold text-slate-900 py-3">Document Name</TableHead>
<TableHead className="w-[15%] min-w-[100px] font-semibold text-slate-900 py-3">Type</TableHead>
<TableHead className="w-[15%] min-w-[100px] font-semibold text-slate-900 py-3">Upload Date</TableHead>
<TableHead className="w-[15%] min-w-[140px] font-semibold text-slate-900 py-3">Uploaded By</TableHead>
<TableHead className="text-right w-[10%] min-w-[80px] font-semibold text-slate-900 py-3">Actions</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{getDocumentsForStage(selectedStage || '').map((doc: any, index: number) => (
<TableRow key={doc.id} className="hover:bg-slate-50/50 transition-colors" data-testid={`onboarding-document-row-${index}`}>
<TableCell className="py-3"><div className="flex items-center gap-2 min-w-0"><FileText className="w-4 h-4 text-slate-400 shrink-0" /><span className="truncate font-medium text-slate-700" title={doc.fileName} data-testid={`onboarding-document-name-${index}`}>{doc.fileName}</span></div></TableCell>
<TableCell className="py-3"><Badge variant="outline" className="capitalize whitespace-nowrap font-normal border-slate-200 bg-white" data-testid={`onboarding-document-type-${index}`}>{doc.documentType?.toLowerCase() || 'Other'}</Badge></TableCell>
<TableCell className="py-3 whitespace-nowrap text-slate-600">{formatDateTime(doc.createdAt)}</TableCell>
<TableCell className="py-3 text-slate-600">{doc.uploader?.fullName || (doc.uploadedBy ? 'System User' : 'Applicant')}</TableCell>
<TableCell className="text-right py-3">
<div className="flex gap-1 justify-end">
<Button variant="ghost" size="icon" className="h-8 w-8 text-slate-400 hover:text-indigo-600 hover:bg-indigo-50 rounded-full" onClick={() => { setPreviewDoc(doc); setShowPreviewModal(true); }} data-testid={`onboarding-document-preview-${index}`}><Eye className="w-4 h-4" /></Button>
<Button variant="ghost" size="icon" className="h-8 w-8 text-slate-400 hover:text-amber-600 hover:bg-amber-50 rounded-full" onClick={() => { const baseUrl = (import.meta as any).env?.VITE_API_URL || 'http://localhost:5000'; window.open(`${baseUrl}/${doc.filePath}`, '_blank'); }} data-testid={`onboarding-document-download-${index}`}><Download className="w-4 h-4" /></Button>
</div>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
) : (
<div className="flex-1 flex flex-col items-center justify-center py-12 text-center border rounded-lg bg-slate-50/30" data-testid="onboarding-documents-empty"><div className="w-16 h-16 rounded-full bg-slate-100 flex items-center justify-center mb-4"><FileText className="w-8 h-8 text-slate-300" /></div><h3 className="text-slate-900 font-semibold mb-2">No Documents Found</h3><p className="text-slate-600 text-sm max-w-[250px]">No documents have been uploaded for this stage yet.</p></div>
)}
<div className="flex flex-col sm:flex-row gap-3 pt-2 mt-auto">
<Button className="flex-1 bg-amber-600 hover:bg-amber-700 text-white font-bold py-3 sm:py-5 rounded-xl shadow-lg shadow-amber-600/15 transition-all hover:scale-[1.01] active:scale-[0.99]" onClick={() => setShowUploadForm(true)} data-testid="onboarding-documents-upload-button"><Upload className="w-5 h-5 mr-3" />Upload Document</Button>
<Button variant="outline" className="flex-1 sm:flex-none py-3 sm:py-5 px-8 rounded-xl border-slate-200 font-semibold text-slate-600 hover:bg-slate-50" onClick={() => setShowDocumentsModal(false)} data-testid="onboarding-documents-close-button">Close</Button>
</div>
</div>
) : (
<div className="space-y-6 py-4" data-testid="onboarding-documents-upload-form">
<div className="grid gap-6 bg-slate-50/50 p-4 sm:p-6 rounded-2xl border border-slate-200">
<div className="grid grid-cols-1 sm:grid-cols-2 gap-6">
<div className="space-y-2">
<Label className="text-slate-700 font-semibold px-1">Stage context <span className="text-red-500">*</span></Label>
<Select value={selectedStage || 'null'} onValueChange={(val) => setSelectedStage(val === 'null' ? null : val)}>
<SelectTrigger className="bg-white border-slate-200 h-11 rounded-xl focus:ring-amber-500 shadow-sm" data-testid="onboarding-documents-stage-select"><SelectValue placeholder="Select stage" /></SelectTrigger>
<SelectContent>
<SelectItem value="null">General / No Stage</SelectItem>
{flattenedStages.map((s: any, idx: number) => <SelectItem key={`${s.name}-${idx}`} value={s.name}>{s.parentBranch ? `${s.parentBranch}: ${s.name}` : s.name}</SelectItem>)}
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label className="text-slate-700 font-semibold px-1">Document Type <span className="text-red-500">*</span></Label>
<Select value={uploadDocType} onValueChange={setUploadDocType}>
<SelectTrigger className="bg-white border-slate-200 h-11 rounded-xl focus:ring-amber-500 shadow-sm" data-testid="onboarding-documents-type-select"><SelectValue placeholder="Select type" /></SelectTrigger>
<SelectContent>
{(() => {
const baseDocs = ['Other'];
const stageConfigs = documentConfigs.filter((c: any) => {
const cfgStage = c.stageCode?.trim();
const selStage = (selectedStage || 'General').trim();
if (cfgStage === selStage) return true;
if (selStage.startsWith('EOR:') && cfgStage === 'EOR') return true;
if (!selectedStage && cfgStage === 'General') return true;
return false;
});
let filteredDocs: string[] = [];
if (stageConfigs.length > 0) filteredDocs = stageConfigs.map((c: any) => c.documentType);
else if (!selectedStage || selectedStage === 'General') {
filteredDocs = ['PAN Card', 'GST Certificate', 'Aadhaar Card', 'Passport Size Photograph', 'Partnership Deed', 'LLP Agreement', 'Certificate of Incorporation', 'Board Resolution', 'Firm Registration Certificate', 'Cancelled Check', 'Bank Statement', 'Other'];
} else if (selectedStage?.toLowerCase().includes('architecture')) {
filteredDocs = ['Architecture Blueprint', 'Site Plan', 'Proposed Site City Map', 'Site Readiness Report', 'Architecture Completion Certificate', 'Other'];
} else if (selectedStage?.toLowerCase().includes('fdd')) {
filteredDocs = ['FDD Final Audit Report', 'Bank Statement', 'Income Tax Returns (ITR)', 'CIBIL Report', 'Other'];
} else filteredDocs = baseDocs;
if (selectedStage?.startsWith('EOR: ')) {
const eorItem = selectedStage.replace('EOR: ', '');
if (!filteredDocs.includes(eorItem)) filteredDocs = [eorItem, ...filteredDocs];
}
return Array.from(new Set(filteredDocs)).map((doc, idx) => <SelectItem key={`${doc}-${idx}`} value={doc} data-testid={`onboarding-documents-type-option-${idx}`}>{doc}</SelectItem>);
})()}
</SelectContent>
</Select>
</div>
</div>
<div className="space-y-2">
<Label className="text-slate-700 font-semibold px-1">Select File <span className="text-red-500">*</span></Label>
<Input type="file" className="bg-white border-slate-200 h-12 rounded-xl focus:ring-amber-500 shadow-sm file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-sm file:font-semibold file:bg-amber-50 file:text-amber-700 hover:file:bg-amber-100 cursor-pointer" onChange={(e) => setUploadFile(e.target.files ? e.target.files[0] : null)} data-testid="onboarding-documents-file-input" />
</div>
</div>
<div className="flex flex-col sm:flex-row gap-3 pt-4">
<Button className="flex-1 order-2 sm:order-1 py-3 sm:py-5 rounded-xl border-slate-200 font-semibold text-slate-600 hover:bg-slate-50" variant="outline" onClick={() => setShowUploadForm(false)} disabled={isUploading} data-testid="onboarding-documents-upload-cancel">Cancel</Button>
<Button className="flex-1 order-1 sm:order-2 bg-amber-600 hover:bg-amber-700 text-white font-bold py-3 sm:py-5 rounded-xl shadow-lg shadow-amber-600/15 transition-all hover:scale-[1.01] active:scale-[0.99]" onClick={async () => { await handleUpload(); setShowUploadForm(false); }} disabled={!uploadFile || !uploadDocType || isUploading} data-testid="onboarding-documents-upload-submit">
{isUploading ? <span className="flex items-center gap-2"><div className="w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin" />Uploading...</span> : <span className="flex items-center gap-2"><Upload className="w-5 h-5" />Confirm Upload</span>}
</Button>
</div>
</div>
)}
</DialogContent>
</Dialog>
<DocumentPreviewModal isOpen={showPreviewModal} onClose={() => setShowPreviewModal(false)} document={previewDoc} />
<Dialog open={showFddFinalizeModal} onOpenChange={setShowFddFinalizeModal}>
<DialogContent className="max-w-md p-0 overflow-hidden border-none shadow-2xl rounded-3xl" data-testid="onboarding-fdd-finalize-modal">
<div className="bg-slate-950 p-8 flex items-center justify-center relative overflow-hidden"><div className="absolute inset-0 bg-gradient-to-br from-amber-600/20 to-transparent" /><div className="w-20 h-20 bg-amber-600/20 rounded-full flex items-center justify-center animate-pulse relative z-10 shadow-[0_0_40px_rgba(245,158,11,0.2)]"><ShieldCheck className="w-10 h-10 text-amber-500" /></div></div>
<div className="p-8 space-y-6 bg-white">
<DialogHeader>
<DialogTitle className="text-2xl font-black text-slate-900 text-center tracking-tight">Finalize FDD Audit</DialogTitle>
<DialogDescription className="text-slate-500 text-center pt-2 leading-relaxed text-sm font-medium">You are about to submit your final findings. This action will <span className="font-bold text-slate-900 underline decoration-amber-500 decoration-2">lock the audit session</span> and trigger the LOI approval workflow.</DialogDescription>
</DialogHeader>
<div className="space-y-4">
{(currentUser?.role !== 'FDD' && currentUser?.roleCode !== 'FDD') && (
<div className="space-y-2">
<Label className="text-[10px] font-black uppercase tracking-widest text-slate-400">Auditor Recommendation <span className="text-red-500">*</span></Label>
<div className="flex gap-2">
{['Recommended', 'Qualified with Observations', 'Not Recommended'].map((rec) => (
<Button key={rec} variant={fddAuditRecommendation === rec ? 'default' : 'outline'} className={cn("flex-1 h-10 font-bold text-[9px] uppercase tracking-wider rounded-xl transition-all", fddAuditRecommendation === rec && rec === 'Recommended' && "bg-emerald-600 hover:bg-emerald-700", fddAuditRecommendation === rec && rec === 'Qualified with Observations' && "bg-amber-500 hover:bg-amber-600", fddAuditRecommendation === rec && rec === 'Not Recommended' && "bg-red-600 hover:bg-red-700")} onClick={() => setFddAuditRecommendation(rec)} data-testid={`onboarding-fdd-recommendation-${rec.replace(/\s+/g, '-').toLowerCase()}`}>{rec}</Button>
))}
</div>
</div>
)}
<div className="space-y-2">
<Label className="text-[10px] font-black uppercase tracking-widest text-slate-400">Findings Summary</Label>
<Textarea
placeholder="Summarize key financial findings or discrepancies..."
className="min-h-[100px] rounded-xl border-slate-200 focus:ring-amber-500 text-sm"
value={fddAuditFindings}
onChange={(e) => setFddAuditFindings(e.target.value)}
data-testid="onboarding-fdd-findings-textarea"
/>
</div>
</div>
<div className="bg-amber-50 p-4 rounded-2xl flex gap-3 border border-amber-100"><Info className="w-5 h-5 text-amber-600 shrink-0 mt-0.5" /><p className="text-[11px] text-amber-800 font-medium italic">Ensure the final PDF report is uploaded first. This satisfies the FDD statutory requirement.</p></div>
<div className="flex flex-col sm:flex-row gap-3 pt-2">
<Button variant="outline" className="w-full sm:flex-1 h-12 rounded-2xl font-bold text-slate-600 hover:bg-slate-50 border-slate-200" onClick={() => setShowFddFinalizeModal(false)} disabled={isFinalizingFdd} data-testid="onboarding-fdd-finalize-cancel">Cancel</Button>
<Button
className="w-full sm:flex-1 h-12 rounded-2xl font-bold bg-slate-950 hover:bg-slate-900 text-white shadow-lg shadow-slate-200 transition-all active:scale-95 border-b-4 border-amber-500"
disabled={isFinalizingFdd || !fddAuditFindings}
data-testid="onboarding-fdd-finalize-submit"
onClick={async () => {
try {
setIsFinalizingFdd(true);
await onboardingService.submitStageDecision({
applicationId: application!.id,
stageCode: 'FDD_VERIFICATION',
decision: 'Approved',
remarks: (currentUser?.role === 'FDD' || currentUser?.roleCode === 'FDD')
? `Findings: ${fddAuditFindings}`
: `[RECOMMENDATION: ${fddAuditRecommendation}] \nFindings: ${fddAuditFindings}`,
nextStatus: 'LOI In Progress',
nextProgress: 65
});
toast.success('FDD Audit finalized and submitted.');
setShowFddFinalizeModal(false);
fetchApplication();
} catch {
toast.error('Submission failed');
} finally {
setIsFinalizingFdd(false);
}
}}
>
{isFinalizingFdd ? <Loader2 className="w-5 h-5 animate-spin" /> : 'Confirm & Submit'}
</Button>
</div>
</div>
</DialogContent>
</Dialog>
<Dialog open={showFddFlagModal} onOpenChange={setShowFddFlagModal}>
<DialogContent className="max-w-md p-0 overflow-hidden border-none shadow-2xl rounded-3xl" data-testid="onboarding-fdd-flag-modal">
<div className="bg-slate-950 p-8 flex items-center justify-center relative overflow-hidden"><div className="absolute inset-0 bg-gradient-to-br from-red-600/20 to-transparent" /><div className="w-20 h-20 bg-red-600/20 rounded-full flex items-center justify-center relative z-10 shadow-[0_0_40px_rgba(220,38,38,0.2)]"><ShieldAlert className="w-10 h-10 text-red-500" /></div></div>
<div className="p-8 space-y-6 bg-white text-center">
<DialogHeader>
<DialogTitle className="text-2xl font-black text-slate-900 tracking-tight">Flag Non-Responsive</DialogTitle>
<DialogDescription className="text-slate-500 pt-2 leading-relaxed text-sm font-medium">Are you sure you want to flag this applicant? This will notify the DD Admin that the audit cannot proceed due to applicant's non-cooperation.</DialogDescription>
</DialogHeader>
<div className="bg-red-50 p-4 rounded-2xl flex gap-3 border border-red-100"><AlertCircle className="w-5 h-5 text-red-600 shrink-0 mt-0.5" /><p className="text-[11px] text-red-800 text-left font-medium">"Applicant is unresponsive to multiple queries and financial document requests."</p></div>
<div className="flex flex-col sm:flex-row gap-3 pt-2">
<Button variant="outline" className="w-full sm:flex-1 h-12 rounded-2xl font-bold text-slate-600 hover:bg-slate-50 border-slate-200" onClick={() => setShowFddFlagModal(false)} disabled={isFddFlagging} data-testid="onboarding-fdd-flag-cancel">Go Back</Button>
<Button
className="w-full sm:flex-1 h-12 rounded-2xl font-bold bg-slate-950 hover:bg-slate-900 text-white shadow-lg shadow-slate-200 transition-all active:scale-95 border-b-4 border-red-600"
disabled={isFddFlagging}
data-testid="onboarding-fdd-flag-submit"
onClick={async () => {
try {
setIsFddFlagging(true);
await onboardingService.submitStageDecision({
applicationId: application!.id,
stageCode: 'FDD_VERIFICATION',
decision: 'Rejected',
remarks: 'Applicant is non-responsive to FDD queries.'
});
toast.error('Applicant flagged as non-responsive.');
setShowFddFlagModal(false);
fetchApplication();
} catch {
toast.error('Action failed');
} finally {
setIsFddFlagging(false);
}
}}
>
{isFddFlagging ? <Loader2 className="w-5 h-5 animate-spin" /> : 'Confirm Flag'}
</Button>
</div>
</div>
</DialogContent>
</Dialog>
<Dialog open={showFirmTypeModal} onOpenChange={setShowFirmTypeModal}>
<DialogContent className="max-w-md p-0 overflow-hidden rounded-3xl border-none shadow-2xl" data-testid="onboarding-firm-type-modal">
<div className="bg-amber-600 p-8 text-white">
<div className="w-16 h-16 rounded-2xl bg-white/20 flex items-center justify-center mb-6 backdrop-blur-sm border border-white/30 shadow-inner"><Building2 className="w-8 h-8 text-white" /></div>
<h3 className="text-2xl font-black tracking-tight mb-2">Update Firm Type</h3>
<p className="text-amber-100/80 text-sm font-medium leading-relaxed">Select the proposed legal constitution for this dealership application.</p>
</div>
<div className="p-8 space-y-6 bg-white">
<div className="space-y-2">
<Label className="text-[10px] text-slate-400 uppercase tracking-widest font-black">Proposed Legal Constitution <span className="text-red-500">*</span></Label>
<Select value={tempFirmType} onValueChange={setTempFirmType}>
<SelectTrigger className="h-12 rounded-xl border-slate-200 focus:ring-amber-500" data-testid="onboarding-firm-type-select"><SelectValue placeholder="Select Firm Type" /></SelectTrigger>
<SelectContent>
<SelectItem value="Proprietorship" data-testid="onboarding-firm-type-proprietorship">Proprietorship</SelectItem>
<SelectItem value="Partnership" data-testid="onboarding-firm-type-partnership">Partnership</SelectItem>
<SelectItem value="Limited Liability partnership" data-testid="onboarding-firm-type-llp">LLP (Limited Liability partnership)</SelectItem>
<SelectItem value="Private Limited Company" data-testid="onboarding-firm-type-pvt-ltd">Private Limited Company</SelectItem>
<SelectItem value="Public Limited Company" data-testid="onboarding-firm-type-pub-ltd">Public Limited Company</SelectItem>
</SelectContent>
</Select>
</div>
<div className="flex gap-3 pt-2">
<Button variant="outline" className="flex-1 h-12 rounded-xl font-bold text-slate-600 border-slate-200" onClick={() => setShowFirmTypeModal(false)} disabled={updatingFirmType} data-testid="onboarding-firm-type-cancel">Cancel</Button>
<Button className="flex-1 h-12 rounded-xl font-bold bg-amber-600 hover:bg-amber-700 text-white shadow-lg shadow-amber-200 transition-all active:scale-95" disabled={updatingFirmType || !tempFirmType} onClick={handleUpdateFirmType} data-testid="onboarding-firm-type-submit">{updatingFirmType ? <Loader2 className="w-5 h-5 animate-spin" /> : 'Update Type'}</Button>
</div>
</div>
</DialogContent>
</Dialog>
</>
);
}