end to end flow testing for all modules paralley enhancing profile schema to gather all info
This commit is contained in:
parent
6542f0fb30
commit
dad534c169
@ -354,6 +354,8 @@ export const ApplicationDetails = () => {
|
||||
stageApprovals: data.stageApprovals || [],
|
||||
fddAssignments: data.fddAssignments || [],
|
||||
constitutionType: data.constitutionType,
|
||||
architectureStatus: data.architectureStatus,
|
||||
statutoryStatus: data.statutoryStatus,
|
||||
};
|
||||
setApplication(mappedApp);
|
||||
if (data.uploadedDocuments) {
|
||||
@ -1202,7 +1204,7 @@ export const ApplicationDetails = () => {
|
||||
id: 12,
|
||||
name: 'LOA',
|
||||
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.',
|
||||
evaluators: Array.from(new Set((application.participants || [])
|
||||
.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'
|
||||
].includes(application.status);
|
||||
|
||||
const finalDepositVerified = getDeposit('FINAL')?.status === 'Verified';
|
||||
const finalDepositVerified = getDeposit('FIRST_FILL')?.status === 'Verified';
|
||||
const isLoaLocked = application.status === 'LOA Pending' && !finalDepositVerified;
|
||||
|
||||
// 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 className="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||
<div className="space-y-5">
|
||||
<div>
|
||||
<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>
|
||||
{/* Auditor Recommendation Hidden as per request */}
|
||||
|
||||
<div>
|
||||
<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 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 ? (
|
||||
<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" />
|
||||
<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 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" />
|
||||
<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>
|
||||
@ -3169,7 +3164,7 @@ export const ApplicationDetails = () => {
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{/* Initial Security Deposit */}
|
||||
{(() => {
|
||||
const deposit = getDeposit('INITIAL');
|
||||
const deposit = getDeposit('SECURITY_DEPOSIT');
|
||||
const config = paymentConfigs.SECURITY_DEPOSIT;
|
||||
const expectedAmount = config?.amount || 500000;
|
||||
|
||||
@ -3252,7 +3247,7 @@ export const ApplicationDetails = () => {
|
||||
|
||||
{/* Final Security Deposit */}
|
||||
{(() => {
|
||||
const deposit = getDeposit('FINAL');
|
||||
const deposit = getDeposit('FIRST_FILL');
|
||||
const config = paymentConfigs.FIRST_FILL;
|
||||
const expectedAmount = config?.amount || 1500000;
|
||||
|
||||
@ -4856,26 +4851,28 @@ export const ApplicationDetails = () => {
|
||||
</DialogHeader>
|
||||
|
||||
<div className="space-y-4">
|
||||
<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>
|
||||
))}
|
||||
{(currentUser?.role !== 'FDD' && currentUser?.roleCode !== 'FDD') && (
|
||||
<div className="space-y-2">
|
||||
<Label className="text-[10px] font-black uppercase tracking-widest text-slate-400">Auditor Recommendation</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>
|
||||
)}
|
||||
|
||||
<div className="space-y-2">
|
||||
<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,
|
||||
stageCode: 'FDD_VERIFICATION',
|
||||
decision: 'Approved',
|
||||
remarks: `[RECOMMENDATION: ${fddAuditRecommendation}] \nFindings: ${fddAuditFindings}`,
|
||||
remarks: (currentUser?.role === 'FDD' || currentUser?.roleCode === 'FDD')
|
||||
? `Findings: ${fddAuditFindings}`
|
||||
: `[RECOMMENDATION: ${fddAuditRecommendation}] \nFindings: ${fddAuditFindings}`,
|
||||
nextStatus: 'LOI In Progress',
|
||||
nextProgress: 65
|
||||
});
|
||||
|
||||
@ -252,6 +252,45 @@ export function ConstitutionalChangeDetails({ requestId, onBack }: Constitutiona
|
||||
</CardContent>
|
||||
</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">
|
||||
{/* Main Content */}
|
||||
<div className="lg:col-span-2 space-y-6">
|
||||
|
||||
@ -17,7 +17,7 @@ import {
|
||||
import { WorkNotesPage } from './WorkNotesPage';
|
||||
import { toast } from 'sonner';
|
||||
import { DocumentPreviewModal } from '../ui/DocumentPreviewModal';
|
||||
import { formatDateTime, cn } from '@/components/ui/utils';
|
||||
import { formatDateTime } from '@/components/ui/utils';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
@ -45,10 +45,9 @@ export function FDDApplicationDetails() {
|
||||
const [selectedPreviewDoc, setSelectedPreviewDoc] = useState<any>(null);
|
||||
const [showFinalizeModal, setShowFinalizeModal] = useState(false);
|
||||
const [showFlagModal, setShowFlagModal] = useState(false);
|
||||
const [fddAuditRecommendation, setFddAuditRecommendation] = useState<string>('Recommended');
|
||||
const [fddAuditFindings, setFddAuditFindings] = useState<string>('');
|
||||
const user = useSelector((state: RootState) => state.auth.user);
|
||||
const isFddRole = user?.role === 'FDD' || user?.role === 'FDD Auditor';
|
||||
const isFddRole = user?.role === 'FDD';
|
||||
|
||||
useEffect(() => {
|
||||
if (id) fetchApplication();
|
||||
@ -202,6 +201,14 @@ export function FDDApplicationDetails() {
|
||||
<Upload className="w-4 h-4" />
|
||||
{uploading ? 'Uploading...' : 'Upload Report'}
|
||||
</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
|
||||
disabled={uploading}
|
||||
onClick={() => setShowFlagModal(true)}
|
||||
@ -545,26 +552,6 @@ export function FDDApplicationDetails() {
|
||||
</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>
|
||||
@ -594,16 +581,12 @@ export function FDDApplicationDetails() {
|
||||
toast.error('Please provide findings.');
|
||||
return;
|
||||
}
|
||||
if (!assignment?.id) {
|
||||
toast.error('Assignment not found.');
|
||||
return;
|
||||
}
|
||||
|
||||
setUploading(true);
|
||||
const res: any = await API.submitFddReport({
|
||||
assignmentId: assignment.id,
|
||||
assignmentId: assignment?.id,
|
||||
applicationId: id,
|
||||
findings: fddAuditFindings,
|
||||
recommendation: fddAuditRecommendation
|
||||
recommendation: null
|
||||
});
|
||||
|
||||
if (res.data?.success) {
|
||||
|
||||
@ -49,7 +49,7 @@ export function FinanceOnboardingPage({ onViewPaymentDetails }: FinanceOnboardin
|
||||
const getRelevantPaymentStatus = (app: any) => {
|
||||
if (!app.securityDeposits || app.securityDeposits.length === 0) return 'Awaiting Payment';
|
||||
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);
|
||||
return deposit ? deposit.status : 'Awaiting Payment';
|
||||
};
|
||||
|
||||
@ -33,7 +33,7 @@ interface FinancePaymentDetailsPageProps {
|
||||
export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaymentDetailsPageProps) {
|
||||
const [application, setApplication] = useState<any>(null);
|
||||
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 [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [configs, setConfigs] = useState<any>({});
|
||||
@ -68,7 +68,7 @@ export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaym
|
||||
|
||||
setPaymentDetails({
|
||||
verificationTransactionId: '',
|
||||
receivedAmount: activeType === 'INITIAL' ? initialDefault.toString() : finalDefault.toString(),
|
||||
receivedAmount: activeType === 'SECURITY_DEPOSIT' ? initialDefault.toString() : finalDefault.toString(),
|
||||
receivedDate: new Date().toISOString().split('T')[0],
|
||||
verificationRemarks: ''
|
||||
});
|
||||
@ -110,7 +110,7 @@ export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaym
|
||||
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();
|
||||
} catch (error) {
|
||||
toast.error('Failed to verify payment');
|
||||
@ -134,7 +134,7 @@ export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaym
|
||||
remarks: paymentDetails.verificationRemarks
|
||||
});
|
||||
|
||||
toast.error(`${activeType === 'INITIAL' ? 'Security Deposit' : 'First Fill'} rejected`);
|
||||
toast.error(`${activeType === 'SECURITY_DEPOSIT' ? 'Security Deposit' : 'First Fill'} rejected`);
|
||||
await fetchData();
|
||||
} catch (error) {
|
||||
toast.error('Failed to reject payment');
|
||||
@ -167,17 +167,17 @@ export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaym
|
||||
<div className="flex gap-2 mt-2">
|
||||
<Button
|
||||
size="sm"
|
||||
variant={activeType === 'INITIAL' ? 'default' : 'outline'}
|
||||
className={activeType === 'INITIAL' ? 'bg-amber-600 hover:bg-amber-700' : ''}
|
||||
onClick={() => setActiveType('INITIAL')}
|
||||
variant={activeType === 'SECURITY_DEPOSIT' ? 'default' : 'outline'}
|
||||
className={activeType === 'SECURITY_DEPOSIT' ? 'bg-amber-600 hover:bg-amber-700' : ''}
|
||||
onClick={() => setActiveType('SECURITY_DEPOSIT')}
|
||||
>
|
||||
Security Deposit
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant={activeType === 'FINAL' ? 'default' : 'outline'}
|
||||
className={activeType === 'FINAL' ? 'bg-amber-600 hover:bg-amber-700' : ''}
|
||||
onClick={() => setActiveType('FINAL')}
|
||||
variant={activeType === 'FIRST_FILL' ? 'default' : 'outline'}
|
||||
className={activeType === 'FIRST_FILL' ? 'bg-amber-600 hover:bg-amber-700' : ''}
|
||||
onClick={() => setActiveType('FIRST_FILL')}
|
||||
>
|
||||
First Fill
|
||||
</Button>
|
||||
@ -210,7 +210,7 @@ export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaym
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-slate-900 font-bold">
|
||||
{activeType === 'INITIAL' ? 'Security Deposit' : 'First Fill'}
|
||||
{activeType === 'SECURITY_DEPOSIT' ? 'Security Deposit' : 'First Fill'}
|
||||
</p>
|
||||
<p className="text-sm text-slate-600">
|
||||
{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">
|
||||
<Label className="text-slate-500 block mb-1">Expected Amount</Label>
|
||||
<p className="text-2xl font-bold text-amber-900">
|
||||
₹{(activeType === 'INITIAL'
|
||||
₹{(activeType === 'SECURITY_DEPOSIT'
|
||||
? (configs.SECURITY_DEPOSIT?.amount || 500000)
|
||||
: (configs.FIRST_FILL?.amount || 1500000)
|
||||
).toLocaleString()}
|
||||
@ -319,13 +319,13 @@ export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaym
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{application.uploadedDocuments?.filter((d: any) =>
|
||||
activeType === 'INITIAL'
|
||||
activeType === 'SECURITY_DEPOSIT'
|
||||
? d.documentType?.toLowerCase().includes('security') && d.documentType?.toLowerCase().includes('deposit')
|
||||
: d.documentType?.toLowerCase().includes('first') && d.documentType?.toLowerCase().includes('fill')
|
||||
).length > 0 ? (
|
||||
<div className="space-y-3">
|
||||
{application.uploadedDocuments.filter((d: any) =>
|
||||
activeType === 'INITIAL'
|
||||
activeType === 'SECURITY_DEPOSIT'
|
||||
? d.documentType?.toLowerCase().includes('security') && d.documentType?.toLowerCase().includes('deposit')
|
||||
: d.documentType?.toLowerCase().includes('first') && d.documentType?.toLowerCase().includes('fill')
|
||||
).map((doc: any, index: number) => (
|
||||
@ -393,7 +393,7 @@ export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaym
|
||||
<Input
|
||||
id="receivedAmount"
|
||||
type="number"
|
||||
placeholder={(activeType === 'INITIAL' ? 500000 : 1500000).toString()}
|
||||
placeholder={(activeType === 'SECURITY_DEPOSIT' ? 500000 : 1500000).toString()}
|
||||
disabled={activeDeposit?.status === 'Verified'}
|
||||
className="mt-1"
|
||||
value={paymentDetails.receivedAmount}
|
||||
@ -468,10 +468,10 @@ export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaym
|
||||
<CardContent className="text-xs text-slate-300 space-y-3">
|
||||
<p>Once verified, the following will occur:</p>
|
||||
<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>Digital {activeType === 'INITIAL' ? 'LOI' : 'LOA'} generation will be unlocked</li>
|
||||
<li>This payment confirms the {activeType === 'INITIAL' ? 'Security Deposit' : 'First Fill'}</li>
|
||||
<li>Digital {activeType === 'SECURITY_DEPOSIT' ? 'LOI' : 'LOA'} generation will be unlocked</li>
|
||||
<li>This payment confirms the {activeType === 'SECURITY_DEPOSIT' ? 'Security Deposit' : 'First Fill'}</li>
|
||||
</ul>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
@ -313,33 +313,33 @@ export function ResignationDetails({ resignationId, onBack, currentUser }: Resig
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<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>
|
||||
<Label className="text-slate-600">Dealer Name</Label>
|
||||
<p>{resignationData?.outlet?.name}</p>
|
||||
<p>{resignationData?.dealer?.fullName || resignationData?.outlet?.name}</p>
|
||||
</div>
|
||||
<div>
|
||||
<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 className="col-span-2">
|
||||
<Label className="text-slate-600">Address</Label>
|
||||
<p>{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>
|
||||
<p>{resignationData?.dealer?.dealerProfile?.registeredAddress || resignationData?.outlet?.address}</p>
|
||||
</div>
|
||||
</div>
|
||||
</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>
|
||||
<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>
|
||||
<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>
|
||||
<Label className="text-slate-600">City Category</Label>
|
||||
|
||||
@ -10,7 +10,7 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '.
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '../ui/table';
|
||||
import { Alert, AlertDescription, AlertTitle } from '../ui/alert';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { User, mockWorkNotes } from '../../lib/mock-data';
|
||||
import { User } from '../../lib/mock-data';
|
||||
import { toast } from 'sonner';
|
||||
import { terminationService } from '../../services/termination.service';
|
||||
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 (
|
||||
<div className="space-y-6">
|
||||
@ -326,7 +326,7 @@ export function TerminationDetails({ terminationId, onBack, currentUser }: Termi
|
||||
</Button>
|
||||
<div>
|
||||
<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>
|
||||
<Badge className={getSeverityColor(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"
|
||||
onClick={() => navigate(`/worknotes/termination/${terminationId}`, {
|
||||
state: {
|
||||
applicationName: terminationData?.dealerName || 'Termination',
|
||||
applicationName: request?.dealer?.businessName || 'Termination',
|
||||
registrationNumber: terminationId || '',
|
||||
participants: terminationData?.participants || []
|
||||
participants: request?.participants || []
|
||||
}
|
||||
})}
|
||||
>
|
||||
@ -516,7 +516,11 @@ export function TerminationDetails({ terminationId, onBack, currentUser }: Termi
|
||||
</div>
|
||||
<div>
|
||||
<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>
|
||||
</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>
|
||||
</div>
|
||||
<div>
|
||||
<Label className="text-slate-600">LOA</Label>
|
||||
<p>{request.loa || 'N/A'}</p>
|
||||
<Label className="text-slate-600">LOA Date</Label>
|
||||
<p>{request.dealer?.loaDate ? formatDateTime(request.dealer.loaDate, 'date') : 'N/A'}</p>
|
||||
</div>
|
||||
<div>
|
||||
<Label className="text-slate-600">LOI</Label>
|
||||
<p>{request.loi || 'N/A'}</p>
|
||||
<Label className="text-slate-600">LOI Date</Label>
|
||||
<p>{request.dealer?.loiDate ? formatDateTime(request.dealer.loiDate, 'date') : 'N/A'}</p>
|
||||
</div>
|
||||
<div>
|
||||
<Label className="text-slate-600">Last 6 Months Sales</Label>
|
||||
|
||||
@ -253,32 +253,55 @@ export function WorkNotesPage(props: Partial<WorkNotesPageProps>) {
|
||||
}
|
||||
}, [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(() => {
|
||||
if (requestId && requestType === 'application') {
|
||||
const fetchApplicationDetails = async () => {
|
||||
try {
|
||||
const appData = await onboardingService.getApplicationById(requestId);
|
||||
if (appData) {
|
||||
// Update participants if not provided
|
||||
if (externalParticipants.length === 0 && appData.participants) {
|
||||
setExternalParticipants(appData.participants);
|
||||
}
|
||||
// Update metadata if not provided via props/state
|
||||
if (!props.applicationName && !location.state?.applicationName && appData.companyName) {
|
||||
setAppName(appData.companyName);
|
||||
}
|
||||
if (!props.registrationNumber && !location.state?.registrationNumber && appData.registrationNumber) {
|
||||
setRegNumber(appData.registrationNumber);
|
||||
}
|
||||
if (!requestId || !requestType) return;
|
||||
|
||||
const fetchDetails = async () => {
|
||||
try {
|
||||
let data: any = null;
|
||||
if (requestType === 'application') {
|
||||
data = await onboardingService.getApplicationById(requestId);
|
||||
if (data) {
|
||||
if (externalParticipants.length === 0 && data.participants) setExternalParticipants(data.participants);
|
||||
if (!appName || appName === 'Application') setAppName(data.companyName || 'Application');
|
||||
if (!regNumber) setRegNumber(data.registrationNumber || '');
|
||||
}
|
||||
} else if (requestType === 'termination') {
|
||||
const { API } = await import('../../api/API');
|
||||
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 || '');
|
||||
}
|
||||
} else if (requestType === 'constitutional' || requestType === 'constitutional-change') {
|
||||
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) {
|
||||
console.error('Failed to fetch application details:', error);
|
||||
}
|
||||
};
|
||||
fetchApplicationDetails();
|
||||
}
|
||||
}, [requestId, requestType, externalParticipants.length, props.applicationName, props.registrationNumber, location.state]);
|
||||
} catch (error) {
|
||||
console.error(`Failed to fetch ${requestType} details:`, error);
|
||||
}
|
||||
};
|
||||
|
||||
fetchDetails();
|
||||
}, [requestId, requestType, externalParticipants.length, appName, regNumber]);
|
||||
|
||||
// Auto-scroll logic
|
||||
const messagesEndRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
@ -311,7 +311,11 @@ export function FinanceDashboard({ onNavigate, onViewPaymentDetails, onViewAudit
|
||||
</div>
|
||||
<div>
|
||||
<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>
|
||||
<p className="text-slate-600">Amount</p>
|
||||
@ -384,7 +388,11 @@ export function FinanceDashboard({ onNavigate, onViewPaymentDetails, onViewAudit
|
||||
</div>
|
||||
<div>
|
||||
<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>
|
||||
<p className="text-slate-600">Amount</p>
|
||||
|
||||
@ -137,6 +137,7 @@ export interface Application {
|
||||
areaId?: string;
|
||||
districtId?: string;
|
||||
fddAssignments?: any[];
|
||||
statutoryStatus?: string;
|
||||
}
|
||||
|
||||
export interface Participant {
|
||||
@ -157,6 +158,7 @@ export interface User {
|
||||
email: string;
|
||||
password: string;
|
||||
role: UserRole;
|
||||
roleCode?: string;
|
||||
avatar?: string;
|
||||
}
|
||||
|
||||
|
||||
@ -36,7 +36,8 @@ export const initializeAuth = createAsyncThunk(
|
||||
name: user.fullName || user.email.split('@')[0],
|
||||
email: user.email,
|
||||
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,
|
||||
token
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user