end to end flow testing for all modules paralley enhancing profile schema to gather all info

This commit is contained in:
laxman h 2026-04-10 20:59:14 +05:30
parent 6542f0fb30
commit dad534c169
11 changed files with 217 additions and 150 deletions

View File

@ -354,6 +354,8 @@ export const ApplicationDetails = () => {
stageApprovals: data.stageApprovals || [], stageApprovals: data.stageApprovals || [],
fddAssignments: data.fddAssignments || [], fddAssignments: data.fddAssignments || [],
constitutionType: data.constitutionType, constitutionType: data.constitutionType,
architectureStatus: data.architectureStatus,
statutoryStatus: data.statutoryStatus,
}; };
setApplication(mappedApp); setApplication(mappedApp);
if (data.uploadedDocuments) { if (data.uploadedDocuments) {
@ -1202,7 +1204,7 @@ export const ApplicationDetails = () => {
id: 12, id: 12,
name: 'LOA', name: 'LOA',
status: getStageStatus('LOA', () => ['EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) ? 'completed' : application.status === 'LOA Pending' ? 'active' : 'pending'), status: getStageStatus('LOA', () => ['EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) ? 'completed' : application.status === 'LOA Pending' ? 'active' : 'pending'),
isLocked: application.status === 'LOA Pending' && getDeposit('FINAL')?.status !== 'Verified', isLocked: application.status === 'LOA Pending' && getDeposit('FIRST_FILL')?.status !== 'Verified',
lockMessage: 'First Fill (₹15L) must be verified by Finance before LOA Approval.', lockMessage: 'First Fill (₹15L) must be verified by Finance before LOA Approval.',
evaluators: Array.from(new Set((application.participants || []) evaluators: Array.from(new Set((application.participants || [])
.filter((p: any) => p.metadata?.stageCode === 'LOA_APPROVAL' || p.metadata?.allAssignments?.includes('LOA_APPROVAL')) .filter((p: any) => p.metadata?.stageCode === 'LOA_APPROVAL' || p.metadata?.allAssignments?.includes('LOA_APPROVAL'))
@ -1682,7 +1684,7 @@ export const ApplicationDetails = () => {
'LOA Pending', 'EOR In Progress', 'EOR Complete', 'Inauguration' 'LOA Pending', 'EOR In Progress', 'EOR Complete', 'Inauguration'
].includes(application.status); ].includes(application.status);
const finalDepositVerified = getDeposit('FINAL')?.status === 'Verified'; const finalDepositVerified = getDeposit('FIRST_FILL')?.status === 'Verified';
const isLoaLocked = application.status === 'LOA Pending' && !finalDepositVerified; const isLoaLocked = application.status === 'LOA Pending' && !finalDepositVerified;
// Sequential Enforcement for LOI APPROVAL (SRS 6.16.3.5) // Sequential Enforcement for LOI APPROVAL (SRS 6.16.3.5)
@ -2024,21 +2026,7 @@ export const ApplicationDetails = () => {
<div key={report.id} className="p-6 space-y-6 bg-white"> <div key={report.id} className="p-6 space-y-6 bg-white">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8"> <div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
<div className="space-y-5"> <div className="space-y-5">
<div> {/* Auditor Recommendation Hidden as per request */}
<Label className="text-[10px] text-slate-400 uppercase tracking-widest font-black mb-2 block">Auditor Recommendation</Label>
<div className={cn(
"inline-flex items-center gap-2 px-3 py-1.5 rounded-full border text-xs font-black shadow-sm",
report.recommendation === 'Recommended' || report.recommendation === 'Green' ? "bg-green-50 border-green-200 text-green-700" :
report.recommendation === 'Qualified with Observations' || report.recommendation === 'Amber' ? "bg-amber-50 border-amber-200 text-amber-700" :
"bg-red-50 border-red-200 text-red-700"
)}>
<div className={cn("w-2 h-2 rounded-full",
report.recommendation === 'Recommended' || report.recommendation === 'Green' ? "bg-green-500" :
report.recommendation === 'Qualified with Observations' || report.recommendation === 'Amber' ? "bg-amber-500" : "bg-red-500"
)} />
{report.recommendation?.toUpperCase()}
</div>
</div>
<div> <div>
<Label className="text-[10px] text-slate-400 uppercase tracking-widest font-black mb-2 block">Findings Summary</Label> <Label className="text-[10px] text-slate-400 uppercase tracking-widest font-black mb-2 block">Findings Summary</Label>
@ -2093,16 +2081,23 @@ export const ApplicationDetails = () => {
</div> </div>
)} )}
<div className="pt-4 flex items-center gap-3"> <div className="pt-4 flex flex-col gap-2">
<div className="flex items-center gap-2">
<div className="flex items-center gap-1.5 bg-slate-50 border border-slate-100 px-3 py-1 rounded-full">
<User className="w-3.5 h-3.5 text-slate-500" />
<span className="text-[10px] font-bold text-slate-600 uppercase">Submitted by: {report.submitter?.fullName || 'Auditor'}</span>
</div>
</div>
{report.verifiedAt ? ( {report.verifiedAt ? (
<div className="flex items-center gap-2 bg-green-50 border border-green-100 px-3 py-1.5 rounded-full"> <div className="flex items-center gap-2 bg-green-50 border border-green-100 px-3 py-1.5 rounded-full w-fit">
<CheckCircle className="w-4 h-4 text-green-600" /> <CheckCircle className="w-4 h-4 text-green-600" />
<span className="text-[10px] font-black text-green-700 uppercase">Audit Verified by Finance</span> <span className="text-[10px] font-black text-green-700 uppercase"> Verified by {report.verifier?.fullName || 'Admin'}</span>
</div> </div>
) : ( ) : (
<div className="flex items-center gap-2 bg-amber-50 border border-amber-100 px-3 py-1.5 rounded-full"> <div className="flex items-center gap-2 bg-amber-50 border border-amber-100 px-3 py-1.5 rounded-full w-fit">
<Clock className="w-4 h-4 text-amber-600" /> <Clock className="w-4 h-4 text-amber-600" />
<span className="text-[10px] font-black text-amber-700 uppercase">Pending Finance Review</span> <span className="text-[10px] font-black text-amber-700 uppercase">Pending Review</span>
</div> </div>
)} )}
</div> </div>
@ -3169,7 +3164,7 @@ export const ApplicationDetails = () => {
<div className="grid grid-cols-1 md:grid-cols-2 gap-6"> <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{/* Initial Security Deposit */} {/* Initial Security Deposit */}
{(() => { {(() => {
const deposit = getDeposit('INITIAL'); const deposit = getDeposit('SECURITY_DEPOSIT');
const config = paymentConfigs.SECURITY_DEPOSIT; const config = paymentConfigs.SECURITY_DEPOSIT;
const expectedAmount = config?.amount || 500000; const expectedAmount = config?.amount || 500000;
@ -3252,7 +3247,7 @@ export const ApplicationDetails = () => {
{/* Final Security Deposit */} {/* Final Security Deposit */}
{(() => { {(() => {
const deposit = getDeposit('FINAL'); const deposit = getDeposit('FIRST_FILL');
const config = paymentConfigs.FIRST_FILL; const config = paymentConfigs.FIRST_FILL;
const expectedAmount = config?.amount || 1500000; const expectedAmount = config?.amount || 1500000;
@ -4856,6 +4851,7 @@ export const ApplicationDetails = () => {
</DialogHeader> </DialogHeader>
<div className="space-y-4"> <div className="space-y-4">
{(currentUser?.role !== 'FDD' && currentUser?.roleCode !== 'FDD') && (
<div className="space-y-2"> <div className="space-y-2">
<Label className="text-[10px] font-black uppercase tracking-widest text-slate-400">Auditor Recommendation</Label> <Label className="text-[10px] font-black uppercase tracking-widest text-slate-400">Auditor Recommendation</Label>
<div className="flex gap-2"> <div className="flex gap-2">
@ -4876,6 +4872,7 @@ export const ApplicationDetails = () => {
))} ))}
</div> </div>
</div> </div>
)}
<div className="space-y-2"> <div className="space-y-2">
<Label className="text-[10px] font-black uppercase tracking-widest text-slate-400">Findings Summary</Label> <Label className="text-[10px] font-black uppercase tracking-widest text-slate-400">Findings Summary</Label>
@ -4914,7 +4911,9 @@ export const ApplicationDetails = () => {
applicationId: application!.id, applicationId: application!.id,
stageCode: 'FDD_VERIFICATION', stageCode: 'FDD_VERIFICATION',
decision: 'Approved', decision: 'Approved',
remarks: `[RECOMMENDATION: ${fddAuditRecommendation}] \nFindings: ${fddAuditFindings}`, remarks: (currentUser?.role === 'FDD' || currentUser?.roleCode === 'FDD')
? `Findings: ${fddAuditFindings}`
: `[RECOMMENDATION: ${fddAuditRecommendation}] \nFindings: ${fddAuditFindings}`,
nextStatus: 'LOI In Progress', nextStatus: 'LOI In Progress',
nextProgress: 65 nextProgress: 65
}); });

View File

@ -252,6 +252,45 @@ export function ConstitutionalChangeDetails({ requestId, onBack }: Constitutiona
</CardContent> </CardContent>
</Card> </Card>
{/* Establishment Details */}
<Card>
<CardHeader>
<CardTitle>Establishment & Dealer Codes</CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-2 md:grid-cols-4 gap-6">
<div>
<p className="text-slate-600 text-sm mb-1">Sales Code</p>
<p className="text-slate-900">{request.dealer?.dealerProfile?.dealerCode?.salesCode || 'N/A'}</p>
</div>
<div>
<p className="text-slate-600 text-sm mb-1">Service Code</p>
<p className="text-slate-900">{request.dealer?.dealerProfile?.dealerCode?.serviceCode || 'N/A'}</p>
</div>
<div>
<p className="text-slate-600 text-sm mb-1">GMA Code</p>
<p className="text-slate-900">{request.dealer?.dealerProfile?.dealerCode?.gmaCode || 'N/A'}</p>
</div>
<div>
<p className="text-slate-600 text-sm mb-1">Gear Code</p>
<p className="text-slate-900">{request.dealer?.dealerProfile?.dealerCode?.gearCode || 'N/A'}</p>
</div>
<div>
<p className="text-slate-600 text-sm mb-1">Inauguration Date</p>
<p className="text-slate-900">{request.dealer?.dealerProfile?.onboardedAt ? formatDateTime(request.dealer.dealerProfile.onboardedAt, 'date') : 'N/A'}</p>
</div>
<div>
<p className="text-slate-600 text-sm mb-1">LOI Date</p>
<p className="text-slate-900">{request.dealer?.dealerProfile?.loiDate ? formatDateTime(request.dealer.dealerProfile.loiDate, 'date') : 'N/A'}</p>
</div>
<div>
<p className="text-slate-600 text-sm mb-1">LOA Date</p>
<p className="text-slate-900">{request.dealer?.dealerProfile?.loaDate ? formatDateTime(request.dealer.dealerProfile.loaDate, 'date') : 'N/A'}</p>
</div>
</div>
</CardContent>
</Card>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6"> <div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
{/* Main Content */} {/* Main Content */}
<div className="lg:col-span-2 space-y-6"> <div className="lg:col-span-2 space-y-6">

View File

@ -17,7 +17,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 { formatDateTime } from '@/components/ui/utils';
import { import {
Dialog, Dialog,
DialogContent, DialogContent,
@ -45,10 +45,9 @@ 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>(''); const [fddAuditFindings, setFddAuditFindings] = useState<string>('');
const user = useSelector((state: RootState) => state.auth.user); const user = useSelector((state: RootState) => state.auth.user);
const isFddRole = user?.role === 'FDD' || user?.role === 'FDD Auditor'; const isFddRole = user?.role === 'FDD';
useEffect(() => { useEffect(() => {
if (id) fetchApplication(); if (id) fetchApplication();
@ -202,6 +201,14 @@ export function FDDApplicationDetails() {
<Upload className="w-4 h-4" /> <Upload className="w-4 h-4" />
{uploading ? 'Uploading...' : 'Upload Report'} {uploading ? 'Uploading...' : 'Upload Report'}
</button> </button>
<button
disabled={uploading}
onClick={() => setShowFinalizeModal(true)}
className="flex items-center gap-2 px-4 py-2 bg-slate-900 text-white font-bold text-xs uppercase tracking-wider rounded-lg hover:bg-slate-800 transition-all shadow-lg shadow-slate-200"
>
<CheckCircle2 className="w-4 h-4" />
{uploading ? 'Processing...' : 'Submit Final Findings'}
</button>
<button <button
disabled={uploading} disabled={uploading}
onClick={() => setShowFlagModal(true)} onClick={() => setShowFlagModal(true)}
@ -545,26 +552,6 @@ export function FDDApplicationDetails() {
</div> </div>
<div className="space-y-4 pt-2"> <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"> <div className="space-y-2">
<Label className="text-[10px] font-black uppercase tracking-widest text-slate-400">Detailed Audit Findings & Remarks</Label> <Label className="text-[10px] font-black uppercase tracking-widest text-slate-400">Detailed Audit Findings & Remarks</Label>
@ -594,16 +581,12 @@ export function FDDApplicationDetails() {
toast.error('Please provide findings.'); toast.error('Please provide findings.');
return; return;
} }
if (!assignment?.id) {
toast.error('Assignment not found.');
return;
}
setUploading(true); setUploading(true);
const res: any = await API.submitFddReport({ const res: any = await API.submitFddReport({
assignmentId: assignment.id, assignmentId: assignment?.id,
applicationId: id,
findings: fddAuditFindings, findings: fddAuditFindings,
recommendation: fddAuditRecommendation recommendation: null
}); });
if (res.data?.success) { if (res.data?.success) {

View File

@ -49,7 +49,7 @@ export function FinanceOnboardingPage({ onViewPaymentDetails }: FinanceOnboardin
const getRelevantPaymentStatus = (app: any) => { const getRelevantPaymentStatus = (app: any) => {
if (!app.securityDeposits || app.securityDeposits.length === 0) return 'Awaiting Payment'; if (!app.securityDeposits || app.securityDeposits.length === 0) return 'Awaiting Payment';
const s = app.overallStatus || app.status; const s = app.overallStatus || app.status;
const relevantType = (s.includes('LOI') || s === 'PAYMENT_VERIFICATION' || s === 'Security Details') ? 'INITIAL' : 'FINAL'; const relevantType = (s.includes('LOI') || s === 'PAYMENT_VERIFICATION' || s === 'Security Details') ? 'SECURITY_DEPOSIT' : 'FIRST_FILL';
const deposit = app.securityDeposits.find((d: any) => d.depositType === relevantType); const deposit = app.securityDeposits.find((d: any) => d.depositType === relevantType);
return deposit ? deposit.status : 'Awaiting Payment'; return deposit ? deposit.status : 'Awaiting Payment';
}; };

View File

@ -33,7 +33,7 @@ interface FinancePaymentDetailsPageProps {
export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaymentDetailsPageProps) { export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaymentDetailsPageProps) {
const [application, setApplication] = useState<any>(null); const [application, setApplication] = useState<any>(null);
const [deposits, setDeposits] = useState<any[]>([]); const [deposits, setDeposits] = useState<any[]>([]);
const [activeType, setActiveType] = useState<'INITIAL' | 'FINAL'>('INITIAL'); const [activeType, setActiveType] = useState<'SECURITY_DEPOSIT' | 'FIRST_FILL'>('SECURITY_DEPOSIT');
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [isSubmitting, setIsSubmitting] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false);
const [configs, setConfigs] = useState<any>({}); const [configs, setConfigs] = useState<any>({});
@ -68,7 +68,7 @@ export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaym
setPaymentDetails({ setPaymentDetails({
verificationTransactionId: '', verificationTransactionId: '',
receivedAmount: activeType === 'INITIAL' ? initialDefault.toString() : finalDefault.toString(), receivedAmount: activeType === 'SECURITY_DEPOSIT' ? initialDefault.toString() : finalDefault.toString(),
receivedDate: new Date().toISOString().split('T')[0], receivedDate: new Date().toISOString().split('T')[0],
verificationRemarks: '' verificationRemarks: ''
}); });
@ -110,7 +110,7 @@ export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaym
status: 'Verified' status: 'Verified'
}); });
toast.success(`${activeType === 'INITIAL' ? 'Security Deposit' : 'First Fill'} verified and approved`); toast.success(`${activeType === 'SECURITY_DEPOSIT' ? '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' ? 'Security Deposit' : 'First Fill'} rejected`); toast.error(`${activeType === 'SECURITY_DEPOSIT' ? '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');
@ -167,17 +167,17 @@ export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaym
<div className="flex gap-2 mt-2"> <div className="flex gap-2 mt-2">
<Button <Button
size="sm" size="sm"
variant={activeType === 'INITIAL' ? 'default' : 'outline'} variant={activeType === 'SECURITY_DEPOSIT' ? 'default' : 'outline'}
className={activeType === 'INITIAL' ? 'bg-amber-600 hover:bg-amber-700' : ''} className={activeType === 'SECURITY_DEPOSIT' ? 'bg-amber-600 hover:bg-amber-700' : ''}
onClick={() => setActiveType('INITIAL')} onClick={() => setActiveType('SECURITY_DEPOSIT')}
> >
Security Deposit Security Deposit
</Button> </Button>
<Button <Button
size="sm" size="sm"
variant={activeType === 'FINAL' ? 'default' : 'outline'} variant={activeType === 'FIRST_FILL' ? 'default' : 'outline'}
className={activeType === 'FINAL' ? 'bg-amber-600 hover:bg-amber-700' : ''} className={activeType === 'FIRST_FILL' ? 'bg-amber-600 hover:bg-amber-700' : ''}
onClick={() => setActiveType('FINAL')} onClick={() => setActiveType('FIRST_FILL')}
> >
First Fill First Fill
</Button> </Button>
@ -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' ? 'Security Deposit' : 'First Fill'} {activeType === 'SECURITY_DEPOSIT' ? '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'
@ -274,7 +274,7 @@ export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaym
<div className="p-4 bg-slate-50 rounded-lg border border-slate-200"> <div className="p-4 bg-slate-50 rounded-lg border border-slate-200">
<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 === 'SECURITY_DEPOSIT'
? (configs.SECURITY_DEPOSIT?.amount || 500000) ? (configs.SECURITY_DEPOSIT?.amount || 500000)
: (configs.FIRST_FILL?.amount || 1500000) : (configs.FIRST_FILL?.amount || 1500000)
).toLocaleString()} ).toLocaleString()}
@ -319,13 +319,13 @@ export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaym
</CardHeader> </CardHeader>
<CardContent> <CardContent>
{application.uploadedDocuments?.filter((d: any) => {application.uploadedDocuments?.filter((d: any) =>
activeType === 'INITIAL' activeType === 'SECURITY_DEPOSIT'
? d.documentType?.toLowerCase().includes('security') && 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') : 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 === 'SECURITY_DEPOSIT'
? d.documentType?.toLowerCase().includes('security') && 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') : d.documentType?.toLowerCase().includes('first') && d.documentType?.toLowerCase().includes('fill')
).map((doc: any, index: number) => ( ).map((doc: any, index: number) => (
@ -393,7 +393,7 @@ export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaym
<Input <Input
id="receivedAmount" id="receivedAmount"
type="number" type="number"
placeholder={(activeType === 'INITIAL' ? 500000 : 1500000).toString()} placeholder={(activeType === 'SECURITY_DEPOSIT' ? 500000 : 1500000).toString()}
disabled={activeDeposit?.status === 'Verified'} disabled={activeDeposit?.status === 'Verified'}
className="mt-1" className="mt-1"
value={paymentDetails.receivedAmount} value={paymentDetails.receivedAmount}
@ -468,10 +468,10 @@ export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaym
<CardContent className="text-xs text-slate-300 space-y-3"> <CardContent className="text-xs text-slate-300 space-y-3">
<p>Once verified, the following will occur:</p> <p>Once verified, the following will occur:</p>
<ul className="list-disc pl-4 space-y-2"> <ul className="list-disc pl-4 space-y-2">
<li>Applicant status will advance to {activeType === 'INITIAL' ? 'LOI Issuance' : 'LOA Approval'}</li> <li>Applicant status will advance to {activeType === 'SECURITY_DEPOSIT' ? '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 === 'SECURITY_DEPOSIT' ? 'LOI' : 'LOA'} generation will be unlocked</li>
<li>This payment confirms the {activeType === 'INITIAL' ? 'Security Deposit' : 'First Fill'}</li> <li>This payment confirms the {activeType === 'SECURITY_DEPOSIT' ? 'Security Deposit' : 'First Fill'}</li>
</ul> </ul>
</CardContent> </CardContent>
</Card> </Card>

View File

@ -313,33 +313,33 @@ export function ResignationDetails({ resignationId, onBack, currentUser }: Resig
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<div className="grid grid-cols-2 md:grid-cols-3 gap-6"> <div className="grid grid-cols-2 md:grid-cols-3 gap-6">
<div>
<Label className="text-slate-600">Dealer Code</Label>
<p>{resignationData?.outlet?.code}</p>
</div>
<div> <div>
<Label className="text-slate-600">Dealer Name</Label> <Label className="text-slate-600">Dealer Name</Label>
<p>{resignationData?.outlet?.name}</p> <p>{resignationData?.dealer?.fullName || resignationData?.outlet?.name}</p>
</div> </div>
<div> <div>
<Label className="text-slate-600">GST</Label> <Label className="text-slate-600">GST</Label>
<p>{resignationData?.outlet?.gstNumber || 'N/A'}</p> <p>{resignationData?.dealer?.dealerProfile?.gstNumber || resignationData?.outlet?.gstNumber || 'N/A'}</p>
</div>
<div>
<Label className="text-slate-600">Sales Code</Label>
<p>{resignationData?.dealer?.dealerProfile?.dealerCode?.salesCode || 'N/A'}</p>
</div>
<div>
<Label className="text-slate-600">Service Code</Label>
<p>{resignationData?.dealer?.dealerProfile?.dealerCode?.serviceCode || 'N/A'}</p>
</div>
<div>
<Label className="text-slate-600">GMA Code</Label>
<p>{resignationData?.dealer?.dealerProfile?.dealerCode?.gmaCode || 'N/A'}</p>
</div>
<div>
<Label className="text-slate-600">Gear Code</Label>
<p>{resignationData?.dealer?.dealerProfile?.dealerCode?.gearCode || 'N/A'}</p>
</div> </div>
<div className="col-span-2"> <div className="col-span-2">
<Label className="text-slate-600">Address</Label> <Label className="text-slate-600">Address</Label>
<p>{resignationData?.outlet?.address}</p> <p>{resignationData?.dealer?.dealerProfile?.registeredAddress || resignationData?.outlet?.address}</p>
</div>
<div>
<Label className="text-slate-600">City</Label>
<p>{resignationData?.outlet?.city}</p>
</div>
<div>
<Label className="text-slate-600">State</Label>
<p>{resignationData?.outlet?.state}</p>
</div>
<div>
<Label className="text-slate-600">Region</Label>
<p>{resignationData?.outlet?.region}</p>
</div> </div>
</div> </div>
</CardContent> </CardContent>
@ -353,11 +353,19 @@ export function ResignationDetails({ resignationId, onBack, currentUser }: Resig
<div className="grid grid-cols-2 md:grid-cols-3 gap-6"> <div className="grid grid-cols-2 md:grid-cols-3 gap-6">
<div> <div>
<Label className="text-slate-600">Inauguration</Label> <Label className="text-slate-600">Inauguration</Label>
<p>{resignationData?.outlet?.inaugurationDate ? new Date(resignationData.outlet.inaugurationDate).toLocaleDateString() : 'N/A'}</p> <p>{resignationData?.dealer?.dealerProfile?.onboardedAt ? formatDateTime(resignationData.dealer.dealerProfile.onboardedAt, 'date') : (resignationData?.outlet?.inaugurationDate ? new Date(resignationData.outlet.inaugurationDate).toLocaleDateString() : 'N/A')}</p>
</div>
<div>
<Label className="text-slate-600">LOA Date</Label>
<p>{resignationData?.dealer?.dealerProfile?.loaDate ? formatDateTime(resignationData.dealer.dealerProfile.loaDate, 'date') : 'N/A'}</p>
</div>
<div>
<Label className="text-slate-600">LOI Date</Label>
<p>{resignationData?.dealer?.dealerProfile?.loiDate ? formatDateTime(resignationData.dealer.dealerProfile.loiDate, 'date') : 'N/A'}</p>
</div> </div>
<div> <div>
<Label className="text-slate-600">Dealership Type</Label> <Label className="text-slate-600">Dealership Type</Label>
<p>{resignationData?.outlet?.type || 'N/A'}</p> <p>{resignationData?.dealer?.dealerProfile?.application?.businessType || resignationData?.outlet?.type || 'N/A'}</p>
</div> </div>
<div> <div>
<Label className="text-slate-600">City Category</Label> <Label className="text-slate-600">City Category</Label>

View File

@ -10,7 +10,7 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '.
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '../ui/table'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '../ui/table';
import { Alert, AlertDescription, AlertTitle } from '../ui/alert'; import { Alert, AlertDescription, AlertTitle } from '../ui/alert';
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { User, mockWorkNotes } from '../../lib/mock-data'; import { User } from '../../lib/mock-data';
import { toast } from 'sonner'; import { toast } from 'sonner';
import { terminationService } from '../../services/termination.service'; import { terminationService } from '../../services/termination.service';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
@ -305,7 +305,7 @@ export function TerminationDetails({ terminationId, onBack, currentUser }: Termi
} }
}; };
const workNotesCount = mockWorkNotes.length; const workNotesCount = (request.worknotes || []).length;
return ( return (
<div className="space-y-6"> <div className="space-y-6">
@ -326,7 +326,7 @@ export function TerminationDetails({ terminationId, onBack, currentUser }: Termi
</Button> </Button>
<div> <div>
<h1 className="text-2xl">{request.requestId || terminationId}</h1> <h1 className="text-2xl">{request.requestId || terminationId}</h1>
<p className="text-slate-600">{request.dealer?.businessName || request.dealerName}</p> <p className="text-slate-600">{request.dealer?.businessName || request.dealer?.legalName || 'Termination'}</p>
</div> </div>
<Badge className={getSeverityColor(request.severity)}> <Badge className={getSeverityColor(request.severity)}>
{request.severity} {request.severity}
@ -438,9 +438,9 @@ export function TerminationDetails({ terminationId, onBack, currentUser }: Termi
className="relative hover:bg-red-50 hover:border-red-300 hover:text-red-700 transition-all shadow-sm" className="relative hover:bg-red-50 hover:border-red-300 hover:text-red-700 transition-all shadow-sm"
onClick={() => navigate(`/worknotes/termination/${terminationId}`, { onClick={() => navigate(`/worknotes/termination/${terminationId}`, {
state: { state: {
applicationName: terminationData?.dealerName || 'Termination', applicationName: request?.dealer?.businessName || 'Termination',
registrationNumber: terminationId || '', registrationNumber: terminationId || '',
participants: terminationData?.participants || [] participants: request?.participants || []
} }
})} })}
> >
@ -516,7 +516,11 @@ export function TerminationDetails({ terminationId, onBack, currentUser }: Termi
</div> </div>
<div> <div>
<Label className="text-slate-600">GMA Code</Label> <Label className="text-slate-600">GMA Code</Label>
<p>{request.dealer?.dealerCode?.gmaCode || request.gmaCode || 'N/A'}</p> <p>{request.dealer?.dealerCode?.gmaCode || 'N/A'}</p>
</div>
<div>
<Label className="text-slate-600">Gear Code</Label>
<p>{request.dealer?.dealerCode?.gearCode || 'N/A'}</p>
</div> </div>
</div> </div>
</CardContent> </CardContent>
@ -533,12 +537,12 @@ export function TerminationDetails({ terminationId, onBack, currentUser }: Termi
<p>{request.dealer?.onboardedAt ? formatDateTime(request.dealer.onboardedAt, 'date') : (request.inauguration || 'N/A')}</p> <p>{request.dealer?.onboardedAt ? formatDateTime(request.dealer.onboardedAt, 'date') : (request.inauguration || 'N/A')}</p>
</div> </div>
<div> <div>
<Label className="text-slate-600">LOA</Label> <Label className="text-slate-600">LOA Date</Label>
<p>{request.loa || 'N/A'}</p> <p>{request.dealer?.loaDate ? formatDateTime(request.dealer.loaDate, 'date') : 'N/A'}</p>
</div> </div>
<div> <div>
<Label className="text-slate-600">LOI</Label> <Label className="text-slate-600">LOI Date</Label>
<p>{request.loi || 'N/A'}</p> <p>{request.dealer?.loiDate ? formatDateTime(request.dealer.loiDate, 'date') : 'N/A'}</p>
</div> </div>
<div> <div>
<Label className="text-slate-600">Last 6 Months Sales</Label> <Label className="text-slate-600">Last 6 Months Sales</Label>

View File

@ -253,32 +253,55 @@ export function WorkNotesPage(props: Partial<WorkNotesPageProps>) {
} }
}, [requestId, requestType, socket]); }, [requestId, requestType, socket]);
// Fetch application details if metadata or participants are missing (e.g. on refresh) // Fetch details if metadata or participants are missing (e.g. on refresh)
useEffect(() => { useEffect(() => {
if (requestId && requestType === 'application') { if (!requestId || !requestType) return;
const fetchApplicationDetails = async () => {
const fetchDetails = async () => {
try { try {
const appData = await onboardingService.getApplicationById(requestId); let data: any = null;
if (appData) { if (requestType === 'application') {
// Update participants if not provided data = await onboardingService.getApplicationById(requestId);
if (externalParticipants.length === 0 && appData.participants) { if (data) {
setExternalParticipants(appData.participants); if (externalParticipants.length === 0 && data.participants) setExternalParticipants(data.participants);
if (!appName || appName === 'Application') setAppName(data.companyName || 'Application');
if (!regNumber) setRegNumber(data.registrationNumber || '');
} }
// Update metadata if not provided via props/state } else if (requestType === 'termination') {
if (!props.applicationName && !location.state?.applicationName && appData.companyName) { const { API } = await import('../../api/API');
setAppName(appData.companyName); const res: any = await API.getTerminationById(requestId);
if (res.data?.success) {
data = res.data.termination;
if (externalParticipants.length === 0 && data.participants) setExternalParticipants(data.participants);
if (!appName || appName === 'Application' || appName === 'Termination') setAppName(data.dealer?.businessName || 'Termination');
if (!regNumber) setRegNumber(data.requestId || '');
} }
if (!props.registrationNumber && !location.state?.registrationNumber && appData.registrationNumber) { } else if (requestType === 'constitutional' || requestType === 'constitutional-change') {
setRegNumber(appData.registrationNumber); const { API } = await import('../../api/API');
const res: any = await API.getConstitutionalChangeById(requestId);
if (res.data?.success) {
data = res.data.request;
if (externalParticipants.length === 0 && data.participants) setExternalParticipants(data.participants);
if (!appName || appName === 'Application' || appName === 'Constitutional Change') setAppName(data.outlet?.name || 'Constitutional Change');
if (!regNumber) setRegNumber(data.requestId || '');
}
} else if (requestType === 'resignation') {
const { API } = await import('../../api/API');
const res: any = await API.getResignationById(requestId);
if (res.data?.success) {
data = res.data.resignation;
if (externalParticipants.length === 0 && data.participants) setExternalParticipants(data.participants);
if (!appName || appName === 'Application' || appName === 'Resignation') setAppName(data.dealer?.businessName || 'Resignation');
if (!regNumber) setRegNumber(data.resignationId || '');
} }
} }
} catch (error) { } catch (error) {
console.error('Failed to fetch application details:', error); console.error(`Failed to fetch ${requestType} details:`, error);
} }
}; };
fetchApplicationDetails();
} fetchDetails();
}, [requestId, requestType, externalParticipants.length, props.applicationName, props.registrationNumber, location.state]); }, [requestId, requestType, externalParticipants.length, appName, regNumber]);
// Auto-scroll logic // Auto-scroll logic
const messagesEndRef = useRef<HTMLDivElement>(null); const messagesEndRef = useRef<HTMLDivElement>(null);

View File

@ -311,7 +311,11 @@ export function FinanceDashboard({ onNavigate, onViewPaymentDetails, onViewAudit
</div> </div>
<div> <div>
<p className="text-slate-600">Type</p> <p className="text-slate-600">Type</p>
<p>{app.paymentType}</p> <p>
{app.paymentType === 'SECURITY_DEPOSIT' ? 'Security Deposit' :
app.paymentType === 'FIRST_FILL' ? 'First Fill' :
app.paymentType}
</p>
</div> </div>
<div> <div>
<p className="text-slate-600">Amount</p> <p className="text-slate-600">Amount</p>
@ -384,7 +388,11 @@ export function FinanceDashboard({ onNavigate, onViewPaymentDetails, onViewAudit
</div> </div>
<div> <div>
<p className="text-slate-600">Type</p> <p className="text-slate-600">Type</p>
<p>{app.paymentType}</p> <p>
{app.paymentType === 'SECURITY_DEPOSIT' ? 'Security Deposit' :
app.paymentType === 'FIRST_FILL' ? 'First Fill' :
app.paymentType}
</p>
</div> </div>
<div> <div>
<p className="text-slate-600">Amount</p> <p className="text-slate-600">Amount</p>

View File

@ -137,6 +137,7 @@ export interface Application {
areaId?: string; areaId?: string;
districtId?: string; districtId?: string;
fddAssignments?: any[]; fddAssignments?: any[];
statutoryStatus?: string;
} }
export interface Participant { export interface Participant {
@ -157,6 +158,7 @@ export interface User {
email: string; email: string;
password: string; password: string;
role: UserRole; role: UserRole;
roleCode?: string;
avatar?: string; avatar?: string;
} }

View File

@ -36,7 +36,8 @@ export const initializeAuth = createAsyncThunk(
name: user.fullName || user.email.split('@')[0], name: user.fullName || user.email.split('@')[0],
email: user.email, email: user.email,
password: '', password: '',
role: typeof user.role === 'string' ? user.role : (user.roleCode || 'User') role: typeof user.role === 'string' ? user.role : (user.roleCode || 'User'),
roleCode: user.roleCode || (typeof user.role === 'string' ? user.role : 'User')
} as User, } as User,
token token
}; };