diff --git a/src/components/public/ApplicationFormPage.tsx b/src/components/public/ApplicationFormPage.tsx index d228806..6273729 100644 --- a/src/components/public/ApplicationFormPage.tsx +++ b/src/components/public/ApplicationFormPage.tsx @@ -65,6 +65,20 @@ export function ApplicationFormPage({ onAdminLogin }: ApplicationFormPageProps) } }; + const isEmailValid = (email: string) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email); + const isMobileValid = (mobile: string) => /^[0-9]{10}$/.test(mobile); + const isPincodeValid = (pincode: string) => /^[0-9]{6}$/.test(pincode); + + const isFormValid = Boolean( + formData.country && formData.stateId && formData.districtId && formData.name && + formData.interestedCity && formData.email && formData.pincode && formData.mobile && + formData.ownRoyalEnfield && formData.age && formData.education && + formData.companyName && formData.source && formData.existingDealer && + formData.description && formData.address && formData.acceptTerms && + otpVerified && isEmailValid(formData.email) && isMobileValid(formData.mobile) && isPincodeValid(formData.pincode) && + (formData.ownRoyalEnfield === 'no' || (formData.ownRoyalEnfield === 'yes' && formData.royalEnfieldModel)) + ); + const handleVerifyMobile = () => { if (!formData.mobile || formData.mobile.length < 10) { toast.error('Please enter a valid mobile number'); @@ -93,6 +107,11 @@ export function ApplicationFormPage({ onAdminLogin }: ApplicationFormPageProps) return; } + if (formData.ownRoyalEnfield === 'yes' && !formData.royalEnfieldModel) { + toast.error('Please select your motorcycle model'); + return; + } + if (!formData.acceptTerms) { toast.error('Please accept the terms and conditions'); return; @@ -145,10 +164,10 @@ export function ApplicationFormPage({ onAdminLogin }: ApplicationFormPageProps) }; const reModels = [ - "Classic 650", "Scram 440", "Goan Classic 350", "Bear 650", "Guerrilla 450", - "Shotgun 650", "Himalayan 450", "Bullet 350", "Super Meteor 650", "Hunter 350", - "Scram 411", "Meteor 350", "Interceptor INT 650", "Continental GT 650", - "Classic 350", "Other Royal Enfield motorcycle" + "Continental GT", "Interceptor 650", "Himalayan", "Classic 350", + "Classic 500", "Thunderbird 350", "Thunderbird 500", "Thunderbird X 350", + "Thunderbird X 500", "Bullet 350", "Bullet 500", "Bullet ES", + "Bullet Trials 350", "Bullet Trials 500", "Other Royal Enfield motorcycle" ]; const sourceOptions = [ @@ -257,24 +276,35 @@ export function ApplicationFormPage({ onAdminLogin }: ApplicationFormPageProps) onChange={(e) => setFormData({...formData, interestedCity: e.target.value})} /> setFormData({...formData, email: e.target.value})} /> setFormData({...formData, pincode: e.target.value})} + onChange={(e) => { + const val = e.target.value.replace(/\D/g, ''); + setFormData({...formData, pincode: val}); + }} />
setFormData({...formData, mobile: e.target.value})} + onChange={(e) => { + const val = e.target.value.replace(/\D/g, ''); + setFormData({...formData, mobile: val}); + }} /> {!otpVerified ? (

- {isDone && branchStage.date ? `Done: ${formatDateTime(branchStage.date)}` : isDone && stageDocs.length > 0 ? `Uploaded: ${formatDateTime(stageDocs[0].updatedAt || stageDocs[0].createdAt)}` : branchStage.status === 'active' ? 'Evaluating' : 'Pending'} + {isDone && branchStage.date ? `Done: ${formatDateTime(branchStage.date)}` : isDone && stageDocs.length > 0 ? `Uploaded: ${formatDateTime(stageDocs[0].updatedAt || stageDocs[0].createdAt)}` : 'Pending'}

@@ -558,14 +558,14 @@ export function ApplicationDetailsTabs(props: ApplicationDetailsTabsProps) {
diff --git a/src/features/onboarding/hooks/useApplicationDetailsStageData.ts b/src/features/onboarding/hooks/useApplicationDetailsStageData.ts index eda63a4..df279ec 100644 --- a/src/features/onboarding/hooks/useApplicationDetailsStageData.ts +++ b/src/features/onboarding/hooks/useApplicationDetailsStageData.ts @@ -62,9 +62,9 @@ export function useApplicationDetailsStageData({ date: application.level1InterviewDate, description: 'DD-ZM + RBM evaluation', evaluators: Array.from(new Set( (application.participants || []) - .filter((p: any) => - p.metadata?.interviewLevel === 1 || - p.metadata?.interviewLevel === '1' || + .filter((p: any) => + p.metadata?.interviewLevel === 1 || + p.metadata?.interviewLevel === '1' || p.metadata?.allAssignments?.includes(1) || p.metadata?.allAssignments?.includes('1') || hasAnyRole(p, ['DD-ZM', 'RBM']) @@ -78,9 +78,9 @@ export function useApplicationDetailsStageData({ date: application.level2InterviewDate, description: 'DD Lead + ZBH evaluation', evaluators: Array.from(new Set( (application.participants || []) - .filter((p: any) => - p.metadata?.interviewLevel === 2 || - p.metadata?.interviewLevel === '2' || + .filter((p: any) => + p.metadata?.interviewLevel === 2 || + p.metadata?.interviewLevel === '2' || p.metadata?.allAssignments?.includes(2) || p.metadata?.allAssignments?.includes('2') || hasAnyRole(p, ['DD Lead', 'ZBH']) @@ -94,9 +94,9 @@ export function useApplicationDetailsStageData({ date: application.level3InterviewDate, description: 'NBH + DD Head evaluation', evaluators: Array.from(new Set( (application.participants || []) - .filter((p: any) => - p.metadata?.interviewLevel === 3 || - p.metadata?.interviewLevel === '3' || + .filter((p: any) => + p.metadata?.interviewLevel === 3 || + p.metadata?.interviewLevel === '3' || p.metadata?.allAssignments?.includes(3) || p.metadata?.allAssignments?.includes('3') || hasAnyRole(p, ['NBH', 'DD Head']) @@ -120,15 +120,16 @@ export function useApplicationDetailsStageData({ id: 10, name: 'LOI Issue', status: getStageStatus('LOI Issue'), date: application.loiIssueDate, description: 'Letter of Intent issued', isParallel: true, branches: [ - { name: 'LOI Documents', color: 'blue', stages: - documentConfigs.some((c: any) => c.stageCode === 'LOI Issue') - ? documentConfigs.filter((c: any) => c.stageCode === 'LOI Issue').map((c: any, i: number) => ({ + { + name: 'LOI Documents', color: 'green', stages: + documentConfigs.some((c: any) => c.stageCode === 'LOI Issue') + ? documentConfigs.filter((c: any) => c.stageCode === 'LOI Issue').map((c: any, i: number) => ({ id: `10a-${i}`, name: c.documentType, status: isDocumentUploaded(c.documentType) ? 'completed' : 'active', description: c.isMandatory ? `Upload ${c.documentType} (Mandatory)` : `Upload ${c.documentType}` })) - : [ + : [ { id: '10a-1', name: 'Letter of Intent', status: isDocumentUploaded('Letter of Intent') || isDocumentUploaded('LOI') ? 'completed' : 'active', description: 'Letter of Intent document' }, { id: '10a-2', name: 'Signed LOI', status: isDocumentUploaded('Signed LOI') || isDocumentUploaded('LOI Signed Copy') ? 'completed' : 'active', description: 'Signed Letter of Intent' }, ] @@ -139,24 +140,28 @@ export function useApplicationDetailsStageData({ id: 11, name: 'Dealer Code Generation', status: getStageStatus('Dealer Code Generation'), date: application.dealerCodeDate, description: 'Dealer code generated and assigned', isParallel: true, branches: [ - { name: 'Architectural Work', color: 'green', stages: [ - { id: '11a-1', name: 'Architecture Assignment', status: application.architectureAssignedTo ? 'completed' : application.status === 'Architecture Team Assigned' ? 'active' : 'pending', description: 'Assigned to architecture team' }, - { id: '11a-2', name: 'Site Plan Blueprint', status: isDocumentUploaded('Architecture Blueprint') ? 'completed' : application.architectureAssignedTo ? 'active' : 'pending', description: 'Blueprints and site plans' }, - { id: '11a-3', name: 'Architecture Work', status: application.architectureStatus === 'COMPLETED' ? 'completed' : (application.architectureStatus === 'IN_PROGRESS' || isDocumentUploaded('Architecture Blueprint')) ? 'active' : 'pending', description: 'Final architecture approval' }, - ]}, - { name: 'Statutory Documents', color: 'green', stages: [ - { id: '11b-1', name: 'GST', status: isDocumentUploaded('GST Certificate') || isDocumentUploaded('GST') ? 'completed' : 'active', description: 'GST certificate' }, - { id: '11b-2', name: 'PAN', status: isDocumentUploaded('PAN Card') || isDocumentUploaded('PAN') ? 'completed' : 'active', description: 'PAN card' }, - { id: '11b-3', name: 'Nodal Agreement', status: isDocumentUploaded('Nodal Agreement') ? 'completed' : 'active', description: 'Nodal agreement document' }, - { id: '11b-4', name: 'Cancelled Check', status: isDocumentUploaded('Cancelled Check') ? 'completed' : 'active', description: 'Cancelled check copy' }, - { id: '11b-5', name: 'Partnership Deed/LLP/MOA/AOA/COI', status: isDocumentUploaded('Partnership Deed/LLP/MOA/AOA/COI') || isDocumentUploaded('Partnership Deed') ? 'completed' : 'active', description: 'Business entity documents' }, - { id: '11b-6', name: 'Firm Registration Certificate', status: isDocumentUploaded('Firm Registration Certificate') || isDocumentUploaded('Firm Registration') ? 'completed' : 'active', description: 'Firm registration certificate' }, - { id: '11b-7', name: 'Rental agreement/ Lease agreement / Own/ Land agreement', status: isDocumentUploaded('Rental agreement/ Lease agreement / Own/ Land agreement') || isDocumentUploaded('Property Document') ? 'completed' : 'active', description: 'Property agreement document' }, - { id: '11b-8', name: 'Virtual Code', status: isDocumentUploaded('Virtual Code') || isDocumentUploaded('Virtual Code Confirmation') ? 'completed' : 'active', description: 'Virtual code availability' }, - { id: '11b-9', name: 'Domain ID', status: isDocumentUploaded('Domain ID') || isDocumentUploaded('Domain ID Setup') ? 'completed' : 'active', description: 'Domain ID setup' }, - { id: '11b-10', name: 'MSD Configuration', status: isDocumentUploaded('MSD Configuration') ? 'completed' : 'active', description: 'Microsoft Dynamics configuration' }, - { id: '11b-11', name: 'LOI Acknowledgement Copy', status: isDocumentUploaded('LOI Acknowledgement Copy') || isDocumentUploaded('LOI Acknowledgement') ? 'completed' : 'active', description: 'LOI acknowledgement copy' }, - ]}, + { + name: 'Architectural Work', color: 'green', stages: [ + { id: '11a-1', name: 'Architecture Assignment', status: application.architectureAssignedTo ? 'completed' : application.status === 'Architecture Team Assigned' ? 'active' : 'pending', description: 'Assigned to architecture team' }, + { id: '11a-2', name: 'Site Plan Blueprint', status: isDocumentUploaded('Architecture Blueprint') ? 'completed' : application.architectureAssignedTo ? 'active' : 'pending', description: 'Blueprints and site plans' }, + { id: '11a-3', name: 'Architecture Work', status: application.architectureStatus === 'COMPLETED' ? 'completed' : (application.architectureStatus === 'IN_PROGRESS' || isDocumentUploaded('Architecture Blueprint')) ? 'active' : 'pending', description: 'Final architecture approval' }, + ] + }, + { + name: 'Statutory Documents', color: 'green', stages: [ + { id: '11b-1', name: 'GST', status: isDocumentUploaded('GST Certificate') || isDocumentUploaded('GST') ? 'completed' : 'active', description: 'GST certificate' }, + { id: '11b-2', name: 'PAN', status: isDocumentUploaded('PAN Card') || isDocumentUploaded('PAN') ? 'completed' : 'active', description: 'PAN card' }, + { id: '11b-3', name: 'Nodal Agreement', status: isDocumentUploaded('Nodal Agreement') ? 'completed' : 'active', description: 'Nodal agreement document' }, + { id: '11b-4', name: 'Cancelled Check', status: isDocumentUploaded('Cancelled Check') ? 'completed' : 'active', description: 'Cancelled check copy' }, + { id: '11b-5', name: 'Partnership Deed/LLP/MOA/AOA/COI', status: isDocumentUploaded('Partnership Deed/LLP/MOA/AOA/COI') || isDocumentUploaded('Partnership Deed') ? 'completed' : 'active', description: 'Business entity documents' }, + { id: '11b-6', name: 'Firm Registration Certificate', status: isDocumentUploaded('Firm Registration Certificate') || isDocumentUploaded('Firm Registration') ? 'completed' : 'active', description: 'Firm registration certificate' }, + { id: '11b-7', name: 'Rental agreement/ Lease agreement / Own/ Land agreement', status: isDocumentUploaded('Rental agreement/ Lease agreement / Own/ Land agreement') || isDocumentUploaded('Property Document') ? 'completed' : 'active', description: 'Property agreement document' }, + { id: '11b-8', name: 'Virtual Code', status: isDocumentUploaded('Virtual Code') || isDocumentUploaded('Virtual Code Confirmation') ? 'completed' : 'active', description: 'Virtual code availability' }, + { id: '11b-9', name: 'Domain ID', status: isDocumentUploaded('Domain ID') || isDocumentUploaded('Domain ID Setup') ? 'completed' : 'active', description: 'Domain ID setup' }, + { id: '11b-10', name: 'MSD Configuration', status: isDocumentUploaded('MSD Configuration') ? 'completed' : 'active', description: 'Microsoft Dynamics configuration' }, + { id: '11b-11', name: 'LOI Acknowledgement Copy', status: isDocumentUploaded('LOI Acknowledgement Copy') || isDocumentUploaded('LOI Acknowledgement') ? 'completed' : 'active', description: 'LOI acknowledgement copy' }, + ] + }, ] }, { diff --git a/src/features/resignation/pages/ResignationDetails.tsx b/src/features/resignation/pages/ResignationDetails.tsx index d090f72..4327883 100644 --- a/src/features/resignation/pages/ResignationDetails.tsx +++ b/src/features/resignation/pages/ResignationDetails.tsx @@ -45,7 +45,7 @@ const TERMINAL_STAGE_LABELS = ['REJECTED', 'Rejected', 'REVOKED', 'Revoked', 'WI const RESIGNATION_STAGE_ALIASES: Record = { 'ASM': ['ASM', 'ASM Review', 'Submission', 'Submitted'], - 'RBM': ['RBM', 'RBM Review', 'Regional Review'], + 'RBM': ['RBM', 'RBM Review', 'Regional Review', 'RBM + DD-ZM Review'], 'ZBH': ['ZBH', 'ZBH Review', 'ZM Review'], 'DD Lead': ['DD Lead', 'DD Lead Review', 'DDL Review'], 'NBH': ['NBH', 'NBH Approval', 'NBH Review'], @@ -128,7 +128,7 @@ export function ResignationDetails({ resignationId, onBack, currentUser }: Resig // Progress stages logic based on live data const progressStages = [ { id: 1, name: 'ASM Review', key: 'ASM', description: 'Area Sales Manager review' }, - { id: 2, name: 'RBM Review', key: 'RBM', description: 'Regional Business Manager evaluation' }, + { id: 2, name: 'RBM + DD-ZM Review', key: 'RBM', description: 'Joint approval by Regional Business Manager and DD-ZM' }, { id: 3, name: 'ZBH Review', key: 'ZBH', description: 'Zonal Business Head approval' }, { id: 4, name: 'DD Lead Review', key: 'DD Lead', description: 'DD Lead final review' }, { id: 5, name: 'NBH Approval', key: 'NBH', description: 'National Business Head approval' }, @@ -203,10 +203,11 @@ export function ResignationDetails({ resignationId, onBack, currentUser }: Resig }; const permissions = getResignationPermissions(); + const isNationalLevel = ['Super Admin', 'DD Lead', 'DD Head', 'NBH', 'DD Admin', 'Legal Admin'].includes(currentUser?.role || ''); const stageAliases: Record = { 'ASM': ['ASM', 'ASM Review', 'Request Initiated'], - 'RBM': ['RBM', 'RBM Review'], + 'RBM': ['RBM', 'RBM Review', 'RBM + DD-ZM Review'], 'ZBH': ['ZBH', 'ZBH Review'], 'DD Lead': ['DD Lead', 'DD Lead Review', 'Lead Review'], 'NBH': ['NBH', 'NBH Approval', 'NBH Review'], @@ -562,6 +563,9 @@ export function ResignationDetails({ resignationId, onBack, currentUser }: Resig Progress Documents Audit Trail + {isNationalLevel && ( + Approval Summary + )} {/* Details Tab */} @@ -750,19 +754,27 @@ export function ResignationDetails({ resignationId, onBack, currentUser }: Resig

{stage.description}

- {timelineEntry && ( -
-
- - {timelineEntry.user || 'System'} - - - {timelineEntry.action} - -
-
- {timelineEntry.comments || timelineEntry.remarks || 'No remarks provided.'} -
+ + {stageTimelineEntries.length > 0 && ( +
+ {stageTimelineEntries.map((entry: any, i: number) => ( +
+
+ + {entry.user || 'System'} + + + {entry.action} + + + {formatDateTime(entry.timestamp || entry.createdAt)} + +
+
+ {entry.comments || entry.remarks || 'No remarks provided.'} +
+
+ ))}
)}
@@ -938,6 +950,64 @@ export function ResignationDetails({ resignationId, onBack, currentUser }: Resig + + {/* Approval Summary Tab */} + {isNationalLevel && ( + + + +
+ Approval Summary + Comprehensive view of all approvals and remarks +
+ {permissions.canApprove && ( + + )} +
+ + + + + Stage + Approver + Action + Remarks + Date + + + + {(resignationData?.timeline || []).length > 0 ? ( + resignationData.timeline.map((entry: any, index: number) => ( + + {entry.stage} + + {entry.user || 'System'} + + {entry.action} + + {entry.remarks || entry.comments || '-'} + + + {formatDateTime(entry.timestamp || entry.createdAt)} + + + )) + ) : ( + + + No approval records found + + + )} + +
+
+
+
+ )} {/* Action Dialogs */} diff --git a/src/styles/globals.css b/src/styles/globals.css index 982aed0..1e6bd21 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -10,7 +10,7 @@ --card-foreground: oklch(0.145 0 0); --popover: oklch(1 0 0); --popover-foreground: oklch(0.145 0 0); - --primary: #daaa00; + --primary: #da291c; --primary-foreground: oklch(1 0 0); --secondary: oklch(0.95 0.0058 264.53); --secondary-foreground: #030213;