fdd againseparated from normal dashboard and fdd approval moved to admin authority
This commit is contained in:
parent
1578a1708e
commit
078afa3590
@ -15,6 +15,7 @@ import { FinanceDashboard } from './components/dashboard/FinanceDashboard';
|
|||||||
import { DealerDashboard } from './components/dashboard/DealerDashboard';
|
import { DealerDashboard } from './components/dashboard/DealerDashboard';
|
||||||
import { ProspectiveDashboardPage } from './components/dashboard/ProspectiveDashboardPage';
|
import { ProspectiveDashboardPage } from './components/dashboard/ProspectiveDashboardPage';
|
||||||
import { FDDDashboardPage } from './components/dashboard/FDDDashboardPage';
|
import { FDDDashboardPage } from './components/dashboard/FDDDashboardPage';
|
||||||
|
import { FDDApplicationDetails } from './components/applications/FDDApplicationDetails';
|
||||||
import { ApplicationsPage } from './components/applications/ApplicationsPage';
|
import { ApplicationsPage } from './components/applications/ApplicationsPage';
|
||||||
import { AllApplicationsPage } from './components/applications/AllApplicationsPage';
|
import { AllApplicationsPage } from './components/applications/AllApplicationsPage';
|
||||||
import { OpportunityRequestsPage } from './components/applications/OpportunityRequestsPage';
|
import { OpportunityRequestsPage } from './components/applications/OpportunityRequestsPage';
|
||||||
@ -149,6 +150,7 @@ export default function App() {
|
|||||||
'/questionnaire-builder': 'Questionnaire Builder',
|
'/questionnaire-builder': 'Questionnaire Builder',
|
||||||
'/approval-policies': 'Approval Policies',
|
'/approval-policies': 'Approval Policies',
|
||||||
'/fdd-dashboard': 'FDD Dashboard',
|
'/fdd-dashboard': 'FDD Dashboard',
|
||||||
|
'/fdd-details': 'Audit Workspace',
|
||||||
};
|
};
|
||||||
return titles[pathname] || 'Dashboard';
|
return titles[pathname] || 'Dashboard';
|
||||||
};
|
};
|
||||||
@ -228,6 +230,7 @@ export default function App() {
|
|||||||
|
|
||||||
{/* FDD Routes - Integrated into Layout */}
|
{/* FDD Routes - Integrated into Layout */}
|
||||||
<Route path="/fdd-dashboard" element={<FDDDashboardPage />} />
|
<Route path="/fdd-dashboard" element={<FDDDashboardPage />} />
|
||||||
|
<Route path="/fdd-details/:id" element={<FDDApplicationDetails />} />
|
||||||
|
|
||||||
{/* Admin/Lead Routes */}
|
{/* Admin/Lead Routes */}
|
||||||
<Route path="/opportunity-requests" element={<OpportunityRequestsPage onViewDetails={(id) => navigate(`/applications/${id}`)} />} />
|
<Route path="/opportunity-requests" element={<OpportunityRequestsPage onViewDetails={(id) => navigate(`/applications/${id}`)} />} />
|
||||||
|
|||||||
@ -180,6 +180,11 @@ export const API = {
|
|||||||
getEorChecklistForRelocation: (relocationId: string) => client.get(`/eor/relocation/${relocationId}`),
|
getEorChecklistForRelocation: (relocationId: string) => client.get(`/eor/relocation/${relocationId}`),
|
||||||
updateEorChecklistItem: (checklistId: string, data: any) => client.post(`/eor/item/${checklistId}`, data),
|
updateEorChecklistItem: (checklistId: string, data: any) => client.post(`/eor/item/${checklistId}`, data),
|
||||||
submitEorAudit: (checklistId: string, data: any) => client.post(`/eor/audit/${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;
|
export default API;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -15,6 +15,7 @@ import {
|
|||||||
import { WorkNotesPage } from './WorkNotesPage';
|
import { WorkNotesPage } from './WorkNotesPage';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { DocumentPreviewModal } from '../ui/DocumentPreviewModal';
|
import { DocumentPreviewModal } from '../ui/DocumentPreviewModal';
|
||||||
|
import { formatDateTime, cn } from '@/components/ui/utils';
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
@ -25,11 +26,15 @@ import {
|
|||||||
} from '../ui/dialog';
|
} from '../ui/dialog';
|
||||||
import { Button } from '../ui/button';
|
import { Button } from '../ui/button';
|
||||||
import { AlertTriangle, Info, ShieldCheck } from 'lucide-react';
|
import { AlertTriangle, Info, ShieldCheck } from 'lucide-react';
|
||||||
|
import { Label } from '../ui/label';
|
||||||
|
import { Textarea } from '../ui/textarea';
|
||||||
|
|
||||||
|
|
||||||
export function FDDApplicationDetails() {
|
export function FDDApplicationDetails() {
|
||||||
const { id } = useParams<{ id: string }>();
|
const { id } = useParams<{ id: string }>();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [application, setApplication] = useState<any>(null);
|
const [application, setApplication] = useState<any>(null);
|
||||||
|
const [assignment, setAssignment] = useState<any>(null);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [uploading, setUploading] = useState(false);
|
const [uploading, setUploading] = useState(false);
|
||||||
const [selectedDocType, setSelectedDocType] = useState('');
|
const [selectedDocType, setSelectedDocType] = useState('');
|
||||||
@ -38,6 +43,8 @@ export function FDDApplicationDetails() {
|
|||||||
const [selectedPreviewDoc, setSelectedPreviewDoc] = useState<any>(null);
|
const [selectedPreviewDoc, setSelectedPreviewDoc] = useState<any>(null);
|
||||||
const [showFinalizeModal, setShowFinalizeModal] = useState(false);
|
const [showFinalizeModal, setShowFinalizeModal] = useState(false);
|
||||||
const [showFlagModal, setShowFlagModal] = useState(false);
|
const [showFlagModal, setShowFlagModal] = useState(false);
|
||||||
|
const [fddAuditRecommendation, setFddAuditRecommendation] = useState<string>('Recommended');
|
||||||
|
const [fddAuditFindings, setFddAuditFindings] = useState<string>('');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (id) fetchApplication();
|
if (id) fetchApplication();
|
||||||
@ -46,12 +53,16 @@ export function FDDApplicationDetails() {
|
|||||||
const fetchApplication = async () => {
|
const fetchApplication = async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
const response: any = await API.getApplicationById(id!);
|
const [appRes, assRes]: any = await Promise.all([
|
||||||
if (response.data?.success) {
|
API.getApplicationById(id!),
|
||||||
setApplication(response.data.data);
|
API.getFddAssignment(id!)
|
||||||
} else {
|
]);
|
||||||
toast.error(response.data?.message || 'Failed to authorize access');
|
|
||||||
navigate('/fdd-dashboard');
|
if (appRes.data?.success) {
|
||||||
|
setApplication(appRes.data.data);
|
||||||
|
}
|
||||||
|
if (assRes.data?.success) {
|
||||||
|
setAssignment(assRes.data.data);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching application:', error);
|
console.error('Error fetching application:', error);
|
||||||
@ -74,7 +85,8 @@ export function FDDApplicationDetails() {
|
|||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('file', file);
|
formData.append('file', file);
|
||||||
formData.append('documentType', selectedDocType);
|
formData.append('documentType', selectedDocType);
|
||||||
formData.append('requestId', id!);
|
formData.append('stage', 'FDD');
|
||||||
|
formData.append('applicationId', id!);
|
||||||
formData.append('requestType', 'application');
|
formData.append('requestType', 'application');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -120,11 +132,14 @@ export function FDDApplicationDetails() {
|
|||||||
|
|
||||||
const isFDDStageActive = application.currentStage === 'FDD_VERIFICATION' || application.currentStage === 'FDD';
|
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
|
// 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
|
// 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 (
|
return (
|
||||||
<div className="flex flex-col gap-6 max-w-7xl mx-auto mb-10">
|
<div className="flex flex-col gap-6 max-w-7xl mx-auto mb-10">
|
||||||
@ -159,12 +174,15 @@ export function FDDApplicationDetails() {
|
|||||||
setUploading(true);
|
setUploading(true);
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('file', file);
|
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);
|
formData.append('applicationId', application.id);
|
||||||
|
|
||||||
await API.uploadDocument(application.id, formData);
|
const res: any = await API.uploadDocument(application.id, formData);
|
||||||
toast.success('FDD Audit Report uploaded successfully');
|
if (res.data?.success) {
|
||||||
fetchApplication(); // Refresh to show the new doc
|
toast.success('FDD Final Audit Report uploaded successfully');
|
||||||
|
fetchApplication();
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
toast.error('Upload failed');
|
toast.error('Upload failed');
|
||||||
} finally {
|
} finally {
|
||||||
@ -253,7 +271,6 @@ export function FDDApplicationDetails() {
|
|||||||
>
|
>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
Work Notes
|
Work Notes
|
||||||
<span className="bg-slate-100 text-slate-500 px-1.5 py-0.5 rounded text-[10px]">0</span>
|
|
||||||
</div>
|
</div>
|
||||||
{activeTab === 'worknotes' && <div className="absolute bottom-[-1px] left-0 right-0 h-0.5 bg-blue-600" />}
|
{activeTab === 'worknotes' && <div className="absolute bottom-[-1px] left-0 right-0 h-0.5 bg-blue-600" />}
|
||||||
</button>
|
</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"
|
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="">Select Document Category...</option>
|
||||||
<option value="Final FDD Audit Report">Final FDD Audit Report</option>
|
<option value="FDD Final Audit Report">FDD Final Audit Report</option>
|
||||||
<option value="Bank Statement Analysis">Bank Statement Analysis</option>
|
<option value="Bank Statement">Bank Statement</option>
|
||||||
<option value="Credit Compliance Report">Credit Compliance Report</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="Business Valuation Report">Business Valuation Report</option>
|
||||||
<option value="Property Verification Report">Property Verification Report</option>
|
<option value="Property Documents">Property Documents</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<div className="relative">
|
<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>
|
<span className="text-[8px] bg-slate-100 text-slate-500 px-1 py-0.5 rounded uppercase font-bold tracking-tighter">APPLICANT</span>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-[10px] text-slate-400 font-medium">
|
<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}`}
|
{doc.uploader?.fullName && ` • by ${doc.uploader.fullName}`}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@ -378,9 +396,6 @@ export function FDDApplicationDetails() {
|
|||||||
>
|
>
|
||||||
<Eye className="w-4 h-4" />
|
<Eye className="w-4 h-4" />
|
||||||
</button>
|
</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>
|
||||||
</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>
|
<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>
|
</div>
|
||||||
<p className="text-[10px] text-slate-400 font-medium">
|
<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}`}
|
{doc.uploader?.fullName && ` • by ${doc.uploader.fullName}`}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@ -422,9 +437,6 @@ export function FDDApplicationDetails() {
|
|||||||
>
|
>
|
||||||
<Eye className="w-4 h-4" />
|
<Eye className="w-4 h-4" />
|
||||||
</button>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
@ -476,10 +488,6 @@ export function FDDApplicationDetails() {
|
|||||||
<p className="font-bold text-slate-800">{application.email}</p>
|
<p className="font-bold text-slate-800">{application.email}</p>
|
||||||
<p className="text-slate-500 font-medium">{application.phone}</p>
|
<p className="text-slate-500 font-medium">{application.phone}</p>
|
||||||
</div>
|
</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>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
@ -516,19 +524,52 @@ export function FDDApplicationDetails() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="p-8 space-y-4">
|
<div className="p-8 space-y-4">
|
||||||
<DialogHeader>
|
<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">
|
<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>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
<div className="bg-amber-50 p-4 rounded-xl flex gap-3 border border-amber-100 italic">
|
<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" />
|
<Info className="w-5 h-5 text-amber-600 shrink-0 mt-0.5" />
|
||||||
<p className="text-xs text-amber-800 leading-normal">
|
<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>
|
</p>
|
||||||
</div>
|
</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">
|
<DialogFooter className="flex flex-col sm:flex-row gap-3 pt-4 sm:pt-6">
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
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"
|
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 () => {
|
onClick={async () => {
|
||||||
try {
|
try {
|
||||||
|
if (!fddAuditFindings.trim()) {
|
||||||
|
toast.error('Please provide findings.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!assignment?.id) {
|
||||||
|
toast.error('Assignment not found.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
setUploading(true);
|
setUploading(true);
|
||||||
const res: any = await API.submitStageDecision({
|
const res: any = await API.submitFddReport({
|
||||||
applicationId: application.id,
|
assignmentId: assignment.id,
|
||||||
stageCode: 'FDD_VERIFICATION',
|
findings: fddAuditFindings,
|
||||||
decision: 'Approved',
|
recommendation: fddAuditRecommendation
|
||||||
remarks: 'FDD Verification completed and report uploaded.',
|
|
||||||
nextStatus: 'LOI In Progress',
|
|
||||||
nextProgress: 65
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.data?.success) {
|
if (res.data?.success) {
|
||||||
toast.success('FDD Report submitted successfully.');
|
toast.success('FDD Report submitted successfully.');
|
||||||
setShowFinalizeModal(false);
|
setShowFinalizeModal(false);
|
||||||
navigate('/fdd-dashboard');
|
fetchApplication();
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
toast.error('Failed to submit report');
|
toast.error('Failed to submit report');
|
||||||
@ -608,15 +656,14 @@ export function FDDApplicationDetails() {
|
|||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
try {
|
try {
|
||||||
setUploading(true);
|
setUploading(true);
|
||||||
await API.submitStageDecision({
|
await API.addWorknote({
|
||||||
applicationId: application.id,
|
requestId: id,
|
||||||
stageCode: 'FDD_VERIFICATION',
|
requestType: 'application',
|
||||||
decision: 'Rejected',
|
content: 'FLAGGED: Applicant is non-responsive to FDD queries.'
|
||||||
remarks: 'Applicant is non-responsive to FDD queries.'
|
|
||||||
});
|
});
|
||||||
toast.error('Application flagged and returned to admin.');
|
toast.error('Application flagged for non-responsiveness.');
|
||||||
setShowFlagModal(false);
|
setShowFlagModal(false);
|
||||||
navigate('/fdd-dashboard');
|
fetchApplication();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
toast.error('Action failed');
|
toast.error('Action failed');
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '.
|
|||||||
import { Progress } from '../ui/progress';
|
import { Progress } from '../ui/progress';
|
||||||
import {
|
import {
|
||||||
ArrowLeft,
|
ArrowLeft,
|
||||||
DollarSign,
|
IndianRupee,
|
||||||
CheckCircle,
|
CheckCircle,
|
||||||
XCircle,
|
XCircle,
|
||||||
Upload,
|
Upload,
|
||||||
@ -461,7 +461,7 @@ export function FinanceFnFDetailsPage({ fnfId, onBack }: FinanceFnFDetailsPagePr
|
|||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<div className="w-12 h-12 rounded-full bg-amber-100 flex items-center justify-center">
|
<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>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-slate-900">Settlement Pending Finance Approval</p>
|
<p className="text-slate-900">Settlement Pending Finance Approval</p>
|
||||||
@ -624,7 +624,7 @@ export function FinanceFnFDetailsPage({ fnfId, onBack }: FinanceFnFDetailsPagePr
|
|||||||
<Card>
|
<Card>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle className="flex items-center gap-2">
|
<CardTitle className="flex items-center gap-2">
|
||||||
<DollarSign className="w-5 h-5" />
|
<IndianRupee className="w-5 h-5" />
|
||||||
Settlement Calculation Summary
|
Settlement Calculation Summary
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { Card, CardContent, CardDescription } from '../ui/card';
|
import { Card, CardContent } from '../ui/card';
|
||||||
import { Badge } from '../ui/badge';
|
import { Badge } from '../ui/badge';
|
||||||
import { Button } from '../ui/button';
|
import { Button } from '../ui/button';
|
||||||
import {
|
import {
|
||||||
@ -11,13 +11,10 @@ import {
|
|||||||
TableRow,
|
TableRow,
|
||||||
} from '../ui/table';
|
} from '../ui/table';
|
||||||
import {
|
import {
|
||||||
DollarSign,
|
IndianRupee,
|
||||||
CheckCircle,
|
CheckCircle,
|
||||||
Clock,
|
Clock,
|
||||||
FileText,
|
|
||||||
Calendar,
|
|
||||||
CreditCard,
|
CreditCard,
|
||||||
ShieldCheck
|
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { onboardingService } from '../../services/onboarding.service';
|
import { onboardingService } from '../../services/onboarding.service';
|
||||||
@ -27,10 +24,9 @@ interface FinanceOnboardingPageProps {
|
|||||||
onViewAuditDetails?: (applicationId: string) => void;
|
onViewAuditDetails?: (applicationId: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function FinanceOnboardingPage({ onViewPaymentDetails, onViewAuditDetails }: FinanceOnboardingPageProps = {}) {
|
export function FinanceOnboardingPage({ onViewPaymentDetails }: FinanceOnboardingPageProps = {}) {
|
||||||
const [applications, setApplications] = useState<any[]>([]);
|
const [applications, setApplications] = useState<any[]>([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [activeTab, setActiveTab] = useState<'payments' | 'audits'>('payments');
|
|
||||||
const [filterStatus, setFilterStatus] = useState<'all' | 'pending' | 'verified'>('pending');
|
const [filterStatus, setFilterStatus] = useState<'all' | 'pending' | 'verified'>('pending');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -58,12 +54,6 @@ export function FinanceOnboardingPage({ onViewPaymentDetails, onViewAuditDetails
|
|||||||
return deposit ? deposit.status : 'Awaiting Payment';
|
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
|
// Filter for Payment Mode
|
||||||
const paymentApps = applications.filter((app: any) => {
|
const paymentApps = applications.filter((app: any) => {
|
||||||
const s = app.overallStatus || app.status;
|
const s = app.overallStatus || app.status;
|
||||||
@ -73,41 +63,20 @@ export function FinanceOnboardingPage({ onViewPaymentDetails, onViewAuditDetails
|
|||||||
].includes(s);
|
].includes(s);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Filter for FDD Audit Mode
|
const displayApps = paymentApps.filter(app => {
|
||||||
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 = activeTab === 'payments'
|
|
||||||
? paymentApps.filter(app => {
|
|
||||||
const status = getRelevantPaymentStatus(app);
|
const status = getRelevantPaymentStatus(app);
|
||||||
if (filterStatus === 'all') return true;
|
if (filterStatus === 'all') return true;
|
||||||
if (filterStatus === 'pending') return status !== 'Verified';
|
if (filterStatus === 'pending') return status !== 'Verified';
|
||||||
if (filterStatus === 'verified') return status === 'Verified';
|
if (filterStatus === 'verified') return status === 'Verified';
|
||||||
return true;
|
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) => {
|
const handleAction = (appId: string) => {
|
||||||
if (activeTab === 'audits') {
|
if (onViewPaymentDetails) {
|
||||||
if (onViewAuditDetails) {
|
|
||||||
onViewAuditDetails(appId);
|
|
||||||
}
|
|
||||||
} else if (onViewPaymentDetails) {
|
|
||||||
onViewPaymentDetails(appId);
|
onViewPaymentDetails(appId);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const pendingPayments = paymentApps.filter(app => getRelevantPaymentStatus(app) !== 'Verified').length;
|
|
||||||
const pendingAudits = auditApps.filter(app => getFddApprovalStatus(app) !== 'Approved').length;
|
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-center p-20 text-blue-600">
|
<div className="flex items-center justify-center p-20 text-blue-600">
|
||||||
@ -122,8 +91,8 @@ export function FinanceOnboardingPage({ onViewPaymentDetails, onViewAuditDetails
|
|||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex items-center justify-between bg-white p-6 rounded-2xl border border-slate-100 shadow-sm">
|
<div className="flex items-center justify-between bg-white p-6 rounded-2xl border border-slate-100 shadow-sm">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-3xl font-bold text-slate-900 tracking-tight mb-1">Financial Operations</h1>
|
<h1 className="text-3xl font-bold text-slate-900 tracking-tight mb-1">Payment Verification</h1>
|
||||||
<p className="text-slate-500">Manage payment verifications and FDD audit report reviews</p>
|
<p className="text-slate-500">Review and verify dealer security deposits and first fill payments</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<Button onClick={fetchApplications} variant="outline" size="sm" className="bg-white hover:bg-slate-50">
|
<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>
|
||||||
</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="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">
|
<div className="inline-flex p-1 bg-slate-100 rounded-xl">
|
||||||
<Button
|
<div className="flex items-center px-4 py-2 bg-white rounded-lg text-slate-900 shadow-sm font-medium text-sm">
|
||||||
variant={activeTab === 'payments' ? 'default' : 'ghost'}
|
<IndianRupee className="w-4 h-4 mr-2 text-blue-600" />
|
||||||
className={activeTab === 'payments' ? 'rounded-lg bg-white text-slate-900 shadow-sm hover:bg-white' : 'rounded-lg text-slate-600'}
|
Pending Payments ({paymentApps.filter(a => getRelevantPaymentStatus(a) !== 'Verified').length})
|
||||||
onClick={() => { setActiveTab('payments'); setFilterStatus('pending'); }}
|
</div>
|
||||||
>
|
|
||||||
<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>
|
</div>
|
||||||
|
|
||||||
<div className="flex gap-2">
|
<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">
|
<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 className="py-4 pl-6">Application Details</TableHead>
|
||||||
<TableHead>Location</TableHead>
|
<TableHead>Location</TableHead>
|
||||||
<TableHead>{activeTab === 'payments' ? 'Payment Stage' : 'Audit Stage'}</TableHead>
|
<TableHead>Payment Stage</TableHead>
|
||||||
<TableHead>Current Status</TableHead>
|
<TableHead>Current Status</TableHead>
|
||||||
<TableHead className="text-right pr-6">Workflow Action</TableHead>
|
<TableHead className="text-right pr-6">Workflow Action</TableHead>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
@ -198,7 +155,7 @@ export function FinanceOnboardingPage({ onViewPaymentDetails, onViewAuditDetails
|
|||||||
<TableBody>
|
<TableBody>
|
||||||
{displayApps.length > 0 ? (
|
{displayApps.length > 0 ? (
|
||||||
displayApps.map((app) => {
|
displayApps.map((app) => {
|
||||||
const statusLabel = activeTab === 'payments' ? getRelevantPaymentStatus(app) : getFddApprovalStatus(app);
|
const statusLabel = getRelevantPaymentStatus(app);
|
||||||
return (
|
return (
|
||||||
<TableRow key={app.id} className="hover:bg-blue-50/20 group transition-all">
|
<TableRow key={app.id} className="hover:bg-blue-50/20 group transition-all">
|
||||||
<TableCell className="py-4 pl-6">
|
<TableCell className="py-4 pl-6">
|
||||||
@ -217,25 +174,16 @@ export function FinanceOnboardingPage({ onViewPaymentDetails, onViewAuditDetails
|
|||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{activeTab === 'payments' ? (
|
|
||||||
<>
|
|
||||||
<CreditCard className="w-4 h-4 text-slate-400" />
|
<CreditCard className="w-4 h-4 text-slate-400" />
|
||||||
<span className="text-sm font-medium">
|
<span className="text-sm font-medium">
|
||||||
{app.overallStatus === 'LOI Issued' || app.overallStatus === 'Security Details' ? 'Initial (₹2L)' : 'Final (₹15L)'}
|
{ (app.overallStatus === 'LOI Issued' || app.overallStatus === 'Security Details' || app.overallStatus === 'LOI In Progress') ? 'Security Deposit (₹5L)' : 'First Fill (₹15L)'}
|
||||||
</span>
|
</span>
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<ShieldCheck className="w-4 h-4 text-slate-400" />
|
|
||||||
<span className="text-sm font-medium">FDD Report Approval</span>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<Badge
|
<Badge
|
||||||
className={
|
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' :
|
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'
|
'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">
|
<TableCell className="text-right pr-6">
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
variant={statusLabel === 'Verified' || statusLabel === 'Approved' ? 'outline' : 'default'}
|
variant={statusLabel === 'Verified' ? 'outline' : 'default'}
|
||||||
className={statusLabel !== 'Verified' && statusLabel !== 'Approved'
|
className={statusLabel !== 'Verified'
|
||||||
? (activeTab === 'payments' ? 'bg-blue-600 hover:bg-blue-700' : 'bg-indigo-600 hover:bg-indigo-700 shadow-md')
|
? 'bg-blue-600 hover:bg-blue-700 shadow-md'
|
||||||
: 'bg-white text-slate-600 border-slate-200'}
|
: 'bg-white text-slate-600 border-slate-200'}
|
||||||
onClick={() => handleAction(app.applicationId || app.id)}
|
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'}
|
{statusLabel === 'Verified' ? 'View Receipt' : 'Record Payment'}
|
||||||
</>
|
</>
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<ShieldCheck className="w-4 h-4 mr-2" />
|
|
||||||
{statusLabel === 'Approved' ? 'View Decision' : 'Review Audit'}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Button>
|
</Button>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
@ -271,12 +212,12 @@ export function FinanceOnboardingPage({ onViewPaymentDetails, onViewAuditDetails
|
|||||||
})
|
})
|
||||||
) : (
|
) : (
|
||||||
<TableRow>
|
<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="flex flex-col items-center gap-3">
|
||||||
<div className="w-12 h-12 bg-slate-50 rounded-full flex items-center justify-center">
|
<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" />
|
<CheckCircle className="w-6 h-6 text-slate-200" />
|
||||||
</div>
|
</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>
|
</div>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import { Label } from '../ui/label';
|
|||||||
import { Textarea } from '../ui/textarea';
|
import { Textarea } from '../ui/textarea';
|
||||||
import {
|
import {
|
||||||
ArrowLeft,
|
ArrowLeft,
|
||||||
DollarSign,
|
IndianRupee,
|
||||||
CheckCircle,
|
CheckCircle,
|
||||||
XCircle,
|
XCircle,
|
||||||
FileText,
|
FileText,
|
||||||
@ -63,8 +63,8 @@ export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaym
|
|||||||
verificationRemarks: activeDeposit.remarks || ''
|
verificationRemarks: activeDeposit.remarks || ''
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const initialDefault = configs.INITIAL_SECURITY_DEPOSIT?.amount || 500000;
|
const initialDefault = configs.SECURITY_DEPOSIT?.amount || 500000;
|
||||||
const finalDefault = configs.FINAL_SECURITY_DEPOSIT?.amount || 1500000;
|
const finalDefault = configs.FIRST_FILL?.amount || 1500000;
|
||||||
|
|
||||||
setPaymentDetails({
|
setPaymentDetails({
|
||||||
verificationTransactionId: '',
|
verificationTransactionId: '',
|
||||||
@ -110,7 +110,7 @@ export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaym
|
|||||||
status: 'Verified'
|
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();
|
await fetchData();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error('Failed to verify payment');
|
toast.error('Failed to verify payment');
|
||||||
@ -134,7 +134,7 @@ export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaym
|
|||||||
remarks: paymentDetails.verificationRemarks
|
remarks: paymentDetails.verificationRemarks
|
||||||
});
|
});
|
||||||
|
|
||||||
toast.error(`${activeType === 'INITIAL' ? 'Advance' : 'Final'} payment rejected`);
|
toast.error(`${activeType === 'INITIAL' ? 'Security Deposit' : 'First Fill'} rejected`);
|
||||||
await fetchData();
|
await fetchData();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error('Failed to reject payment');
|
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' : ''}
|
className={activeType === 'INITIAL' ? 'bg-amber-600 hover:bg-amber-700' : ''}
|
||||||
onClick={() => setActiveType('INITIAL')}
|
onClick={() => setActiveType('INITIAL')}
|
||||||
>
|
>
|
||||||
Advance Payment
|
Security Deposit
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
@ -179,7 +179,7 @@ export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaym
|
|||||||
className={activeType === 'FINAL' ? 'bg-amber-600 hover:bg-amber-700' : ''}
|
className={activeType === 'FINAL' ? 'bg-amber-600 hover:bg-amber-700' : ''}
|
||||||
onClick={() => setActiveType('FINAL')}
|
onClick={() => setActiveType('FINAL')}
|
||||||
>
|
>
|
||||||
Final Security Deposit
|
First Fill
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -201,7 +201,7 @@ export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaym
|
|||||||
activeDeposit?.status === 'Rejected' ? "bg-red-100" :
|
activeDeposit?.status === 'Rejected' ? "bg-red-100" :
|
||||||
"bg-amber-100"
|
"bg-amber-100"
|
||||||
)}>
|
)}>
|
||||||
<DollarSign className={cn(
|
<IndianRupee className={cn(
|
||||||
"w-6 h-6",
|
"w-6 h-6",
|
||||||
activeDeposit?.status === 'Verified' ? "text-green-600" :
|
activeDeposit?.status === 'Verified' ? "text-green-600" :
|
||||||
activeDeposit?.status === 'Rejected' ? "text-red-600" :
|
activeDeposit?.status === 'Rejected' ? "text-red-600" :
|
||||||
@ -210,7 +210,7 @@ export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaym
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-slate-900 font-bold">
|
<p className="text-slate-900 font-bold">
|
||||||
{activeType === 'INITIAL' ? 'Advance Payment' : 'Final Security Deposit'}
|
{activeType === 'INITIAL' ? 'Security Deposit' : 'First Fill'}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-sm text-slate-600">
|
<p className="text-sm text-slate-600">
|
||||||
{activeDeposit?.status === 'Verified'
|
{activeDeposit?.status === 'Verified'
|
||||||
@ -275,8 +275,8 @@ export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaym
|
|||||||
<Label className="text-slate-500 block mb-1">Expected Amount</Label>
|
<Label className="text-slate-500 block mb-1">Expected Amount</Label>
|
||||||
<p className="text-2xl font-bold text-amber-900">
|
<p className="text-2xl font-bold text-amber-900">
|
||||||
₹{(activeType === 'INITIAL'
|
₹{(activeType === 'INITIAL'
|
||||||
? (configs.INITIAL_SECURITY_DEPOSIT?.amount || 500000)
|
? (configs.SECURITY_DEPOSIT?.amount || 500000)
|
||||||
: (configs.FINAL_SECURITY_DEPOSIT?.amount || 1500000)
|
: (configs.FIRST_FILL?.amount || 1500000)
|
||||||
).toLocaleString()}
|
).toLocaleString()}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@ -320,14 +320,14 @@ export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaym
|
|||||||
<CardContent>
|
<CardContent>
|
||||||
{application.uploadedDocuments?.filter((d: any) =>
|
{application.uploadedDocuments?.filter((d: any) =>
|
||||||
activeType === 'INITIAL'
|
activeType === 'INITIAL'
|
||||||
? d.documentType?.toLowerCase().includes('initial') && d.documentType?.toLowerCase().includes('deposit')
|
? d.documentType?.toLowerCase().includes('security') && d.documentType?.toLowerCase().includes('deposit')
|
||||||
: d.documentType?.toLowerCase().includes('final') && d.documentType?.toLowerCase().includes('deposit')
|
: d.documentType?.toLowerCase().includes('first') && d.documentType?.toLowerCase().includes('fill')
|
||||||
).length > 0 ? (
|
).length > 0 ? (
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{application.uploadedDocuments.filter((d: any) =>
|
{application.uploadedDocuments.filter((d: any) =>
|
||||||
activeType === 'INITIAL'
|
activeType === 'INITIAL'
|
||||||
? d.documentType?.toLowerCase().includes('initial') && d.documentType?.toLowerCase().includes('deposit')
|
? d.documentType?.toLowerCase().includes('security') && d.documentType?.toLowerCase().includes('deposit')
|
||||||
: d.documentType?.toLowerCase().includes('final') && d.documentType?.toLowerCase().includes('deposit')
|
: d.documentType?.toLowerCase().includes('first') && d.documentType?.toLowerCase().includes('fill')
|
||||||
).map((doc: any, index: number) => (
|
).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 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">
|
<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>Applicant status will advance to {activeType === 'INITIAL' ? 'LOI Issuance' : 'LOA Approval'}</li>
|
||||||
<li>Email notification will be sent to Applicant</li>
|
<li>Email notification will be sent to Applicant</li>
|
||||||
<li>Digital {activeType === 'INITIAL' ? 'LOI' : 'LOA'} generation will be unlocked</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>
|
</ul>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { useState, useEffect } from 'react';
|
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 { Card, CardContent, CardDescription, CardHeader, CardTitle } from '../ui/card';
|
||||||
import { Badge } from '../ui/badge';
|
import { Badge } from '../ui/badge';
|
||||||
import { Button } from '../ui/button';
|
import { Button } from '../ui/button';
|
||||||
@ -283,7 +283,7 @@ export function FnFPage({ currentUser, onViewDetails }: FnFPageProps) {
|
|||||||
fnfCase.status === 'Under Review' ? 'bg-orange-100' :
|
fnfCase.status === 'Under Review' ? 'bg-orange-100' :
|
||||||
'bg-green-100'
|
'bg-green-100'
|
||||||
}`}>
|
}`}>
|
||||||
<DollarSign className={`w-6 h-6 ${
|
<IndianRupee className={`w-6 h-6 ${
|
||||||
fnfCase.status === 'New' ? 'text-blue-600' :
|
fnfCase.status === 'New' ? 'text-blue-600' :
|
||||||
fnfCase.status === 'In Progress' ? 'text-yellow-600' :
|
fnfCase.status === 'In Progress' ? 'text-yellow-600' :
|
||||||
fnfCase.status === 'Under Review' ? 'text-orange-600' :
|
fnfCase.status === 'Under Review' ? 'text-orange-600' :
|
||||||
@ -359,7 +359,7 @@ export function FnFPage({ currentUser, onViewDetails }: FnFPageProps) {
|
|||||||
<div className="flex items-start justify-between">
|
<div className="flex items-start justify-between">
|
||||||
<div className="flex items-start gap-4 flex-1">
|
<div className="flex items-start gap-4 flex-1">
|
||||||
<div className="p-3 bg-yellow-100 rounded-lg">
|
<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>
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<div className="flex items-center gap-3 mb-2">
|
<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 && (
|
{displaySettlements.filter((c: any) => c.status === 'In Progress').length === 0 && (
|
||||||
<div className="text-center py-12 text-slate-500">
|
<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>
|
<p>No cases in progress</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@ -422,7 +422,7 @@ export function FnFPage({ currentUser, onViewDetails }: FnFPageProps) {
|
|||||||
<div className="flex items-start justify-between">
|
<div className="flex items-start justify-between">
|
||||||
<div className="flex items-start gap-4 flex-1">
|
<div className="flex items-start gap-4 flex-1">
|
||||||
<div className="p-3 bg-orange-100 rounded-lg">
|
<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>
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<div className="flex items-center gap-3 mb-2">
|
<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 && (
|
{displaySettlements.filter(c => c.status === 'Under Review' || c.status === 'Calculated').length === 0 && (
|
||||||
<div className="text-center py-12 text-slate-500">
|
<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>
|
<p>No cases under review</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -195,8 +195,8 @@ export function ProspectiveApplicationDetails({ id, onBack }: Props) {
|
|||||||
<option value="PAN Card">PAN Card</option>
|
<option value="PAN Card">PAN Card</option>
|
||||||
<option value="GST Certificate">GST Certificate</option>
|
<option value="GST Certificate">GST Certificate</option>
|
||||||
<option value="Aadhaar Card">Aadhaar Card</option>
|
<option value="Aadhaar Card">Aadhaar Card</option>
|
||||||
<option value="Initial Security Deposit Receipt">Initial Security Deposit Receipt</option>
|
<option value="Security Deposit Receipt">Security Deposit Receipt</option>
|
||||||
<option value="Final Security Deposit Receipt">Final Security Deposit Receipt</option>
|
<option value="First Fill Receipt">First Fill Receipt</option>
|
||||||
<option value="Partnership Deed">Partnership Deed</option>
|
<option value="Partnership Deed">Partnership Deed</option>
|
||||||
<option value="LLP Agreement">LLP Agreement</option>
|
<option value="LLP Agreement">LLP Agreement</option>
|
||||||
<option value="Certificate of Incorporation">Certificate of Incorporation</option>
|
<option value="Certificate of Incorporation">Certificate of Incorporation</option>
|
||||||
|
|||||||
@ -162,7 +162,7 @@ export function FDDDashboardPage() {
|
|||||||
<tr
|
<tr
|
||||||
key={app.id}
|
key={app.id}
|
||||||
className="hover:bg-slate-50 transition-colors cursor-pointer group"
|
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">
|
<td className="px-6 py-4">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
|
|||||||
@ -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 { Card, CardContent, CardDescription, CardHeader, CardTitle } from '../ui/card';
|
||||||
import { Badge } from '../ui/badge';
|
import { Badge } from '../ui/badge';
|
||||||
import { Button } from '../ui/button';
|
import { Button } from '../ui/button';
|
||||||
@ -78,7 +78,7 @@ export function FinanceDashboard({ onNavigate, onViewPaymentDetails, onViewAudit
|
|||||||
const stage = app.currentStage;
|
const stage = app.currentStage;
|
||||||
return [
|
return [
|
||||||
'LOI In Progress', 'LOI Issued', 'LOA Pending', 'Dealer Code Generation',
|
'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'
|
'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'
|
||||||
].includes(s) || stage === 'Finance';
|
].includes(s) || stage === 'Finance';
|
||||||
});
|
});
|
||||||
@ -171,7 +171,7 @@ export function FinanceDashboard({ onNavigate, onViewPaymentDetails, onViewAudit
|
|||||||
|
|
||||||
const pendingOnboarding = applications.filter(app => getRelevantPaymentStatus(app) !== 'Verified');
|
const pendingOnboarding = applications.filter(app => getRelevantPaymentStatus(app) !== 'Verified');
|
||||||
const verifiedOnboarding = 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 pendingFnF = mockFnFCases.filter(f => !f.hasFinanceSummary);
|
||||||
const completedFnF = 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">
|
<Tabs defaultValue="onboarding" className="w-full">
|
||||||
<TabsList className="grid w-full grid-cols-2">
|
<TabsList className="grid w-full grid-cols-2">
|
||||||
<TabsTrigger value="onboarding">
|
<TabsTrigger value="onboarding">
|
||||||
<DollarSign className="w-4 h-4 mr-2" />
|
<IndianRupee className="w-4 h-4 mr-2" />
|
||||||
Onboarding
|
Onboarding
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
<TabsTrigger value="fnf">
|
<TabsTrigger value="fnf">
|
||||||
@ -343,7 +343,7 @@ export function FinanceDashboard({ onNavigate, onViewPaymentDetails, onViewAudit
|
|||||||
<div>
|
<div>
|
||||||
<p className="text-slate-600">Stage</p>
|
<p className="text-slate-600">Stage</p>
|
||||||
<p className="text-amber-700 font-bold">
|
<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>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@ -365,20 +365,6 @@ export function FinanceDashboard({ onNavigate, onViewPaymentDetails, onViewAudit
|
|||||||
<FileText className="w-4 h-4 mr-2" />
|
<FileText className="w-4 h-4 mr-2" />
|
||||||
View Details
|
View Details
|
||||||
</Button>
|
</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
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
className="bg-green-600 hover:bg-green-700 font-bold"
|
className="bg-green-600 hover:bg-green-700 font-bold"
|
||||||
@ -391,7 +377,6 @@ export function FinanceDashboard({ onNavigate, onViewPaymentDetails, onViewAudit
|
|||||||
<CheckCircle className="w-4 h-4 mr-2" />
|
<CheckCircle className="w-4 h-4 mr-2" />
|
||||||
Verify Payment
|
Verify Payment
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
|||||||
@ -42,7 +42,7 @@ export const DocumentPreviewModal: React.FC<DocumentPreviewModalProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={isOpen} onOpenChange={onClose}>
|
<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 ? (
|
{document ? (
|
||||||
<>
|
<>
|
||||||
<div className="flex items-center justify-between p-4 border-b bg-slate-50">
|
<div className="flex items-center justify-between p-4 border-b bg-slate-50">
|
||||||
|
|||||||
@ -160,5 +160,20 @@ export const onboardingService = {
|
|||||||
const response: any = await API.updateApplication(id, data);
|
const response: any = await API.updateApplication(id, data);
|
||||||
if (!response.ok) throw new Error(response.data?.message || 'Failed to update application');
|
if (!response.ok) throw new Error(response.data?.message || 'Failed to update application');
|
||||||
return response.data;
|
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;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user