progress track enhnced tested upto eor step work flow service file added for deale onboarding
This commit is contained in:
parent
7fa34dd3d6
commit
574e648618
@ -51,7 +51,7 @@ import { SocketProvider } from './context/SocketContext';
|
|||||||
// Layout Component
|
// Layout Component
|
||||||
const AppLayout = ({ onLogout, title }: { onLogout: () => void, title: string }) => {
|
const AppLayout = ({ onLogout, title }: { onLogout: () => void, title: string }) => {
|
||||||
return (
|
return (
|
||||||
<div className="flex h-screen bg-slate-50">
|
<div className="flex h-screen bg-slate-50 overflow-hidden">
|
||||||
<Sidebar onLogout={onLogout} />
|
<Sidebar onLogout={onLogout} />
|
||||||
<div className="flex-1 flex flex-col overflow-hidden">
|
<div className="flex-1 flex flex-col overflow-hidden">
|
||||||
<Header title={title} onRefresh={() => window.location.reload()} />
|
<Header title={title} onRefresh={() => window.location.reload()} />
|
||||||
|
|||||||
@ -322,6 +322,7 @@ export function ApplicationDetails() {
|
|||||||
regionId: data.regionId,
|
regionId: data.regionId,
|
||||||
areaId: data.areaId,
|
areaId: data.areaId,
|
||||||
districtId: data.districtId,
|
districtId: data.districtId,
|
||||||
|
stageApprovals: data.stageApprovals || [],
|
||||||
};
|
};
|
||||||
setApplication(mappedApp);
|
setApplication(mappedApp);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -978,7 +979,7 @@ export function ApplicationDetails() {
|
|||||||
{
|
{
|
||||||
id: 9,
|
id: 9,
|
||||||
name: 'Security Details',
|
name: 'Security Details',
|
||||||
status: getStageStatus('Security Details', () => ['Payment Pending', 'Dealer Code Generation', 'Architecture Team Assigned', 'Architecture Document Upload', 'Architecture Team Completion', 'LOA Pending', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) ? 'completed' : 'pending'),
|
status: getStageStatus('Security Details', () => ['LOI Issued', 'Statutory LOI Ack', 'Dealer Code Generation', 'Architecture Team Assigned', 'Architecture Document Upload', 'Architecture Team Completion', 'LOA Pending', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) ? 'completed' : application.status === 'Payment Pending' ? 'active' : 'pending'),
|
||||||
date: application.securityDetailsDate,
|
date: application.securityDetailsDate,
|
||||||
description: 'Security verification',
|
description: 'Security verification',
|
||||||
documentsUploaded: 3
|
documentsUploaded: 3
|
||||||
@ -986,7 +987,7 @@ export function ApplicationDetails() {
|
|||||||
{
|
{
|
||||||
id: 10,
|
id: 10,
|
||||||
name: 'LOI Issue',
|
name: 'LOI Issue',
|
||||||
status: getStageStatus('LOI Issue', () => ['Statutory LOI Ack', 'Dealer Code Generation', 'Architecture Team Assigned', 'Architecture Document Upload', 'Architecture Team Completion', 'LOA Pending', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) ? 'completed' : ['Payment Pending', 'LOI Issued'].includes(application.status) ? 'active' : 'pending'),
|
status: getStageStatus('LOI Issue', () => ['Statutory LOI Ack', 'Dealer Code Generation', 'Architecture Team Assigned', 'Architecture Document Upload', 'Architecture Team Completion', 'LOA Pending', 'EOR In Progress', 'EOR Complete', 'Inauguration', 'Approved', 'Onboarded'].includes(application.status) ? 'completed' : application.status === 'LOI Issued' ? 'active' : 'pending'),
|
||||||
date: application.loiIssueDate,
|
date: application.loiIssueDate,
|
||||||
description: 'Letter of Intent issued',
|
description: 'Letter of Intent issued',
|
||||||
documentsUploaded: 1
|
documentsUploaded: 1
|
||||||
@ -1580,7 +1581,9 @@ export function ApplicationDetails() {
|
|||||||
|
|
||||||
const currentStageCode = policyManagedStages[application.status];
|
const currentStageCode = policyManagedStages[application.status];
|
||||||
const currentUserStageAction = application.stageApprovals?.find(
|
const currentUserStageAction = application.stageApprovals?.find(
|
||||||
(a: any) => a.stageCode === currentStageCode && a.actorUserId === currentUser?.id
|
(a: any) =>
|
||||||
|
a.stageCode === currentStageCode &&
|
||||||
|
String(a.actorUserId) === String(currentUser?.id)
|
||||||
);
|
);
|
||||||
|
|
||||||
const hasMadeStageDecision = !!currentUserStageAction;
|
const hasMadeStageDecision = !!currentUserStageAction;
|
||||||
@ -1591,7 +1594,7 @@ export function ApplicationDetails() {
|
|||||||
['Approved', 'Rejected', 'Selected'].includes(currentUserEvaluation?.recommendation || '');
|
['Approved', 'Rejected', 'Selected'].includes(currentUserEvaluation?.recommendation || '');
|
||||||
|
|
||||||
// Final visibility flags
|
// Final visibility flags
|
||||||
const isAdmin = currentUser && ['DD Admin', 'Super Admin', 'NBH', 'DD Lead', 'DD Head'].includes(currentUser.role);
|
const isAdmin = currentUser && ['DD Admin', 'Super Admin', 'NBH', 'DD Lead', 'DD Head', 'Finance'].includes(currentUser.role);
|
||||||
const isAdministrativeStage = [
|
const isAdministrativeStage = [
|
||||||
'Level 3 Approved', 'FDD Verification',
|
'Level 3 Approved', 'FDD Verification',
|
||||||
'LOI In Progress', 'LOI Issued', 'Statutory LOI Ack',
|
'LOI In Progress', 'LOI Issued', 'Statutory LOI Ack',
|
||||||
@ -1817,48 +1820,124 @@ export function ApplicationDetails() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
{processStages.map((stage, index) => (
|
{(() => {
|
||||||
|
const getApproverStatus = (stageCode: string | number) => {
|
||||||
|
const stageParticipants = (application.participants || []).filter((p: any) =>
|
||||||
|
p.metadata?.stageCode === stageCode ||
|
||||||
|
p.metadata?.allAssignments?.includes(stageCode) ||
|
||||||
|
(typeof stageCode === 'number' && (p.metadata?.interviewLevel === stageCode || p.metadata?.allAssignments?.includes(stageCode))) ||
|
||||||
|
(typeof stageCode === 'string' && !isNaN(Number(stageCode)) && (p.metadata?.interviewLevel === Number(stageCode) || p.metadata?.allAssignments?.includes(Number(stageCode))))
|
||||||
|
);
|
||||||
|
|
||||||
|
return stageParticipants.map((p: any) => {
|
||||||
|
const saCode = typeof stageCode === 'number' ? `INTERVIEW_LEVEL_${stageCode}` : stageCode;
|
||||||
|
const approval = (application.stageApprovals || []).find((sa: any) =>
|
||||||
|
sa.stageCode === saCode &&
|
||||||
|
String(sa.actorUserId) === String(p.userId)
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: p.user?.name || 'Unknown',
|
||||||
|
role: p.user?.role || 'Reviewer',
|
||||||
|
status: approval ? (approval.decision === 'Approved' ? 'approved' : 'rejected') : 'pending'
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderApprovers = (stageName: string) => {
|
||||||
|
const stageMapping: Record<string, string | number> = {
|
||||||
|
'1st Level Interview': 1,
|
||||||
|
'2nd Level Interview': 2,
|
||||||
|
'3rd Level Interview': 3,
|
||||||
|
'LOI Approval': 'LOI_APPROVAL',
|
||||||
|
'LOA': 'LOA_APPROVAL'
|
||||||
|
};
|
||||||
|
|
||||||
|
const stageCode = stageMapping[stageName];
|
||||||
|
if (!stageCode) return null;
|
||||||
|
|
||||||
|
const approvers = getApproverStatus(stageCode);
|
||||||
|
if (approvers.length === 0) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-wrap gap-2 mt-3">
|
||||||
|
{approvers.map((approver, i) => (
|
||||||
|
<div key={i} className="group relative flex items-center gap-1.5 bg-slate-50 border border-slate-200 rounded-full pl-1 pr-2.5 py-0.5 transition-all hover:bg-white hover:shadow-sm">
|
||||||
|
<div className={cn(
|
||||||
|
"w-6 h-6 rounded-full flex items-center justify-center text-[10px] font-bold text-white",
|
||||||
|
approver.status === 'approved' ? "bg-green-500" : approver.status === 'rejected' ? "bg-red-500" : "bg-slate-300"
|
||||||
|
)}>
|
||||||
|
{approver.name.split(' ').map(n => n[0]).join('').substring(0, 2).toUpperCase()}
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<span className="text-[10px] font-medium text-slate-700 leading-none">{approver.name}</span>
|
||||||
|
<span className="text-[8px] text-slate-500 leading-none mt-0.5">{approver.role}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Status Dot Overlay */}
|
||||||
|
<div className={cn(
|
||||||
|
"absolute -top-0.5 -right-0.5 w-2 h-2 rounded-full border border-white",
|
||||||
|
approver.status === 'approved' ? "bg-green-500" : approver.status === 'rejected' ? "bg-red-500" : "bg-amber-400"
|
||||||
|
)} />
|
||||||
|
|
||||||
|
{/* Tooltip */}
|
||||||
|
<div className="absolute bottom-full left-1/2 -translate-x-1/2 mb-2 px-2 py-1 bg-slate-900 text-white text-[10px] rounded opacity-0 group-hover:opacity-100 pointer-events-none transition-opacity whitespace-nowrap z-50">
|
||||||
|
{approver.role}: {approver.status.toUpperCase()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return processStages.map((stage, index) => (
|
||||||
<div key={stage.id}>
|
<div key={stage.id}>
|
||||||
<div className="flex gap-4 pb-8">
|
<div className="flex gap-4 pb-8">
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<div className={`w-10 h-10 rounded-full flex items-center justify-center border-2 ${stage.status === 'completed'
|
<div className={`w-10 h-10 rounded-full flex items-center justify-center border-2 z-10 relative ${stage.status === 'completed'
|
||||||
? 'bg-green-500 border-green-500'
|
? 'bg-green-500 border-green-500 text-white shadow-sm'
|
||||||
: stage.status === 'active'
|
: stage.status === 'active'
|
||||||
? 'bg-amber-500 border-amber-500'
|
? 'bg-amber-500 border-amber-500 text-white animate-pulse-subtle'
|
||||||
: 'bg-slate-200 border-slate-300'
|
: 'bg-white border-slate-300 text-slate-400 shadow-none'
|
||||||
}`}>
|
}`}>
|
||||||
{stage.isParallel ? (
|
{stage.isParallel ? (
|
||||||
<GitBranch className="w-5 h-5 text-white" />
|
<GitBranch className="w-5 h-5" />
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{stage.status === 'completed' && (
|
{stage.status === 'completed' ? (
|
||||||
<CheckCircle className="w-5 h-5 text-white" />
|
<Check className="w-5 h-5" />
|
||||||
)}
|
) : stage.status === 'active' ? (
|
||||||
{stage.status === 'active' && (
|
|
||||||
<Clock className="w-5 h-5 text-white" />
|
<Clock className="w-5 h-5 text-white" />
|
||||||
)}
|
) : (
|
||||||
{stage.status === 'pending' && (
|
<div className="w-3 h-3 bg-slate-300 rounded-full"></div>
|
||||||
<div className="w-3 h-3 bg-slate-400 rounded-full"></div>
|
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{index < processStages.length - 1 && !stage.isParallel && (
|
{index < processStages.length - 1 && !stage.isParallel && (
|
||||||
<div className={`absolute top-10 left-1/2 -translate-x-1/2 w-0.5 h-full ${stage.status === 'completed' ? 'bg-green-500' : 'bg-slate-300'
|
<div className={`absolute top-10 left-1/2 -translate-x-1/2 w-0.5 h-full z-0 ${stage.status === 'completed' ? 'bg-green-500/30' : 'bg-slate-200'
|
||||||
}`}></div>
|
}`}></div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 pt-1">
|
<div className="flex-1 pt-1">
|
||||||
<p className="text-slate-900">{stage.name}</p>
|
<p className={cn(
|
||||||
|
"font-bold transition-colors",
|
||||||
|
stage.status === 'completed' ? "text-green-700" : stage.status === 'active' ? "text-amber-700" : "text-slate-900"
|
||||||
|
)}>{stage.name}</p>
|
||||||
{stage.description && (
|
{stage.description && (
|
||||||
<p className="text-slate-600 text-sm mt-0.5">{stage.description}</p>
|
<p className="text-slate-600 text-sm mt-0.5 leading-relaxed">{stage.description}</p>
|
||||||
)}
|
)}
|
||||||
{stage.evaluators && stage.evaluators.length > 0 ? (
|
|
||||||
<p className="text-amber-600 text-sm mt-0.5">
|
{renderApprovers(stage.name as string)}
|
||||||
|
|
||||||
|
{stage.evaluators && stage.evaluators.length > 0 && !['LOI Approval', 'LOA', '1st Level Interview', '2nd Level Interview', '3rd Level Interview'].includes(stage.name as string) && (
|
||||||
|
<p className="text-amber-600 text-xs mt-1.5 flex items-center gap-1 bg-amber-50 w-fit px-2 py-0.5 rounded border border-amber-100">
|
||||||
|
<User className="w-3 h-3" />
|
||||||
Evaluators: {stage.evaluators.join(' + ')}
|
Evaluators: {stage.evaluators.join(' + ')}
|
||||||
</p>
|
</p>
|
||||||
) : (() => {
|
)}
|
||||||
// Determine expected count for this stage
|
|
||||||
|
{(() => {
|
||||||
const expectedMap: Record<number, number> = {
|
const expectedMap: Record<number, number> = {
|
||||||
4: 2, // L1 Interview (ZM + RBM)
|
4: 2, // L1 Interview (ZM + RBM)
|
||||||
5: 2, // L2 Interview (ZBH + DD Lead)
|
5: 2, // L2 Interview (ZBH + DD Lead)
|
||||||
@ -1897,7 +1976,7 @@ export function ApplicationDetails() {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
})()}
|
})()}
|
||||||
{/* Stage Docs Link */}
|
|
||||||
{(() => {
|
{(() => {
|
||||||
const stageDocsCount = documents.filter(doc =>
|
const stageDocsCount = documents.filter(doc =>
|
||||||
doc.stage === stage.name ||
|
doc.stage === stage.name ||
|
||||||
@ -1920,7 +1999,7 @@ export function ApplicationDetails() {
|
|||||||
);
|
);
|
||||||
})()}
|
})()}
|
||||||
|
|
||||||
<p className="text-slate-500 mt-1">
|
<p className="text-slate-500 mt-1 text-xs">
|
||||||
{stage.status === 'completed' && stage.date && `Completed: ${new Date(stage.date).toLocaleDateString()}`}
|
{stage.status === 'completed' && stage.date && `Completed: ${new Date(stage.date).toLocaleDateString()}`}
|
||||||
{stage.status === 'active' && 'In Progress'}
|
{stage.status === 'active' && 'In Progress'}
|
||||||
{stage.status === 'pending' && 'Pending'}
|
{stage.status === 'pending' && 'Pending'}
|
||||||
@ -1928,7 +2007,6 @@ export function ApplicationDetails() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Parallel Branches */}
|
|
||||||
{stage.isParallel && stage.branches && (
|
{stage.isParallel && stage.branches && (
|
||||||
<div className="ml-5 mb-8">
|
<div className="ml-5 mb-8">
|
||||||
{stage.branches.map((branch, branchIndex) => {
|
{stage.branches.map((branch, branchIndex) => {
|
||||||
@ -1938,7 +2016,6 @@ export function ApplicationDetails() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={branchIndex} className="mb-6 last:mb-0">
|
<div key={branchIndex} className="mb-6 last:mb-0">
|
||||||
{/* Branch Header - Clickable */}
|
|
||||||
<button
|
<button
|
||||||
onClick={() => setExpandedBranches(prev => ({
|
onClick={() => setExpandedBranches(prev => ({
|
||||||
...prev,
|
...prev,
|
||||||
@ -1959,21 +2036,20 @@ export function ApplicationDetails() {
|
|||||||
<GitBranch className={`w-4 h-4 ${branchColor === 'blue' ? 'text-blue-700' : 'text-green-700'}`} />
|
<GitBranch className={`w-4 h-4 ${branchColor === 'blue' ? 'text-blue-700' : 'text-green-700'}`} />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 text-left">
|
<div className="flex-1 text-left">
|
||||||
<p className={`${branchColor === 'blue' ? 'text-blue-900' : 'text-green-900'}`}>
|
<p className={`${branchColor === 'blue' ? 'text-blue-900' : 'text-green-900'} font-semibold`}>
|
||||||
{branch.name}
|
{branch.name}
|
||||||
</p>
|
</p>
|
||||||
<p className={`text-sm ${branchColor === 'blue' ? 'text-blue-700' : 'text-green-700'}`}>
|
<p className={`text-xs ${branchColor === 'blue' ? 'text-blue-700' : 'text-green-700'}`}>
|
||||||
{branch.stages.length} steps
|
{branch.stages.length} steps
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{/* Branch Content - Expandable */}
|
|
||||||
{isExpanded && (
|
{isExpanded && (
|
||||||
<div className="mt-4 ml-8 border-l-2 border-slate-200 pl-6 space-y-6">
|
<div className="mt-4 ml-8 border-l-2 border-slate-200 pl-6 space-y-6">
|
||||||
{branch.stages.map((branchStage) => (
|
{branch.stages.map((branchStage) => (
|
||||||
<div key={branchStage.id} className="relative">
|
<div key={branchStage.id} className="relative">
|
||||||
<div className="flex gap-4">
|
<div className="flex gap-4 text-xs">
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<div className={`w-8 h-8 rounded-full flex items-center justify-center border-2 ${branchStage.status === 'completed'
|
<div className={`w-8 h-8 rounded-full flex items-center justify-center border-2 ${branchStage.status === 'completed'
|
||||||
? `${branchColor === 'blue' ? 'bg-blue-500 border-blue-500' : 'bg-green-500 border-green-500'}`
|
? `${branchColor === 'blue' ? 'bg-blue-500 border-blue-500' : 'bg-green-500 border-green-500'}`
|
||||||
@ -1981,24 +2057,21 @@ export function ApplicationDetails() {
|
|||||||
? 'bg-amber-500 border-amber-500'
|
? 'bg-amber-500 border-amber-500'
|
||||||
: 'bg-slate-200 border-slate-300'
|
: 'bg-slate-200 border-slate-300'
|
||||||
}`}>
|
}`}>
|
||||||
{branchStage.status === 'completed' && (
|
{branchStage.status === 'completed' ? (
|
||||||
<CheckCircle className="w-4 h-4 text-white" />
|
<Check className="w-4 h-4 text-white" />
|
||||||
)}
|
) : branchStage.status === 'active' ? (
|
||||||
{branchStage.status === 'active' && (
|
|
||||||
<Clock className="w-4 h-4 text-white" />
|
<Clock className="w-4 h-4 text-white" />
|
||||||
)}
|
) : (
|
||||||
{branchStage.status === 'pending' && (
|
|
||||||
<div className="w-2 h-2 bg-slate-400 rounded-full"></div>
|
<div className="w-2 h-2 bg-slate-400 rounded-full"></div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<p className="text-slate-900">{branchStage.name}</p>
|
<p className="font-semibold text-slate-800">{branchStage.name}</p>
|
||||||
{branchStage.description && (
|
{branchStage.description && (
|
||||||
<p className="text-slate-600 text-sm mt-0.5">{branchStage.description}</p>
|
<p className="text-slate-500 text-xs mt-0.5">{branchStage.description}</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Branch Stage Docs Link */}
|
|
||||||
{(() => {
|
{(() => {
|
||||||
const branchDocsCount = documents.filter(doc =>
|
const branchDocsCount = documents.filter(doc =>
|
||||||
doc.documentType?.toLowerCase().includes(branchStage.name.toLowerCase().split(' ')[0]) ||
|
doc.documentType?.toLowerCase().includes(branchStage.name.toLowerCase().split(' ')[0]) ||
|
||||||
@ -2012,17 +2085,17 @@ export function ApplicationDetails() {
|
|||||||
setSelectedStage(branchStage.name);
|
setSelectedStage(branchStage.name);
|
||||||
setShowDocumentsModal(true);
|
setShowDocumentsModal(true);
|
||||||
}}
|
}}
|
||||||
className="text-xs font-medium text-blue-700 hover:text-blue-800 flex items-center gap-1 bg-blue-50 px-2 py-0.5 rounded border border-blue-200"
|
className="text-[10px] font-medium text-blue-700 hover:text-blue-800 flex items-center gap-1 bg-blue-50 px-1.5 py-0.5 rounded border border-blue-100"
|
||||||
>
|
>
|
||||||
<FileText className="w-3 h-3" />
|
<FileText className="w-2.5 h-2.5" />
|
||||||
{branchDocsCount > 0 ? `${branchDocsCount} Documents Uploaded` : 'Upload Document'}
|
{branchDocsCount > 0 ? `${branchDocsCount} Docs` : 'Upload'}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})()}
|
})()}
|
||||||
<p className="text-slate-500 text-sm mt-1">
|
<p className="text-slate-400 text-[10px] mt-1">
|
||||||
{branchStage.status === 'completed' && branchStage.date && `Completed: ${new Date(branchStage.date).toLocaleDateString()}`}
|
{branchStage.status === 'completed' && branchStage.date && `Done: ${new Date(branchStage.date).toLocaleDateString()}`}
|
||||||
{branchStage.status === 'active' && 'In Progress'}
|
{branchStage.status === 'active' && 'Evaluating'}
|
||||||
{branchStage.status === 'pending' && 'Pending'}
|
{branchStage.status === 'pending' && 'Pending'}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@ -2034,13 +2107,12 @@ export function ApplicationDetails() {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
<div className="h-8 w-0.5 bg-slate-300 ml-5 opacity-50"></div>
|
||||||
{/* Connecting line to next stage */}
|
|
||||||
<div className="h-8 w-0.5 bg-slate-300 ml-5"></div>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))
|
||||||
|
})()}
|
||||||
</div>
|
</div>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
@ -2101,7 +2173,6 @@ export function ApplicationDetails() {
|
|||||||
</div>
|
</div>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
{/* Interviews Tab */}
|
|
||||||
{/* Interviews Tab */}
|
{/* Interviews Tab */}
|
||||||
<TabsContent value="interviews" className="space-y-6">
|
<TabsContent value="interviews" className="space-y-6">
|
||||||
<div>
|
<div>
|
||||||
@ -2672,14 +2743,25 @@ export function ApplicationDetails() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Dedicated Onboarding Button - Appears ONLY when everything is ready (last step) */}
|
{/* Dedicated Onboarding Button - Appears ONLY when everything is ready (last step) */}
|
||||||
{isAdmin && ['Inauguration', 'EOR Complete', 'Approved', 'EOR In Progress', 'LOA Pending'].includes(application.status) && !application.dealer && (
|
{isAdmin && application.status === 'Inauguration' && !application.dealer && (
|
||||||
|
<div className="space-y-2">
|
||||||
|
{eorProgress < 100 && (
|
||||||
|
<Alert variant="destructive" className="bg-amber-50 border-amber-200 text-amber-800 py-2">
|
||||||
|
<AlertCircle className="h-4 w-4 text-amber-600" />
|
||||||
|
<AlertDescription className="text-xs">
|
||||||
|
EOR Checklist must be 100% complete before onboarding. (Current: {eorProgress.toFixed(0)}%)
|
||||||
|
</AlertDescription>
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
<Button
|
<Button
|
||||||
className="w-full bg-green-600 hover:bg-green-700 font-bold shadow-lg shadow-green-100"
|
className="w-full bg-green-600 hover:bg-green-700 font-bold shadow-lg shadow-green-100 disabled:bg-slate-300 disabled:text-slate-500"
|
||||||
onClick={() => setShowOnboardModal(true)}
|
onClick={() => setShowOnboardModal(true)}
|
||||||
|
disabled={eorProgress < 100}
|
||||||
>
|
>
|
||||||
<CheckCircle className="w-4 h-4 mr-2" />
|
<CheckCircle className="w-4 h-4 mr-2" />
|
||||||
Onboard as Dealer (Final Step)
|
Onboard as Dealer (Final Step)
|
||||||
</Button>
|
</Button>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Dealer Onboarded Status & Link */}
|
{/* Dealer Onboarded Status & Link */}
|
||||||
@ -2799,7 +2881,7 @@ export function ApplicationDetails() {
|
|||||||
</div >
|
</div >
|
||||||
|
|
||||||
{/* Approve Modal */}
|
{/* Approve Modal */}
|
||||||
< Dialog open={showApproveModal} onOpenChange={setShowApproveModal} >
|
<Dialog open={showApproveModal} onOpenChange={setShowApproveModal}>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>Approve Application</DialogTitle>
|
<DialogTitle>Approve Application</DialogTitle>
|
||||||
@ -3476,10 +3558,10 @@ export function ApplicationDetails() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog >
|
</Dialog>
|
||||||
|
|
||||||
{/* Feedback Details Modal */}
|
{/* Feedback Details Modal */}
|
||||||
< Dialog open={showFeedbackDetailsModal} onOpenChange={setShowFeedbackDetailsModal} >
|
<Dialog open={showFeedbackDetailsModal} onOpenChange={setShowFeedbackDetailsModal}>
|
||||||
<DialogContent className="max-w-2xl max-h-[90vh] overflow-y-auto">
|
<DialogContent className="max-w-2xl max-h-[90vh] overflow-y-auto">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>Interview Feedback Details</DialogTitle>
|
<DialogTitle>Interview Feedback Details</DialogTitle>
|
||||||
|
|||||||
@ -106,7 +106,7 @@ export function Sidebar({ onLogout }: SidebarProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`bg-slate-900 text-white h-screen flex flex-col transition-all duration-300 ${collapsed ? 'w-20' : 'w-64'
|
className={`bg-slate-900 text-white h-screen flex flex-col transition-all duration-300 overflow-hidden ${collapsed ? 'w-20' : 'w-64'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{/* Header with Logo */}
|
{/* Header with Logo */}
|
||||||
@ -153,7 +153,7 @@ export function Sidebar({ onLogout }: SidebarProps) {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Menu Items */}
|
{/* Menu Items */}
|
||||||
<nav className="flex-1 p-4 space-y-2">
|
<nav className="flex-1 p-4 space-y-2 overflow-y-auto custom-scrollbar">
|
||||||
{menuItems.map((item) => {
|
{menuItems.map((item) => {
|
||||||
const Icon = item.icon;
|
const Icon = item.icon;
|
||||||
const isActive = activeView === item.id;
|
const isActive = activeView === item.id;
|
||||||
|
|||||||
@ -190,7 +190,7 @@ html {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.custom-scrollbar::-webkit-scrollbar {
|
.custom-scrollbar::-webkit-scrollbar {
|
||||||
width: 5px;
|
width: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-scrollbar::-webkit-scrollbar-track {
|
.custom-scrollbar::-webkit-scrollbar-track {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user