fdd againseparated from normal dashboard and fdd approval moved to admin authority

This commit is contained in:
laxman h 2026-04-08 19:02:47 +05:30
parent 1578a1708e
commit 078afa3590
13 changed files with 650 additions and 407 deletions

View File

@ -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 */}
<Route path="/fdd-dashboard" element={<FDDDashboardPage />} />
<Route path="/fdd-details/:id" element={<FDDApplicationDetails />} />
{/* Admin/Lead Routes */}
<Route path="/opportunity-requests" element={<OpportunityRequestsPage onViewDetails={(id) => navigate(`/applications/${id}`)} />} />

View File

@ -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;

File diff suppressed because it is too large Load Diff

View File

@ -15,6 +15,7 @@ import {
import { WorkNotesPage } from './WorkNotesPage';
import { toast } from 'sonner';
import { DocumentPreviewModal } from '../ui/DocumentPreviewModal';
import { formatDateTime, cn } from '@/components/ui/utils';
import {
Dialog,
DialogContent,
@ -25,11 +26,15 @@ import {
} from '../ui/dialog';
import { Button } from '../ui/button';
import { AlertTriangle, Info, ShieldCheck } from 'lucide-react';
import { Label } from '../ui/label';
import { Textarea } from '../ui/textarea';
export function FDDApplicationDetails() {
const { id } = useParams<{ id: string }>();
const navigate = useNavigate();
const [application, setApplication] = useState<any>(null);
const [assignment, setAssignment] = useState<any>(null);
const [loading, setLoading] = useState(true);
const [uploading, setUploading] = useState(false);
const [selectedDocType, setSelectedDocType] = useState('');
@ -38,6 +43,8 @@ export function FDDApplicationDetails() {
const [selectedPreviewDoc, setSelectedPreviewDoc] = useState<any>(null);
const [showFinalizeModal, setShowFinalizeModal] = useState(false);
const [showFlagModal, setShowFlagModal] = useState(false);
const [fddAuditRecommendation, setFddAuditRecommendation] = useState<string>('Recommended');
const [fddAuditFindings, setFddAuditFindings] = useState<string>('');
useEffect(() => {
if (id) fetchApplication();
@ -46,12 +53,16 @@ export function FDDApplicationDetails() {
const fetchApplication = async () => {
setLoading(true);
try {
const response: any = await API.getApplicationById(id!);
if (response.data?.success) {
setApplication(response.data.data);
} else {
toast.error(response.data?.message || 'Failed to authorize access');
navigate('/fdd-dashboard');
const [appRes, assRes]: any = await Promise.all([
API.getApplicationById(id!),
API.getFddAssignment(id!)
]);
if (appRes.data?.success) {
setApplication(appRes.data.data);
}
if (assRes.data?.success) {
setAssignment(assRes.data.data);
}
} catch (error) {
console.error('Error fetching application:', error);
@ -74,7 +85,8 @@ export function FDDApplicationDetails() {
const formData = new FormData();
formData.append('file', file);
formData.append('documentType', selectedDocType);
formData.append('requestId', id!);
formData.append('stage', 'FDD');
formData.append('applicationId', id!);
formData.append('requestType', 'application');
try {
@ -120,11 +132,14 @@ export function FDDApplicationDetails() {
const isFDDStageActive = application.currentStage === 'FDD_VERIFICATION' || application.currentStage === 'FDD';
// Check if report is already submitted in assignment
const isReportSubmitted = assignment?.status === 'Report Submitted';
// Check if the application has already passed the FDD stage
const isCompleted = !isFDDStageActive && (application.overallStatus !== 'Active' || application.currentProgress >= 75);
const isCompleted = !isFDDStageActive && (application.overallStatus !== 'Active' || application.currentProgress >= 75) || isReportSubmitted;
// Check if the application has not yet arrived at the FDD stage
const isNotReachedYet = !isFDDStageActive && application.currentProgress < 70 && !isCompleted;
const isNotReachedYet = !isFDDStageActive && application.currentProgress < 70 && !isReportSubmitted;
return (
<div className="flex flex-col gap-6 max-w-7xl mx-auto mb-10">
@ -159,12 +174,15 @@ export function FDDApplicationDetails() {
setUploading(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 API.uploadDocument(application.id, formData);
toast.success('FDD Audit Report uploaded successfully');
fetchApplication(); // Refresh to show the new doc
const res: any = await API.uploadDocument(application.id, formData);
if (res.data?.success) {
toast.success('FDD Final Audit Report uploaded successfully');
fetchApplication();
}
} catch (err) {
toast.error('Upload failed');
} finally {
@ -253,7 +271,6 @@ export function FDDApplicationDetails() {
>
<div className="flex items-center gap-2">
Work Notes
<span className="bg-slate-100 text-slate-500 px-1.5 py-0.5 rounded text-[10px]">0</span>
</div>
{activeTab === 'worknotes' && <div className="absolute bottom-[-1px] left-0 right-0 h-0.5 bg-blue-600" />}
</button>
@ -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"
>
<option value="">Select Document Category...</option>
<option value="Final FDD Audit Report">Final FDD Audit Report</option>
<option value="Bank Statement Analysis">Bank Statement Analysis</option>
<option value="Credit Compliance Report">Credit Compliance Report</option>
<option value="FDD Final Audit Report">FDD Final Audit Report</option>
<option value="Bank Statement">Bank Statement</option>
<option value="Income Tax Returns (ITR)">Income Tax Returns (ITR)</option>
<option value="CIBIL Report">CIBIL Report</option>
<option value="Business Valuation Report">Business Valuation Report</option>
<option value="Property Verification Report">Property Verification Report</option>
<option value="Property Documents">Property Documents</option>
</select>
<div className="relative">
@ -365,7 +383,7 @@ export function FDDApplicationDetails() {
<span className="text-[8px] bg-slate-100 text-slate-500 px-1 py-0.5 rounded uppercase font-bold tracking-tighter">APPLICANT</span>
</div>
<p className="text-[10px] text-slate-400 font-medium">
{doc.documentType} {new Date(doc.createdAt).toLocaleDateString()}
{doc.documentType} {formatDateTime(doc.createdAt)}
{doc.uploader?.fullName && ` • by ${doc.uploader.fullName}`}
</p>
</div>
@ -378,9 +396,6 @@ export function FDDApplicationDetails() {
>
<Eye className="w-4 h-4" />
</button>
<a href={`http://localhost:5000${doc.filePath?.startsWith('/') ? '' : '/'}${doc.filePath}`} target="_blank" className="p-1.5 hover:bg-white rounded text-blue-600 transition-all">
<Upload className="w-3.5 h-3.5 rotate-180" />
</a>
</div>
</div>
))}
@ -409,7 +424,7 @@ export function FDDApplicationDetails() {
<span className="text-[8px] bg-amber-500 text-white px-1 py-0.5 rounded uppercase font-bold tracking-tighter">YOUR AUDIT REPORT</span>
</div>
<p className="text-[10px] text-slate-400 font-medium">
{doc.documentType} {new Date(doc.createdAt).toLocaleDateString()}
{doc.documentType} {formatDateTime(doc.createdAt)}
{doc.uploader?.fullName && ` • by ${doc.uploader.fullName}`}
</p>
</div>
@ -422,9 +437,6 @@ export function FDDApplicationDetails() {
>
<Eye className="w-4 h-4" />
</button>
<a href={`http://localhost:5000${doc.filePath?.startsWith('/') ? '' : '/'}${doc.filePath}`} target="_blank" className="p-1.5 hover:bg-white rounded text-blue-600 transition-all">
<Upload className="w-3.5 h-3.5 rotate-180" />
</a>
</div>
</div>
))}
@ -476,10 +488,6 @@ export function FDDApplicationDetails() {
<p className="font-bold text-slate-800">{application.email}</p>
<p className="text-slate-500 font-medium">{application.phone}</p>
</div>
<div className="space-y-1 pt-4 border-t border-slate-50 text-xs">
<p className="text-[10px] font-bold text-slate-400 uppercase tracking-wider">FDD Due Date</p>
<p className="font-bold text-slate-800">April 25, 2026</p>
</div>
</CardContent>
</Card>
@ -516,19 +524,52 @@ export function FDDApplicationDetails() {
</div>
<div className="p-8 space-y-4">
<DialogHeader>
<DialogTitle className="text-2xl font-bold text-slate-900 text-center">Finalize Audit Report</DialogTitle>
<DialogTitle className="text-2xl font-bold text-slate-900 text-center">Submit Audit Report</DialogTitle>
<DialogDescription className="text-slate-500 text-center pt-2 leading-relaxed text-base">
You are about to submit your final findings. This action will <span className="font-bold text-slate-800 underline decoration-amber-500 decoration-2">lock the report</span> and move the application to the next stage.
You are about to submit your final findings. This action will <span className="font-bold text-slate-800 underline decoration-amber-500 decoration-2">notify the Admin</span> for review and approval.
</DialogDescription>
</DialogHeader>
<div className="bg-amber-50 p-4 rounded-xl flex gap-3 border border-amber-100 italic">
<Info className="w-5 h-5 text-amber-600 shrink-0 mt-0.5" />
<p className="text-xs text-amber-800 leading-normal">
Ensure all required financial documents are uploaded and verified before proceeding.
Once submitted, you cannot edit the findings. Ensure all documents are uploaded.
</p>
</div>
<div className="space-y-4 pt-2">
<div className="space-y-2">
<Label className="text-[10px] font-black uppercase tracking-widest text-slate-400">Auditor Recommendation</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)}
>
{rec}
</Button>
))}
</div>
</div>
<div className="space-y-2">
<Label className="text-[10px] font-black uppercase tracking-widest text-slate-400">Detailed Audit Findings & Remarks</Label>
<Textarea
placeholder="Enter detailed financial observations..."
className="min-h-[120px] bg-slate-50 border-slate-200 rounded-xl focus:ring-amber-500 text-sm resize-none"
value={fddAuditFindings}
onChange={(e) => setFddAuditFindings(e.target.value)}
/>
</div>
</div>
<DialogFooter className="flex flex-col sm:flex-row gap-3 pt-4 sm:pt-6">
<Button
variant="outline"
@ -542,19 +583,26 @@ export function FDDApplicationDetails() {
className="w-full sm:flex-1 h-12 rounded-xl font-bold bg-slate-950 hover:bg-slate-900 text-white shadow-lg shadow-slate-200 transition-all active:scale-95 border-b-2 border-amber-600"
onClick={async () => {
try {
if (!fddAuditFindings.trim()) {
toast.error('Please provide findings.');
return;
}
if (!assignment?.id) {
toast.error('Assignment not found.');
return;
}
setUploading(true);
const res: any = await API.submitStageDecision({
applicationId: application.id,
stageCode: 'FDD_VERIFICATION',
decision: 'Approved',
remarks: 'FDD Verification completed and report uploaded.',
nextStatus: 'LOI In Progress',
nextProgress: 65
const res: any = await API.submitFddReport({
assignmentId: assignment.id,
findings: fddAuditFindings,
recommendation: fddAuditRecommendation
});
if (res.data?.success) {
toast.success('FDD Report submitted successfully.');
setShowFinalizeModal(false);
navigate('/fdd-dashboard');
fetchApplication();
}
} catch (e) {
toast.error('Failed to submit report');
@ -608,15 +656,14 @@ export function FDDApplicationDetails() {
onClick={async () => {
try {
setUploading(true);
await API.submitStageDecision({
applicationId: application.id,
stageCode: 'FDD_VERIFICATION',
decision: 'Rejected',
remarks: 'Applicant is non-responsive to FDD queries.'
await API.addWorknote({
requestId: id,
requestType: 'application',
content: 'FLAGGED: Applicant is non-responsive to FDD queries.'
});
toast.error('Application flagged and returned to admin.');
toast.error('Application flagged for non-responsiveness.');
setShowFlagModal(false);
navigate('/fdd-dashboard');
fetchApplication();
} catch (e) {
toast.error('Action failed');
} finally {

View File

@ -12,7 +12,7 @@ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '.
import { Progress } from '../ui/progress';
import {
ArrowLeft,
DollarSign,
IndianRupee,
CheckCircle,
XCircle,
Upload,
@ -461,7 +461,7 @@ export function FinanceFnFDetailsPage({ fnfId, onBack }: FinanceFnFDetailsPagePr
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<div className="w-12 h-12 rounded-full bg-amber-100 flex items-center justify-center">
<DollarSign className="w-6 h-6 text-amber-600" />
<IndianRupee className="w-5 h-5" />
</div>
<div>
<p className="text-slate-900">Settlement Pending Finance Approval</p>
@ -624,7 +624,7 @@ export function FinanceFnFDetailsPage({ fnfId, onBack }: FinanceFnFDetailsPagePr
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<DollarSign className="w-5 h-5" />
<IndianRupee className="w-5 h-5" />
Settlement Calculation Summary
</CardTitle>
</CardHeader>

View File

@ -1,5 +1,5 @@
import { useState, useEffect } from 'react';
import { Card, CardContent, CardDescription } from '../ui/card';
import { Card, CardContent } from '../ui/card';
import { Badge } from '../ui/badge';
import { Button } from '../ui/button';
import {
@ -11,13 +11,10 @@ import {
TableRow,
} from '../ui/table';
import {
DollarSign,
IndianRupee,
CheckCircle,
Clock,
FileText,
Calendar,
CreditCard,
ShieldCheck
} from 'lucide-react';
import { toast } from 'sonner';
import { onboardingService } from '../../services/onboarding.service';
@ -27,10 +24,9 @@ interface FinanceOnboardingPageProps {
onViewAuditDetails?: (applicationId: string) => void;
}
export function FinanceOnboardingPage({ onViewPaymentDetails, onViewAuditDetails }: FinanceOnboardingPageProps = {}) {
export function FinanceOnboardingPage({ onViewPaymentDetails }: FinanceOnboardingPageProps = {}) {
const [applications, setApplications] = useState<any[]>([]);
const [loading, setLoading] = useState(true);
const [activeTab, setActiveTab] = useState<'payments' | 'audits'>('payments');
const [filterStatus, setFilterStatus] = useState<'all' | 'pending' | 'verified'>('pending');
useEffect(() => {
@ -58,12 +54,6 @@ export function FinanceOnboardingPage({ onViewPaymentDetails, onViewAuditDetails
return deposit ? deposit.status : 'Awaiting Payment';
};
const getFddApprovalStatus = (app: any) => {
// Check for Finance approval record specifically in LOI_APPROVAL stage
const financeApproval = app.stageApprovals?.find((a: any) => a.stageCode === 'LOI_APPROVAL' && a.actorRole === 'Finance');
return financeApproval ? financeApproval.decision : 'Pending Review';
};
// Filter for Payment Mode
const paymentApps = applications.filter((app: any) => {
const s = app.overallStatus || app.status;
@ -73,41 +63,20 @@ export function FinanceOnboardingPage({ onViewPaymentDetails, onViewAuditDetails
].includes(s);
});
// Filter for FDD Audit Mode
const auditApps = applications.filter((app: any) => {
const s = app.overallStatus || app.status;
return s === 'LOI In Progress' || s === 'FDD Verification' || s === 'LOI_APPROVAL';
const displayApps = paymentApps.filter(app => {
const status = getRelevantPaymentStatus(app);
if (filterStatus === 'all') return true;
if (filterStatus === 'pending') return status !== 'Verified';
if (filterStatus === 'verified') return status === 'Verified';
return true;
});
const displayApps = activeTab === 'payments'
? paymentApps.filter(app => {
const status = getRelevantPaymentStatus(app);
if (filterStatus === 'all') return true;
if (filterStatus === 'pending') return status !== 'Verified';
if (filterStatus === 'verified') return status === 'Verified';
return true;
})
: auditApps.filter(app => {
const status = getFddApprovalStatus(app);
if (filterStatus === 'all') return true;
if (filterStatus === 'pending') return status !== 'Approved';
if (filterStatus === 'verified') return status === 'Approved';
return true;
});
const handleAction = (appId: string) => {
if (activeTab === 'audits') {
if (onViewAuditDetails) {
onViewAuditDetails(appId);
}
} else if (onViewPaymentDetails) {
if (onViewPaymentDetails) {
onViewPaymentDetails(appId);
}
};
const pendingPayments = paymentApps.filter(app => getRelevantPaymentStatus(app) !== 'Verified').length;
const pendingAudits = auditApps.filter(app => getFddApprovalStatus(app) !== 'Approved').length;
if (loading) {
return (
<div className="flex items-center justify-center p-20 text-blue-600">
@ -122,8 +91,8 @@ export function FinanceOnboardingPage({ onViewPaymentDetails, onViewAuditDetails
{/* Header */}
<div className="flex items-center justify-between bg-white p-6 rounded-2xl border border-slate-100 shadow-sm">
<div>
<h1 className="text-3xl font-bold text-slate-900 tracking-tight mb-1">Financial Operations</h1>
<p className="text-slate-500">Manage payment verifications and FDD audit report reviews</p>
<h1 className="text-3xl font-bold text-slate-900 tracking-tight mb-1">Payment Verification</h1>
<p className="text-slate-500">Review and verify dealer security deposits and first fill payments</p>
</div>
<div className="flex items-center gap-3">
<Button onClick={fetchApplications} variant="outline" size="sm" className="bg-white hover:bg-slate-50">
@ -133,25 +102,13 @@ export function FinanceOnboardingPage({ onViewPaymentDetails, onViewAuditDetails
</div>
</div>
{/* Mode Switcher */}
{/* Mode Switcher - Simplified */}
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4">
<div className="inline-flex p-1 bg-slate-100 rounded-xl">
<Button
variant={activeTab === 'payments' ? 'default' : 'ghost'}
className={activeTab === 'payments' ? 'rounded-lg bg-white text-slate-900 shadow-sm hover:bg-white' : 'rounded-lg text-slate-600'}
onClick={() => { setActiveTab('payments'); setFilterStatus('pending'); }}
>
<DollarSign className="w-4 h-4 mr-2" />
Payments ({pendingPayments})
</Button>
<Button
variant={activeTab === 'audits' ? 'default' : 'ghost'}
className={activeTab === 'audits' ? 'rounded-lg bg-white text-slate-900 shadow-sm hover:bg-white' : 'rounded-lg text-slate-600'}
onClick={() => { setActiveTab('audits'); setFilterStatus('pending'); }}
>
<FileText className="w-4 h-4 mr-2" />
FDD Audit Reviews ({pendingAudits})
</Button>
<div className="flex items-center px-4 py-2 bg-white rounded-lg text-slate-900 shadow-sm font-medium text-sm">
<IndianRupee className="w-4 h-4 mr-2 text-blue-600" />
Pending Payments ({paymentApps.filter(a => getRelevantPaymentStatus(a) !== 'Verified').length})
</div>
</div>
<div className="flex gap-2">
@ -190,7 +147,7 @@ export function FinanceOnboardingPage({ onViewPaymentDetails, onViewAuditDetails
<TableRow className="border-b border-slate-100 uppercase text-[10px] tracking-wider font-bold text-slate-400">
<TableHead className="py-4 pl-6">Application Details</TableHead>
<TableHead>Location</TableHead>
<TableHead>{activeTab === 'payments' ? 'Payment Stage' : 'Audit Stage'}</TableHead>
<TableHead>Payment Stage</TableHead>
<TableHead>Current Status</TableHead>
<TableHead className="text-right pr-6">Workflow Action</TableHead>
</TableRow>
@ -198,7 +155,7 @@ export function FinanceOnboardingPage({ onViewPaymentDetails, onViewAuditDetails
<TableBody>
{displayApps.length > 0 ? (
displayApps.map((app) => {
const statusLabel = activeTab === 'payments' ? getRelevantPaymentStatus(app) : getFddApprovalStatus(app);
const statusLabel = getRelevantPaymentStatus(app);
return (
<TableRow key={app.id} className="hover:bg-blue-50/20 group transition-all">
<TableCell className="py-4 pl-6">
@ -217,25 +174,16 @@ export function FinanceOnboardingPage({ onViewPaymentDetails, onViewAuditDetails
</TableCell>
<TableCell>
<div className="flex items-center gap-2">
{activeTab === 'payments' ? (
<>
<CreditCard className="w-4 h-4 text-slate-400" />
<span className="text-sm font-medium">
{app.overallStatus === 'LOI Issued' || app.overallStatus === 'Security Details' ? 'Initial (₹2L)' : 'Final (₹15L)'}
</span>
</>
) : (
<>
<ShieldCheck className="w-4 h-4 text-slate-400" />
<span className="text-sm font-medium">FDD Report Approval</span>
</>
)}
<CreditCard className="w-4 h-4 text-slate-400" />
<span className="text-sm font-medium">
{ (app.overallStatus === 'LOI Issued' || app.overallStatus === 'Security Details' || app.overallStatus === 'LOI In Progress') ? 'Security Deposit (₹5L)' : 'First Fill (₹15L)'}
</span>
</div>
</TableCell>
<TableCell>
<Badge
className={
statusLabel === 'Verified' || statusLabel === 'Approved' ? 'bg-emerald-50 text-emerald-700 border-emerald-100 px-3 py-1 rounded-full' :
statusLabel === 'Verified' ? 'bg-emerald-50 text-emerald-700 border-emerald-100 px-3 py-1 rounded-full' :
statusLabel === 'Rejected' ? 'bg-rose-50 text-rose-700 border-rose-100 px-3 py-1 rounded-full' :
'bg-amber-50 text-amber-700 border-amber-100 px-3 py-1 rounded-full'
}
@ -247,23 +195,16 @@ export function FinanceOnboardingPage({ onViewPaymentDetails, onViewAuditDetails
<TableCell className="text-right pr-6">
<Button
size="sm"
variant={statusLabel === 'Verified' || statusLabel === 'Approved' ? 'outline' : 'default'}
className={statusLabel !== 'Verified' && statusLabel !== 'Approved'
? (activeTab === 'payments' ? 'bg-blue-600 hover:bg-blue-700' : 'bg-indigo-600 hover:bg-indigo-700 shadow-md')
variant={statusLabel === 'Verified' ? 'outline' : 'default'}
className={statusLabel !== 'Verified'
? 'bg-blue-600 hover:bg-blue-700 shadow-md'
: 'bg-white text-slate-600 border-slate-200'}
onClick={() => handleAction(app.applicationId || app.id)}
>
{activeTab === 'payments' ? (
<>
<DollarSign className="w-4 h-4 mr-2" />
<IndianRupee className="w-4 h-4 mr-2" />
{statusLabel === 'Verified' ? 'View Receipt' : 'Record Payment'}
</>
) : (
<>
<ShieldCheck className="w-4 h-4 mr-2" />
{statusLabel === 'Approved' ? 'View Decision' : 'Review Audit'}
</>
)}
</Button>
</TableCell>
</TableRow>
@ -271,12 +212,12 @@ export function FinanceOnboardingPage({ onViewPaymentDetails, onViewAuditDetails
})
) : (
<TableRow>
<TableCell colSpan={6} className="h-48 text-center text-slate-400">
<TableCell colSpan={5} className="h-48 text-center text-slate-400">
<div className="flex flex-col items-center gap-3">
<div className="w-12 h-12 bg-slate-50 rounded-full flex items-center justify-center">
<CheckCircle className="w-6 h-6 text-slate-200" />
</div>
<p className="text-sm">No applications pending in the {activeTab} queue</p>
<p className="text-sm">No applications pending in the queue</p>
</div>
</TableCell>
</TableRow>

View File

@ -7,7 +7,7 @@ import { Label } from '../ui/label';
import { Textarea } from '../ui/textarea';
import {
ArrowLeft,
DollarSign,
IndianRupee,
CheckCircle,
XCircle,
FileText,
@ -63,8 +63,8 @@ export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaym
verificationRemarks: activeDeposit.remarks || ''
});
} else {
const initialDefault = configs.INITIAL_SECURITY_DEPOSIT?.amount || 500000;
const finalDefault = configs.FINAL_SECURITY_DEPOSIT?.amount || 1500000;
const initialDefault = configs.SECURITY_DEPOSIT?.amount || 500000;
const finalDefault = configs.FIRST_FILL?.amount || 1500000;
setPaymentDetails({
verificationTransactionId: '',
@ -110,7 +110,7 @@ export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaym
status: 'Verified'
});
toast.success(`${activeType === 'INITIAL' ? 'Advance' : 'Final'} payment verified and approved`);
toast.success(`${activeType === 'INITIAL' ? 'Security Deposit' : 'First Fill'} verified and approved`);
await fetchData();
} catch (error) {
toast.error('Failed to verify payment');
@ -134,7 +134,7 @@ export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaym
remarks: paymentDetails.verificationRemarks
});
toast.error(`${activeType === 'INITIAL' ? 'Advance' : 'Final'} payment rejected`);
toast.error(`${activeType === 'INITIAL' ? 'Security Deposit' : 'First Fill'} rejected`);
await fetchData();
} catch (error) {
toast.error('Failed to reject payment');
@ -171,7 +171,7 @@ export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaym
className={activeType === 'INITIAL' ? 'bg-amber-600 hover:bg-amber-700' : ''}
onClick={() => setActiveType('INITIAL')}
>
Advance Payment
Security Deposit
</Button>
<Button
size="sm"
@ -179,7 +179,7 @@ export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaym
className={activeType === 'FINAL' ? 'bg-amber-600 hover:bg-amber-700' : ''}
onClick={() => setActiveType('FINAL')}
>
Final Security Deposit
First Fill
</Button>
</div>
</div>
@ -201,7 +201,7 @@ export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaym
activeDeposit?.status === 'Rejected' ? "bg-red-100" :
"bg-amber-100"
)}>
<DollarSign className={cn(
<IndianRupee className={cn(
"w-6 h-6",
activeDeposit?.status === 'Verified' ? "text-green-600" :
activeDeposit?.status === 'Rejected' ? "text-red-600" :
@ -210,7 +210,7 @@ export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaym
</div>
<div>
<p className="text-slate-900 font-bold">
{activeType === 'INITIAL' ? 'Advance Payment' : 'Final Security Deposit'}
{activeType === 'INITIAL' ? 'Security Deposit' : 'First Fill'}
</p>
<p className="text-sm text-slate-600">
{activeDeposit?.status === 'Verified'
@ -275,8 +275,8 @@ export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaym
<Label className="text-slate-500 block mb-1">Expected Amount</Label>
<p className="text-2xl font-bold text-amber-900">
{(activeType === 'INITIAL'
? (configs.INITIAL_SECURITY_DEPOSIT?.amount || 500000)
: (configs.FINAL_SECURITY_DEPOSIT?.amount || 1500000)
? (configs.SECURITY_DEPOSIT?.amount || 500000)
: (configs.FIRST_FILL?.amount || 1500000)
).toLocaleString()}
</p>
</div>
@ -320,14 +320,14 @@ export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaym
<CardContent>
{application.uploadedDocuments?.filter((d: any) =>
activeType === 'INITIAL'
? d.documentType?.toLowerCase().includes('initial') && d.documentType?.toLowerCase().includes('deposit')
: d.documentType?.toLowerCase().includes('final') && d.documentType?.toLowerCase().includes('deposit')
? d.documentType?.toLowerCase().includes('security') && d.documentType?.toLowerCase().includes('deposit')
: d.documentType?.toLowerCase().includes('first') && d.documentType?.toLowerCase().includes('fill')
).length > 0 ? (
<div className="space-y-3">
{application.uploadedDocuments.filter((d: any) =>
activeType === 'INITIAL'
? d.documentType?.toLowerCase().includes('initial') && d.documentType?.toLowerCase().includes('deposit')
: d.documentType?.toLowerCase().includes('final') && d.documentType?.toLowerCase().includes('deposit')
? d.documentType?.toLowerCase().includes('security') && d.documentType?.toLowerCase().includes('deposit')
: d.documentType?.toLowerCase().includes('first') && d.documentType?.toLowerCase().includes('fill')
).map((doc: any, index: number) => (
<div key={index} className="flex items-center justify-between p-3 bg-white rounded-lg border border-slate-200 hover:shadow-sm transition-shadow">
<div className="flex items-center gap-3">
@ -471,6 +471,7 @@ export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaym
<li>Applicant status will advance to {activeType === 'INITIAL' ? 'LOI Issuance' : 'LOA Approval'}</li>
<li>Email notification will be sent to Applicant</li>
<li>Digital {activeType === 'INITIAL' ? 'LOI' : 'LOA'} generation will be unlocked</li>
<li>This payment confirms the {activeType === 'INITIAL' ? 'Security Deposit' : 'First Fill'}</li>
</ul>
</CardContent>
</Card>

View File

@ -1,5 +1,5 @@
import { useState, useEffect } from 'react';
import { DollarSign, Calendar, Eye, Send, FileCheck, Loader2 } from 'lucide-react';
import { IndianRupee, Calendar, Eye, Send, FileCheck, Loader2 } from 'lucide-react';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '../ui/card';
import { Badge } from '../ui/badge';
import { Button } from '../ui/button';
@ -279,15 +279,15 @@ export function FnFPage({ currentUser, onViewDetails }: FnFPageProps) {
<div className="flex items-start gap-4 flex-1">
<div className={`p-3 rounded-lg ${
fnfCase.status === 'New' ? 'bg-blue-100' :
fnfCase.status === 'In Progress' ? 'bg-yellow-100' :
fnfCase.status === 'Under Review' ? 'bg-orange-100' :
'bg-green-100'
fnfCase.status === 'In Progress' ? 'bg-yellow-100' :
fnfCase.status === 'Under Review' ? 'bg-orange-100' :
'bg-green-100'
}`}>
<DollarSign className={`w-6 h-6 ${
<IndianRupee className={`w-6 h-6 ${
fnfCase.status === 'New' ? 'text-blue-600' :
fnfCase.status === 'In Progress' ? 'text-yellow-600' :
fnfCase.status === 'Under Review' ? 'text-orange-600' :
'text-green-600'
fnfCase.status === 'In Progress' ? 'text-yellow-600' :
fnfCase.status === 'Under Review' ? 'text-orange-600' :
'text-green-600'
}`} />
</div>
<div className="flex-1">
@ -359,7 +359,7 @@ export function FnFPage({ currentUser, onViewDetails }: FnFPageProps) {
<div className="flex items-start justify-between">
<div className="flex items-start gap-4 flex-1">
<div className="p-3 bg-yellow-100 rounded-lg">
<DollarSign className="w-6 h-6 text-yellow-600" />
<IndianRupee className="w-6 h-6 text-yellow-600" />
</div>
<div className="flex-1">
<div className="flex items-center gap-3 mb-2">
@ -404,7 +404,7 @@ export function FnFPage({ currentUser, onViewDetails }: FnFPageProps) {
))}
{displaySettlements.filter((c: any) => c.status === 'In Progress').length === 0 && (
<div className="text-center py-12 text-slate-500">
<DollarSign className="w-12 h-12 mx-auto mb-4 text-slate-400" />
<IndianRupee className="w-12 h-12 mx-auto mb-4 text-slate-400" />
<p>No cases in progress</p>
</div>
)}
@ -422,7 +422,7 @@ export function FnFPage({ currentUser, onViewDetails }: FnFPageProps) {
<div className="flex items-start justify-between">
<div className="flex items-start gap-4 flex-1">
<div className="p-3 bg-orange-100 rounded-lg">
<DollarSign className="w-6 h-6 text-orange-600" />
<IndianRupee className="w-6 h-6 text-orange-600" />
</div>
<div className="flex-1">
<div className="flex items-center gap-3 mb-2">
@ -469,7 +469,7 @@ export function FnFPage({ currentUser, onViewDetails }: FnFPageProps) {
))}
{displaySettlements.filter(c => c.status === 'Under Review' || c.status === 'Calculated').length === 0 && (
<div className="text-center py-12 text-slate-500">
<DollarSign className="w-12 h-12 mx-auto mb-4 text-slate-400" />
<IndianRupee className="w-12 h-12 mx-auto mb-4 text-slate-400" />
<p>No cases under review</p>
</div>
)}

View File

@ -195,8 +195,8 @@ export function ProspectiveApplicationDetails({ id, onBack }: Props) {
<option value="PAN Card">PAN Card</option>
<option value="GST Certificate">GST Certificate</option>
<option value="Aadhaar Card">Aadhaar Card</option>
<option value="Initial Security Deposit Receipt">Initial Security Deposit Receipt</option>
<option value="Final Security Deposit Receipt">Final Security Deposit Receipt</option>
<option value="Security Deposit Receipt">Security Deposit Receipt</option>
<option value="First Fill Receipt">First Fill Receipt</option>
<option value="Partnership Deed">Partnership Deed</option>
<option value="LLP Agreement">LLP Agreement</option>
<option value="Certificate of Incorporation">Certificate of Incorporation</option>

View File

@ -162,7 +162,7 @@ export function FDDDashboardPage() {
<tr
key={app.id}
className="hover:bg-slate-50 transition-colors cursor-pointer group"
onClick={() => navigate(`/applications/${app.id}`)}
onClick={() => navigate(`/fdd-details/${app.id}`)}
>
<td className="px-6 py-4">
<div className="flex items-center gap-3">

View File

@ -1,4 +1,4 @@
import { DollarSign, FileText, CheckCircle, Plus, Trash2, Save, Calculator, Clock, TrendingUp, TrendingDown, ShieldCheck } from 'lucide-react';
import { IndianRupee, FileText, CheckCircle, Plus, Trash2, Save, Calculator, Clock, TrendingUp, TrendingDown } from 'lucide-react';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '../ui/card';
import { Badge } from '../ui/badge';
import { Button } from '../ui/button';
@ -78,7 +78,7 @@ export function FinanceDashboard({ onNavigate, onViewPaymentDetails, onViewAudit
const stage = app.currentStage;
return [
'LOI In Progress', 'LOI Issued', 'LOA Pending', 'Dealer Code Generation',
'LOI_APPROVAL', 'LOA_APPROVAL', 'PAYMENT_VERIFICATION', 'SECURITY_DEPOSIT',
'LOA_APPROVAL', 'PAYMENT_VERIFICATION', 'SECURITY_DEPOSIT',
'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'
].includes(s) || stage === 'Finance';
});
@ -171,7 +171,7 @@ export function FinanceDashboard({ onNavigate, onViewPaymentDetails, onViewAudit
const pendingOnboarding = applications.filter(app => getRelevantPaymentStatus(app) !== 'Verified');
const verifiedOnboarding = applications.filter(app => getRelevantPaymentStatus(app) === 'Verified');
const pendingAudits = applications.filter(app => app.status === 'LOI_APPROVAL' || app.overallStatus === 'LOI In Progress');
const pendingAudits = applications.filter(app => app.status === 'FDD_VERIFICATION' || app.overallStatus === 'FDD Verification');
const pendingFnF = mockFnFCases.filter(f => !f.hasFinanceSummary);
const completedFnF = mockFnFCases.filter(f => f.hasFinanceSummary);
@ -289,7 +289,7 @@ export function FinanceDashboard({ onNavigate, onViewPaymentDetails, onViewAudit
<Tabs defaultValue="onboarding" className="w-full">
<TabsList className="grid w-full grid-cols-2">
<TabsTrigger value="onboarding">
<DollarSign className="w-4 h-4 mr-2" />
<IndianRupee className="w-4 h-4 mr-2" />
Onboarding
</TabsTrigger>
<TabsTrigger value="fnf">
@ -343,7 +343,7 @@ export function FinanceDashboard({ onNavigate, onViewPaymentDetails, onViewAudit
<div>
<p className="text-slate-600">Stage</p>
<p className="text-amber-700 font-bold">
{app.status === 'LOI_APPROVAL' || app.status === 'PAYMENT_VERIFICATION' ? 'Initial Deposit' : 'Final Deposit'}
{app.status === 'PAYMENT_VERIFICATION' ? 'Security Deposit' : 'First Fill'}
</p>
</div>
<div>
@ -365,33 +365,18 @@ export function FinanceDashboard({ onNavigate, onViewPaymentDetails, onViewAudit
<FileText className="w-4 h-4 mr-2" />
View Details
</Button>
{app.status === 'LOI_APPROVAL' || app.overallStatus === 'LOI In Progress' ? (
<Button
size="sm"
className="bg-amber-600 hover:bg-amber-700 font-bold"
onClick={() => {
if (onViewAuditDetails) {
onViewAuditDetails(app.applicationId || app.id);
}
}}
>
<ShieldCheck className="w-4 h-4 mr-2" />
Review Audit
</Button>
) : (
<Button
size="sm"
className="bg-green-600 hover:bg-green-700 font-bold"
onClick={() => {
if (onViewPaymentDetails) {
onViewPaymentDetails(app.applicationId || app.id);
}
}}
>
<CheckCircle className="w-4 h-4 mr-2" />
Verify Payment
</Button>
)}
<Button
size="sm"
className="bg-green-600 hover:bg-green-700 font-bold"
onClick={() => {
if (onViewPaymentDetails) {
onViewPaymentDetails(app.applicationId || app.id);
}
}}
>
<CheckCircle className="w-4 h-4 mr-2" />
Verify Payment
</Button>
</div>
</div>
</CardContent>

View File

@ -42,7 +42,7 @@ export const DocumentPreviewModal: React.FC<DocumentPreviewModalProps> = ({
return (
<Dialog open={isOpen} onOpenChange={onClose}>
<DialogContent className="max-w-4xl h-[85vh] flex flex-col p-0 overflow-hidden bg-white shadow-2xl border-none">
<DialogContent className="max-w-[80vw] w-[80vw] h-[85vh] flex flex-col p-0 overflow-hidden bg-white shadow-2xl border-none">
{document ? (
<>
<div className="flex items-center justify-between p-4 border-b bg-slate-50">

View File

@ -160,5 +160,20 @@ export const onboardingService = {
const response: any = await API.updateApplication(id, data);
if (!response.ok) throw new Error(response.data?.message || 'Failed to update application');
return response.data;
},
submitFddReport: async (data: any) => {
const response: any = await API.submitFddReport(data);
if (!response.ok) throw new Error(response.data?.message || 'Failed to submit FDD report');
return response.data;
},
getFddAssignment: async (applicationId: string) => {
const response: any = await API.getFddAssignment(applicationId);
if (!response.ok) throw new Error(response.data?.message || 'Failed to fetch FDD assignment');
return response.data;
},
assignFddAgency: async (data: any) => {
const response: any = await API.assignFddAgency(data);
if (!response.ok) throw new Error(response.data?.message || 'Failed to assign FDD agency');
return response.data;
}
};