added joint approval in resignation for RBM DD-ZM approval stage
This commit is contained in:
parent
352c656a9e
commit
95032cf2a7
@ -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})}
|
||||
/>
|
||||
<Input
|
||||
type="email"
|
||||
placeholder="Email Id*"
|
||||
className="h-[44px] border-[#cccccc] rounded-none px-4 text-[14px] focus-visible:ring-1 focus-visible:ring-black placeholder:text-[#999999]"
|
||||
value={formData.email}
|
||||
onChange={(e) => setFormData({...formData, email: e.target.value})}
|
||||
/>
|
||||
<Input
|
||||
type="text"
|
||||
maxLength={6}
|
||||
placeholder="Pincode*"
|
||||
className="h-[44px] border-[#cccccc] rounded-none px-4 text-[14px] focus-visible:ring-1 focus-visible:ring-black placeholder:text-[#999999]"
|
||||
value={formData.pincode}
|
||||
onChange={(e) => setFormData({...formData, pincode: e.target.value})}
|
||||
onChange={(e) => {
|
||||
const val = e.target.value.replace(/\D/g, '');
|
||||
setFormData({...formData, pincode: val});
|
||||
}}
|
||||
/>
|
||||
|
||||
<div className="relative">
|
||||
<Input
|
||||
type="text"
|
||||
maxLength={10}
|
||||
placeholder="Mobile No.*"
|
||||
className="h-[44px] border-[#cccccc] rounded-none px-4 text-[14px] focus-visible:ring-1 focus-visible:ring-black placeholder:text-[#999999]"
|
||||
value={formData.mobile}
|
||||
onChange={(e) => setFormData({...formData, mobile: e.target.value})}
|
||||
onChange={(e) => {
|
||||
const val = e.target.value.replace(/\D/g, '');
|
||||
setFormData({...formData, mobile: val});
|
||||
}}
|
||||
/>
|
||||
{!otpVerified ? (
|
||||
<button
|
||||
@ -301,7 +331,13 @@ export function ApplicationFormPage({ onAdminLogin }: ApplicationFormPageProps)
|
||||
type="radio"
|
||||
className="hidden"
|
||||
checked={formData.ownRoyalEnfield === val}
|
||||
onChange={() => setFormData({...formData, ownRoyalEnfield: val})}
|
||||
onChange={() => {
|
||||
setFormData({
|
||||
...formData,
|
||||
ownRoyalEnfield: val,
|
||||
royalEnfieldModel: val === 'no' ? '' : formData.royalEnfieldModel
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<span className="text-[14px] capitalize">{val}</span>
|
||||
</label>
|
||||
@ -316,18 +352,19 @@ export function ApplicationFormPage({ onAdminLogin }: ApplicationFormPageProps)
|
||||
onChange={(e) => setFormData({...formData, age: e.target.value})}
|
||||
/>
|
||||
|
||||
<div className="relative">
|
||||
<select
|
||||
className="w-full h-[44px] px-4 border border-[#cccccc] appearance-none bg-white text-[14px] outline-none disabled:bg-slate-50"
|
||||
value={formData.royalEnfieldModel}
|
||||
disabled={formData.ownRoyalEnfield !== 'yes'}
|
||||
onChange={(e) => setFormData({...formData, royalEnfieldModel: e.target.value})}
|
||||
>
|
||||
<option value="">Motorcycle Owned</option>
|
||||
{reModels.map(m => <option key={m} value={m}>{m}</option>)}
|
||||
</select>
|
||||
<ChevronDown className="absolute right-4 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-500 pointer-events-none" />
|
||||
</div>
|
||||
{formData.ownRoyalEnfield === 'yes' && (
|
||||
<div className="relative">
|
||||
<select
|
||||
className="w-full h-[44px] px-4 border border-[#cccccc] appearance-none bg-white text-[14px] outline-none"
|
||||
value={formData.royalEnfieldModel}
|
||||
onChange={(e) => setFormData({...formData, royalEnfieldModel: e.target.value})}
|
||||
>
|
||||
<option value="">Select Motorcycle*</option>
|
||||
{reModels.map(m => <option key={m} value={m}>{m}</option>)}
|
||||
</select>
|
||||
<ChevronDown className="absolute right-4 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-500 pointer-events-none" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Input
|
||||
placeholder="Education Qualification*"
|
||||
@ -406,14 +443,15 @@ export function ApplicationFormPage({ onAdminLogin }: ApplicationFormPageProps)
|
||||
onCheckedChange={(checked) => setFormData({...formData, acceptTerms: checked as boolean})}
|
||||
/>
|
||||
<label htmlFor="terms" className="text-[14px] font-medium cursor-pointer">
|
||||
I accept the <b>terms and conditions</b> as well as <b>privacy policy</b>.
|
||||
I accept the <b>terms and conditions</b> as well as <b>privacy policy</b>.<span className="text-red-500">*</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
className="h-12 px-10 bg-black text-white flex items-center gap-3 hover:bg-slate-900 transition-colors"
|
||||
disabled={!isFormValid}
|
||||
className="h-12 px-10 bg-black text-white flex items-center gap-3 hover:bg-slate-900 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
<span className="font-bold uppercase tracking-wider text-[14px]">Submit</span>
|
||||
<ChevronRight className="w-4 h-4" />
|
||||
|
||||
@ -489,7 +489,7 @@ export function ApplicationDetailsTabs(props: ApplicationDetailsTabsProps) {
|
||||
</button>
|
||||
</div>
|
||||
<p className="text-slate-400 text-[10px] mt-1" data-testid={`onboarding-progress-branch-stage-status-${branchKey}-${bsIdx}`}>
|
||||
{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'}
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
@ -558,14 +558,14 @@ export function ApplicationDetailsTabs(props: ApplicationDetailsTabsProps) {
|
||||
<TableCell>
|
||||
<div className="flex justify-end gap-2">
|
||||
<Button size="sm" variant="outline" data-testid={`onboarding-document-preview-${idx}`} onClick={() => {
|
||||
setPreviewDoc(doc);
|
||||
setShowPreviewModal(true);
|
||||
setPreviewDoc(doc);
|
||||
setShowPreviewModal(true);
|
||||
}}>
|
||||
<Eye className="w-3 h-3 text-slate-500" />
|
||||
</Button>
|
||||
<Button size="sm" variant="outline" data-testid={`onboarding-document-download-${idx}`} onClick={() => {
|
||||
const baseUrl = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:5000';
|
||||
window.open(`${baseUrl}/${doc.filePath}`, '_blank');
|
||||
const baseUrl = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:5000';
|
||||
window.open(`${baseUrl}/${doc.filePath}`, '_blank');
|
||||
}}>
|
||||
<Download className="w-3 h-3 text-slate-500" />
|
||||
</Button>
|
||||
|
||||
@ -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' },
|
||||
]
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@ -45,7 +45,7 @@ const TERMINAL_STAGE_LABELS = ['REJECTED', 'Rejected', 'REVOKED', 'Revoked', 'WI
|
||||
|
||||
const RESIGNATION_STAGE_ALIASES: Record<string, string[]> = {
|
||||
'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<string, string[]> = {
|
||||
'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
|
||||
<TabsTrigger value="progress" className="data-[state=active]:bg-white">Progress</TabsTrigger>
|
||||
<TabsTrigger value="documents" className="data-[state=active]:bg-white">Documents</TabsTrigger>
|
||||
<TabsTrigger value="audit" className="data-[state=active]:bg-white">Audit Trail</TabsTrigger>
|
||||
{isNationalLevel && (
|
||||
<TabsTrigger value="approvals" className="data-[state=active]:bg-white">Approval Summary</TabsTrigger>
|
||||
)}
|
||||
</TabsList>
|
||||
|
||||
{/* Details Tab */}
|
||||
@ -750,19 +754,27 @@ export function ResignationDetails({ resignationId, onBack, currentUser }: Resig
|
||||
</div>
|
||||
<p className="text-slate-600 text-sm mb-1">{stage.description}</p>
|
||||
|
||||
{timelineEntry && (
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<Badge variant="secondary" className="bg-slate-100 text-[10px] font-bold uppercase">
|
||||
{timelineEntry.user || 'System'}
|
||||
</Badge>
|
||||
<span className="text-[10px] text-slate-500 italic">
|
||||
{timelineEntry.action}
|
||||
</span>
|
||||
</div>
|
||||
<div className="bg-slate-50 p-3 rounded-lg border border-slate-100 text-sm text-slate-700 shadow-sm">
|
||||
{timelineEntry.comments || timelineEntry.remarks || 'No remarks provided.'}
|
||||
</div>
|
||||
|
||||
{stageTimelineEntries.length > 0 && (
|
||||
<div className="space-y-4 mt-3">
|
||||
{stageTimelineEntries.map((entry: any, i: number) => (
|
||||
<div key={i} className="space-y-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<Badge variant="secondary" className="bg-slate-100 text-[10px] font-bold uppercase">
|
||||
{entry.user || 'System'}
|
||||
</Badge>
|
||||
<span className="text-[10px] text-slate-500 italic">
|
||||
{entry.action}
|
||||
</span>
|
||||
<span className="text-[10px] text-slate-400 ml-auto">
|
||||
{formatDateTime(entry.timestamp || entry.createdAt)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="bg-slate-50 p-3 rounded-lg border border-slate-100 text-sm text-slate-700 shadow-sm">
|
||||
{entry.comments || entry.remarks || 'No remarks provided.'}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@ -938,6 +950,64 @@ export function ResignationDetails({ resignationId, onBack, currentUser }: Resig
|
||||
</CardContent>
|
||||
</Card>
|
||||
</TabsContent>
|
||||
|
||||
{/* Approval Summary Tab */}
|
||||
{isNationalLevel && (
|
||||
<TabsContent value="approvals">
|
||||
<Card>
|
||||
<CardHeader className="flex flex-row items-center justify-between">
|
||||
<div>
|
||||
<CardTitle>Approval Summary</CardTitle>
|
||||
<CardDescription>Comprehensive view of all approvals and remarks</CardDescription>
|
||||
</div>
|
||||
{permissions.canApprove && (
|
||||
<Button onClick={() => handleAction('approve')} className="bg-green-600 hover:bg-green-700">
|
||||
<Check className="w-4 h-4 mr-2" />
|
||||
Approve Request
|
||||
</Button>
|
||||
)}
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Stage</TableHead>
|
||||
<TableHead>Approver</TableHead>
|
||||
<TableHead>Action</TableHead>
|
||||
<TableHead>Remarks</TableHead>
|
||||
<TableHead>Date</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{(resignationData?.timeline || []).length > 0 ? (
|
||||
resignationData.timeline.map((entry: any, index: number) => (
|
||||
<TableRow key={index}>
|
||||
<TableCell className="font-medium whitespace-nowrap">{entry.stage}</TableCell>
|
||||
<TableCell>
|
||||
<Badge variant="outline">{entry.user || 'System'}</Badge>
|
||||
</TableCell>
|
||||
<TableCell className="whitespace-nowrap">{entry.action}</TableCell>
|
||||
<TableCell className="max-w-[400px]">
|
||||
{entry.remarks || entry.comments || '-'}
|
||||
</TableCell>
|
||||
<TableCell className="text-slate-500 whitespace-nowrap">
|
||||
{formatDateTime(entry.timestamp || entry.createdAt)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell colSpan={5} className="text-center py-6 text-slate-500">
|
||||
No approval records found
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</TabsContent>
|
||||
)}
|
||||
</Tabs>
|
||||
|
||||
{/* Action Dialogs */}
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user