From 7126d4b6bff4f7bd53e5a3abf8cc285e4d2b0cb6 Mon Sep 17 00:00:00 2001 From: laxmanhalaki Date: Mon, 13 Apr 2026 09:25:42 +0530 Subject: [PATCH] new mail templates added dealer table enhanced add bank details added --- src/api/API.ts | 4 + .../applications/ApplicationDetails.tsx | 392 +++-- .../applications/BankDetailsModal.tsx | 130 ++ .../ConstitutionalChangeDetails.tsx | 107 +- .../applications/ConstitutionalChangePage.tsx | 38 +- .../applications/FinanceFddDetailPage.tsx | 9 +- .../applications/FinanceFnFDetailsPage.tsx | 761 ++++++---- .../applications/FinanceFnFPage.tsx | 11 +- .../FinancePaymentDetailsPage.tsx | 2 +- src/components/applications/FnFDetails.tsx | 1325 ++++++++++++----- src/components/applications/FnFPage.tsx | 5 +- .../MasterPage/EmailTemplates.tsx | 3 +- .../MasterPage/LocationManagement.tsx | 5 +- .../applications/NonOpportunitiesPage.tsx | 3 +- .../ProspectiveApplicationDetails.tsx | 390 +++-- .../applications/RelocationRequestPage.tsx | 5 +- .../applications/ResignationDetails.tsx | 253 +++- .../applications/ResignationPage.tsx | 21 +- .../applications/TerminationDetails.tsx | 123 +- src/components/dashboard/FinanceDashboard.tsx | 5 +- .../dashboard/ProspectiveDashboardPage.tsx | 3 +- .../dealer/DealerConstitutionalChangePage.tsx | 3 +- .../dealer/DealerRelocationPage.tsx | 3 +- .../dealer/DealerResignationPage.tsx | 3 +- src/lib/dateUtils.ts | 42 + src/lib/mock-data.ts | 8 + src/services/settlement.service.ts | 5 + src/styles/globals.css | 2 +- 28 files changed, 2631 insertions(+), 1030 deletions(-) create mode 100644 src/components/applications/BankDetailsModal.tsx create mode 100644 src/lib/dateUtils.ts diff --git a/src/api/API.ts b/src/api/API.ts index 65a1278..d35dc9a 100644 --- a/src/api/API.ts +++ b/src/api/API.ts @@ -101,6 +101,9 @@ export const API = { getDealerById: (id: string) => client.get(`/dealer/${id}`), updateDealer: (id: string, data: any) => client.put(`/dealer/${id}`, data), getDealerDashboard: () => client.get('/dealer/dashboard'), + getDealerBankDetails: (dealerId: string) => client.get(`/dealer/${dealerId}/bank-details`), + saveBankDetail: (dealerId: string, data: any) => client.post(`/dealer/${dealerId}/bank-details`, data), + deleteBankDetail: (id: string) => client.delete(`/dealer/bank-details/${id}`), // Email Templates getEmailTemplates: () => client.get('/admin/email-templates'), @@ -152,6 +155,7 @@ export const API = { getFnFSettlementById: (id: string) => client.get(`/settlement/fnf/${id}`), calculateFnF: (id: string) => client.post(`/settlement/fnf/${id}/calculate`), updateFnF: (id: string, data: any) => client.put(`/settlement/fnf/${id}`, data), + getSettlementDepartments: () => client.get('/settlement/departments'), // Line items addLineItem: (fnfId: string, data: any) => client.post(`/settlement/fnf/${fnfId}/line-items`, data), diff --git a/src/components/applications/ApplicationDetails.tsx b/src/components/applications/ApplicationDetails.tsx index 294b2a4..1ebc723 100644 --- a/src/components/applications/ApplicationDetails.tsx +++ b/src/components/applications/ApplicationDetails.tsx @@ -50,6 +50,7 @@ import { Info, ShieldAlert, CheckCircle2, + CreditCard, } from 'lucide-react'; import { Progress } from '../ui/progress'; import { Textarea } from '../ui/textarea'; @@ -356,6 +357,14 @@ export const ApplicationDetails = () => { constitutionType: data.constitutionType, architectureStatus: data.architectureStatus, statutoryStatus: data.statutoryStatus, + panNumber: data.panNumber, + gstNumber: data.gstNumber, + bankName: data.bankName, + accountNumber: data.accountNumber, + ifscCode: data.ifscCode, + branchName: data.branchName, + accountHolderName: data.accountHolderName, + registeredAddress: data.registeredAddress, }; setApplication(mappedApp); if (data.uploadedDocuments) { @@ -477,6 +486,50 @@ export const ApplicationDetails = () => { const [previewDoc, setPreviewDoc] = useState(null); const [showPreviewModal, setShowPreviewModal] = useState(false); const [selectedInterviewerId, setSelectedInterviewerId] = useState(''); + const [isEditingStatutory, setIsEditingStatutory] = useState(false); + const [statutoryForm, setStatutoryForm] = useState({ + accountHolderName: '', + panNumber: '', + gstNumber: '', + bankName: '', + accountNumber: '', + ifscCode: '', + registeredAddress: '' + }); + + const handleEditStatutory = () => { + if (!application) return; + setStatutoryForm({ + accountHolderName: application.accountHolderName || '', + panNumber: application.panNumber || '', + gstNumber: application.gstNumber || '', + bankName: application.bankName || '', + accountNumber: application.accountNumber || '', + ifscCode: application.ifscCode || '', + registeredAddress: application.registeredAddress || '' + }); + setIsEditingStatutory(true); + }; + + const [isSavingStatutory, setIsSavingStatutory] = useState(false); + const handleSaveStatutory = async () => { + try { + setIsSavingStatutory(true); + await onboardingService.updateApplication(applicationId!, statutoryForm); + toast.success('Statutory & Bank details updated successfully'); + setIsEditingStatutory(false); + fetchApplication(true); + } catch (error) { + console.error('Failed to update statutory details', error); + toast.error('Failed to update details'); + } finally { + setIsSavingStatutory(false); + } + }; + + const canEditStatutory = currentUser?.roleCode === 'Super Admin' || currentUser?.roleCode === 'DD Admin'; + const isAdmin = currentUser?.roleCode === 'Super Admin' || currentUser?.roleCode === 'DD Admin'; + const [interviews, setInterviews] = useState([]); const [isScheduling, setIsScheduling] = useState(false); const [showAssignArchitectureModal, setShowAssignArchitectureModal] = useState(false); @@ -1672,75 +1725,72 @@ export const ApplicationDetails = () => { currentUserEvaluation?.decision === 'Rejected' || ['Approved', 'Rejected', 'Selected'].includes(currentUserEvaluation?.recommendation || ''); - // Final visibility flags - const isAdmin = currentUser && ['DD Admin', 'Super Admin', 'NBH', 'DD Lead', 'DD Head', 'Finance', 'Finance Admin', 'FDD', 'ZBH', 'RBM'].includes(currentUser.role); - const isAdministrativeStage = [ - 'Level 3 Approved', 'FDD Verification', - 'LOI In Progress', 'LOI Issued', 'Statutory LOI Ack', - 'Architecture Team Assigned', 'Architecture Document Upload', 'Architecture Team Completion', - 'Statutory GST', 'Statutory PAN', 'Statutory Nodal', 'Statutory Check', - 'Statutory Partnership', 'Statutory Firm Reg', 'Statutory Rental', - 'Statutory Virtual Code', 'Statutory Domain', 'Statutory MSD', - 'LOA Pending', 'EOR In Progress', 'EOR Complete', 'Inauguration' - ].includes(application.status); - - const finalDepositVerified = getDeposit('FIRST_FILL')?.status === 'Verified'; - const isLoaLocked = application.status === 'LOA Pending' && !finalDepositVerified; - - // Sequential Enforcement for LOI APPROVAL (SRS 6.16.3.5) - // Approval Chain: DD-Head -> NBH - // Sequential Enforcement for LOI APPROVAL (SRS 6.16.3.5) - // Approval Chain: DD-Head -> NBH - const ddHeadApproved = application.stageApprovals?.some( - (a: any) => a.stageCode === 'LOI_APPROVAL' && a.actorRole === 'DD Head' && a.decision === 'Approved' - ); - - // Sequential Enforcement for LOA APPROVAL (SRS 6.18.3.1) - // Approval Chain: DD-Head -> NBH - const ddHeadLoaApproved = application.stageApprovals?.some( - (a: any) => a.stageCode === 'LOA_APPROVAL' && a.actorRole === 'DD Head' && a.decision === 'Approved' - ); - - let sequenceMet = true; - if (!['Super Admin', 'DD Admin'].includes(currentUser?.role || '')) { - // FDD Stage Restriction: Only DD Admin or Super Admin can approve - if (application.status === 'FDD Verification' || application.status === 'Level 3 Approved') { - sequenceMet = false; + // Centralized Permissions Utility (Consolidates 500 lines of fragmented logic) + const getApplicationPermissions = () => { + if (!application || !currentUser) { + return { canApprove: false, canReject: false, canSchedule: false, canAssign: false, isLoaLocked: false, showDecisionMessage: false }; } - // LOI Sequence Enforcement - if (application.status === 'LOI In Progress') { - if (currentUser?.role === 'NBH') { - sequenceMet = !!ddHeadApproved; // NBH can only approve after DD Head - } - // Roles not in the sequence (like Admin or Finance) should not see the buttons for LOI issuance decision - if (!['DD Head', 'NBH'].includes(currentUser?.role || '')) { - sequenceMet = false; - } - } + // 1. Core Flags + const isAdminRole = ['DD Admin', 'Super Admin', 'NBH', 'DD Lead', 'DD Head', 'Finance', 'Finance Admin', 'FDD', 'ZBH', 'RBM'].includes(currentUser.role); + const isAdministrativeStage = [ + 'Level 3 Approved', 'FDD Verification', + 'LOI In Progress', 'LOI Issued', 'Statutory LOI Ack', + 'Architecture Team Assigned', 'Architecture Document Upload', 'Architecture Team Completion', + 'Statutory GST', 'Statutory PAN', 'Statutory Nodal', 'Statutory Check', + 'Statutory Partnership', 'Statutory Firm Reg', 'Statutory Rental', + 'Statutory Virtual Code', 'Statutory Domain', 'Statutory MSD', + 'LOA Pending', 'EOR In Progress', 'EOR Complete', 'Inauguration' + ].includes(application.status); - // LOA Sequence Enforcement - if (application.status === 'LOA Pending') { - if (currentUser?.role === 'NBH') { - sequenceMet = !!ddHeadLoaApproved; // NBH can only approve after DD Head - } - // Roles not in the sequence (like Finance or FDD) should not see the buttons for LOA decision - if (!['DD Head', 'NBH'].includes(currentUser?.role || '')) { - sequenceMet = false; - } - } - } + const isLoaLocked = application.status === 'LOA Pending' && getDeposit('FIRST_FILL')?.status !== 'Verified'; + const isFinalState = application.status === 'Onboarded' || application.status === 'Rejected' || application.status === 'Approved'; - // Show Approve/Reject if: - // 1. It's an interview and feedback is submitted AND no decision made yet - // 2. OR it's an administrative stage and user is Admin AND hasn't made a decision yet AND sequence is valid - const shouldShowApproveReject = - !isLoaLocked && ( - (!hasMadeDecisionForUser && !!hasSubmittedFeedbackForActive) || - (!!isAdmin && !!isAdministrativeStage && !hasMadeStageDecision && !!sequenceMet) + // 2. Interview Specific Logic + const activeInterviewForUser = (interviews || []).find(i => + ['Scheduled', 'Rescheduled', 'Pending', 'In Progress'].includes(i.status) && + i.participants?.some((p: any) => p.userId === currentUser?.id) + ); + const hasSubmittedFeedback = !!(activeInterviewForUser || lastInterviewForUser)?.evaluations?.find( + (e: any) => e.evaluatorId === currentUser?.id ); - const shouldShowDecisionMessage = !!hasMadeDecisionForUser && (!isAdministrativeStage || !!hasMadeStageDecision); + // 3. Sequential Sequence Check + const ddHeadApproved = application.stageApprovals?.some((a: any) => a.stageCode === 'LOI_APPROVAL' && a.actorRole === 'DD Head' && a.decision === 'Approved'); + const ddHeadLoaApproved = application.stageApprovals?.some((a: any) => a.stageCode === 'LOA_APPROVAL' && a.actorRole === 'DD Head' && a.decision === 'Approved'); + + let sequenceMet = true; + if (!['Super Admin', 'DD Admin'].includes(currentUser.role)) { + if (application.status === 'FDD Verification' || application.status === 'Level 3 Approved') sequenceMet = false; + if (application.status === 'LOI In Progress') sequenceMet = (currentUser.role === 'NBH') ? !!ddHeadApproved : (currentUser.role === 'DD Head'); + if (application.status === 'LOA Pending') sequenceMet = (currentUser.role === 'NBH') ? !!ddHeadLoaApproved : (currentUser.role === 'DD Head'); + } + + // 4. Decision Tracking + const hasMadeStageDecision = !!application.stageApprovals?.find(a => policyManagedStages[application.status] === a.stageCode && String(a.actorUserId) === String(currentUser.id)); + const hasMadeInterviewDecision = ['Approved', 'Rejected', 'Selected'].includes(currentUserEvaluation?.decision || currentUserEvaluation?.recommendation || ''); + const hasMadeDecisionTotal = hasMadeStageDecision || hasMadeInterviewDecision; + + // 5. Final Permission Bits + const isDecisionMade = hasMadeDecisionTotal || hasMadeStageDecision; + const canApproveReject = !isLoaLocked && !isFinalState && !isDecisionMade && ( + (!!activeInterviewForUser && !!hasSubmittedFeedback) || + (isAdminRole && isAdministrativeStage && sequenceMet) + ); + + return { + canApprove: canApproveReject, + canReject: canApproveReject, + isLoaLocked, + showDecisionMessage: isDecisionMade && (!isAdministrativeStage || hasMadeStageDecision), + canSchedule: ['DD Admin', 'Super Admin', 'DD AM', 'ASM'].includes(currentUser.role) && + !isFinalState && + !([1, 2, 3].every(level => (interviews || []).some(i => i.level === level))), + canAssign: ['DD Admin', 'Super Admin', 'DD AM'].includes(currentUser.role) + }; + }; + + const permissions = getApplicationPermissions(); @@ -2045,8 +2095,7 @@ export const ApplicationDetails = () => {
-

{report.reportDocument.fileName}

-

SUBMITTED {new Date(report.createdAt).toLocaleDateString()}

+

SUBMITTED {formatDateTime(report.createdAt)}

@@ -2387,6 +2436,141 @@ export const ApplicationDetails = () => {

Past Experience

{application.pastExperience || 'N/A'}

+ +
+
+

+ Statutory & Bank Information +

+ {canEditStatutory && !isEditingStatutory && ( + + )} +
+ + {isEditingStatutory ? ( +
+
+
+ + setStatutoryForm({...statutoryForm, accountHolderName: e.target.value})} + placeholder="Enter Legal Entity Name" + className="bg-white border-slate-200" + /> +
+
+ + setStatutoryForm({...statutoryForm, panNumber: e.target.value.toUpperCase()})} + placeholder="10-digit PAN" + maxLength={10} + className="bg-white border-slate-200 uppercase" + /> +
+
+ + setStatutoryForm({...statutoryForm, gstNumber: e.target.value.toUpperCase()})} + placeholder="15-digit GSTIN" + maxLength={15} + className="bg-white border-slate-200 uppercase" + /> +
+
+ + setStatutoryForm({...statutoryForm, registeredAddress: e.target.value})} + placeholder="Enter Registered Office Address" + className="bg-white border-slate-200" + /> +
+
+ + setStatutoryForm({...statutoryForm, bankName: e.target.value})} + placeholder="Enter Bank Name" + className="bg-white border-slate-200" + /> +
+
+ + setStatutoryForm({...statutoryForm, accountNumber: e.target.value})} + placeholder="Enter Account Number" + className="bg-white border-slate-200" + /> +
+
+ + setStatutoryForm({...statutoryForm, ifscCode: e.target.value.toUpperCase()})} + placeholder="11-digit IFSC" + maxLength={11} + className="bg-white border-slate-200 uppercase" + /> +
+
+
+ + +
+
+ ) : ( +
+
+

Legal Entity Name

+

{application.accountHolderName || 'Pending'}

+
+
+

PAN Number

+

{application.panNumber || 'Pending'}

+
+
+

GST Number

+

{application.gstNumber || 'Pending'}

+
+
+

Registered Address

+

{application.registeredAddress || 'Pending'}

+
+
+

Bank Details

+

{application.bankName || 'N/A'}

+

A/C: {application.accountNumber || 'N/A'}

+

IFSC: {application.ifscCode || 'N/A'}

+
+
+ )} +
@@ -2679,38 +2863,7 @@ export const ApplicationDetails = () => { - {/* Branch Approval Button */} - {(() => { - const branchConfig = branchColor === 'blue' - ? { status: application.architectureStatus, role: 'ARCHITECTURE', stageCode: 'ARCHITECTURE_WORK' } - : { status: application.statutoryStatus, role: 'Legal Admin', stageCode: 'STATUTORY_WORK' }; - - if (branchConfig.status !== 'COMPLETED' && (currentUser?.roleCode === branchConfig.role || currentUser?.roleCode === 'Super Admin' || currentUser?.roleCode === 'DD Admin')) { - return ( - - ); - } - return null; - })()} + {isExpanded && ( @@ -2826,7 +2979,7 @@ export const ApplicationDetails = () => { {doc.fileName} {doc.documentType} - {new Date(doc.createdAt).toLocaleDateString()} + {formatDateTime(doc.createdAt)} {doc.uploader?.fullName || (doc.uploadedBy ? 'Unknown User' : 'Applicant')} @@ -2919,7 +3072,7 @@ export const ApplicationDetails = () => {

Level {interview.level} Interview - ({new Date(interview.scheduleDate).toLocaleDateString()} - {interview.interviewType}) + ({formatDateTime(interview.scheduleDate)} - {interview.interviewType})

{interview.evaluations && interview.evaluations.length > 0 ? ( @@ -3204,7 +3357,7 @@ export const ApplicationDetails = () => { {deposit?.paymentReference && (
Ref: {deposit.paymentReference} - {deposit.verifiedAt && {new Date(deposit.verifiedAt).toLocaleDateString()}} + {deposit.verifiedAt && {formatDateTime(deposit.verifiedAt)}}
)} @@ -3287,7 +3440,7 @@ export const ApplicationDetails = () => { {deposit?.paymentReference && (
Ref: {deposit.paymentReference} - {deposit.verifiedAt && {new Date(deposit.verifiedAt).toLocaleDateString()}} + {deposit.verifiedAt && {formatDateTime(deposit.verifiedAt)}}
)} @@ -3352,7 +3505,7 @@ export const ApplicationDetails = () => {

{log.description || log.action}

- {new Date(log.timestamp).toLocaleString()} + {formatDateTime(log.timestamp)}

by {log.userName || 'System'}

@@ -3418,7 +3571,7 @@ export const ApplicationDetails = () => { {application.deadline && (

Questionnaire Deadline

-

{new Date(application.deadline).toLocaleDateString()}

+

{formatDateTime(application.deadline)}

)} @@ -3434,7 +3587,7 @@ export const ApplicationDetails = () => { {/* Show Approve/Reject block */} - {isLoaLocked && ( + {permissions.isLoaLocked && ( Stage Locked @@ -3444,10 +3597,10 @@ export const ApplicationDetails = () => { )} - {shouldShowApproveReject && ( + {permissions.canApprove && ( <> - {currentUser && ['DD Admin', 'Super Admin', 'DD AM', 'ASM'].includes(currentUser.role) && - !([1, 2, 3].every(level => interviews.some(i => i.level === level))) && ( - - )} + {permissions.canSchedule && ( + + )} {currentUser && ['DD Admin', 'Super Admin'].includes(currentUser.role) && application.status === 'Dealer Code Generation' && ( <> @@ -4639,7 +4791,7 @@ export const ApplicationDetails = () => { - {new Date(doc.createdAt).toLocaleDateString('en-GB')} + {formatDateTime(doc.createdAt)} {doc.uploader?.fullName || (doc.uploadedBy ? 'System User' : 'Applicant')} diff --git a/src/components/applications/BankDetailsModal.tsx b/src/components/applications/BankDetailsModal.tsx new file mode 100644 index 0000000..4bc99b8 --- /dev/null +++ b/src/components/applications/BankDetailsModal.tsx @@ -0,0 +1,130 @@ +import React from 'react'; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from "../ui/dialog"; +import { Button } from "../ui/button"; +import { Label } from "../ui/label"; +import { Input } from "../ui/input"; +import { Loader2 } from "lucide-react"; + +interface BankDetailsModalProps { + isOpen: boolean; + onClose: () => void; + onSubmit: (e: React.FormEvent) => void; + editingBank: any; + isSubmitting: boolean; +} + +export const BankDetailsModal: React.FC = ({ + isOpen, + onClose, + onSubmit, + editingBank, + isSubmitting +}) => { + return ( + + + + {editingBank ? 'Edit Bank Details' : 'Add Bank Account'} + + Enter the dealer's bank information for settlement transfers. + + +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ + +
+
+
+ + + + +
+
+
+ ); +}; diff --git a/src/components/applications/ConstitutionalChangeDetails.tsx b/src/components/applications/ConstitutionalChangeDetails.tsx index f812630..0bf608a 100644 --- a/src/components/applications/ConstitutionalChangeDetails.tsx +++ b/src/components/applications/ConstitutionalChangeDetails.tsx @@ -81,7 +81,7 @@ const getStatusColor = (status: string) => { return 'bg-slate-100 text-slate-700 border-slate-300'; }; -export function ConstitutionalChangeDetails({ requestId, onBack }: ConstitutionalChangeDetailsProps) { +export function ConstitutionalChangeDetails({ requestId, onBack, currentUser }: ConstitutionalChangeDetailsProps) { const navigate = useNavigate(); const [isActionDialogOpen, setIsActionDialogOpen] = useState(false); const [actionType, setActionType] = useState<'approve' | 'reject' | 'hold'>('approve'); @@ -152,6 +152,41 @@ export function ConstitutionalChangeDetails({ requestId, onBack }: Constitutiona const currentStageIndex = getCurrentStageIndex(); + // Centralized Permissions Utility (Fixes security gap where buttons showed for everyone) + const getConstitutionalPermissions = () => { + if (!request || !currentUser) { + return { canApprove: false, canReject: false, canHold: false }; + } + + const currentStage = request.currentStage; + const status = request.status; + const userRole = currentUser.role; + + const isFinalState = ['Completed', 'Rejected', 'Hold'].includes(status); + + // Find stage definition + const stageDef = workflowStages.find(s => s.name === currentStage || s.key === currentStage); + + // Role matching logic (Handles Role names from constants vs workflow mapping) + const isCurrentlyAssigned = currentUser.roleCode === 'SUPER_ADMIN' || ( + (stageDef?.role === 'ASM' && userRole === 'ASM') || + (stageDef?.role === 'ZM/RBM' && (userRole === 'ZM' || userRole === 'RBM')) || + (stageDef?.role === 'ZBH' && userRole === 'ZBH') || + (stageDef?.role === 'DD Lead' && userRole === 'DD Lead') || + (stageDef?.role === 'DD Head' && userRole === 'DD Head') || + (stageDef?.role === 'NBH' && userRole === 'NBH') || + (stageDef?.role === 'Legal Team' && (userRole === 'Legal' || userRole === 'Legal Admin')) + ); + + return { + canApprove: isCurrentlyAssigned && !isFinalState, + canReject: isCurrentlyAssigned && !isFinalState, + canHold: isCurrentlyAssigned && !isFinalState + }; + }; + + const permissions = getConstitutionalPermissions(); + const handleAction = (type: 'approve' | 'reject' | 'hold') => { setActionType(type); setIsActionDialogOpen(true); @@ -203,7 +238,7 @@ export function ConstitutionalChangeDetails({ requestId, onBack }: Constitutiona

{request.requestId} - Constitutional Change Details

- {request.outlet?.name || 'N/A'} ({request.outlet?.code || 'N/A'}) + {request.dealer?.dealerProfile?.businessName || request.outlet?.name || 'N/A'} ({request.dealer?.dealerProfile?.dealerCode?.dealerCode || request.outlet?.code || 'N/A'})

@@ -221,9 +256,9 @@ export function ConstitutionalChangeDetails({ requestId, onBack }: Constitutiona

Dealer Details

-

{request.outlet?.name || 'N/A'}

-

{request.outlet?.code || 'N/A'}

-

{request.outlet?.city || request.outlet?.address || 'N/A'}

+

{request.dealer?.dealerProfile?.businessName || request.outlet?.name || 'N/A'}

+

{request.dealer?.dealerProfile?.dealerCode?.dealerCode || request.outlet?.code || 'N/A'}

+

{request.dealer?.dealerProfile?.registeredAddress || request.outlet?.city || request.outlet?.address || 'N/A'}

Constitutional Change

@@ -605,32 +640,44 @@ export function ConstitutionalChangeDetails({ requestId, onBack }: Constitutiona Actions - + {permissions.canApprove && ( + + )} - + {permissions.canReject && ( + + )} + + {!permissions.canApprove && !permissions.canReject && ( +
+

+ {permissions.isFinalState ? 'This request is finalized.' : 'No actions available for your role at this stage.'} +

+
+ )}
-
- +
+

Calculation Formula

@@ -1160,7 +1260,7 @@ export function FinanceFnFDetailsPage({ fnfId, onBack }: FinanceFnFDetailsPagePr - + Final Settlement Summary @@ -1210,8 +1310,8 @@ export function FinanceFnFDetailsPage({ fnfId, onBack }: FinanceFnFDetailsPagePr

-
- +
+

Calculation Formula

@@ -1292,7 +1392,11 @@ export function FinanceFnFDetailsPage({ fnfId, onBack }: FinanceFnFDetailsPagePr {dept.departmentName} - + {dept.status} @@ -1330,7 +1434,7 @@ export function FinanceFnFDetailsPage({ fnfId, onBack }: FinanceFnFDetailsPagePr filePath: dept.supportingDocument, documentType: 'Departmental Clearance Proof' })} - className="flex items-center gap-1 text-[10px] text-blue-600 hover:underline" + className="flex items-center gap-1 text-[10px] text-amber-600 hover:underline" > View Proof @@ -1346,10 +1450,10 @@ export function FinanceFnFDetailsPage({ fnfId, onBack }: FinanceFnFDetailsPagePr {/* Important Notes */} - +

- +

Department Response Guidelines

    @@ -1363,6 +1467,7 @@ export function FinanceFnFDetailsPage({ fnfId, onBack }: FinanceFnFDetailsPagePr + {/* Submitted Documents */} @@ -1446,51 +1551,93 @@ export function FinanceFnFDetailsPage({ fnfId, onBack }: FinanceFnFDetailsPagePr - {/* Bank Account Details */} - - - - Dealer Bank Account Details - - - Bank account for settlement transfer (if payable to dealer) - - - -
    -
    - -

    {fnfCase.bankDetails.accountName}

    -
    -
    - -

    {fnfCase.bankDetails.accountNumber}

    -
    -
    - -

    {fnfCase.bankDetails.ifscCode}

    -
    -
    - -

    {fnfCase.bankDetails.bankName}

    -
    -
    - -

    {fnfCase.bankDetails.branch}

    -
    + +
    + + + Dealer Bank Account Details + + + Manage bank accounts for settlement transfer +
    + +
    + +
    + {bankDetails.length > 0 ? ( + bankDetails.map((bank: any) => ( + + {bank.isPrimary && ( +
    + Primary +
    + )} + +
    +
    + +

    {bank.accountHolderName}

    +
    +
    +
    + +

    {bank.bankName}

    +
    +
    + +

    {bank.ifscCode}

    +
    +
    +
    + +

    {bank.accountNumber}

    +
    -
    -
    - -
    -

    Bank Verification Required

    -

    - Please verify bank account details before processing settlement payment -

    +
    + + +
    +
    + + + )) + ) : ( +
    + +

    No bank details found

    -
    + )}
    @@ -1511,168 +1658,236 @@ export function FinanceFnFDetailsPage({ fnfId, onBack }: FinanceFnFDetailsPagePr -
    - - setSettlementDetails({ ...settlementDetails, paymentMode: e.target.value })} - /> -
    + {fnfCase.status === 'Completed' ? ( +
    +
    +
    + + Settlement Completed +
    +

    + This settlement has been finalized and processed. +

    +
    -
    - - setSettlementDetails({ ...settlementDetails, verificationTransactionId: e.target.value })} - /> -
    +
    +
    + Settlement Date + {formatDateTime(settlementDetails.settlementDate)} +
    +
    + Payment Mode + {settlementDetails.paymentMode} +
    +
    + Transaction ID + + {settlementDetails.verificationTransactionId} + +
    +
    + Final Amount + ₹{parseFloat(settlementDetails.settlementAmount).toLocaleString()} +
    +
    -
    - - setSettlementDetails({ ...settlementDetails, bankReference: e.target.value })} - /> -
    + {settlementDetails.verificationRemarks && ( +
    + +
    + {settlementDetails.verificationRemarks} +
    +
    + )} -
    - - setSettlementDetails({ ...settlementDetails, settlementAmount: e.target.value })} - /> -
    + +
    + ) : ( + <> + {/* Settlement Checklist */} +
    +

    + + Compliance Checklist +

    +
    + {SETTLEMENT_CHECKLIST.map(item => ( +
    + toggleChecklist(item.id)} + className="w-4 h-4 mt-1 rounded border-slate-300 text-amber-600 focus:ring-amber-500" + /> + +
    + ))} +
    +
    -
    - - { - const adjustments = e.target.value; - const adjustedAmount = settlement.settlementAmount + parseFloat(adjustments || '0'); - setSettlementDetails({ - ...settlementDetails, - adjustments, - settlementAmount: adjustedAmount.toString() - }); - }} - /> - {parseFloat(settlementDetails.adjustments) !== 0 && ( -

    - - Adjusted amount: ₹{settlementDetails.settlementAmount} -

    - )} -
    +
    + + setSettlementDetails({ ...settlementDetails, paymentMode: e.target.value })} + /> +
    -
    - - setSettlementDetails({ ...settlementDetails, settlementDate: e.target.value })} - /> -
    +
    + + setSettlementDetails({ ...settlementDetails, verificationTransactionId: e.target.value })} + /> +
    -
    - -