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});
+ }}
/>
- {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;