theme have color enhanced and smtp setup done

This commit is contained in:
Laxman 2026-05-20 20:16:50 +05:30
parent faa29a7511
commit 06116af31a
52 changed files with 1106 additions and 652 deletions

View File

@ -207,6 +207,10 @@ export const API = {
updateConstitutionalChange: (id: string, action: ConstitutionalChangeAction, data?: { comments?: string; remarks?: string }) =>
client.post(`/constitutional-change/${id}/action`, { action, ...data }),
uploadConstitutionalDocuments: (id: string, documents: any[]) => client.post(`/constitutional-change/${id}/documents`, { documents }),
verifyConstitutionalDocument: (id: string, documentId: string) =>
client.post(`/constitutional-change/${id}/documents/${documentId}/verify`),
rejectConstitutionalDocument: (id: string, documentId: string, data?: { remarks?: string }) =>
client.post(`/constitutional-change/${id}/documents/${documentId}/reject`, data || {}),
// SLA
getSlaConfigs: () => client.get('/master/sla-configs'),

View File

@ -47,7 +47,6 @@ export function Sidebar({ onLogout }: SidebarProps) {
const [flyout, setFlyout] = useState<FlyoutState | null>(null);
const hoverTimeout = useRef<ReturnType<typeof setTimeout> | null>(null);
const currentRole = currentUser?.role || currentUser?.roleCode || '';
const hasRole = (roles: string[]) => {
const normalizedTargetRoles = roles.map((r) => r.toLowerCase());
const userRole = String(currentUser?.role || '').toLowerCase();

View File

@ -1,10 +1,9 @@
import React from 'react';
import { Badge } from '@/components/ui/badge';
import { SlaBucket, SlaStatusSnapshot } from '@/services/sla.service';
const BUCKET_CLASS: Record<SlaBucket, string> = {
healthy: 'bg-emerald-100 text-emerald-800 border-emerald-200',
warning: 'bg-amber-100 text-amber-800 border-amber-200',
warning: 'bg-red-50 text-red-800 border-red-200',
critical: 'bg-orange-100 text-orange-800 border-orange-200',
breached: 'bg-red-100 text-red-800 border-red-200'
};

View File

@ -38,8 +38,8 @@ export const DocumentPreviewModal: React.FC<DocumentPreviewModalProps> = ({
<>
<div className="flex items-center justify-between p-4 border-b bg-slate-50">
<div className="flex items-center gap-3">
<div className="w-10 h-10 rounded-lg bg-amber-100 flex items-center justify-center border border-amber-200">
<Eye className="w-5 h-5 text-amber-600" />
<div className="w-10 h-10 rounded-lg bg-red-50 flex items-center justify-center border border-red-200">
<Eye className="w-5 h-5 text-re-red" />
</div>
<div>
<DialogTitle className="text-sm font-bold text-slate-900 leading-none mb-1">

View File

@ -7,9 +7,12 @@ import { cn } from "./utils";
function Progress({
className,
indicatorClassName,
value,
...props
}: React.ComponentProps<typeof ProgressPrimitive.Root>) {
}: React.ComponentProps<typeof ProgressPrimitive.Root> & {
indicatorClassName?: string;
}) {
return (
<ProgressPrimitive.Root
data-slot="progress"
@ -21,7 +24,7 @@ function Progress({
>
<ProgressPrimitive.Indicator
data-slot="progress-indicator"
className="bg-primary h-full w-full flex-1 transition-all"
className={cn("bg-primary h-full w-full flex-1 transition-all", indicatorClassName)}
style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
/>
</ProgressPrimitive.Root>

View File

@ -93,7 +93,7 @@ const ACTION_BADGE_CLASS: Record<string, string> = {
DELETED: 'bg-rose-100 text-rose-700 border-rose-200',
ACTIVATED: 'bg-emerald-100 text-emerald-700 border-emerald-200',
DEACTIVATED: 'bg-slate-200 text-slate-700 border-slate-300',
INITIALIZED: 'bg-amber-100 text-amber-700 border-amber-200',
INITIALIZED: 'bg-red-50 text-re-red-hover border-red-200',
SUBMITTED: 'bg-indigo-100 text-indigo-700 border-indigo-200',
ASSIGNED: 'bg-violet-100 text-violet-700 border-violet-200',
UNASSIGNED: 'bg-slate-200 text-slate-700 border-slate-300',

View File

@ -94,13 +94,13 @@ export function ProspectiveLoginPage() {
return (
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900 p-4 overflow-y-auto">
<div className="absolute inset-0 overflow-hidden pointer-events-none">
<div className="absolute -top-40 -right-40 w-80 h-80 bg-amber-600/10 rounded-full blur-3xl"></div>
<div className="absolute -bottom-40 -left-40 w-80 h-80 bg-amber-600/10 rounded-full blur-3xl"></div>
<div className="absolute -top-40 -right-40 w-80 h-80 bg-re-red/10 rounded-full blur-3xl"></div>
<div className="absolute -bottom-40 -left-40 w-80 h-80 bg-re-red/10 rounded-full blur-3xl"></div>
</div>
<div className="relative w-full max-w-md">
<div className="text-center mb-8">
<div className="inline-flex items-center justify-center w-20 h-20 bg-amber-600 rounded-full mb-4">
<div className="inline-flex items-center justify-center w-20 h-20 bg-re-red rounded-full mb-4">
<svg viewBox="0 0 24 24" className="w-12 h-12 text-white" fill="currentColor">
<path d="M12 2L4 6v6c0 5.5 3.8 10.7 8 12 4.2-1.3 8-6.5 8-12V6l-8-4zm0 2.2l6 3v4.8c0 4.5-3.1 8.7-6 10-2.9-1.3-6-5.5-6-10V7.2l6-3z"></path>
<circle cx="12" cy="12" r="3"></circle>
@ -122,8 +122,8 @@ export function ProspectiveLoginPage() {
</Button>
<div className="flex items-center gap-3 mb-2">
<div className="p-2 bg-amber-100 rounded-lg">
<Smartphone className="w-6 h-6 text-amber-600" />
<div className="p-2 bg-red-50 rounded-lg">
<Smartphone className="w-6 h-6 text-re-red" />
</div>
<div>
<h2 className="text-slate-900 text-lg font-semibold">Dealer Login</h2>
@ -158,7 +158,7 @@ export function ProspectiveLoginPage() {
<Button
type="submit"
className="w-full bg-amber-600 hover:bg-amber-700 h-9"
className="w-full bg-re-red hover:bg-re-red-hover h-9"
disabled={isLoading || phone.length < 10}
>
{isLoading ? 'Sending...' : 'Send OTP'}
@ -198,7 +198,7 @@ export function ProspectiveLoginPage() {
<Button
type="submit"
className="w-full bg-amber-600 hover:bg-amber-700 h-9"
className="w-full bg-re-red hover:bg-re-red-hover h-9"
disabled={isLoading || otp.length < 6}
>
{isLoading ? 'Verifying...' : 'Verify OTP'}
@ -207,7 +207,7 @@ export function ProspectiveLoginPage() {
<div className="text-center text-sm">
<button
type="button"
className="text-amber-600 hover:text-amber-700 font-medium"
className="text-re-red hover:text-re-red-hover font-medium"
onClick={() => setStep('PHONE')}
>
Change Phone Number
@ -215,7 +215,7 @@ export function ProspectiveLoginPage() {
<span className="mx-2 text-slate-400">|</span>
<button
type="button"
className="text-amber-600 hover:text-amber-700 font-medium"
className="text-re-red hover:text-re-red-hover font-medium"
onClick={handleSendOtp}
disabled={isLoading}
>

View File

@ -17,6 +17,14 @@ import { useSlaBatchStatus } from '@/hooks/useSlaBatchStatus';
import { OFFBOARDING_ACTIONS } from '@/lib/offboarding-actions';
import { useNavigate } from 'react-router-dom';
import { formatDateTime } from '@/components/ui/utils';
import {
getCurrentStageBadgeClass,
getOffboardingRequestStatusBadgeClass,
getStatusLabelBadgeClass,
getStatusProgressBarClass,
isOffboardingTerminalNegative,
WORKFLOW_IN_PROGRESS_ACCENT,
} from '@/lib/offboardingDisplay';
interface ConstitutionalChangeDetailsProps {
requestId: string;
@ -88,19 +96,12 @@ const getTypeColor = (type: string) => {
}
};
const getStatusColor = (status: string) => {
const s = String(status || '');
if (s === 'Completed' || s === 'Verified' || s === 'APPROVED' || s === 'COMPLETED' || s === 'CREATED' || /^DOCUMENT/i.test(s)) {
return 'bg-green-100 text-green-700 border-green-300';
}
if (s.includes('Revoked') || s === 'REVOKED') return 'bg-orange-100 text-orange-800 border-orange-300';
if (s.includes('Rejected') || s === 'REJECTED') return 'bg-red-100 text-red-700 border-red-300';
if (s === 'SENT BACK' || s.includes('Review') || s.includes('Pending') || s === 'In Progress' || s === 'Submitted') {
return 'bg-yellow-100 text-yellow-700 border-yellow-300';
}
if (s === 'UPDATED') return 'bg-slate-100 text-slate-700 border-slate-300';
return 'bg-slate-100 text-slate-700 border-slate-300';
};
const getStatusColor = (status: string) => getStatusLabelBadgeClass(status);
const getDocChecklistUploadButtonClass = (isRejected: boolean) =>
isRejected
? 'h-8 px-2 text-red-700 hover:bg-red-50 hover:text-red-800'
: 'h-8 px-2 text-slate-700 hover:bg-slate-50';
/** Audit rows were stored as UPDATED for approvals; avoid treating "UPDATED" as pending via substring "update". */
const getConstitutionalHistoryPresentation = (entry: any) => {
@ -233,7 +234,7 @@ export function ConstitutionalChangeDetails({ requestId, onBack, currentUser }:
if (isLoading) {
return (
<div className="flex flex-col items-center justify-center min-h-[400px] space-y-4">
<Loader2 className="w-8 h-8 text-amber-600 animate-spin" />
<Loader2 className="w-8 h-8 text-re-red animate-spin" />
<p className="text-slate-600">Loading request details...</p>
</div>
);
@ -312,9 +313,9 @@ export function ConstitutionalChangeDetails({ requestId, onBack, currentUser }:
(String(request.status || '') === 'Completed' && !['Rejected', 'Revoked'].includes(String(request.currentStage || '')));
/** SRS §12.2 — closed failure states: do not show misleading step progress. */
const workflowTerminalNegative =
['Rejected', 'Revoked'].includes(String(request.status || '')) ||
['Rejected', 'Revoked'].includes(String(request.currentStage || ''));
const workflowTerminalNegative = isOffboardingTerminalNegative(request.status, request.currentStage);
const statusProgressBarClass = getStatusProgressBarClass(request.status, request.currentStage);
const requestStatusBadgeClass = getOffboardingRequestStatusBadgeClass(request.status, request.currentStage);
const getLatestStageTimelineEntry = (stageName: string) => {
const aliases: Record<string, string[]> = {
@ -512,6 +513,7 @@ export function ConstitutionalChangeDetails({ requestId, onBack, currentUser }:
? existingDocs.findIndex((d: any) => Number(d?.docNumber) === selectedDocType)
: -1;
const payloadDoc = {
id: globalThis.crypto?.randomUUID?.() ?? `doc-${Date.now()}-${selectedDocType}`,
docNumber: selectedDocType,
name: documentNames[selectedDocType] || 'Other',
fileName: uploadFile.name,
@ -541,22 +543,19 @@ export function ConstitutionalChangeDetails({ requestId, onBack, currentUser }:
}
};
const resolveDocumentId = (doc: any, index: number) =>
doc?.id != null ? String(doc.id) : String(index);
const handleVerifyDocument = async (targetDoc: any, targetIndex: number) => {
try {
const existingDocs = Array.isArray(request.documents) ? [...request.documents] : [];
const updatedDocs = existingDocs.map((doc: any, index: number) => {
const isTargetByIndex = index === targetIndex;
const isTargetByDocNumber = targetDoc.docNumber && doc.docNumber === targetDoc.docNumber;
if (!(isTargetByIndex || isTargetByDocNumber)) return doc;
return { ...doc, status: 'Verified', verifiedOn: new Date().toISOString(), verifiedBy: currentUser?.name || 'System' };
});
const response = await API.uploadConstitutionalDocuments(requestId, updatedDocs) as any;
const documentId = resolveDocumentId(targetDoc, targetIndex);
const response = await API.verifyConstitutionalDocument(requestId, documentId) as any;
if (response.data?.success) {
toast.success('Document verified successfully');
fetchRequestDetails();
await fetchRequestDetails({ silent: true });
if (request?.id) await fetchAuditLogs(request.id);
} else {
toast.error('Failed to verify document');
toast.error(response.data?.message || 'Failed to verify document');
}
} catch (error) {
console.error('Verify document error:', error);
@ -566,31 +565,29 @@ export function ConstitutionalChangeDetails({ requestId, onBack, currentUser }:
const submitRejectDocument = async () => {
if (rejectDocIndex == null || !String(rejectDocReason).trim()) {
toast.error('Please enter a rejection reason (SRS document verification).');
toast.error('Please enter a rejection reason.');
return;
}
const targetDoc = (request?.documents || [])[rejectDocIndex];
if (!targetDoc) {
toast.error('Document not found');
return;
}
try {
setIsRejectingDoc(true);
const existingDocs = Array.isArray(request.documents) ? [...request.documents] : [];
const updatedDocs = existingDocs.map((doc: any, index: number) => {
if (index !== rejectDocIndex) return doc;
return {
...doc,
status: 'Rejected',
rejectedOn: new Date().toISOString(),
rejectedBy: (currentUser as any)?.fullName || 'System',
rejectionReason: rejectDocReason.trim()
};
});
const response = await API.uploadConstitutionalDocuments(requestId, updatedDocs) as any;
const documentId = resolveDocumentId(targetDoc, rejectDocIndex);
const response = await API.rejectConstitutionalDocument(requestId, documentId, {
remarks: rejectDocReason.trim()
}) as any;
if (response.data?.success) {
toast.success('Document marked as rejected');
setRejectDocDialogOpen(false);
setRejectDocIndex(null);
setRejectDocReason('');
await fetchRequestDetails({ silent: true });
if (request?.id) await fetchAuditLogs(request.id);
} else {
toast.error('Failed to reject document');
toast.error(response.data?.message || 'Failed to reject document');
}
} catch (error) {
console.error('Reject document error:', error);
@ -620,7 +617,7 @@ export function ConstitutionalChangeDetails({ requestId, onBack, currentUser }:
</p>
</div>
</div>
<Badge className={getStatusColor(request.status)}>
<Badge className={requestStatusBadgeClass}>
{request.status}
</Badge>
<SlaBadge status={getSla('constitutional', requestId)} />
@ -655,7 +652,12 @@ export function ConstitutionalChangeDetails({ requestId, onBack, currentUser }:
<p className="text-slate-600 text-sm mb-1">Request Information</p>
<p className="text-slate-900 text-sm">Submitted: {formatDateTime(request.createdAt)}</p>
<p className="text-slate-600 text-sm">By: {request.dealer?.fullName || 'Dealer'}</p>
<p className="text-slate-900 text-sm mt-2">Current Stage: {request.currentStage}</p>
<div className="mt-2 flex flex-wrap items-center gap-2">
<span className="text-slate-600 text-sm">Current Stage:</span>
<Badge className={getCurrentStageBadgeClass(request.currentStage, request.status)}>
{request.currentStage}
</Badge>
</div>
</div>
</div>
@ -722,16 +724,18 @@ export function ConstitutionalChangeDetails({ requestId, onBack, currentUser }:
<CardContent>
{/* Workflow Progress Tab */}
<TabsContent value="workflow" className="mt-0">
<TabsContent value="workflow" className="mt-0 status-progress-ui">
{/* Progress Bar */}
<div className="mb-8">
<div className="flex items-center justify-between mb-2">
<span className="text-slate-900">Overall Progress</span>
<span className="text-slate-600">{request.progressPercentage}%</span>
<Badge className={`${statusProgressBarClass} text-white border-transparent hover:opacity-90`}>
{request.progressPercentage}% Complete
</Badge>
</div>
<div className="h-3 bg-slate-200 rounded-full overflow-hidden">
<div
className="h-full bg-amber-600 transition-all duration-500"
className={`h-full transition-all duration-500 ${statusProgressBarClass}`}
style={{ width: `${request.progressPercentage}%` }}
/>
</div>
@ -790,13 +794,13 @@ export function ConstitutionalChangeDetails({ requestId, onBack, currentUser }:
{/* Status Icon */}
<div className="flex flex-col items-center">
<div className={`w-10 h-10 rounded-full flex items-center justify-center ${isCompleted ? 'bg-green-100' :
isCurrent ? 'bg-amber-100' :
isCurrent ? WORKFLOW_IN_PROGRESS_ACCENT.icon :
'bg-slate-100'
}`}>
{isCompleted ? (
<CheckCircle2 className="w-5 h-5 text-green-600" />
) : isCurrent ? (
<Clock className="w-5 h-5 text-amber-600" />
<Clock className="w-5 h-5 text-re-red" />
) : (
<AlertCircle className="w-5 h-5 text-slate-400" />
)}
@ -808,20 +812,19 @@ export function ConstitutionalChangeDetails({ requestId, onBack, currentUser }:
</div>
{/* Stage Info */}
<div className={`flex-1 pb-8 ${isCurrent ? 'bg-amber-50 -ml-4 pl-4 pr-4 py-3 rounded-lg border border-amber-200' : ''}`}>
<div className={`flex-1 pb-8 ${isCurrent ? WORKFLOW_IN_PROGRESS_ACCENT.panel : ''}`}>
<div className="flex items-center justify-between">
<div>
<h4 className={`${isCurrent ? 'text-amber-900' : 'text-slate-900'}`}>
<h4 className={`${isCurrent ? WORKFLOW_IN_PROGRESS_ACCENT.title : 'text-slate-900'}`}>
{formatStageLabel(stage.name)}
</h4>
<p className={`text-sm ${isCurrent ? 'text-amber-700' : 'text-slate-600'}`}>
<p className={`text-sm ${isCurrent ? WORKFLOW_IN_PROGRESS_ACCENT.subtitle : 'text-slate-600'}`}>
{`Responsible: ${formatStageRole(stage.role)}`}
</p>
</div>
<Badge className={
isCompleted ? 'bg-green-100 text-green-700 border-green-300' :
isCurrent ? 'bg-amber-100 text-amber-700 border-amber-300' :
isCurrent ? WORKFLOW_IN_PROGRESS_ACCENT.stageBadge :
'bg-slate-100 text-slate-500 border-slate-300'
}>
{isCompleted ? 'Completed' : isCurrent ? 'In Progress' : 'Pending'}
@ -906,7 +909,7 @@ export function ConstitutionalChangeDetails({ requestId, onBack, currentUser }:
<DialogTrigger asChild>
<Button
size="sm"
className="bg-amber-600 hover:bg-amber-700"
className="bg-re-red hover:bg-re-red-hover"
onClick={() => {
setDocTypeLocked(false);
setSelectedDocType(null);
@ -930,8 +933,8 @@ export function ConstitutionalChangeDetails({ requestId, onBack, currentUser }:
{docTypeLocked && selectedDocType != null ? (
<div>
<Label>Document</Label>
<div className="mt-1 flex items-center gap-2 bg-amber-50 border border-amber-200 rounded-md px-3 h-10">
<Badge className="bg-amber-600 text-white border-transparent">
<div className="mt-1 flex items-center gap-2 bg-red-50 border border-red-200 rounded-md px-3 h-10">
<Badge className="bg-re-red text-white border-transparent">
{documentNames[selectedDocType] || `Document ${selectedDocType}`}
</Badge>
</div>
@ -940,7 +943,7 @@ export function ConstitutionalChangeDetails({ requestId, onBack, currentUser }:
<div>
<Label>Document Type</Label>
<select
className="mt-1 flex h-10 w-full rounded-md border border-slate-200 bg-white px-3 py-2 text-sm ring-offset-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-amber-500 focus-visible:ring-offset-2"
className="mt-1 flex h-10 w-full rounded-md border border-slate-200 bg-white px-3 py-2 text-sm ring-offset-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-re-red focus-visible:ring-offset-2"
value={selectedDocType != null ? String(selectedDocType) : ''}
onChange={(e) => {
const v = e.target.value;
@ -967,7 +970,7 @@ export function ConstitutionalChangeDetails({ requestId, onBack, currentUser }:
Cancel
</Button>
<Button
className="bg-amber-600 hover:bg-amber-700"
className="bg-re-red hover:bg-re-red-hover"
onClick={handleUploadDocument}
disabled={isUploadingDoc}
>
@ -1004,7 +1007,9 @@ export function ConstitutionalChangeDetails({ requestId, onBack, currentUser }:
{uploaded && (
<p className={isRejected ? 'text-red-700 text-sm' : ok ? 'text-green-700 text-sm' : 'text-slate-600 text-sm'}>
{uploaded.fileName || uploaded.name}
{isRejected && uploaded.rejectionReason ? `${uploaded.rejectionReason}` : ''}
{isRejected && (uploaded.rejectionReason || uploaded.rejectionRemarks)
? `${uploaded.rejectionReason || uploaded.rejectionRemarks}`
: ''}
</p>
)}
</div>
@ -1023,7 +1028,7 @@ export function ConstitutionalChangeDetails({ requestId, onBack, currentUser }:
<Button
size="sm"
variant="ghost"
className="h-8 px-2 text-amber-700 hover:bg-amber-50"
className={getDocChecklistUploadButtonClass(!!isRejected)}
onClick={() => {
setSelectedDocType(docNum);
setUploadFile(null);
@ -1062,7 +1067,10 @@ export function ConstitutionalChangeDetails({ requestId, onBack, currentUser }:
</TableHeader>
<TableBody>
{(request.documents || []).map((doc: any, index: number) => (
<TableRow key={index}>
<TableRow
key={index}
className={String(doc.status) === 'Rejected' ? 'bg-red-50/80' : undefined}
>
<TableCell className="text-slate-900">
{doc.docNumber ? documentNames[doc.docNumber] : doc.name}
</TableCell>
@ -1166,7 +1174,7 @@ export function ConstitutionalChangeDetails({ requestId, onBack, currentUser }:
<div key={entry.id || index} className="flex items-start gap-4 pb-4 border-b border-slate-200 last:border-0">
<div className={`w-10 h-10 rounded-full flex items-center justify-center flex-shrink-0 ${pres.variant === 'success' ? 'bg-green-100' :
pres.variant === 'danger' ? 'bg-red-100' :
pres.variant === 'pending' ? 'bg-amber-100' :
pres.variant === 'pending' ? 'bg-red-50' :
'bg-slate-100'
}`}>
{pres.variant === 'success' ? (
@ -1174,7 +1182,7 @@ export function ConstitutionalChangeDetails({ requestId, onBack, currentUser }:
) : pres.variant === 'danger' ? (
<AlertCircle className="w-5 h-5 text-red-600" />
) : pres.variant === 'pending' ? (
<Clock className="w-5 h-5 text-amber-600" />
<Clock className="w-5 h-5 text-re-red" />
) : (
<Clock className="w-5 h-5 text-slate-500" />
)}
@ -1223,7 +1231,21 @@ export function ConstitutionalChangeDetails({ requestId, onBack, currentUser }:
<CardContent className="space-y-4">
<div>
<p className="text-slate-600 text-sm">Current Stage</p>
<p className="text-slate-900">{request.currentStage}</p>
<Badge className={getCurrentStageBadgeClass(request.currentStage, request.status)}>
{request.currentStage}
</Badge>
</div>
<div>
<p className="text-slate-600 text-sm">Progress</p>
<div className="flex items-center gap-2 mt-2">
<div className="flex-1 h-2 bg-slate-200 rounded-full overflow-hidden">
<div
className={`h-full transition-all duration-300 ${statusProgressBarClass}`}
style={{ width: `${request.progressPercentage ?? 0}%` }}
/>
</div>
<span className="text-slate-900">{request.progressPercentage ?? 0}%</span>
</div>
</div>
</CardContent>
</Card>
@ -1279,7 +1301,7 @@ export function ConstitutionalChangeDetails({ requestId, onBack, currentUser }:
{permissions.canSendBack && (
<Button
variant="outline"
className="w-full border-amber-300 text-amber-900 hover:bg-amber-50"
className="w-full border-red-300 text-red-900 hover:bg-red-50"
onClick={() => handleAction('sendBack')}
disabled={isActionLoading}
>
@ -1295,7 +1317,7 @@ export function ConstitutionalChangeDetails({ requestId, onBack, currentUser }:
{permissions.canRevoke && (
<Button
variant="outline"
className="w-full border-orange-300 text-orange-900 hover:bg-orange-50"
className="w-full border-red-300 text-re-red-hover hover:bg-red-50"
onClick={() => handleAction('revoke')}
disabled={isActionLoading}
>
@ -1406,9 +1428,9 @@ export function ConstitutionalChangeDetails({ requestId, onBack, currentUser }:
type="submit"
className={
actionType === 'approve' ? 'bg-green-600 hover:bg-green-700' :
actionType === 'reject' ? 'bg-red-600 hover:bg-red-700' :
actionType === 'sendBack' ? 'bg-amber-600 hover:bg-amber-700' :
'bg-orange-600 hover:bg-orange-700'
actionType === 'reject' ? 'bg-re-red hover:bg-re-red-hover' :
actionType === 'sendBack' ? 'bg-re-red hover:bg-re-red-hover' :
'bg-re-red hover:bg-re-red-hover'
}
disabled={isActionLoading}
>
@ -1434,7 +1456,7 @@ export function ConstitutionalChangeDetails({ requestId, onBack, currentUser }:
<DialogHeader>
<DialogTitle>Reject document</DialogTitle>
<DialogDescription>
Per SRS relocation-style verification states, mark this upload as Rejected and provide a reason for the dealer.
Mark this upload as rejected and provide a reason. The action is recorded in the audit trail.
</DialogDescription>
</DialogHeader>
<div className="space-y-3">

View File

@ -15,6 +15,11 @@ import { API } from '@/api/API';
import { SlaBadge } from '@/components/sla/SlaBadge';
import { useSlaBatchStatus } from '@/hooks/useSlaBatchStatus';
import { formatDateTime } from '@/components/ui/utils';
import {
getCurrentStageBadgeClass,
getRequestStatusBadgeClass,
getStatusProgressBarClass,
} from '@/lib/statusProgressTheme';
import { normalizeDealerProfileConstitution } from '@/lib/constitutional-change';
import {
Pagination,
@ -62,13 +67,8 @@ const documentNames: Record<number, string> = {
[OTHER_DOCUMENT_DOC_NUMBER]: 'Other'
};
const getStatusColor = (status: string) => {
if (status === 'Completed') return 'bg-green-100 text-green-700 border-green-300';
if (status.includes('Review') || status.includes('Pending')) return 'bg-yellow-100 text-yellow-700 border-yellow-300';
if (status.includes('Rejected')) return 'bg-red-100 text-red-700 border-red-300';
if (status.includes('Collection')) return 'bg-blue-100 text-blue-700 border-blue-300';
return 'bg-slate-100 text-slate-700 border-slate-300';
};
const getStatusColor = (status: string, currentStage?: string) =>
getRequestStatusBadgeClass(status, currentStage);
const getTypeColor = (type: string) => {
switch(type) {
@ -295,7 +295,7 @@ export function ConstitutionalChangePage({ onViewDetails }: ConstitutionalChange
title: 'Submitted / Review',
value: paginationMeta?.stats?.pending || 0,
icon: Calendar,
color: 'bg-yellow-500',
color: 'bg-re-red',
},
{
title: 'Completed',
@ -316,7 +316,7 @@ export function ConstitutionalChangePage({ onViewDetails }: ConstitutionalChange
{/* Loading Overlay */}
{isLoading && (
<div className="fixed inset-0 bg-slate-900/20 backdrop-blur-sm z-50 flex items-center justify-center">
<Loader2 className="w-8 h-8 text-amber-600 animate-spin" />
<Loader2 className="w-8 h-8 text-re-red animate-spin" />
</div>
)}
@ -331,7 +331,7 @@ export function ConstitutionalChangePage({ onViewDetails }: ConstitutionalChange
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
<DialogTrigger asChild>
<Button className="bg-amber-600 hover:bg-amber-700">
<Button className="bg-re-red hover:bg-re-red-hover">
<Plus className="w-4 h-4 mr-2" />
New Request
</Button>
@ -492,7 +492,7 @@ export function ConstitutionalChangePage({ onViewDetails }: ConstitutionalChange
</Button>
<Button
type="submit"
className="bg-amber-600 hover:bg-amber-700"
className="bg-re-red hover:bg-re-red-hover"
disabled={
!dealerData ||
!targetType ||
@ -605,7 +605,7 @@ export function ConstitutionalChangePage({ onViewDetails }: ConstitutionalChange
</TableCell>
<TableCell>
<div className="flex flex-wrap items-center gap-1">
<Badge variant="outline" className="border-slate-300 text-slate-700">
<Badge className={getCurrentStageBadgeClass(request.currentStage, request.status)}>
{request.currentStage}
</Badge>
<SlaBadge status={getSla('constitutional', request.id || request.requestId)} compact />
@ -615,7 +615,7 @@ export function ConstitutionalChangePage({ onViewDetails }: ConstitutionalChange
<div className="flex items-center gap-2">
<div className="flex-1 h-2 bg-slate-200 rounded-full overflow-hidden">
<div
className="h-full bg-amber-600 transition-all duration-300"
className={`h-full transition-all duration-300 ${getStatusProgressBarClass(request.status, request.currentStage)}`}
style={{ width: `${request.progressPercentage || 0}%` }}
/>
</div>
@ -686,14 +686,14 @@ export function ConstitutionalChangePage({ onViewDetails }: ConstitutionalChange
</TableCell>
<TableCell>
<div className="flex flex-wrap items-center gap-1">
<Badge variant="outline" className="border-slate-300 text-slate-700">
<Badge className={getCurrentStageBadgeClass(request.currentStage, request.status)}>
{request.currentStage}
</Badge>
<SlaBadge status={getSla('constitutional', request.id || request.requestId)} compact />
</div>
</TableCell>
<TableCell>
<Badge className={getStatusColor(request.status)}>
<Badge className={getStatusColor(request.status, request.currentStage)}>
{request.status}
</Badge>
</TableCell>
@ -766,7 +766,7 @@ export function ConstitutionalChangePage({ onViewDetails }: ConstitutionalChange
<div className="flex items-center gap-2">
<div className="flex-1 h-2 bg-slate-200 rounded-full overflow-hidden">
<div
className="h-full bg-amber-600 transition-all duration-300"
className={`h-full transition-all duration-300 ${getStatusProgressBarClass(request.status, request.currentStage)}`}
style={{ width: `${request.progressPercentage || 0}%` }}
/>
</div>
@ -775,7 +775,7 @@ export function ConstitutionalChangePage({ onViewDetails }: ConstitutionalChange
</TableCell>
<TableCell>
<div className="flex flex-wrap items-center gap-1">
<Badge variant="outline" className="border-slate-300 text-slate-700">
<Badge className={getCurrentStageBadgeClass(request.currentStage, request.status)}>
{request.currentStage}
</Badge>
<SlaBadge status={getSla('constitutional', request.id || request.requestId)} compact />
@ -846,7 +846,7 @@ export function ConstitutionalChangePage({ onViewDetails }: ConstitutionalChange
</div>
</TableCell>
<TableCell>
<Badge className={getStatusColor(request.status)}>
<Badge className={getStatusColor(request.status, request.currentStage)}>
{request.status}
</Badge>
</TableCell>

View File

@ -15,18 +15,15 @@ import { dealerService } from '@/services/dealer.service';
import { formatDateTime } from '@/components/ui/utils';
import { API } from '@/api/API';
import { normalizeDealerProfileConstitution } from '@/lib/constitutional-change';
import { getRequestStatusBadgeClass } from '@/lib/statusProgressTheme';
interface DealerConstitutionalChangePageProps {
currentUser?: UserType | null;
onViewDetails?: (id: string) => void;
}
const getStatusColor = (status: string) => {
if (status === 'Completed') return 'bg-green-100 text-green-700 border-green-300';
if (status.includes('Review') || status.includes('Pending')) return 'bg-yellow-100 text-yellow-700 border-yellow-300';
if (status.includes('Rejected')) return 'bg-red-100 text-red-700 border-red-300';
return 'bg-slate-100 text-slate-700 border-slate-300';
};
const getStatusColor = (status: string, currentStage?: string) =>
getRequestStatusBadgeClass(status, currentStage);
export function DealerConstitutionalChangePage({ onViewDetails }: DealerConstitutionalChangePageProps) {
const [isDialogOpen, setIsDialogOpen] = useState(false);
@ -128,7 +125,7 @@ export function DealerConstitutionalChangePage({ onViewDetails }: DealerConstitu
title: 'Pending',
value: requests.filter(r => r.status !== 'Completed' && r.status !== 'Rejected').length,
icon: Calendar,
color: 'bg-yellow-500',
color: 'bg-re-red',
},
{
title: 'Completed',

View File

@ -42,7 +42,7 @@ export function Dashboard({ onNavigate }: DashboardProps) {
title: 'Level 1 Pending',
value: dashboardStats.level1Pending,
icon: Clock,
color: 'bg-amber-500',
color: 'bg-red-500',
trend: { value: 3, isPositive: false },
filter: 'Level 1 Pending'
},
@ -210,7 +210,7 @@ export function Dashboard({ onNavigate }: DashboardProps) {
<div className="w-24 text-slate-700">{item.location}</div>
<div className="flex-1 bg-slate-200 rounded-full h-8 relative overflow-hidden">
<div
className="bg-amber-600 h-full rounded-full transition-all flex items-center justify-end px-3"
className="bg-re-red h-full rounded-full transition-all flex items-center justify-end px-3"
style={{ width: `${(item.count / maxLocationCount) * 100}%` }}
>
<span className="text-white">{item.count}</span>
@ -237,7 +237,7 @@ export function Dashboard({ onNavigate }: DashboardProps) {
className="flex items-start gap-4 p-3 hover:bg-slate-50 rounded-lg cursor-pointer transition-colors"
onClick={() => onNavigate('applications')}
>
<div className="w-10 h-10 bg-amber-100 rounded-full flex items-center justify-center flex-shrink-0">
<div className="w-10 h-10 bg-red-50 rounded-full flex items-center justify-center flex-shrink-0">
{activity.action === 'Approved' && <CheckCircle className="w-5 h-5 text-green-600" />}
{activity.action === 'Interview Scheduled' && <Clock className="w-5 h-5 text-blue-600" />}
{activity.action === 'Document Uploaded' && <FileText className="w-5 h-5 text-purple-600" />}

View File

@ -184,7 +184,7 @@ export function FDDDashboardPage() {
<td className="px-6 py-4">
<Badge className={`px-3 py-1 rounded-full text-[10px] uppercase font-bold tracking-wider ${
app.overallStatus === 'Completed' ? 'bg-green-100 text-green-700' :
'bg-amber-100 text-amber-700'
'bg-red-50 text-re-red-hover'
}`}>
{app.overallStatus === 'Active' ? 'FDD Pending' : app.overallStatus}
</Badge>

View File

@ -214,7 +214,7 @@ export function FinanceDashboard({ onNavigate, onViewPaymentDetails, onViewAudit
if (loading) {
return (
<div className="flex items-center justify-center p-20 text-amber-600">
<div className="flex items-center justify-center p-20 text-re-red">
<Clock className="w-8 h-8 animate-spin mr-3" />
<span>Loading Finance Data...</span>
</div>
@ -234,7 +234,7 @@ export function FinanceDashboard({ onNavigate, onViewPaymentDetails, onViewAudit
{/* Stats Cards */}
<div className="grid grid-cols-1 md:grid-cols-5 gap-3">
<Card
className="cursor-pointer hover:shadow-lg transition-shadow border-amber-200 bg-amber-50/20"
className="cursor-pointer hover:shadow-lg transition-shadow border-red-200 bg-red-50/20"
onClick={() => {
if (pendingAudits.length > 0 && onViewAuditDetails) {
onViewAuditDetails(pendingAudits[0].applicationId || pendingAudits[0].id);
@ -244,12 +244,12 @@ export function FinanceDashboard({ onNavigate, onViewPaymentDetails, onViewAudit
}}
>
<CardHeader className="pb-3">
<CardDescription className="text-amber-600 font-bold">Pending Audits</CardDescription>
<CardTitle className="text-3xl text-amber-600">{pendingAudits.length}</CardTitle>
<CardDescription className="text-re-red font-bold">Pending Audits</CardDescription>
<CardTitle className="text-3xl text-re-red">{pendingAudits.length}</CardTitle>
</CardHeader>
<CardContent>
<p className="text-slate-600 text-xs font-medium">FDD Sign-offs</p>
<Button variant="link" className="p-0 h-auto text-amber-600 mt-2 text-xs">
<Button variant="link" className="p-0 h-auto text-re-red mt-2 text-xs">
Review Now
</Button>
</CardContent>
@ -265,7 +265,7 @@ export function FinanceDashboard({ onNavigate, onViewPaymentDetails, onViewAudit
</CardHeader>
<CardContent>
<p className="text-slate-600">Onboarding Payments</p>
<Button variant="link" className="p-0 h-auto text-amber-600 mt-2">
<Button variant="link" className="p-0 h-auto text-re-red mt-2">
View All
</Button>
</CardContent>
@ -281,7 +281,7 @@ export function FinanceDashboard({ onNavigate, onViewPaymentDetails, onViewAudit
</CardHeader>
<CardContent>
<p className="text-slate-600">Total Validated</p>
<Button variant="link" className="p-0 h-auto text-amber-600 mt-2">
<Button variant="link" className="p-0 h-auto text-re-red mt-2">
View All
</Button>
</CardContent>
@ -297,7 +297,7 @@ export function FinanceDashboard({ onNavigate, onViewPaymentDetails, onViewAudit
</CardHeader>
<CardContent>
<p className="text-slate-600">Offboarding Cases</p>
<Button variant="link" className="p-0 h-auto text-amber-600 mt-2">
<Button variant="link" className="p-0 h-auto text-re-red mt-2">
View All
</Button>
</CardContent>
@ -313,7 +313,7 @@ export function FinanceDashboard({ onNavigate, onViewPaymentDetails, onViewAudit
</CardHeader>
<CardContent>
<p className="text-slate-600">Settlements Done</p>
<Button variant="link" className="p-0 h-auto text-amber-600 mt-2">
<Button variant="link" className="p-0 h-auto text-re-red mt-2">
View All
</Button>
</CardContent>
@ -381,7 +381,7 @@ export function FinanceDashboard({ onNavigate, onViewPaymentDetails, onViewAudit
</div>
<div>
<p className="text-slate-600">Amount</p>
<p className="text-amber-700 font-bold">
<p className="text-re-red-hover font-bold">
{parseFloat(app.amount).toLocaleString('en-IN')}
</p>
</div>
@ -709,7 +709,7 @@ export function FinanceDashboard({ onNavigate, onViewPaymentDetails, onViewAudit
<Button
size="sm"
onClick={handleAddLineItem}
className="w-full bg-amber-600 hover:bg-amber-700"
className="w-full bg-re-red hover:bg-re-red-hover"
>
<Plus className="w-4 h-4" />
</Button>
@ -837,7 +837,7 @@ export function FinanceDashboard({ onNavigate, onViewPaymentDetails, onViewAudit
</div>
<div>
<p className="text-xs text-slate-500 uppercase tracking-wider mb-1">Current Status</p>
<Badge variant="outline" className="bg-amber-100 text-amber-700 border-amber-200">
<Badge variant="outline" className="bg-red-50 text-re-red-hover border-red-200">
{selectedFnF.status}
</Badge>
</div>

View File

@ -784,7 +784,7 @@ export function FinanceFnFDetailsPage({ fnfId, onBack }: FinanceFnFDetailsPagePr
if (loading) {
return (
<div className="flex items-center justify-center p-12">
<Loader2 className="w-8 h-8 animate-spin text-amber-600" />
<Loader2 className="w-8 h-8 animate-spin text-re-red" />
</div>
);
}
@ -812,11 +812,11 @@ export function FinanceFnFDetailsPage({ fnfId, onBack }: FinanceFnFDetailsPagePr
</div>
{/* Status Banner */}
<Card className="border-amber-200 bg-amber-50">
<Card className="border-red-200 bg-red-50">
<CardContent className="pt-6">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<div className="size-12 shrink-0 aspect-square rounded-full bg-amber-100 flex items-center justify-center">
<div className="size-12 shrink-0 aspect-square rounded-full bg-red-50 flex items-center justify-center">
<IndianRupee className="w-5 h-5" />
</div>
<div>
@ -825,7 +825,7 @@ export function FinanceFnFDetailsPage({ fnfId, onBack }: FinanceFnFDetailsPagePr
</div>
</div>
<div className="flex gap-2">
<Badge className="bg-amber-600">
<Badge className="bg-re-red">
{fnfCase.status}
</Badge>
<Badge variant={fnfCase.terminationType === 'Resignation' ? 'default' : 'secondary'}>
@ -932,7 +932,7 @@ export function FinanceFnFDetailsPage({ fnfId, onBack }: FinanceFnFDetailsPagePr
</div>
<div>
<Label className="text-slate-500">Status</Label>
<Badge className="bg-amber-600">
<Badge className="bg-re-red">
{fnfCase.status}
</Badge>
</div>
@ -994,9 +994,9 @@ export function FinanceFnFDetailsPage({ fnfId, onBack }: FinanceFnFDetailsPagePr
<span className="text-slate-900">Total Receivables (from Dealer)</span>
<span className="text-red-700 text-lg">- {settlement.receivables.toLocaleString('en-IN')}</span>
</div>
<div className="flex justify-between items-center p-3 bg-amber-50 rounded-lg">
<div className="flex justify-between items-center p-3 bg-red-50 rounded-lg">
<span className="text-slate-900">Total Deductions</span>
<span className="text-amber-700 text-lg">- {settlement.deductions.toLocaleString('en-IN')}</span>
<span className="text-re-red-hover text-lg">- {settlement.deductions.toLocaleString('en-IN')}</span>
</div>
</div>
@ -1030,8 +1030,8 @@ export function FinanceFnFDetailsPage({ fnfId, onBack }: FinanceFnFDetailsPagePr
</div>
</div>
<div className="flex items-start gap-3 p-4 bg-blue-50 border border-amber-200 rounded-lg">
<AlertCircle className="w-5 h-5 text-amber-600 mt-0.5" />
<div className="flex items-start gap-3 p-4 bg-blue-50 border border-red-200 rounded-lg">
<AlertCircle className="w-5 h-5 text-re-red mt-0.5" />
<div>
<p className="text-sm text-slate-900 mb-1">Calculation Formula</p>
<p className="text-sm text-slate-600">
@ -1401,12 +1401,12 @@ export function FinanceFnFDetailsPage({ fnfId, onBack }: FinanceFnFDetailsPagePr
</Card>
{/* Deductions - Editable */}
<Card className="border-amber-200 bg-amber-50">
<Card className="border-red-200 bg-red-50">
<CardHeader>
<div className="flex items-center justify-between">
<div>
<CardTitle className="text-base flex items-center gap-2">
<AlertCircle className="w-5 h-5 text-amber-600" />
<AlertCircle className="w-5 h-5 text-re-red" />
Deductions (Editable)
</CardTitle>
<CardDescription>Add or modify pending claims and deductions</CardDescription>
@ -1512,7 +1512,7 @@ export function FinanceFnFDetailsPage({ fnfId, onBack }: FinanceFnFDetailsPagePr
</Table>
{/* Add New Deduction */}
<div className="border-t border-amber-300 pt-4 space-y-3">
<div className="border-t border-red-300 pt-4 space-y-3">
<p className="text-sm text-slate-700">Add New Deduction Item:</p>
<div className="grid grid-cols-12 gap-2">
<Select
@ -1541,17 +1541,17 @@ export function FinanceFnFDetailsPage({ fnfId, onBack }: FinanceFnFDetailsPagePr
onChange={(e) => setNewDeduction({ ...newDeduction, amount: e.target.value })}
className="col-span-3"
/>
<Button onClick={handleAddDeduction} className="col-span-1 bg-amber-600 hover:bg-amber-700">
<Button onClick={handleAddDeduction} className="col-span-1 bg-re-red hover:bg-re-red-hover">
<Plus className="w-4 h-4" />
</Button>
</div>
</div>
{/* Total */}
<div className="pt-3 border-t-2 border-amber-400">
<div className="pt-3 border-t-2 border-red-300">
<div className="flex justify-between items-center">
<span className="text-slate-900">Total Deductions</span>
<span className="text-amber-700 text-xl">
<span className="text-re-red-hover text-xl">
{settlement.deductions.toLocaleString('en-IN')}
</span>
</div>
@ -1563,7 +1563,7 @@ export function FinanceFnFDetailsPage({ fnfId, onBack }: FinanceFnFDetailsPagePr
<Card className="border-2 border-blue-300 bg-blue-50">
<CardHeader>
<CardTitle className="flex items-center gap-2">
<CheckCircle className="w-5 h-5 text-amber-600" />
<CheckCircle className="w-5 h-5 text-re-red" />
Final Settlement Summary
</CardTitle>
</CardHeader>
@ -1579,7 +1579,7 @@ export function FinanceFnFDetailsPage({ fnfId, onBack }: FinanceFnFDetailsPagePr
</div>
<div className="flex justify-between items-center p-3 bg-white rounded-lg">
<span className="text-slate-900">Total Deductions</span>
<span className="text-amber-700 text-lg">- {settlement.deductions.toLocaleString('en-IN')}</span>
<span className="text-re-red-hover text-lg">- {settlement.deductions.toLocaleString('en-IN')}</span>
</div>
</div>
@ -1613,8 +1613,8 @@ export function FinanceFnFDetailsPage({ fnfId, onBack }: FinanceFnFDetailsPagePr
</div>
</div>
<div className="flex items-start gap-3 p-4 bg-white border border-amber-200 rounded-lg">
<AlertCircle className="w-5 h-5 text-amber-600 mt-0.5" />
<div className="flex items-start gap-3 p-4 bg-white border border-red-200 rounded-lg">
<AlertCircle className="w-5 h-5 text-re-red mt-0.5" />
<div>
<p className="text-sm text-slate-900 mb-1">Calculation Formula</p>
<p className="text-sm text-slate-600">
@ -1758,7 +1758,7 @@ export function FinanceFnFDetailsPage({ fnfId, onBack }: FinanceFnFDetailsPagePr
filePath: dept.supportingDocument,
documentType: 'Departmental Clearance Proof'
})}
className="flex items-center gap-1 text-[10px] text-amber-600 hover:underline"
className="flex items-center gap-1 text-[10px] text-re-red hover:underline"
>
<Paperclip className="w-3 h-3" />
View Proof
@ -1774,10 +1774,10 @@ export function FinanceFnFDetailsPage({ fnfId, onBack }: FinanceFnFDetailsPagePr
</Card>
{/* Important Notes */}
<Card className="bg-blue-50 border-amber-200">
<Card className="bg-blue-50 border-red-200">
<CardContent className="pt-6">
<div className="flex items-start gap-3">
<AlertCircle className="w-5 h-5 text-amber-600 mt-0.5" />
<AlertCircle className="w-5 h-5 text-re-red mt-0.5" />
<div>
<p className="text-sm text-slate-900 mb-1">Department Response Guidelines</p>
<ul className="text-sm text-slate-700 space-y-1">
@ -1820,7 +1820,7 @@ export function FinanceFnFDetailsPage({ fnfId, onBack }: FinanceFnFDetailsPagePr
filePath: doc.url,
documentType: doc.type
})}
className="text-amber-600 hover:text-amber-700 text-[10px] font-semibold flex items-center gap-1"
className="text-re-red hover:text-re-red-hover text-[10px] font-semibold flex items-center gap-1"
>
<Paperclip className="w-3 h-3" /> PREVIEW
</button>
@ -1873,7 +1873,7 @@ export function FinanceFnFDetailsPage({ fnfId, onBack }: FinanceFnFDetailsPagePr
</CardHeader>
<CardContent>
<div className="space-y-4">
<div className="border-2 border-dashed border-slate-300 rounded-lg p-8 text-center hover:border-amber-400 hover:bg-amber-50 transition-colors">
<div className="border-2 border-dashed border-slate-300 rounded-lg p-8 text-center hover:border-red-300 hover:bg-red-50 transition-colors">
<Upload className="w-8 h-8 text-slate-400 mx-auto mb-2" />
<p className="text-slate-600 mb-2">Click to upload or drag and drop</p>
<p className="text-sm text-slate-500">PDF, DOC, DOCX, PNG, JPG, XLSX (max 10MB)</p>
@ -1910,7 +1910,7 @@ export function FinanceFnFDetailsPage({ fnfId, onBack }: FinanceFnFDetailsPagePr
</div>
<Button
size="sm"
className="bg-amber-600"
className="bg-re-red"
onClick={() => {
setEditingBank(null);
setIsBankModalOpen(true);
@ -1924,9 +1924,9 @@ export function FinanceFnFDetailsPage({ fnfId, onBack }: FinanceFnFDetailsPagePr
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{bankDetails.length > 0 ? (
bankDetails.map((bank: any) => (
<Card key={bank.id} className={`relative ${bank.isPrimary ? 'border-amber-500 bg-blue-50/30' : ''}`}>
<Card key={bank.id} className={`relative ${bank.isPrimary ? 'border-re-red bg-blue-50/30' : ''}`}>
{bank.isPrimary && (
<div className="absolute top-0 right-0 p-1 bg-amber-600 text-white text-[10px] uppercase font-bold px-2 rounded-bl">
<div className="absolute top-0 right-0 p-1 bg-re-red text-white text-[10px] uppercase font-bold px-2 rounded-bl">
Primary
</div>
)}
@ -1955,7 +1955,7 @@ export function FinanceFnFDetailsPage({ fnfId, onBack }: FinanceFnFDetailsPagePr
<Button
variant="ghost"
size="sm"
className="h-7 text-[11px] text-amber-600"
className="h-7 text-[11px] text-re-red"
onClick={() => {
setEditingBank(bank);
setIsBankModalOpen(true);
@ -2056,7 +2056,7 @@ export function FinanceFnFDetailsPage({ fnfId, onBack }: FinanceFnFDetailsPagePr
{/* Settlement Checklist */}
<div className="bg-slate-50 border border-slate-200 rounded-lg p-4 mb-4">
<p className="text-sm font-bold text-slate-900 mb-3 flex items-center gap-2">
<CheckCircle className="w-4 h-4 text-amber-600" />
<CheckCircle className="w-4 h-4 text-re-red" />
Compliance Checklist
</p>
<div className="space-y-3">
@ -2067,7 +2067,7 @@ export function FinanceFnFDetailsPage({ fnfId, onBack }: FinanceFnFDetailsPagePr
id={`check-${item.id}`}
checked={checklist.includes(item.id)}
onChange={() => toggleChecklist(item.id)}
className="w-4 h-4 mt-1 rounded border-slate-300 text-amber-600 focus:ring-amber-500"
className="w-4 h-4 mt-1 rounded border-slate-300 text-re-red focus:ring-re-red"
/>
<label htmlFor={`check-${item.id}`} className="text-sm text-slate-700 leading-tight">
{item.label}
@ -2146,7 +2146,7 @@ export function FinanceFnFDetailsPage({ fnfId, onBack }: FinanceFnFDetailsPagePr
}}
/>
{parseFloat(settlementDetails.adjustments) !== 0 && (
<p className="text-sm text-amber-600 mt-1 flex items-center gap-1">
<p className="text-sm text-re-red mt-1 flex items-center gap-1">
<AlertCircle className="w-3 h-3" />
Adjusted amount: {settlementDetails.settlementAmount}
</p>
@ -2197,7 +2197,7 @@ export function FinanceFnFDetailsPage({ fnfId, onBack }: FinanceFnFDetailsPagePr
<Button
variant="outline"
className="w-full border-blue-300 text-amber-600 hover:bg-blue-50"
className="w-full border-blue-300 text-re-red hover:bg-blue-50"
onClick={handleRequestClarification}
disabled={submitting}
>

View File

@ -150,7 +150,7 @@ export function FinanceFnFPage({ onViewFnFDetails }: FinanceFnFPageProps = {}) {
if (loading) {
return (
<div className="flex items-center justify-center p-12">
<Loader2 className="w-8 h-8 animate-spin text-amber-600" />
<Loader2 className="w-8 h-8 animate-spin text-re-red" />
</div>
);
}
@ -172,7 +172,7 @@ export function FinanceFnFPage({ onViewFnFDetails }: FinanceFnFPageProps = {}) {
<CardContent>
<div className="flex items-center justify-between">
<div className="text-slate-900 text-2xl">{pendingCount}</div>
<Calculator className="w-8 h-8 text-amber-600" />
<Calculator className="w-8 h-8 text-re-red" />
</div>
</CardContent>
</Card>
@ -224,21 +224,21 @@ export function FinanceFnFPage({ onViewFnFDetails }: FinanceFnFPageProps = {}) {
<Button
variant={filterStatus === 'all' ? 'default' : 'outline'}
onClick={() => setFilterStatus('all')}
className={filterStatus === 'all' ? 'bg-amber-600 hover:bg-amber-700' : ''}
className={filterStatus === 'all' ? 'bg-re-red hover:bg-re-red-hover' : ''}
>
All Cases ({displaySettlements.length})
</Button>
<Button
variant={filterStatus === 'pending' ? 'default' : 'outline'}
onClick={() => setFilterStatus('pending')}
className={filterStatus === 'pending' ? 'bg-amber-600 hover:bg-amber-700' : ''}
className={filterStatus === 'pending' ? 'bg-re-red hover:bg-re-red-hover' : ''}
>
Pending Review ({pendingCount})
</Button>
<Button
variant={filterStatus === 'approved' ? 'default' : 'outline'}
onClick={() => setFilterStatus('approved')}
className={filterStatus === 'approved' ? 'bg-amber-600 hover:bg-amber-700' : ''}
className={filterStatus === 'approved' ? 'bg-re-red hover:bg-re-red-hover' : ''}
>
Approved ({approvedCount})
</Button>
@ -314,7 +314,7 @@ export function FinanceFnFPage({ onViewFnFDetails }: FinanceFnFPageProps = {}) {
<TableCell>
<Badge
variant={fnfCase.status === 'Settlement Approved' ? 'default' : 'secondary'}
className={fnfCase.status === 'Settlement Approved' ? 'bg-green-600 text-white' : 'bg-amber-600 text-white'}
className={fnfCase.status === 'Settlement Approved' ? 'bg-green-600 text-white' : 'bg-re-red text-white'}
>
{fnfCase.status}
</Badge>
@ -323,7 +323,7 @@ export function FinanceFnFPage({ onViewFnFDetails }: FinanceFnFPageProps = {}) {
<Button
size="sm"
variant={fnfCase.status === 'Pending Finance Review' ? 'default' : 'outline'}
className={fnfCase.status === 'Pending Finance Review' ? 'bg-amber-600 hover:bg-amber-700' : ''}
className={fnfCase.status === 'Pending Finance Review' ? 'bg-re-red hover:bg-re-red-hover' : ''}
onClick={() => handleViewDetails(fnfCase)}
>
<FileText className="w-4 h-4 mr-2" />
@ -442,10 +442,10 @@ export function FinanceFnFPage({ onViewFnFDetails }: FinanceFnFPageProps = {}) {
</div>
{/* Deductions */}
<Card className="border-amber-200 bg-amber-50">
<Card className="border-red-200 bg-red-50">
<CardHeader>
<CardTitle className="text-base flex items-center gap-2">
<AlertCircle className="w-5 h-5 text-amber-600" />
<AlertCircle className="w-5 h-5 text-re-red" />
Deductions
</CardTitle>
<CardDescription>Pending claims and deductions</CardDescription>
@ -479,9 +479,9 @@ export function FinanceFnFPage({ onViewFnFDetails }: FinanceFnFPageProps = {}) {
<span className="text-slate-900">Total Receivables (from Dealer)</span>
<span className="text-red-600 text-lg">- {settlement.receivables.toLocaleString()}</span>
</div>
<div className="flex justify-between items-center p-3 bg-amber-50 rounded">
<div className="flex justify-between items-center p-3 bg-red-50 rounded">
<span className="text-slate-900">Total Deductions</span>
<span className="text-amber-600 text-lg">- {settlement.deductions.toLocaleString()}</span>
<span className="text-re-red text-lg">- {settlement.deductions.toLocaleString()}</span>
</div>
</div>
@ -695,7 +695,7 @@ export function FinanceFnFPage({ onViewFnFDetails }: FinanceFnFPageProps = {}) {
<p className="text-sm text-slate-500">Status</p>
<Badge
variant={selectedCase.status === 'Settlement Approved' ? 'default' : 'secondary'}
className={selectedCase.status === 'Settlement Approved' ? 'bg-green-600 text-white' : 'bg-amber-600 text-white'}
className={selectedCase.status === 'Settlement Approved' ? 'bg-green-600 text-white' : 'bg-re-red text-white'}
>
{selectedCase.status}
</Badge>
@ -793,10 +793,10 @@ export function FinanceFnFPage({ onViewFnFDetails }: FinanceFnFPageProps = {}) {
</Card>
{/* Deductions */}
<Card className="border-amber-200 bg-amber-50">
<Card className="border-red-200 bg-red-50">
<CardHeader>
<CardTitle className="text-base flex items-center gap-2">
<AlertCircle className="w-5 h-5 text-amber-600" />
<AlertCircle className="w-5 h-5 text-re-red" />
Deductions
</CardTitle>
</CardHeader>
@ -850,7 +850,7 @@ export function FinanceFnFPage({ onViewFnFDetails }: FinanceFnFPageProps = {}) {
</Button>
{selectedCase?.status === 'Pending Finance Review' && (
<Button
className="bg-amber-600 hover:bg-amber-700"
className="bg-re-red hover:bg-re-red-hover"
onClick={() => {
setShowDetailsDialog(false);
handleReviewCase(selectedCase);

View File

@ -146,7 +146,7 @@ export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaym
if (loading) {
return (
<div className="flex items-center justify-center p-20">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-amber-600"></div>
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-re-red"></div>
</div>
);
}
@ -168,7 +168,7 @@ export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaym
<Button
size="sm"
variant={activeType === 'SECURITY_DEPOSIT' ? 'default' : 'outline'}
className={activeType === 'SECURITY_DEPOSIT' ? 'bg-amber-600 hover:bg-amber-700' : ''}
className={activeType === 'SECURITY_DEPOSIT' ? 'bg-re-red hover:bg-re-red-hover' : ''}
onClick={() => setActiveType('SECURITY_DEPOSIT')}
>
Security Deposit
@ -176,7 +176,7 @@ export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaym
<Button
size="sm"
variant={activeType === 'FIRST_FILL' ? 'default' : 'outline'}
className={activeType === 'FIRST_FILL' ? 'bg-amber-600 hover:bg-amber-700' : ''}
className={activeType === 'FIRST_FILL' ? 'bg-re-red hover:bg-re-red-hover' : ''}
onClick={() => setActiveType('FIRST_FILL')}
>
First Fill
@ -190,7 +190,7 @@ export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaym
"border",
activeDeposit?.status === 'Verified' ? "border-green-200 bg-green-50" :
activeDeposit?.status === 'Rejected' ? "border-red-200 bg-red-50" :
"border-amber-200 bg-amber-50"
"border-red-200 bg-red-50"
)}>
<CardContent className="pt-6">
<div className="flex items-center justify-between">
@ -199,13 +199,13 @@ export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaym
"w-12 h-12 rounded-full flex items-center justify-center",
activeDeposit?.status === 'Verified' ? "bg-green-100" :
activeDeposit?.status === 'Rejected' ? "bg-red-100" :
"bg-amber-100"
"bg-red-50"
)}>
<IndianRupee className={cn(
"w-6 h-6",
activeDeposit?.status === 'Verified' ? "text-green-600" :
activeDeposit?.status === 'Rejected' ? "text-red-600" :
"text-amber-600"
"text-re-red"
)} />
</div>
<div>
@ -224,7 +224,7 @@ export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaym
<Badge className={cn(
activeDeposit?.status === 'Verified' ? "bg-green-600" :
activeDeposit?.status === 'Rejected' ? "bg-red-600" :
"bg-amber-600 text-white"
"bg-re-red text-white"
)}>
{activeDeposit?.status || 'No Record'}
</Badge>
@ -237,7 +237,7 @@ export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaym
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2 text-xl">
<User className="w-5 h-5 text-amber-600" />
<User className="w-5 h-5 text-re-red" />
Applicant Information
</CardTitle>
</CardHeader>
@ -265,7 +265,7 @@ export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaym
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2 text-xl">
<CreditCard className="w-5 h-5 text-amber-600" />
<CreditCard className="w-5 h-5 text-re-red" />
Deposit Tracking
</CardTitle>
</CardHeader>
@ -273,7 +273,7 @@ export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaym
<div className="grid grid-cols-2 gap-4">
<div className="p-4 bg-slate-50 rounded-lg border border-slate-200">
<Label className="text-slate-500 block mb-1">Expected Amount</Label>
<p className="text-2xl font-bold text-amber-900">
<p className="text-2xl font-bold text-red-900">
{(activeType === 'SECURITY_DEPOSIT'
? (configs.SECURITY_DEPOSIT?.amount || 500000)
: (configs.FIRST_FILL?.amount || 1500000)
@ -312,7 +312,7 @@ export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaym
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2 text-xl">
<FileText className="w-5 h-5 text-amber-600" />
<FileText className="w-5 h-5 text-re-red" />
Verification Evidence
</CardTitle>
<CardDescription>Documents uploaded by the applicant for payment proof</CardDescription>
@ -342,7 +342,7 @@ export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaym
<Button
variant="ghost"
size="sm"
className="text-amber-600 hover:text-amber-700 hover:bg-amber-50"
className="text-re-red hover:text-re-red-hover hover:bg-red-50"
onClick={() => {
setPreviewDoc(doc);
setShowPreviewModal(true);
@ -364,10 +364,10 @@ export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaym
</div>
<div className="space-y-6">
<Card className="border-amber-100 shadow-sm">
<CardHeader className="bg-amber-50/50">
<Card className="border-red-100 shadow-sm">
<CardHeader className="bg-red-50/50">
<CardTitle className="flex items-center gap-2 text-lg">
<Wallet className="w-5 h-5 text-amber-600" />
<Wallet className="w-5 h-5 text-re-red" />
Finance Action
</CardTitle>
</CardHeader>
@ -431,7 +431,7 @@ export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaym
<Button
className={cn(
"w-full transition-all duration-200",
activeDeposit?.status === 'Verified' ? "bg-green-600 hover:bg-green-600 opacity-90" : "bg-amber-600 hover:bg-amber-700"
activeDeposit?.status === 'Verified' ? "bg-green-600 hover:bg-green-600 opacity-90" : "bg-re-red hover:bg-re-red-hover"
)}
onClick={handleApprovePayment}
disabled={isSubmitting || activeDeposit?.status === 'Verified'}
@ -461,7 +461,7 @@ export function FinancePaymentDetailsPage({ applicationId, onBack }: FinancePaym
<Card className="bg-slate-900 text-white border-none shadow-xl">
<CardHeader>
<CardTitle className="text-base font-medium flex items-center gap-2">
<Clock className="w-4 h-4 text-amber-400" />
<Clock className="w-4 h-4 text-re-red" />
Next Steps
</CardTitle>
</CardHeader>

View File

@ -396,7 +396,7 @@ export function FnFDetails({ fnfId, onBack, currentUser }: FnFDetailsProps) {
if (loading) {
return (
<div className="flex items-center justify-center p-12">
<Loader2 className="w-8 h-8 animate-spin text-amber-600" />
<Loader2 className="w-8 h-8 animate-spin text-re-red" />
</div>
);
}
@ -494,7 +494,7 @@ export function FnFDetails({ fnfId, onBack, currentUser }: FnFDetailsProps) {
const getStatusColor = (status: string) => {
switch (status) {
case "New":
return "bg-amber-100 text-blue-700 border-blue-300";
return "bg-red-50 text-blue-700 border-blue-300";
case "In Progress":
return "bg-yellow-100 text-yellow-700 border-yellow-300";
case "Under Review":
@ -599,7 +599,7 @@ export function FnFDetails({ fnfId, onBack, currentUser }: FnFDetailsProps) {
<Badge
className={
fnfCase.requestType === "Resignation"
? "bg-amber-100 text-amber-700 border-amber-300"
? "bg-red-50 text-re-red-hover border-red-300"
: "bg-red-100 text-red-700 border-red-300"
}
>
@ -626,7 +626,7 @@ export function FnFDetails({ fnfId, onBack, currentUser }: FnFDetailsProps) {
{/* {canSendToStakeholders && fnfCase.status === "New" && (
<Button
className="bg-amber-600 hover:bg-blue-700"
className="bg-re-red hover:bg-blue-700"
onClick={() => setSendStakeholdersDialog(true)}
>
<Send className="w-4 h-4 mr-2" />
@ -775,7 +775,7 @@ export function FnFDetails({ fnfId, onBack, currentUser }: FnFDetailsProps) {
)
? "bg-green-100 border-green-600"
: responsesReceived > 0
? "bg-amber-100 border-amber-600"
? "bg-red-50 border-re-red"
: "bg-slate-100 border-slate-300"
}`}
>
@ -785,7 +785,7 @@ export function FnFDetails({ fnfId, onBack, currentUser }: FnFDetailsProps) {
) ? (
<Check className="w-6 h-6 text-green-600" />
) : responsesReceived > 0 ? (
<Users className="w-6 h-6 text-amber-600" />
<Users className="w-6 h-6 text-re-red" />
) : (
<Clock className="w-6 h-6 text-slate-400" />
)}
@ -814,7 +814,7 @@ export function FnFDetails({ fnfId, onBack, currentUser }: FnFDetailsProps) {
)
? "bg-green-600"
: responsesReceived > 0
? "bg-amber-600"
? "bg-re-red"
: "bg-slate-400"
}
>
@ -843,7 +843,7 @@ export function FnFDetails({ fnfId, onBack, currentUser }: FnFDetailsProps) {
fnfCase.status,
)
? "bg-green-50 border-green-200"
: "bg-blue-50 border-amber-200"
: "bg-blue-50 border-red-200"
}
>
<CardContent className="p-4">
@ -910,14 +910,14 @@ export function FnFDetails({ fnfId, onBack, currentUser }: FnFDetailsProps) {
className={`size-12 shrink-0 aspect-square rounded-full flex items-center justify-center border-2 ${fnfCase.status === "Completed"
? "bg-green-100 border-green-600"
: fnfCase.status === "Finance Approval"
? "bg-amber-100 border-amber-600"
? "bg-red-50 border-re-red"
: "bg-slate-100 border-slate-300"
}`}
>
{fnfCase.status === "Completed" ? (
<Check className="w-6 h-6 text-green-600" />
) : fnfCase.status === "Finance Approval" ? (
<FileCheck className="w-6 h-6 text-amber-600" />
<FileCheck className="w-6 h-6 text-re-red" />
) : (
<Clock className="w-6 h-6 text-slate-400" />
)}
@ -940,7 +940,7 @@ export function FnFDetails({ fnfId, onBack, currentUser }: FnFDetailsProps) {
fnfCase.status === "Completed"
? "bg-green-600"
: fnfCase.status === "Finance Approval"
? "bg-amber-600"
? "bg-re-red"
: "bg-slate-400"
}
>
@ -964,7 +964,7 @@ export function FnFDetails({ fnfId, onBack, currentUser }: FnFDetailsProps) {
className={
fnfCase.status === "Completed"
? "bg-green-50 border-green-200"
: "bg-blue-50 border-amber-200"
: "bg-blue-50 border-red-200"
}
>
<CardContent className="p-4">
@ -989,7 +989,7 @@ export function FnFDetails({ fnfId, onBack, currentUser }: FnFDetailsProps) {
"0"}
</p>
</div>
<div className="text-center p-3 bg-amber-100 rounded-lg">
<div className="text-center p-3 bg-red-50 rounded-lg">
<p className="text-xs text-blue-700 mb-1">
Net Amount
</p>
@ -1022,14 +1022,14 @@ export function FnFDetails({ fnfId, onBack, currentUser }: FnFDetailsProps) {
className={`size-12 shrink-0 aspect-square rounded-full flex items-center justify-center border-2 ${fnfCase.status === "Completed"
? "bg-green-100 border-green-600"
: fnfCase.status === "Finance Approval"
? "bg-amber-100 border-amber-600"
? "bg-red-50 border-re-red"
: "bg-slate-100 border-slate-300"
}`}
>
{fnfCase.status === "Completed" ? (
<Check className="w-6 h-6 text-green-600" />
) : fnfCase.status === "Finance Approval" ? (
<MessageSquare className="w-6 h-6 text-amber-600" />
<MessageSquare className="w-6 h-6 text-re-red" />
) : (
<Clock className="w-6 h-6 text-slate-400" />
)}
@ -1052,7 +1052,7 @@ export function FnFDetails({ fnfId, onBack, currentUser }: FnFDetailsProps) {
fnfCase.status === "Completed"
? "bg-green-600"
: fnfCase.status === "Finance Approval"
? "bg-amber-600"
? "bg-re-red"
: "bg-slate-400"
}
>
@ -1255,7 +1255,7 @@ export function FnFDetails({ fnfId, onBack, currentUser }: FnFDetailsProps) {
</CardContent>
</Card>
<Card className="border-amber-200 bg-blue-50/30">
<Card className="border-red-200 bg-blue-50/30">
<CardHeader>
<CardTitle className="text-blue-900">
F&F Settlement Information
@ -1426,7 +1426,7 @@ export function FnFDetails({ fnfId, onBack, currentUser }: FnFDetailsProps) {
<Button
variant="ghost"
size="sm"
className="text-amber-600 hover:text-blue-700"
className="text-re-red hover:text-blue-700"
onClick={() => {
setSelectedDept(dept);
setClearanceForm({
@ -1518,14 +1518,14 @@ export function FnFDetails({ fnfId, onBack, currentUser }: FnFDetailsProps) {
Amount receivable from dealer
</p>
</div>
<div className="p-6 bg-amber-50 rounded-lg border border-amber-200">
<p className="text-sm text-amber-700 mb-2">
<div className="p-6 bg-red-50 rounded-lg border border-red-200">
<p className="text-sm text-re-red-hover mb-2">
Total Deductions
</p>
<p className="text-3xl text-amber-600 font-bold">
<p className="text-3xl text-re-red font-bold">
{fnfCase.totalDeductions?.toLocaleString() || "0"}
</p>
<p className="text-xs text-amber-600 mt-1">
<p className="text-xs text-re-red mt-1">
Warranty holdbacks / Policy penalties
</p>
</div>
@ -1677,7 +1677,7 @@ export function FnFDetails({ fnfId, onBack, currentUser }: FnFDetailsProps) {
setEditingBank(null);
setIsBankModalOpen(true);
}}
className="bg-amber-600"
className="bg-re-red"
>
<Plus className="w-4 h-4 mr-2" />
Add Account
@ -1687,16 +1687,16 @@ export function FnFDetails({ fnfId, onBack, currentUser }: FnFDetailsProps) {
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{bankDetails.length > 0 ? (
bankDetails.map((bank: any) => (
<Card key={bank.id} className={`relative overflow-hidden ${bank.isPrimary ? 'border-amber-500 bg-blue-50/30' : ''}`}>
<Card key={bank.id} className={`relative overflow-hidden ${bank.isPrimary ? 'border-re-red bg-blue-50/30' : ''}`}>
{bank.isPrimary && (
<div className="absolute top-0 right-0 p-1 bg-amber-600 text-white text-[10px] uppercase font-bold px-2 rounded-bl">
<div className="absolute top-0 right-0 p-1 bg-re-red text-white text-[10px] uppercase font-bold px-2 rounded-bl">
Primary
</div>
)}
<CardContent className="p-5">
<div className="flex items-center gap-3 mb-4">
<div className="p-2 bg-amber-100 rounded-lg">
<Building2 className="w-5 h-5 text-amber-600" />
<div className="p-2 bg-red-50 rounded-lg">
<Building2 className="w-5 h-5 text-re-red" />
</div>
<div>
<p className="font-bold text-slate-900">{bank.bankName}</p>
@ -1727,7 +1727,7 @@ export function FnFDetails({ fnfId, onBack, currentUser }: FnFDetailsProps) {
<Button
variant="ghost"
size="sm"
className="h-8 text-amber-600"
className="h-8 text-re-red"
onClick={() => {
setEditingBank(bank);
setIsBankModalOpen(true);
@ -1787,12 +1787,12 @@ export function FnFDetails({ fnfId, onBack, currentUser }: FnFDetailsProps) {
<div className="flex-1">
<div className="flex items-center justify-between mb-1">
<p className="font-semibold text-slate-900 flex items-center gap-2">
{log.action === 'FNF_CREATED' && <Badge className="bg-amber-600 h-2 w-2 p-0 rounded-full" />}
{log.action === 'FNF_CREATED' && <Badge className="bg-re-red h-2 w-2 p-0 rounded-full" />}
{(log.description && !log.newData?.action) ? log.description : (
<>
{getFriendlyActionName(log.newData?.action || log.action)}
{log.newData?.department && (
<span className="text-amber-600 ml-1 font-bold">
<span className="text-re-red ml-1 font-bold">
- {log.newData.department}
</span>
)}
@ -1855,7 +1855,7 @@ export function FnFDetails({ fnfId, onBack, currentUser }: FnFDetailsProps) {
</DialogHeader>
<div className="space-y-4">
<div className="p-4 bg-blue-50 rounded-lg border border-amber-200">
<div className="p-4 bg-blue-50 rounded-lg border border-red-200">
<p className="text-sm text-blue-900 mb-2">
Notifications will be sent to:
</p>
@ -1877,7 +1877,7 @@ export function FnFDetails({ fnfId, onBack, currentUser }: FnFDetailsProps) {
</Button>
<Button
onClick={handleSendToStakeholders}
className="bg-amber-600 hover:bg-blue-700"
className="bg-re-red hover:bg-blue-700"
>
<Send className="w-4 h-4 mr-2" />
Send Notifications
@ -1951,7 +1951,7 @@ export function FnFDetails({ fnfId, onBack, currentUser }: FnFDetailsProps) {
Cancel
</Button>
<Button
className="bg-amber-600 hover:bg-blue-700"
className="bg-re-red hover:bg-blue-700"
onClick={handleUpdateClearance}
disabled={isUpdatingClearance}
>

View File

@ -34,7 +34,7 @@ const getStatusColor = (status: string) => {
const getTypeColor = (type: string) => {
return type === 'Resignation'
? 'bg-amber-100 text-amber-700 border-amber-300'
? 'bg-red-50 text-re-red-hover border-red-300'
: 'bg-red-100 text-red-700 border-red-300';
};

View File

@ -52,7 +52,7 @@ const BUCKET_LABEL: Record<SlaBucket, string> = {
const BUCKET_CLASS: Record<SlaBucket, string> = {
healthy: 'bg-emerald-100 text-emerald-800 border-emerald-200',
warning: 'bg-amber-100 text-amber-800 border-amber-200',
warning: 'bg-red-50 text-red-800 border-red-200',
critical: 'bg-orange-100 text-orange-800 border-orange-200',
breached: 'bg-red-100 text-red-800 border-red-200'
};
@ -74,7 +74,7 @@ function ProgressCell({ percent, bucket }: { percent: number; bucket: SlaBucket
: bucket === 'critical'
? 'bg-orange-500'
: bucket === 'warning'
? 'bg-amber-500'
? 'bg-red-500'
: 'bg-emerald-500';
const capped = Math.min(percent, 100);
return (
@ -134,7 +134,7 @@ function QueueTable({ items, emptyMessage }: { items: SlaQueueItem[]; emptyMessa
href={row.link}
target="_blank"
rel="noopener noreferrer"
className="text-amber-600 hover:text-amber-800"
className="text-re-red hover:text-red-800"
title="Open case"
>
<ExternalLink className="w-4 h-4" />
@ -219,7 +219,7 @@ export const SLAMonitorPanel: React.FC = () => {
<div className="flex flex-wrap items-center justify-between gap-3">
<div>
<h2 className="text-lg font-semibold text-slate-900 flex items-center gap-2">
<Activity className="w-5 h-5 text-amber-600" />
<Activity className="w-5 h-5 text-re-red" />
SLA Operations Monitor
</h2>
<p className="text-sm text-slate-500">
@ -280,7 +280,7 @@ export const SLAMonitorPanel: React.FC = () => {
icon={<AlertTriangle className="w-4 h-4 text-red-600" />}
highlight="red"
/>
<SummaryCard label="Due soon" value={summary?.dueSoonCount ?? '—'} icon={<Timer className="w-4 h-4 text-amber-600" />} />
<SummaryCard label="Due soon" value={summary?.dueSoonCount ?? '—'} icon={<Timer className="w-4 h-4 text-re-red" />} />
<SummaryCard
label="On track"
value={summary?.onTrackCount ?? '—'}
@ -297,7 +297,7 @@ export const SLAMonitorPanel: React.FC = () => {
<Card className="border-slate-200">
<CardHeader className="pb-2">
<CardTitle className="text-sm flex items-center gap-2">
<BarChart3 className="w-4 h-4 text-amber-600" />
<BarChart3 className="w-4 h-4 text-re-red" />
Analytics (last {analytics.periodDays} days)
</CardTitle>
<CardDescription>Breach rate, resolution time, and top delayed stages</CardDescription>
@ -434,7 +434,7 @@ export const SLAMonitorPanel: React.FC = () => {
<Badge variant="outline">{b.status}</Badge>
</TableCell>
<TableCell>
<a href={b.link} target="_blank" rel="noopener noreferrer" className="text-amber-600">
<a href={b.link} target="_blank" rel="noopener noreferrer" className="text-re-red">
<ExternalLink className="w-4 h-4" />
</a>
</TableCell>
@ -621,7 +621,7 @@ function StatusRow({ label, value, ok }: { label: string; value: string; ok?: bo
return (
<div className="flex justify-between">
<span className="text-slate-600">{label}</span>
<span className={ok === false ? 'text-amber-700 font-medium' : 'text-slate-900'}>{value}</span>
<span className={ok === false ? 'text-re-red-hover font-medium' : 'text-slate-900'}>{value}</span>
</div>
);
}

View File

@ -64,7 +64,7 @@ export const SLAConfigPage: React.FC = () => {
<div className="space-y-6 max-w-7xl mx-auto">
<div>
<h1 className="text-2xl font-bold text-slate-900 flex items-center gap-2">
<Clock className="w-6 h-6 text-amber-600" />
<Clock className="w-6 h-6 text-re-red" />
SLA & Escalation
</h1>
<p className="text-slate-500">Configure TAT rules and monitor live queue, breaches, and schedulers</p>
@ -104,13 +104,13 @@ export const SLAConfigPage: React.FC = () => {
<CardHeader className="pb-3 bg-gradient-to-br from-white to-slate-50/50">
<div className="flex items-start justify-between">
<div className="flex items-center gap-3">
<div className="w-10 h-10 rounded-xl bg-amber-50 border border-amber-100 flex items-center justify-center">
<Clock className="w-5 h-5 text-amber-600" />
<div className="w-10 h-10 rounded-xl bg-red-50 border border-red-100 flex items-center justify-center">
<Clock className="w-5 h-5 text-re-red" />
</div>
<div>
<CardTitle className="text-lg">{sla.activityName}</CardTitle>
<CardDescription className="flex items-center gap-1.5 mt-0.5">
<span className="font-semibold text-amber-700">
<span className="font-semibold text-re-red-hover">
Target TAT: {sla.tatHours} {sla.tatUnit}
</span>
</CardDescription>
@ -133,7 +133,7 @@ export const SLAConfigPage: React.FC = () => {
variant="ghost"
size="icon"
onClick={() => handleEdit(sla)}
className="h-8 w-8 text-slate-400 hover:text-amber-600"
className="h-8 w-8 text-slate-400 hover:text-re-red"
>
<Pen className="w-4 h-4" />
</Button>

View File

@ -1,6 +1,6 @@
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { Progress } from '@/components/ui/progress';
import { ApplicationProgressBar } from '@/features/onboarding/components/ApplicationProgressBar';
import { Application } from '@/lib/mock-data';
import { MapPin, Phone, Mail, Award, Calendar, Building } from 'lucide-react';
import { formatDateTime } from '@/components/ui/utils';
@ -17,7 +17,7 @@ export function ApplicationCard({ application, onViewDetails }: ApplicationCardP
'Questionnaire Pending': 'bg-orange-500',
'Questionnaire Completed': 'bg-blue-500',
'Shortlisted': 'bg-cyan-500',
'Level 1 Pending': 'bg-amber-500',
'Level 1 Pending': 'bg-red-500',
'Level 1 Approved': 'bg-green-500',
'Level 2 Pending': 'bg-purple-500',
'Level 2 Approved': 'bg-green-600',
@ -124,7 +124,12 @@ export function ApplicationCard({ application, onViewDetails }: ApplicationCardP
<span className="text-slate-600">Progress</span>
<span className="text-slate-900" data-testid="onboarding-application-card-progress-text">{application.progress}%</span>
</div>
<Progress value={application.progress} className="h-2" data-testid="onboarding-application-card-progress-bar" />
<ApplicationProgressBar
value={application.progress}
status={application.status}
barClassName="h-2 w-full bg-status-progress-soft"
data-testid="onboarding-application-card-progress-bar"
/>
</div>
{/* Deadline Warning */}
@ -138,7 +143,7 @@ export function ApplicationCard({ application, onViewDetails }: ApplicationCardP
<Button
onClick={() => onViewDetails(application.id)}
className="w-full bg-amber-600 hover:bg-amber-700"
className="w-full bg-re-red hover:bg-re-red-hover"
data-testid="onboarding-application-card-view-button"
>
View Details

View File

@ -0,0 +1,44 @@
import { Progress } from '@/components/ui/progress';
import { cn } from '@/components/ui/utils';
import { getStatusProgressBarClass } from '@/lib/statusProgressTheme';
export interface ApplicationProgressBarProps {
value: number;
status?: string | null;
currentStage?: string | null;
/** Track styling — matches Application Details progress tab by default */
barClassName?: string;
showPercent?: boolean;
percentClassName?: string;
'data-testid'?: string;
}
/** Status-colored progress bar (amber / green / re-red) — same strategy as Application Details. */
export function ApplicationProgressBar({
value,
status,
currentStage,
barClassName = 'h-2 w-20 bg-status-progress-soft',
showPercent = false,
percentClassName = 'text-slate-600 shrink-0',
'data-testid': testId,
}: ApplicationProgressBarProps) {
return (
<div
className={cn('flex items-center gap-2 min-w-0', showPercent && 'w-full max-w-[8rem]')}
data-testid={testId ? `${testId}-container` : undefined}
>
<Progress
value={value}
className={barClassName}
indicatorClassName={getStatusProgressBarClass(status, currentStage)}
data-testid={testId}
/>
{showPercent && (
<span className={percentClassName} data-testid={testId ? `${testId}-text` : undefined}>
{value}%
</span>
)}
</div>
);
}

View File

@ -115,7 +115,7 @@ export const BankDetailsModal: React.FC<BankDetailsModalProps> = ({
id="isPrimaryModal"
name="isPrimary"
defaultChecked={editingBank?.isPrimary}
className="w-4 h-4 rounded border-slate-300 text-amber-600 focus:ring-amber-500"
className="w-4 h-4 rounded border-slate-300 text-re-red focus:ring-re-red"
data-testid="onboarding-is-primary-checkbox"
/>
<Label htmlFor="isPrimaryModal" className="text-xs font-medium cursor-pointer">Set as primary account</Label>
@ -124,7 +124,7 @@ export const BankDetailsModal: React.FC<BankDetailsModalProps> = ({
</div>
<DialogFooter>
<Button type="button" variant="outline" size="sm" onClick={onClose} data-testid="onboarding-bank-details-cancel">Cancel</Button>
<Button type="submit" disabled={isSubmitting} size="sm" className="bg-amber-600" data-testid="onboarding-bank-details-submit">
<Button type="submit" disabled={isSubmitting} size="sm" className="bg-re-red" data-testid="onboarding-bank-details-submit">
{isSubmitting ? <Loader2 className="w-4 h-4 animate-spin mr-2" /> : null}
{editingBank ? 'Update Account' : 'Save Bank Details'}
</Button>

View File

@ -33,11 +33,11 @@ const QuestionnaireResponseView: React.FC<QuestionnaireResponseViewProps> = ({ a
<div className="space-y-6" data-testid="onboarding-questionnaire-view">
<div className="flex items-center justify-between mb-4">
<div className="flex items-center gap-3">
<ClipboardList className="w-5 h-5 text-amber-600" />
<ClipboardList className="w-5 h-5 text-re-red" />
<h3 className="text-slate-900">Questionnaire Responses</h3>
</div>
{totalScore !== undefined && (
<Badge className="bg-amber-600" data-testid="onboarding-questionnaire-total-score">Score: {totalScore}/100</Badge>
<Badge className="bg-re-red" data-testid="onboarding-questionnaire-total-score">Score: {totalScore}/100</Badge>
)}
</div>
@ -62,12 +62,12 @@ const QuestionnaireResponseView: React.FC<QuestionnaireResponseViewProps> = ({ a
return (
<div
key={resp.id}
className="border border-slate-200 rounded-lg p-5 hover:border-amber-300 transition-colors"
className="border border-slate-200 rounded-lg p-5 hover:border-red-300 transition-colors"
data-testid={`onboarding-questionnaire-item-${index}`}
>
<div className="flex items-start gap-3 mb-3">
<div className="w-8 h-8 rounded-full bg-amber-100 flex items-center justify-center flex-shrink-0">
<span className="text-amber-600">{index + 1}</span>
<div className="w-8 h-8 rounded-full bg-red-50 flex items-center justify-center flex-shrink-0">
<span className="text-re-red">{index + 1}</span>
</div>
<div className="flex-1">
<div className="flex items-center gap-2 mb-2">

View File

@ -126,9 +126,9 @@ export function ApplicantInformationCard({
data-testid="onboarding-applicant-info-edit-firm-type"
>
Proposed Firm Type
<Pencil className="w-3 h-3 text-slate-300 group-hover:text-amber-600 transition-colors" />
<Pencil className="w-3 h-3 text-slate-300 group-hover:text-re-red transition-colors" />
</p>
<p className="text-slate-900 font-black text-amber-700 tracking-tight leading-none mt-1" data-testid="onboarding-applicant-info-firm-type">
<p className="text-slate-900 font-black text-re-red-hover tracking-tight leading-none mt-1" data-testid="onboarding-applicant-info-firm-type">
{application.constitutionType || 'Not Provided'}
</p>
</div>
@ -214,14 +214,14 @@ export function ApplicantInformationCard({
<div className="pt-6 border-t mt-6">
<div className="flex items-center justify-between mb-4">
<h3 className="text-sm font-black text-slate-900 uppercase tracking-widest flex items-center gap-2">
<CreditCard className="w-4 h-4 text-amber-600" /> Statutory & Bank Information
<CreditCard className="w-4 h-4 text-re-red" /> Statutory & Bank Information
</h3>
{canEditStatutory && !isEditingStatutory && (
<Button
variant="ghost"
size="sm"
onClick={onEditStatutory}
className="h-8 text-amber-600 hover:text-amber-700 hover:bg-amber-50 gap-1.5"
className="h-8 text-re-red hover:text-re-red-hover hover:bg-red-50 gap-1.5"
data-testid="onboarding-applicant-info-edit-statutory"
>
<Pencil className="w-3.5 h-3.5" />
@ -232,7 +232,7 @@ export function ApplicantInformationCard({
{isEditingStatutory ? (
<div
className="bg-slate-50/50 p-6 rounded-xl border-2 border-amber-100 space-y-4"
className="bg-slate-50/50 p-6 rounded-xl border-2 border-red-100 space-y-4"
data-testid="onboarding-applicant-info-statutory-edit-form"
>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
@ -324,7 +324,7 @@ export function ApplicantInformationCard({
size="sm"
onClick={onSaveStatutory}
disabled={isSavingStatutory}
className="bg-amber-600 hover:bg-amber-700"
className="bg-re-red hover:bg-re-red-hover"
data-testid="onboarding-applicant-info-statutory-save"
>
{isSavingStatutory ? <Loader2 className="w-4 h-4 animate-spin" /> : 'Save Details'}

View File

@ -271,9 +271,9 @@ export function ApplicationDetailsActionModals(props: ApplicationDetailsActionMo
<Select value={interviewType} onValueChange={setInterviewType}>
<SelectTrigger className="mt-2" data-testid="onboarding-schedule-type-select"><SelectValue placeholder="Select interview type" /></SelectTrigger>
<SelectContent>
<SelectItem value="level1" disabled={isInterviewCompleted(1) || isInterviewActive(1)}><div className="flex items-center justify-between w-full"><span>Level 1</span>{isInterviewCompleted(1) && <CheckCircle className="w-4 h-4 text-green-500 ml-2 inline" />}{isInterviewActive(1) && <Clock className="w-4 h-4 text-amber-500 ml-2 inline" />}</div></SelectItem>
<SelectItem value="level2" disabled={!isInterviewCompleted(1) || isInterviewCompleted(2) || isInterviewActive(2)}><div className="flex items-center justify-between w-full"><span>Level 2</span>{!isInterviewCompleted(1) && <span className="text-[10px] text-slate-400 ml-2">(Prerequisite: L1)</span>}{isInterviewCompleted(2) && <CheckCircle className="w-4 h-4 text-green-500 ml-2 inline" />}{isInterviewActive(2) && <Clock className="w-4 h-4 text-amber-500 ml-2 inline" />}</div></SelectItem>
<SelectItem value="level3" disabled={!isInterviewCompleted(2) || isInterviewCompleted(3) || isInterviewActive(3)}><div className="flex items-center justify-between w-full"><span>Level 3</span>{!isInterviewCompleted(2) && <span className="text-[10px] text-slate-400 ml-2">(Prerequisite: L2)</span>}{isInterviewCompleted(3) && <CheckCircle className="w-4 h-4 text-green-500 ml-2 inline" />}{isInterviewActive(3) && <Clock className="w-4 h-4 text-amber-500 ml-2 inline" />}</div></SelectItem>
<SelectItem value="level1" disabled={isInterviewCompleted(1) || isInterviewActive(1)}><div className="flex items-center justify-between w-full"><span>Level 1</span>{isInterviewCompleted(1) && <CheckCircle className="w-4 h-4 text-green-500 ml-2 inline" />}{isInterviewActive(1) && <Clock className="w-4 h-4 text-re-red ml-2 inline" />}</div></SelectItem>
<SelectItem value="level2" disabled={!isInterviewCompleted(1) || isInterviewCompleted(2) || isInterviewActive(2)}><div className="flex items-center justify-between w-full"><span>Level 2</span>{!isInterviewCompleted(1) && <span className="text-[10px] text-slate-400 ml-2">(Prerequisite: L1)</span>}{isInterviewCompleted(2) && <CheckCircle className="w-4 h-4 text-green-500 ml-2 inline" />}{isInterviewActive(2) && <Clock className="w-4 h-4 text-re-red ml-2 inline" />}</div></SelectItem>
<SelectItem value="level3" disabled={!isInterviewCompleted(2) || isInterviewCompleted(3) || isInterviewActive(3)}><div className="flex items-center justify-between w-full"><span>Level 3</span>{!isInterviewCompleted(2) && <span className="text-[10px] text-slate-400 ml-2">(Prerequisite: L2)</span>}{isInterviewCompleted(3) && <CheckCircle className="w-4 h-4 text-green-500 ml-2 inline" />}{isInterviewActive(3) && <Clock className="w-4 h-4 text-re-red ml-2 inline" />}</div></SelectItem>
</SelectContent>
</Select>
</div>

View File

@ -69,15 +69,12 @@ export function ApplicationDetailsExtendedModals(props: ApplicationDetailsExtend
getDocumentsForStage,
setPreviewDoc,
setShowPreviewModal,
flattenedStages,
setSelectedStage,
uploadDocType,
setUploadDocType,
setUploadFile,
isUploading,
handleUpload,
uploadFile,
documentConfigs,
showPreviewModal,
previewDoc,
showFddFinalizeModal,
@ -126,7 +123,7 @@ export function ApplicationDetailsExtendedModals(props: ApplicationDetailsExtend
<div className="custom-scrollbar-slim min-h-0 flex-1 overflow-y-auto px-5 py-5">
<div className="space-y-6">
{ktCriteria.length === 0 && (
<div className="rounded-md border border-amber-200 bg-amber-50 px-3 py-2 text-sm text-amber-800">
<div className="rounded-md border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-800">
KT Matrix configuration is not available. Configure it in Master &gt; Interview Configurations.
</div>
)}
@ -211,7 +208,7 @@ export function ApplicationDetailsExtendedModals(props: ApplicationDetailsExtend
<Separator />
{l2Fields.length === 0 && (
<div className="rounded-md border border-amber-200 bg-amber-50 px-3 py-2 text-sm text-amber-800">
<div className="rounded-md border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-800">
Level 2 feedback configuration is not available. Configure it in Master &gt; Interview Configurations.
</div>
)}
@ -315,7 +312,7 @@ export function ApplicationDetailsExtendedModals(props: ApplicationDetailsExtend
<Separator />
{l3Fields.length === 0 && (
<div className="rounded-md border border-amber-200 bg-amber-50 px-3 py-2 text-sm text-amber-800">
<div className="rounded-md border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-800">
Level 3 feedback configuration is not available. Configure it in Master &gt; Interview Configurations.
</div>
)}
@ -371,7 +368,7 @@ export function ApplicationDetailsExtendedModals(props: ApplicationDetailsExtend
<Dialog open={showDocumentsModal} onOpenChange={(open) => { setShowDocumentsModal(open); if (!open) setShowUploadForm(false); }}>
<DialogContent className="max-w-[95vw] sm:max-w-2xl md:max-w-3xl lg:max-w-4xl max-h-[90vh] overflow-hidden flex flex-col p-4 sm:p-6" data-testid="onboarding-documents-modal">
<DialogHeader className="pb-4">
<DialogTitle className="text-xl font-bold flex items-center gap-2"><FileText className="w-5 h-5 text-amber-600" />Documents - {selectedStage || 'General'}</DialogTitle>
<DialogTitle className="text-xl font-bold flex items-center gap-2"><FileText className="w-5 h-5 text-re-red" />Documents - {selectedStage || 'General'}</DialogTitle>
<DialogDescription className="text-slate-500">View and manage documents uploaded for this stage.</DialogDescription>
</DialogHeader>
{!showUploadForm ? (
@ -398,7 +395,7 @@ export function ApplicationDetailsExtendedModals(props: ApplicationDetailsExtend
<TableCell className="text-right py-3">
<div className="flex gap-1 justify-end">
<Button variant="ghost" size="icon" className="h-8 w-8 text-slate-400 hover:text-indigo-600 hover:bg-indigo-50 rounded-full" onClick={() => { setPreviewDoc(doc); setShowPreviewModal(true); }} data-testid={`onboarding-document-preview-${index}`}><Eye className="w-4 h-4" /></Button>
<Button variant="ghost" size="icon" className="h-8 w-8 text-slate-400 hover:text-amber-600 hover:bg-amber-50 rounded-full" onClick={() => { const baseUrl = (import.meta as any).env?.VITE_API_URL || 'http://localhost:5000'; window.open(`${baseUrl}/${doc.filePath}`, '_blank'); }} data-testid={`onboarding-document-download-${index}`}><Download className="w-4 h-4" /></Button>
<Button variant="ghost" size="icon" className="h-8 w-8 text-slate-400 hover:text-re-red hover:bg-red-50 rounded-full" onClick={() => { const baseUrl = (import.meta as any).env?.VITE_API_URL || 'http://localhost:5000'; window.open(`${baseUrl}/${doc.filePath}`, '_blank'); }} data-testid={`onboarding-document-download-${index}`}><Download className="w-4 h-4" /></Button>
</div>
</TableCell>
</TableRow>
@ -410,7 +407,7 @@ export function ApplicationDetailsExtendedModals(props: ApplicationDetailsExtend
<div className="flex-1 flex flex-col items-center justify-center py-12 text-center border rounded-lg bg-slate-50/30" data-testid="onboarding-documents-empty"><div className="w-16 h-16 rounded-full bg-slate-100 flex items-center justify-center mb-4"><FileText className="w-8 h-8 text-slate-300" /></div><h3 className="text-slate-900 font-semibold mb-2">No Documents Found</h3><p className="text-slate-600 text-sm max-w-[250px]">No documents have been uploaded for this stage yet.</p></div>
)}
<div className="flex flex-col sm:flex-row gap-3 pt-2 mt-auto">
<Button className="flex-1 bg-amber-600 hover:bg-amber-700 text-white font-bold py-3 sm:py-5 rounded-xl shadow-lg shadow-amber-600/15 transition-all hover:scale-[1.01] active:scale-[0.99]" onClick={() => setShowUploadForm(true)} data-testid="onboarding-documents-upload-button"><Upload className="w-5 h-5 mr-3" />Upload Document</Button>
<Button className="flex-1 bg-re-red hover:bg-re-red-hover text-white font-bold py-3 sm:py-5 rounded-xl shadow-lg shadow-re-red/15 transition-all hover:scale-[1.01] active:scale-[0.99]" onClick={() => setShowUploadForm(true)} data-testid="onboarding-documents-upload-button"><Upload className="w-5 h-5 mr-3" />Upload Document</Button>
<Button variant="outline" className="flex-1 sm:flex-none py-3 sm:py-5 px-8 rounded-xl border-slate-200 font-semibold text-slate-600 hover:bg-slate-50" onClick={() => setShowDocumentsModal(false)} data-testid="onboarding-documents-close-button">Close</Button>
</div>
</div>
@ -425,7 +422,7 @@ export function ApplicationDetailsExtendedModals(props: ApplicationDetailsExtend
placeholder="Enter document name"
value={uploadDocType}
onChange={(e) => setUploadDocType(e.target.value)}
className="bg-white border-slate-200 h-12 rounded-xl focus:ring-amber-500 shadow-sm"
className="bg-white border-slate-200 h-12 rounded-xl focus:ring-re-red shadow-sm"
data-testid="onboarding-documents-name-input"
/>
</div>
@ -433,7 +430,7 @@ export function ApplicationDetailsExtendedModals(props: ApplicationDetailsExtend
<Label className="text-slate-700 font-semibold px-1">Select File <span className="text-red-500">*</span></Label>
<Input
type="file"
className="bg-white border-slate-200 h-12 rounded-xl focus:ring-amber-500 shadow-sm file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-sm file:font-semibold file:bg-amber-50 file:text-amber-700 hover:file:bg-amber-100 cursor-pointer"
className="bg-white border-slate-200 h-12 rounded-xl focus:ring-re-red shadow-sm file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-sm file:font-semibold file:bg-red-50 file:text-re-red-hover hover:file:bg-red-50 cursor-pointer"
onChange={(e) => {
const file = e.target.files ? e.target.files[0] : null;
setUploadFile(file);
@ -449,7 +446,7 @@ export function ApplicationDetailsExtendedModals(props: ApplicationDetailsExtend
</div>
<div className="flex flex-col sm:flex-row gap-3 pt-4">
<Button className="flex-1 order-2 sm:order-1 py-3 sm:py-5 rounded-xl border-slate-200 font-semibold text-slate-600 hover:bg-slate-50" variant="outline" onClick={() => setShowUploadForm(false)} disabled={isUploading} data-testid="onboarding-documents-upload-cancel">Cancel</Button>
<Button className="flex-1 order-1 sm:order-2 bg-amber-600 hover:bg-amber-700 text-white font-bold py-3 sm:py-5 rounded-xl shadow-lg shadow-amber-600/15 transition-all hover:scale-[1.01] active:scale-[0.99]" onClick={async () => { await handleUpload(); setShowUploadForm(false); }} disabled={!uploadFile || !uploadDocType || isUploading} data-testid="onboarding-documents-upload-submit">
<Button className="flex-1 order-1 sm:order-2 bg-re-red hover:bg-re-red-hover text-white font-bold py-3 sm:py-5 rounded-xl shadow-lg shadow-re-red/15 transition-all hover:scale-[1.01] active:scale-[0.99]" onClick={async () => { await handleUpload(); setShowUploadForm(false); }} disabled={!uploadFile || !uploadDocType || isUploading} data-testid="onboarding-documents-upload-submit">
{isUploading ? <span className="flex items-center gap-2"><div className="w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin" />Uploading...</span> : <span className="flex items-center gap-2"><Upload className="w-5 h-5" />Confirm Upload</span>}
</Button>
</div>
@ -461,11 +458,11 @@ export function ApplicationDetailsExtendedModals(props: ApplicationDetailsExtend
<Dialog open={showFddFinalizeModal} onOpenChange={setShowFddFinalizeModal}>
<DialogContent className="max-w-md p-0 overflow-hidden border-none shadow-2xl rounded-3xl" data-testid="onboarding-fdd-finalize-modal">
<div className="bg-slate-950 p-8 flex items-center justify-center relative overflow-hidden"><div className="absolute inset-0 bg-gradient-to-br from-amber-600/20 to-transparent" /><div className="w-20 h-20 bg-amber-600/20 rounded-full flex items-center justify-center animate-pulse relative z-10 shadow-[0_0_40px_rgba(245,158,11,0.2)]"><ShieldCheck className="w-10 h-10 text-amber-500" /></div></div>
<div className="bg-slate-950 p-8 flex items-center justify-center relative overflow-hidden"><div className="absolute inset-0 bg-gradient-to-br from-re-red/20 to-transparent" /><div className="w-20 h-20 bg-re-red/20 rounded-full flex items-center justify-center animate-pulse relative z-10 shadow-[0_0_40px_rgba(218,41,28,0.2)]"><ShieldCheck className="w-10 h-10 text-re-red" /></div></div>
<div className="p-8 space-y-6 bg-white">
<DialogHeader>
<DialogTitle className="text-2xl font-black text-slate-900 text-center tracking-tight">Finalize FDD Audit</DialogTitle>
<DialogDescription className="text-slate-500 text-center pt-2 leading-relaxed text-sm font-medium">You are about to submit your final findings. This action will <span className="font-bold text-slate-900 underline decoration-amber-500 decoration-2">lock the audit session</span> and trigger the LOI approval workflow.</DialogDescription>
<DialogDescription className="text-slate-500 text-center pt-2 leading-relaxed text-sm font-medium">You are about to submit your final findings. This action will <span className="font-bold text-slate-900 underline decoration-re-red decoration-2">lock the audit session</span> and trigger the LOI approval workflow.</DialogDescription>
</DialogHeader>
<div className="space-y-4">
{(currentUser?.role !== 'FDD' && currentUser?.roleCode !== 'FDD') && (
@ -473,7 +470,7 @@ export function ApplicationDetailsExtendedModals(props: ApplicationDetailsExtend
<Label className="text-[10px] font-black uppercase tracking-widest text-slate-400">Auditor Recommendation <span className="text-red-500">*</span></Label>
<div className="flex gap-2">
{['Recommended', 'Qualified with Observations', 'Not Recommended'].map((rec) => (
<Button key={rec} variant={fddAuditRecommendation === rec ? 'default' : 'outline'} className={cn("flex-1 h-10 font-bold text-[9px] uppercase tracking-wider rounded-xl transition-all", fddAuditRecommendation === rec && rec === 'Recommended' && "bg-emerald-600 hover:bg-emerald-700", fddAuditRecommendation === rec && rec === 'Qualified with Observations' && "bg-amber-500 hover:bg-amber-600", fddAuditRecommendation === rec && rec === 'Not Recommended' && "bg-red-600 hover:bg-red-700")} onClick={() => setFddAuditRecommendation(rec)} data-testid={`onboarding-fdd-recommendation-${rec.replace(/\s+/g, '-').toLowerCase()}`}>{rec}</Button>
<Button key={rec} variant={fddAuditRecommendation === rec ? 'default' : 'outline'} className={cn("flex-1 h-10 font-bold text-[9px] uppercase tracking-wider rounded-xl transition-all", fddAuditRecommendation === rec && rec === 'Recommended' && "bg-emerald-600 hover:bg-emerald-700", fddAuditRecommendation === rec && rec === 'Qualified with Observations' && "bg-red-500 hover:bg-re-red", fddAuditRecommendation === rec && rec === 'Not Recommended' && "bg-red-600 hover:bg-red-700")} onClick={() => setFddAuditRecommendation(rec)} data-testid={`onboarding-fdd-recommendation-${rec.replace(/\s+/g, '-').toLowerCase()}`}>{rec}</Button>
))}
</div>
</div>
@ -482,18 +479,18 @@ export function ApplicationDetailsExtendedModals(props: ApplicationDetailsExtend
<Label className="text-[10px] font-black uppercase tracking-widest text-slate-400">Findings Summary</Label>
<Textarea
placeholder="Summarize key financial findings or discrepancies..."
className="min-h-[100px] rounded-xl border-slate-200 focus:ring-amber-500 text-sm"
className="min-h-[100px] rounded-xl border-slate-200 focus:ring-re-red text-sm"
value={fddAuditFindings}
onChange={(e) => setFddAuditFindings(e.target.value)}
data-testid="onboarding-fdd-findings-textarea"
/>
</div>
</div>
<div className="bg-amber-50 p-4 rounded-2xl flex gap-3 border border-amber-100"><Info className="w-5 h-5 text-amber-600 shrink-0 mt-0.5" /><p className="text-[11px] text-amber-800 font-medium italic">Ensure the final PDF report is uploaded first. This satisfies the FDD statutory requirement.</p></div>
<div className="bg-red-50 p-4 rounded-2xl flex gap-3 border border-red-100"><Info className="w-5 h-5 text-re-red shrink-0 mt-0.5" /><p className="text-[11px] text-red-800 font-medium italic">Ensure the final PDF report is uploaded first. This satisfies the FDD statutory requirement.</p></div>
<div className="flex flex-col sm:flex-row gap-3 pt-2">
<Button variant="outline" className="w-full sm:flex-1 h-12 rounded-2xl font-bold text-slate-600 hover:bg-slate-50 border-slate-200" onClick={() => setShowFddFinalizeModal(false)} disabled={isFinalizingFdd} data-testid="onboarding-fdd-finalize-cancel">Cancel</Button>
<Button
className="w-full sm:flex-1 h-12 rounded-2xl font-bold bg-slate-950 hover:bg-slate-900 text-white shadow-lg shadow-slate-200 transition-all active:scale-95 border-b-4 border-amber-500"
className="w-full sm:flex-1 h-12 rounded-2xl font-bold bg-slate-950 hover:bg-slate-900 text-white shadow-lg shadow-slate-200 transition-all active:scale-95 border-b-4 border-re-red"
disabled={isFinalizingFdd || !fddAuditFindings}
data-testid="onboarding-fdd-finalize-submit"
onClick={async () => {
@ -569,16 +566,16 @@ export function ApplicationDetailsExtendedModals(props: ApplicationDetailsExtend
<Dialog open={showFirmTypeModal} onOpenChange={setShowFirmTypeModal}>
<DialogContent className="max-w-md p-0 overflow-hidden rounded-3xl border-none shadow-2xl" data-testid="onboarding-firm-type-modal">
<div className="bg-amber-600 p-8 text-white">
<div className="bg-re-red p-8 text-white">
<div className="w-16 h-16 rounded-2xl bg-white/20 flex items-center justify-center mb-6 backdrop-blur-sm border border-white/30 shadow-inner"><Building2 className="w-8 h-8 text-white" /></div>
<h3 className="text-2xl font-black tracking-tight mb-2">Update Firm Type</h3>
<p className="text-amber-100/80 text-sm font-medium leading-relaxed">Select the proposed legal constitution for this dealership application.</p>
<p className="text-red-100/80 text-sm font-medium leading-relaxed">Select the proposed legal constitution for this dealership application.</p>
</div>
<div className="p-8 space-y-6 bg-white">
<div className="space-y-2">
<Label className="text-[10px] text-slate-400 uppercase tracking-widest font-black">Proposed Legal Constitution <span className="text-red-500">*</span></Label>
<Select value={tempFirmType} onValueChange={setTempFirmType}>
<SelectTrigger className="h-12 rounded-xl border-slate-200 focus:ring-amber-500" data-testid="onboarding-firm-type-select"><SelectValue placeholder="Select Firm Type" /></SelectTrigger>
<SelectTrigger className="h-12 rounded-xl border-slate-200 focus:ring-re-red" data-testid="onboarding-firm-type-select"><SelectValue placeholder="Select Firm Type" /></SelectTrigger>
<SelectContent>
<SelectItem value="Proprietorship" data-testid="onboarding-firm-type-proprietorship">Proprietorship</SelectItem>
<SelectItem value="Partnership" data-testid="onboarding-firm-type-partnership">Partnership</SelectItem>
@ -590,7 +587,7 @@ export function ApplicationDetailsExtendedModals(props: ApplicationDetailsExtend
</div>
<div className="flex gap-3 pt-2">
<Button variant="outline" className="flex-1 h-12 rounded-xl font-bold text-slate-600 border-slate-200" onClick={() => setShowFirmTypeModal(false)} disabled={updatingFirmType} data-testid="onboarding-firm-type-cancel">Cancel</Button>
<Button className="flex-1 h-12 rounded-xl font-bold bg-amber-600 hover:bg-amber-700 text-white shadow-lg shadow-amber-200 transition-all active:scale-95" disabled={updatingFirmType || !tempFirmType} onClick={handleUpdateFirmType} data-testid="onboarding-firm-type-submit">{updatingFirmType ? <Loader2 className="w-5 h-5 animate-spin" /> : 'Update Type'}</Button>
<Button className="flex-1 h-12 rounded-xl font-bold bg-re-red hover:bg-re-red-hover text-white shadow-lg shadow-red-200 transition-all active:scale-95" disabled={updatingFirmType || !tempFirmType} onClick={handleUpdateFirmType} data-testid="onboarding-firm-type-submit">{updatingFirmType ? <Loader2 className="w-5 h-5 animate-spin" /> : 'Update Type'}</Button>
</div>
</div>
</DialogContent>

View File

@ -1,4 +1,4 @@
import { AlertCircle, CheckCircle, ClipboardList, Download, Eye, FileText, ShieldAlert, ShieldCheck, Upload } from 'lucide-react';
import { AlertCircle, CheckCircle, ClipboardList, Download, Eye, FileText, ShieldCheck, Upload } from 'lucide-react';
import { toast } from 'sonner';
import { onboardingService } from '@/services/onboarding.service';
import { cn, formatDateTime } from '@/components/ui/utils';
@ -8,13 +8,7 @@ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
interface Props {
application: any;
currentUser: any;
documents: any[];
fddAgencies: any[];
selectedAgencyId: string;
setSelectedAgencyId: (v: string) => void;
isAssigningAgency: boolean;
handleAssignAgency: () => void;
setPreviewDoc: (d: any) => void;
setShowPreviewModal: (v: boolean) => void;
setIsUploading: (v: boolean) => void;
@ -24,13 +18,7 @@ interface Props {
export function ApplicationDetailsFddAuditContent({
application,
currentUser,
documents,
fddAgencies,
selectedAgencyId,
setSelectedAgencyId,
isAssigningAgency,
handleAssignAgency,
setPreviewDoc,
setShowPreviewModal,
setIsUploading,
@ -82,47 +70,6 @@ export function ApplicationDetailsFddAuditContent({
The Financial Due Diligence process has not been initiated for this application yet.
</p>
</div>
{(currentUser?.role === 'DD Admin' || currentUser?.role === 'Super Admin') && (
<Card className="border-amber-100 bg-amber-50/30 overflow-hidden rounded-2xl" data-testid="onboarding-fdd-initiate-card">
<CardHeader className="pb-2">
<CardTitle className="text-xs font-black uppercase tracking-widest text-amber-800 flex items-center gap-2">
<ShieldAlert className="w-4 h-4" />
Initiate FDD Audit
</CardTitle>
</CardHeader>
<CardContent>
<div className="flex flex-col md:flex-row gap-4">
<div className="flex-1">
<label className="text-[10px] font-black uppercase tracking-widest text-slate-400 mb-1.5 block">Select FDD Agency <span className="text-red-500">*</span></label>
<select
className="w-full h-11 bg-white border border-slate-200 rounded-xl px-4 text-sm font-medium focus:ring-2 focus:ring-amber-500/20 focus:border-amber-500 outline-none transition-all shadow-sm"
value={selectedAgencyId}
onChange={(e) => setSelectedAgencyId(e.target.value)}
data-testid="onboarding-fdd-agency-select"
>
<option value="">Choose partner agency...</option>
{(fddAgencies || []).map((agency: any) => (
<option key={agency.id} value={agency.id} data-testid={`onboarding-fdd-agency-option-${agency.id}`}>
{agency.fullName || agency.name} ({agency.email})
</option>
))}
</select>
</div>
<div className="flex items-end">
<Button
className="bg-slate-900 text-white hover:bg-slate-800 font-black text-[10px] uppercase tracking-widest px-8 h-11 border-none shadow-lg shadow-slate-900/10 transition-all active:scale-[0.98]"
onClick={handleAssignAgency}
disabled={isAssigningAgency || !selectedAgencyId}
data-testid="onboarding-fdd-assign-button"
>
{isAssigningAgency ? 'Assigning...' : 'Assign & Start Audit'}
</Button>
</div>
</div>
</CardContent>
</Card>
)}
</div>
);
}
@ -132,8 +79,8 @@ export function ApplicationDetailsFddAuditContent({
{hasAssignment && (
<div className="flex items-center justify-between p-4 bg-slate-50 border border-slate-200 rounded-xl mb-6" data-testid="onboarding-fdd-assignment-banner">
<div className="flex items-center gap-3">
<div className="p-2 bg-amber-100 rounded-lg">
<ShieldCheck className="w-5 h-5 text-amber-600" />
<div className="p-2 bg-red-50 rounded-lg">
<ShieldCheck className="w-5 h-5 text-re-red" />
</div>
<div>
<h4 className="text-sm font-bold text-slate-900">FDD Assignment Active</h4>
@ -230,7 +177,7 @@ export function ApplicationDetailsFddAuditContent({
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4" data-testid="onboarding-fdd-support-docs-grid">
{(documents || []).filter(isFddSupportDoc).map((doc: any, index: number) => (
<div key={doc.id} className="group bg-white border border-slate-200 rounded-xl p-4 flex items-center justify-between hover:border-amber-400 transition-all hover:shadow-md" data-testid={`onboarding-fdd-support-doc-${index}`}>
<div key={doc.id} className="group bg-white border border-slate-200 rounded-xl p-4 flex items-center justify-between hover:border-red-300 transition-all hover:shadow-md" data-testid={`onboarding-fdd-support-doc-${index}`}>
<div className="flex items-center gap-3">
<div className="w-10 h-10 rounded-lg bg-slate-50 flex items-center justify-center"><FileText className="w-5 h-5 text-slate-400" /></div>
<div className="overflow-hidden">
@ -239,13 +186,13 @@ export function ApplicationDetailsFddAuditContent({
</div>
</div>
<div className="flex gap-1">
<Button variant="ghost" size="icon" className="h-8 w-8 text-slate-400 hover:text-amber-600 hover:bg-amber-50" onClick={() => {
<Button variant="ghost" size="icon" className="h-8 w-8 text-slate-400 hover:text-re-red hover:bg-red-50" onClick={() => {
const baseUrl = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:5000';
window.open(`${baseUrl}/${doc.filePath}`, '_blank');
}} data-testid={`onboarding-fdd-support-doc-download-${index}`}>
<Download className="w-4 h-4" />
</Button>
<Button variant="ghost" size="icon" className="h-8 w-8 text-slate-400 hover:text-amber-600 hover:bg-amber-50" onClick={() => { setPreviewDoc(doc); setShowPreviewModal(true); }} data-testid={`onboarding-fdd-support-doc-preview-${index}`}>
<Button variant="ghost" size="icon" className="h-8 w-8 text-slate-400 hover:text-re-red hover:bg-red-50" onClick={() => { setPreviewDoc(doc); setShowPreviewModal(true); }} data-testid={`onboarding-fdd-support-doc-preview-${index}`}>
<Eye className="w-4 h-4" />
</Button>
</div>

View File

@ -72,7 +72,7 @@ export function ApplicationDetailsHeader({
<div className="flex flex-wrap gap-2">
<Button
variant="outline"
className="relative hover:bg-amber-50 hover:border-amber-300 hover:text-amber-700 transition-all shadow-sm"
className="relative hover:bg-red-50 hover:border-red-300 hover:text-re-red-hover transition-all shadow-sm"
onClick={onOpenWorknotes}
data-testid="onboarding-details-view-work-notes"
>

View File

@ -16,6 +16,10 @@ import {
Zap,
} from 'lucide-react';
import { cn, formatDateTime } from '@/components/ui/utils';
import {
getRequestStatusBadgeSolidClass,
getStatusProgressBarClass,
} from '@/lib/statusProgressTheme';
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
@ -128,13 +132,8 @@ export function ApplicationDetailsSidebar(props: ApplicationDetailsSidebarProps)
</div>
<div>
<p className="text-slate-600">Current Status</p>
<Badge
className={cn(
"mt-1",
application.status === 'Onboarded' ? "bg-green-600 hover:bg-green-700 text-white" :
application.status === 'Rejected' ? "bg-red-600" :
"bg-amber-600"
)}
<Badge
className={cn('mt-1', getRequestStatusBadgeSolidClass(application.status))}
data-testid="onboarding-details-summary-status"
>
{application.status}
@ -152,7 +151,12 @@ export function ApplicationDetailsSidebar(props: ApplicationDetailsSidebarProps)
<div>
<p className="text-slate-600">Progress</p>
<div className="flex items-center gap-2 mt-2">
<Progress value={application.progress} className="flex-1" data-testid="onboarding-details-summary-progress-bar" />
<Progress
value={application.progress}
className="flex-1 bg-red-50"
indicatorClassName={getStatusProgressBarClass(application.status)}
data-testid="onboarding-details-summary-progress-bar"
/>
<span className="text-slate-900" data-testid="onboarding-details-summary-progress-text">{application.progress}%</span>
</div>
</div>
@ -172,10 +176,10 @@ export function ApplicationDetailsSidebar(props: ApplicationDetailsSidebarProps)
</CardHeader>
<CardContent className="space-y-3">
{permissions.isLoaLocked && (
<Alert variant="destructive" className="mb-4 bg-amber-50 border-amber-200 text-amber-800" data-testid="onboarding-details-loa-locked-alert">
<Lock className="w-4 h-4 text-amber-600" />
<AlertTitle className="text-amber-900 font-semibold">LOA approval locked</AlertTitle>
<AlertDescription className="text-amber-800">
<Alert variant="destructive" className="mb-4 bg-red-50 border-red-200 text-red-800" data-testid="onboarding-details-loa-locked-alert">
<Lock className="w-4 h-4 text-re-red" />
<AlertTitle className="text-red-900 font-semibold">LOA approval locked</AlertTitle>
<AlertDescription className="text-red-800">
<span className="font-medium">First Fill</span> (later-stage payment) must be verified by Finance
before LOA approval can proceed. This is separate from the initial Security Deposit before LOI Issued.
</AlertDescription>
@ -201,10 +205,10 @@ export function ApplicationDetailsSidebar(props: ApplicationDetailsSidebarProps)
)}
{permissions.isSecurityDetailsLocked && (
<Alert variant="destructive" className="mb-4 bg-amber-50 border-amber-200 text-amber-800" data-testid="onboarding-details-security-locked-alert">
<Lock className="w-4 h-4 text-amber-600" />
<AlertTitle className="text-amber-900 font-semibold">Security Deposit approval locked</AlertTitle>
<AlertDescription className="text-amber-800">
<Alert variant="destructive" className="mb-4 bg-red-50 border-red-200 text-red-800" data-testid="onboarding-details-security-locked-alert">
<Lock className="w-4 h-4 text-re-red" />
<AlertTitle className="text-red-900 font-semibold">Security Deposit approval locked</AlertTitle>
<AlertDescription className="text-red-800">
Finance must verify the <span className="font-medium">Security Deposit</span> before this stage can be approved.
You can still use <span className="font-medium">Reject</span> if needed.
</AlertDescription>
@ -234,10 +238,10 @@ export function ApplicationDetailsSidebar(props: ApplicationDetailsSidebarProps)
)}
{isAdmin && (application.status === 'Level 3 Approved' || application.status === 'FDD Verification') && (!application.fddAssignments || application.fddAssignments.length === 0) && (
<Alert className="mb-4 bg-amber-50 border-amber-200 text-amber-800" data-testid="onboarding-details-fdd-assignment-alert">
<AlertCircle className="w-4 h-4 text-amber-600" />
<AlertTitle className="text-amber-900 font-bold">FDD Assignment Required</AlertTitle>
<AlertDescription className="text-amber-800 font-medium">
<Alert className="mb-4 bg-red-50 border-red-200 text-red-800" data-testid="onboarding-details-fdd-assignment-alert">
<AlertCircle className="w-4 h-4 text-re-red" />
<AlertTitle className="text-red-900 font-bold">FDD Assignment Required</AlertTitle>
<AlertDescription className="text-red-800 font-medium">
This application is pending financial due diligence. Please assign an FDD Agency to proceed with the audit.
</AlertDescription>
</Alert>
@ -341,7 +345,7 @@ export function ApplicationDetailsSidebar(props: ApplicationDetailsSidebarProps)
</Select>
</div>
<Button
className="w-full bg-amber-600 hover:bg-amber-700 font-bold h-11"
className="w-full bg-re-red hover:bg-re-red-hover font-bold h-11"
onClick={handleAssignAgency}
disabled={isAssigningAgency || !selectedAgencyId}
data-testid="onboarding-details-assign-fdd-submit"
@ -460,7 +464,7 @@ export function ApplicationDetailsSidebar(props: ApplicationDetailsSidebarProps)
</Select>
</div>
<Button
className="w-full bg-amber-600 hover:bg-amber-700 font-bold h-11"
className="w-full bg-re-red hover:bg-re-red-hover font-bold h-11"
onClick={handleAddParticipant}
disabled={isAssigningParticipant}
data-testid="onboarding-details-assign-user-submit"

View File

@ -19,6 +19,12 @@ import {
User,
} from 'lucide-react';
import { cn, formatDateTime } from '@/components/ui/utils';
import {
getPercentProgressBadgeSolidClass,
getPercentProgressBarClass,
getStatusProgressBadgeSolidClass,
getStatusProgressBarClass,
} from '@/lib/statusProgressTheme';
import QuestionnaireResponseView from '../QuestionnaireResponseView';
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
import { Badge } from '@/components/ui/badge';
@ -149,12 +155,17 @@ export function ApplicationDetailsTabs(props: ApplicationDetailsTabsProps) {
<div>
<div className="flex items-center justify-between mb-4">
<h3 className="text-slate-900">Application Journey</h3>
<Badge className="bg-amber-600" data-testid="onboarding-progress-percentage-badge">{application.progress}% Complete</Badge>
<Badge className={getStatusProgressBadgeSolidClass(application.status)} data-testid="onboarding-progress-percentage-badge">{application.progress}% Complete</Badge>
</div>
<Progress value={application.progress} className="h-3 mb-6" data-testid="onboarding-progress-bar" />
<Progress
value={application.progress}
className="h-3 mb-6 bg-status-progress-soft"
indicatorClassName={getStatusProgressBarClass(application.status)}
data-testid="onboarding-progress-bar"
/>
</div>
<div className="relative" data-testid="onboarding-progress-stages-container">
<div className="relative status-progress-ui" data-testid="onboarding-progress-stages-container">
{(() => {
const interviewRoleMap: Record<number, string[]> = {
1: ['DD-ZM', 'RBM'],
@ -236,7 +247,7 @@ export function ApplicationDetailsTabs(props: ApplicationDetailsTabsProps) {
<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"
approver.status === 'approved' ? "bg-green-500" : approver.status === 'rejected' ? "bg-red-500" : "bg-status-progress"
)} data-testid={`onboarding-stage-approver-status-dot-${stageIndex}-${i}`} />
<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">
@ -255,7 +266,7 @@ export function ApplicationDetailsTabs(props: ApplicationDetailsTabsProps) {
<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 text-white shadow-md'
: stage.status === 'active'
? stage.isLocked ? 'bg-slate-400 border-slate-400 text-white' : 'bg-amber-500 border-amber-500 text-white animate-pulse-subtle'
? stage.isLocked ? 'bg-slate-400 border-slate-400 text-white' : 'bg-status-workflow-active border-status-workflow-active text-white animate-pulse-subtle'
: 'bg-white border-slate-300 text-slate-400 shadow-none'
}`} data-testid={`onboarding-progress-stage-icon-${index}`}>
{stage.isParallel ? (
@ -265,7 +276,7 @@ export function ApplicationDetailsTabs(props: ApplicationDetailsTabsProps) {
<Lock className="w-5 h-5 text-white cursor-help" />
<div className="absolute bottom-full left-1/2 -translate-x-1/2 mb-2 px-3 py-1.5 bg-slate-900 text-white text-[10px] rounded shadow-xl opacity-0 group-hover:opacity-100 pointer-events-none transition-all duration-200 whitespace-nowrap z-[100] border border-slate-700">
<div className="flex flex-col gap-1">
<span className="font-bold text-amber-400 flex items-center gap-1">
<span className="font-bold text-status-progress flex items-center gap-1">
<AlertCircle className="w-3 h-3" /> Stage Locked
</span>
<span>{stage.lockMessage}</span>
@ -293,7 +304,7 @@ export function ApplicationDetailsTabs(props: ApplicationDetailsTabsProps) {
<div className="flex-1 pt-1">
<p className={cn(
"font-bold transition-colors",
stage.status === 'completed' ? "text-green-700" : stage.status === 'active' ? "text-amber-700" : "text-slate-900"
stage.status === 'completed' ? "text-green-700" : stage.status === 'active' ? "text-status-progress-muted" : "text-slate-900"
)} data-testid={`onboarding-progress-stage-name-${index}`}>{stage.name}</p>
{stage.description && (
<p className="text-slate-600 text-sm mt-0.5 leading-relaxed" data-testid={`onboarding-progress-stage-desc-${index}`}>{stage.description}</p>
@ -302,7 +313,7 @@ export function ApplicationDetailsTabs(props: ApplicationDetailsTabsProps) {
{renderApprovers(stage.name as string, index)}
{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" data-testid={`onboarding-progress-stage-evaluators-${index}`}>
<p className="text-status-progress text-xs mt-1.5 flex items-center gap-1 bg-status-progress-soft w-fit px-2 py-0.5 rounded border border-status-progress" data-testid={`onboarding-progress-stage-evaluators-${index}`}>
<User className="w-3 h-3" />
Evaluators: {stage.evaluators.join(' + ')}
</p>
@ -336,8 +347,8 @@ export function ApplicationDetailsTabs(props: ApplicationDetailsTabsProps) {
if (expectedCount && actualCount < expectedCount && application.status !== 'Rejected' && isEligibleForWarning) {
return (
<div className="mt-2" data-testid={`onboarding-progress-stage-warning-${index}`}>
<Alert variant="destructive" className="py-2 px-3 border-amber-200 bg-amber-50 text-amber-800">
<AlertCircle className="h-4 w-4 text-amber-600" />
<Alert variant="destructive" className="py-2 px-3 border-red-200 bg-red-50 text-red-800">
<AlertCircle className="h-4 w-4 text-re-red" />
<AlertTitle className="text-xs font-semibold">Missing Evaluators</AlertTitle>
<AlertDescription className="text-xs">
{actualCount === 0
@ -347,7 +358,7 @@ export function ApplicationDetailsTabs(props: ApplicationDetailsTabsProps) {
<Button
variant="link"
size="sm"
className="h-auto p-0 ml-1 text-xs text-amber-700 underline"
className="h-auto p-0 ml-1 text-xs text-re-red-hover underline"
onClick={handleRetriggerEvaluators}
data-testid={`onboarding-progress-stage-retrigger-${index}`}
>
@ -460,7 +471,7 @@ export function ApplicationDetailsTabs(props: ApplicationDetailsTabsProps) {
<div className={`w-8 h-8 rounded-full flex items-center justify-center border-2 ${isDone
? `${branchColor === 'blue' ? 'bg-blue-500 border-blue-500' : 'bg-green-500 border-green-500'}`
: branchStage.status === 'active'
? 'bg-amber-500 border-amber-500 text-white shadow-sm'
? 'bg-status-workflow-active border-status-workflow-active text-white shadow-sm'
: 'bg-white border-slate-300 text-slate-400'
}`} data-testid={`onboarding-progress-branch-stage-icon-${branchKey}-${bsIdx}`}>
{isDone ? (
@ -531,7 +542,7 @@ export function ApplicationDetailsTabs(props: ApplicationDetailsTabsProps) {
<TabsContent value="documents" className="space-y-4" data-testid="onboarding-tab-content-documents">
<div className="flex items-center justify-between">
<h3 className="text-slate-900">Uploaded Documents</h3>
<Button size="sm" className="bg-amber-600 hover:bg-amber-700" data-testid="onboarding-documents-upload-tab-button" onClick={() => {
<Button size="sm" className="bg-re-red hover:bg-re-red-hover" data-testid="onboarding-documents-upload-tab-button" onClick={() => {
setSelectedStage(null);
setShowDocumentsModal(true);
setShowUploadForm(true);
@ -763,12 +774,17 @@ export function ApplicationDetailsTabs(props: ApplicationDetailsTabsProps) {
{renderFddAuditContent()}
</TabsContent>
<TabsContent value="eor" className="space-y-4" data-testid="onboarding-tab-content-eor">
<TabsContent value="eor" className="space-y-4 status-progress-ui" data-testid="onboarding-tab-content-eor">
<div className="flex items-center justify-between mb-4">
<h3 className="text-slate-900">Essential Operating Requirements</h3>
<Badge className="bg-amber-600" data-testid="onboarding-eor-progress-badge">{Math.round(eorProgress)}% Complete</Badge>
<Badge className={getPercentProgressBadgeSolidClass(eorProgress)} data-testid="onboarding-eor-progress-badge">{Math.round(eorProgress)}% Complete</Badge>
</div>
<Progress value={eorProgress} className="h-3 mb-6" data-testid="onboarding-eor-progress-bar" />
<Progress
value={eorProgress}
className="h-3 mb-6 bg-status-progress-soft"
indicatorClassName={getPercentProgressBarClass(eorProgress)}
data-testid="onboarding-eor-progress-bar"
/>
<div className="space-y-3" data-testid="onboarding-eor-checklist">
{(eorData?.items || eorChecklist).map((item: any, idx: number) => {
@ -803,7 +819,7 @@ export function ApplicationDetailsTabs(props: ApplicationDetailsTabsProps) {
{docType}
</span>
{hasDocument && !item.isCompliant && (
<Badge variant="outline" className="text-[10px] h-4 px-1.5 bg-amber-50 text-amber-600 border-amber-200 uppercase tracking-wider font-bold">
<Badge variant="outline" className="text-[10px] h-4 px-1.5 bg-red-50 text-re-red border-red-200 uppercase tracking-wider font-bold">
Needs Verification
</Badge>
)}
@ -864,7 +880,7 @@ export function ApplicationDetailsTabs(props: ApplicationDetailsTabsProps) {
)}
{!hasDocument && (
<div className="p-2 text-slate-300 group-hover:text-amber-500 transition-colors" data-testid={`onboarding-eor-upload-hint-${idx}`}>
<div className="p-2 text-slate-300 group-hover:text-re-red transition-colors" data-testid={`onboarding-eor-upload-hint-${idx}`}>
<Upload className="w-4 h-4" />
</div>
)}
@ -929,12 +945,12 @@ export function ApplicationDetailsTabs(props: ApplicationDetailsTabsProps) {
<Card className={cn(
"border-l-4",
deposit?.status === 'Verified' ? "border-l-green-500" :
deposit?.status === 'Rejected' ? "border-l-red-500" : "border-l-amber-500"
deposit?.status === 'Rejected' ? "border-l-red-500" : "border-l-re-red"
)} data-testid="onboarding-payment-card-security">
<CardContent className="pt-6">
<div className="flex items-center justify-between mb-4">
<div className="flex items-center gap-2">
<div className="w-8 h-8 rounded bg-amber-50 flex items-center justify-center text-amber-600">
<div className="w-8 h-8 rounded bg-red-50 flex items-center justify-center text-re-red">
<ClipboardList className="w-4 h-4" />
</div>
<span className="font-semibold text-slate-700">Security Deposit</span>
@ -942,7 +958,7 @@ export function ApplicationDetailsTabs(props: ApplicationDetailsTabsProps) {
<Badge className={cn(
deposit?.status === 'Verified' ? "bg-green-100 text-green-700 hover:bg-green-100" :
deposit?.status === 'Rejected' ? "bg-red-100 text-red-700 hover:bg-red-100" :
"bg-amber-100 text-amber-700 hover:bg-amber-100"
"bg-red-50 text-re-red-hover hover:bg-red-50"
)} data-testid="onboarding-payment-status-security">
{deposit?.status || 'Awaiting'}
</Badge>
@ -983,7 +999,7 @@ export function ApplicationDetailsTabs(props: ApplicationDetailsTabsProps) {
<Button
variant="ghost"
size="sm"
className="h-6 px-2 text-[10px] text-amber-600 hover:text-amber-700 hover:bg-amber-50"
className="h-6 px-2 text-[10px] text-re-red hover:text-re-red-hover hover:bg-red-50"
data-testid={`onboarding-payment-doc-view-security-${idx}`}
onClick={() => { setPreviewDoc(doc); setShowPreviewModal(true); }}
>
@ -1011,7 +1027,7 @@ export function ApplicationDetailsTabs(props: ApplicationDetailsTabsProps) {
<Card className={cn(
"border-l-4",
deposit?.status === 'Verified' ? "border-l-green-500" :
deposit?.status === 'Rejected' ? "border-l-red-500" : "border-l-amber-500"
deposit?.status === 'Rejected' ? "border-l-red-500" : "border-l-re-red"
)} data-testid="onboarding-payment-card-first-fill">
<CardContent className="pt-6">
<div className="flex items-center justify-between mb-4">
@ -1024,7 +1040,7 @@ export function ApplicationDetailsTabs(props: ApplicationDetailsTabsProps) {
<Badge className={cn(
deposit?.status === 'Verified' ? "bg-green-100 text-green-700 hover:bg-green-100" :
deposit?.status === 'Rejected' ? "bg-red-100 text-red-700 hover:bg-red-100" :
"bg-amber-100 text-amber-700 hover:bg-amber-100"
"bg-red-50 text-re-red-hover hover:bg-red-50"
)} data-testid="onboarding-payment-status-first-fill">
{deposit?.status || 'Awaiting'}
</Badge>
@ -1091,7 +1107,7 @@ export function ApplicationDetailsTabs(props: ApplicationDetailsTabsProps) {
<div className="space-y-2.5 p-3 pr-4" data-testid="onboarding-audit-logs-container">
{auditLoading ? (
<div className="flex items-center justify-center py-10" data-testid="onboarding-audit-loading">
<div className="animate-spin rounded-full h-6 w-6 border-b-2 border-amber-600" />
<div className="animate-spin rounded-full h-6 w-6 border-b-2 border-re-red" />
<span className="ml-2 text-sm text-slate-500">Loading audit trail</span>
</div>
) : auditLogs.length === 0 ? (

View File

@ -171,6 +171,6 @@ export function auditLogActionBadgeClass(action: string): string {
if (a === 'CREATED' || a.includes('APPROV') || a.includes('COMPLETE')) return 'border-emerald-200 bg-emerald-50/90 text-emerald-900';
if (a.includes('DOCUMENT') || a.includes('UPLOAD') || a.includes('ATTACHMENT')) return 'border-sky-200 bg-sky-50/80 text-sky-900';
if (a.includes('PAYMENT') || a.includes('SECURITY') || a.includes('DEPOSIT')) return 'border-violet-200 bg-violet-50/80 text-violet-900';
if (a.includes('FDD') || a.includes('QUESTIONNAIRE') || a.includes('INTERVIEW')) return 'border-amber-200 bg-amber-50/80 text-amber-900';
if (a.includes('FDD') || a.includes('QUESTIONNAIRE') || a.includes('INTERVIEW')) return 'border-red-200 bg-red-50/80 text-red-900';
return 'border-slate-200 bg-slate-50 text-slate-700';
}

View File

@ -40,7 +40,7 @@ import {
TableHeader,
TableRow,
} from '@/components/ui/table';
import { Progress } from '@/components/ui/progress';
import { ApplicationProgressBar } from '@/features/onboarding/components/ApplicationProgressBar';
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog';
import { Label } from '@/components/ui/label';
import { Textarea } from '@/components/ui/textarea';
@ -215,7 +215,7 @@ export function AllApplicationsPage({ onViewDetails, initialFilter = 'all' }: Al
'In Review': 'bg-slate-100 text-slate-800',
'Level 3 Approved': 'bg-green-100 text-green-800',
'FDD Verification': 'bg-indigo-100 text-indigo-800',
'Payment Pending': 'bg-amber-100 text-amber-800',
'Payment Pending': 'bg-red-50 text-red-800',
'LOI In Progress': 'bg-sky-100 text-sky-800',
'LOI Issued': 'bg-sky-100 text-sky-800',
'Dealer Code Generation': 'bg-purple-100 text-purple-800',
@ -236,19 +236,19 @@ export function AllApplicationsPage({ onViewDetails, initialFilter = 'all' }: Al
'EOR In Progress': 'bg-violet-100 text-violet-800',
'EOR Complete': 'bg-violet-100 text-violet-800',
'LOA Pending': 'bg-pink-100 text-pink-800',
'Inauguration': 'bg-amber-100 text-amber-800',
'Inauguration': 'bg-red-50 text-red-800',
'Approved': 'bg-green-100 text-green-800',
'Rejected': 'bg-red-100 text-red-800',
'Disqualified': 'bg-gray-100 text-gray-800',
'Onboarded': 'bg-emerald-100 text-emerald-800',
'LOI Approved': 'bg-sky-100 text-sky-800',
'Security Deposit In Progress': 'bg-amber-100 text-amber-800',
'Security Deposit In Progress': 'bg-red-50 text-red-800',
'Security Deposit Approved': 'bg-green-100 text-green-800',
'Security Deposit': 'bg-amber-100 text-amber-800',
'Security Deposit': 'bg-red-50 text-red-800',
/** Legacy overallStatus until DB migrated */
'Security Details In Progress': 'bg-amber-100 text-amber-800',
'Security Details In Progress': 'bg-red-50 text-red-800',
'Security Details Approved': 'bg-green-100 text-green-800',
'Security Details': 'bg-amber-100 text-amber-800',
'Security Details': 'bg-red-50 text-red-800',
'LOA Issued': 'bg-pink-100 text-pink-800',
};
return colors[status] || 'bg-gray-100 text-gray-800';
@ -257,12 +257,12 @@ export function AllApplicationsPage({ onViewDetails, initialFilter = 'all' }: Al
return (
<div className="space-y-6">
{/* Info Banner */}
<div className="bg-amber-50 border border-amber-200 rounded-lg p-4" data-testid="onboarding-all-apps-banner">
<div className="bg-red-50 border border-red-200 rounded-lg p-4" data-testid="onboarding-all-apps-banner">
<div className="flex items-start gap-3">
<AlertCircle className="w-5 h-5 text-amber-600 flex-shrink-0 mt-0.5" />
<AlertCircle className="w-5 h-5 text-re-red flex-shrink-0 mt-0.5" />
<div>
<h3 className="text-amber-900 mb-1">DD Workflow - Initial Application Review</h3>
<p className="text-amber-800">
<h3 className="text-red-900 mb-1">DD Workflow - Initial Application Review</h3>
<p className="text-red-800">
This page shows <strong>only applications that haven't been shortlisted yet</strong>. Review and select promising candidates using the <strong>Shortlist</strong> button.
Once shortlisted, applications will be removed from here and moved to the <strong>Dealership Requests</strong> page for further processing.
</p>
@ -331,7 +331,7 @@ export function AllApplicationsPage({ onViewDetails, initialFilter = 'all' }: Al
variant={viewMode === 'grid' ? 'default' : 'outline'}
size="sm"
onClick={() => setViewMode('grid')}
className={viewMode === 'grid' ? 'bg-amber-600 hover:bg-amber-700' : ''}
className={viewMode === 'grid' ? 'bg-re-red hover:bg-re-red-hover' : ''}
data-testid="onboarding-all-apps-grid-view-btn"
>
<Grid3x3 className="w-4 h-4 mr-2" />
@ -341,7 +341,7 @@ export function AllApplicationsPage({ onViewDetails, initialFilter = 'all' }: Al
variant={viewMode === 'table' ? 'default' : 'outline'}
size="sm"
onClick={() => setViewMode('table')}
className={viewMode === 'table' ? 'bg-amber-600 hover:bg-amber-700' : ''}
className={viewMode === 'table' ? 'bg-re-red hover:bg-re-red-hover' : ''}
data-testid="onboarding-all-apps-table-view-btn"
>
<List className="w-4 h-4 mr-2" />
@ -380,7 +380,7 @@ export function AllApplicationsPage({ onViewDetails, initialFilter = 'all' }: Al
{/* Applications Grid/Table */}
{loading ? (
<div className="flex justify-center items-center h-96 bg-white rounded-lg border border-slate-200">
<Loader2 className="w-8 h-8 animate-spin text-amber-600" />
<Loader2 className="w-8 h-8 animate-spin text-re-red" />
</div>
) : viewMode === 'grid' ? (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4" data-testid="onboarding-all-apps-grid-container">
@ -465,8 +465,12 @@ export function AllApplicationsPage({ onViewDetails, initialFilter = 'all' }: Al
</TableCell>
<TableCell>
<div className="flex items-center gap-2">
<Progress value={app.progress} className="w-20" data-testid={`onboarding-all-apps-progress-bar-${idx}`} />
<span className="text-slate-600" data-testid={`onboarding-all-apps-progress-text-${idx}`}>{app.progress}%</span>
<ApplicationProgressBar
value={app.progress}
status={app.status}
showPercent
data-testid={`onboarding-all-apps-progress-bar-${idx}`}
/>
</div>
</TableCell>
<TableCell>

View File

@ -283,7 +283,6 @@ export const ApplicationDetails = () => {
maybeFetchUsersForModal,
handleScheduleInterview,
handleRescheduleInterview,
handleCancelInterview,
handleConfirmCancelInterview,
handleUpload,
handleApprove,
@ -368,7 +367,7 @@ export const ApplicationDetails = () => {
if (loading && !application) {
return (
<div className="flex items-center justify-center min-h-[60vh]">
<Loader2 className="w-10 h-10 animate-spin text-amber-600" />
<Loader2 className="w-10 h-10 animate-spin text-re-red" />
</div>
);
}
@ -405,13 +404,7 @@ export const ApplicationDetails = () => {
const renderFddAuditContent = () => (
<ApplicationDetailsFddAuditContent
application={application}
currentUser={currentUser}
documents={documents}
fddAgencies={fddAgencies}
selectedAgencyId={selectedAgencyId}
setSelectedAgencyId={setSelectedAgencyId}
isAssigningAgency={isAssigningAgency}
handleAssignAgency={handleAssignAgency}
setPreviewDoc={setPreviewDoc}
setShowPreviewModal={setShowPreviewModal}
setIsUploading={setIsUploading}
@ -469,7 +462,6 @@ export const ApplicationDetails = () => {
setShowDocumentsModal={setShowDocumentsModal}
setShowUploadForm={setShowUploadForm}
handleRetriggerEvaluators={handleRetriggerEvaluators}
handleCancelInterview={handleCancelInterview}
handleRescheduleInterview={handleRescheduleInterview}
setSelectedEvaluationForView={setSelectedEvaluationForView}
setShowFeedbackDetailsModal={setShowFeedbackDetailsModal}

View File

@ -30,7 +30,7 @@ import {
TableHeader,
TableRow,
} from '@/components/ui/table';
import { Progress } from '@/components/ui/progress';
import { ApplicationProgressBar } from '@/features/onboarding/components/ApplicationProgressBar';
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
import { Label } from '@/components/ui/label';
import { useSelector } from 'react-redux';
@ -58,7 +58,7 @@ export function ApplicationsPage({ onViewDetails, initialFilter }: ApplicationsP
const [selectedIds, setSelectedIds] = useState<string[]>([]);
const [isSendingReminders, setIsSendingReminders] = useState(false);
const [sortBy, setSortBy] = useState<'date'>('date');
const [sortBy] = useState<'date'>('date');
const [showNewApplicationModal, setShowNewApplicationModal] = useState(false);
const [showMyAssignments, setShowMyAssignments] = useState(false);
@ -203,7 +203,7 @@ export function ApplicationsPage({ onViewDetails, initialFilter }: ApplicationsP
'Questionnaire Pending': 'bg-orange-500',
'Questionnaire Completed': 'bg-blue-500',
'Shortlisted': 'bg-cyan-500',
'Level 1 Pending': 'bg-amber-500',
'Level 1 Pending': 'bg-red-500',
'Level 1 Approved': 'bg-green-500',
'Level 2 Pending': 'bg-purple-500',
'Level 2 Approved': 'bg-green-600',
@ -375,10 +375,12 @@ export function ApplicationsPage({ onViewDetails, initialFilter }: ApplicationsP
{app.residentialAddress}
</TableCell>
<TableCell>
<div className="flex items-center gap-2">
<Progress value={app.progress} className="h-2 w-20" data-testid={`onboarding-application-progress-bar-${idx}`} />
<span className="text-slate-600" data-testid={`onboarding-application-progress-text-${idx}`}>{app.progress}%</span>
</div>
<ApplicationProgressBar
value={app.progress}
status={app.status}
showPercent
data-testid={`onboarding-application-progress-bar-${idx}`}
/>
</TableCell>
<TableCell data-testid={`onboarding-application-date-${idx}`}>
{formatDateTime(app.submissionDate)}
@ -488,7 +490,7 @@ export function ApplicationsPage({ onViewDetails, initialFilter }: ApplicationsP
<Button variant="outline" onClick={() => setShowNewApplicationModal(false)} data-testid="onboarding-new-app-cancel">
Cancel
</Button>
<Button className="bg-amber-600 hover:bg-amber-700" data-testid="onboarding-new-app-submit">
<Button className="bg-re-red hover:bg-re-red-hover" data-testid="onboarding-new-app-submit">
Create Application
</Button>
</div>

View File

@ -408,20 +408,20 @@ export function FDDApplicationDetails() {
{/* SECTION 2: MY SUBMISSIONS */}
<div data-testid="onboarding-fdd-details-my-submissions">
<p className="text-[10px] font-bold text-slate-400 uppercase tracking-wider mb-2 flex items-center gap-2">
<div className="w-1.5 h-1.5 rounded-full bg-amber-500"></div>
<div className="w-1.5 h-1.5 rounded-full bg-red-500"></div>
My Uploaded Reports
</p>
<div className="space-y-2">
{application.uploadedDocuments?.filter((d: any) => d.uploader?.roleCode === 'FDD').map((doc: any, i: number) => (
<div key={i} className="p-3 border border-amber-100 bg-amber-50/30 rounded flex items-center justify-between hover:bg-amber-50 transition-all group" data-testid={`onboarding-fdd-details-my-report-row-${i}`}>
<div key={i} className="p-3 border border-red-100 bg-red-50/30 rounded flex items-center justify-between hover:bg-red-50 transition-all group" data-testid={`onboarding-fdd-details-my-report-row-${i}`}>
<div className="flex items-center gap-3">
<div className="w-8 h-8 rounded bg-amber-100 flex items-center justify-center text-amber-500">
<div className="w-8 h-8 rounded bg-red-50 flex items-center justify-center text-re-red">
<FileText className="w-4 h-4" />
</div>
<div>
<div className="flex items-center gap-2">
<p className="text-xs font-bold text-slate-900" data-testid={`onboarding-fdd-details-my-report-name-${i}`}>{doc.originalName || doc.fileName}</p>
<span className="text-[8px] bg-amber-500 text-white px-1 py-0.5 rounded uppercase font-bold tracking-tighter">YOUR AUDIT REPORT</span>
<span className="text-[8px] bg-red-500 text-white px-1 py-0.5 rounded uppercase font-bold tracking-tighter">YOUR AUDIT REPORT</span>
</div>
<p className="text-[10px] text-slate-400 font-medium" data-testid={`onboarding-fdd-details-my-report-meta-${i}`}>
{doc.documentType} {formatDateTime(doc.createdAt)}
@ -433,7 +433,7 @@ export function FDDApplicationDetails() {
<button
type="button"
onClick={() => handlePreview(doc)}
className="p-1.5 hover:bg-white rounded text-slate-400 hover:text-amber-600 transition-all"
className="p-1.5 hover:bg-white rounded text-slate-400 hover:text-re-red transition-all"
data-testid={`onboarding-fdd-details-my-report-preview-${i}`}
>
<Eye className="w-4 h-4" />
@ -576,22 +576,22 @@ export function FDDApplicationDetails() {
<Dialog open={showFinalizeModal} onOpenChange={setShowFinalizeModal}>
<DialogContent className="max-w-md p-0 overflow-hidden border-none shadow-2xl" data-testid="onboarding-fdd-details-finalize-modal">
<div className="bg-slate-950 p-6 flex items-center justify-center relative overflow-hidden">
<div className="absolute inset-0 bg-gradient-to-br from-amber-600/20 to-transparent" />
<div className="w-16 h-16 bg-amber-600/20 rounded-full flex items-center justify-center animate-pulse relative z-10">
<ShieldCheck className="w-8 h-8 text-amber-500" />
<div className="absolute inset-0 bg-gradient-to-br from-re-red/20 to-transparent" />
<div className="w-16 h-16 bg-re-red/20 rounded-full flex items-center justify-center animate-pulse relative z-10">
<ShieldCheck className="w-8 h-8 text-re-red" />
</div>
</div>
<div className="p-8 space-y-4">
<DialogHeader>
<DialogTitle className="text-2xl font-bold text-slate-900 text-center" data-testid="onboarding-fdd-details-finalize-title">Submit Audit Report</DialogTitle>
<DialogDescription className="text-slate-500 text-center pt-2 leading-relaxed text-base" data-testid="onboarding-fdd-details-finalize-desc">
You are about to submit your final findings. This action will <span className="font-bold text-slate-800 underline decoration-amber-500 decoration-2">notify the Admin</span> for review and approval.
You are about to submit your final findings. This action will <span className="font-bold text-slate-800 underline decoration-re-red decoration-2">notify the Admin</span> for review and approval.
</DialogDescription>
</DialogHeader>
<div className="bg-amber-50 p-4 rounded-xl flex gap-3 border border-amber-100 italic" data-testid="onboarding-fdd-details-finalize-info">
<Info className="w-5 h-5 text-amber-600 shrink-0 mt-0.5" />
<p className="text-xs text-amber-800 leading-normal">
<div className="bg-red-50 p-4 rounded-xl flex gap-3 border border-red-100 italic" data-testid="onboarding-fdd-details-finalize-info">
<Info className="w-5 h-5 text-re-red shrink-0 mt-0.5" />
<p className="text-xs text-red-800 leading-normal">
Once submitted, you cannot edit the findings. Ensure all documents are uploaded.
</p>
</div>
@ -602,7 +602,7 @@ export function FDDApplicationDetails() {
<Label className="text-[10px] font-black uppercase tracking-widest text-slate-400">Detailed Audit Findings & Remarks</Label>
<Textarea
placeholder="Enter detailed financial observations..."
className="min-h-[120px] bg-slate-50 border-slate-200 rounded-xl focus:ring-amber-500 text-sm resize-none"
className="min-h-[120px] bg-slate-50 border-slate-200 rounded-xl focus:ring-re-red text-sm resize-none"
value={fddAuditFindings}
onChange={(e) => setFddAuditFindings(e.target.value)}
data-testid="onboarding-fdd-details-finalize-remarks"
@ -621,7 +621,7 @@ export function FDDApplicationDetails() {
Cancel
</Button>
<Button
className="w-full sm:flex-1 h-12 rounded-xl font-bold bg-slate-950 hover:bg-slate-900 text-white shadow-lg shadow-slate-200 transition-all active:scale-95 border-b-2 border-amber-600"
className="w-full sm:flex-1 h-12 rounded-xl font-bold bg-slate-950 hover:bg-slate-900 text-white shadow-lg shadow-slate-200 transition-all active:scale-95 border-b-2 border-re-red"
data-testid="onboarding-fdd-details-finalize-confirm"
onClick={async () => {
try {

View File

@ -137,7 +137,7 @@ export function FinanceFddDetailPage({ applicationId, onBack }: FinanceFddDetail
if (loading) {
return (
<div className="flex items-center justify-center p-20">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-amber-600"></div>
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-re-red"></div>
</div>
);
}
@ -184,7 +184,7 @@ export function FinanceFddDetailPage({ applicationId, onBack }: FinanceFddDetail
<Badge variant="outline" className="bg-slate-50 text-slate-600 border-slate-200 py-1.5 px-3 rounded-full text-[10px] font-black uppercase tracking-widest" data-testid="onboarding-finance-fdd-app-id">
APP ID: {application.applicationId || application.id}
</Badge>
<Badge className="bg-amber-100 text-amber-700 hover:bg-amber-100 py-1.5 px-3 rounded-full text-[10px] font-black uppercase tracking-widest border border-amber-200" data-testid="onboarding-finance-fdd-status">
<Badge className="bg-red-50 text-re-red-hover hover:bg-red-50 py-1.5 px-3 rounded-full text-[10px] font-black uppercase tracking-widest border border-red-200" data-testid="onboarding-finance-fdd-status">
{application.status}
</Badge>
</div>
@ -293,7 +293,7 @@ export function FinanceFddDetailPage({ applicationId, onBack }: FinanceFddDetail
<div className="space-y-4" data-testid="onboarding-finance-fdd-reports-section">
<div className="flex items-center justify-between px-1">
<h3 className="text-lg font-black text-slate-900 flex items-center gap-2">
<ShieldCheck className="w-5 h-5 text-amber-600" /> Audit Findings & Reports
<ShieldCheck className="w-5 h-5 text-re-red" /> Audit Findings & Reports
</h3>
<Badge variant="outline" className="bg-white text-slate-500 font-bold border-slate-200" data-testid="onboarding-finance-fdd-reports-count-badge">
{assignments.length} Reports Found
@ -309,12 +309,12 @@ export function FinanceFddDetailPage({ applicationId, onBack }: FinanceFddDetail
) : (
<div className="space-y-6">
{assignments.map((assignment: any, idx: number) => (
<Card key={assignment.id} className="border-slate-200 shadow-sm overflow-hidden rounded-2xl group hover:border-amber-400 transition-all duration-300" data-testid={`onboarding-finance-fdd-assignment-card-${idx}`}>
<Card key={assignment.id} className="border-slate-200 shadow-sm overflow-hidden rounded-2xl group hover:border-red-300 transition-all duration-300" data-testid={`onboarding-finance-fdd-assignment-card-${idx}`}>
<div className="bg-white p-6 border-b border-slate-50">
<div className="flex items-center justify-between mb-6">
<div className="flex items-center gap-3">
<div className="w-12 h-12 bg-amber-50 rounded-2xl flex items-center justify-center border border-amber-100 shadow-inner group-hover:scale-105 transition-transform">
<ShieldCheck className="w-6 h-6 text-amber-600" />
<div className="w-12 h-12 bg-red-50 rounded-2xl flex items-center justify-center border border-red-100 shadow-inner group-hover:scale-105 transition-transform">
<ShieldCheck className="w-6 h-6 text-re-red" />
</div>
<div>
<h4 className="text-slate-900 font-black text-lg leading-none">FDD Audit Assignment</h4>
@ -323,7 +323,7 @@ export function FinanceFddDetailPage({ applicationId, onBack }: FinanceFddDetail
</div>
<Badge className={cn(
"py-1 px-3 rounded-full text-[10px] font-black uppercase tracking-widest",
assignment.status === 'completed' ? "bg-green-100 text-green-700" : "bg-amber-100 text-amber-700"
assignment.status === 'completed' ? "bg-green-100 text-green-700" : "bg-red-50 text-re-red-hover"
)} data-testid={`onboarding-finance-fdd-assignment-status-${idx}`}>
{assignment.status}
</Badge>
@ -344,12 +344,12 @@ export function FinanceFddDetailPage({ applicationId, onBack }: FinanceFddDetail
<div className={cn(
"inline-flex items-center gap-2 px-4 py-2 rounded-xl border text-xs font-black shadow-sm",
report.recommendation === 'Green' ? "bg-green-50 border-green-200 text-green-700" :
report.recommendation === 'Amber' ? "bg-amber-50 border-amber-200 text-amber-700" :
report.recommendation === 'Amber' ? "bg-red-50 border-red-200 text-re-red-hover" :
"bg-red-50 border-red-200 text-red-700"
)} data-testid={`onboarding-finance-fdd-report-signal-${idx}-${reportIdx}`}>
<div className={cn("w-2.5 h-2.5 rounded-full animate-pulse",
report.recommendation === 'Green' ? "bg-green-500" :
report.recommendation === 'Amber' ? "bg-amber-500" : "bg-red-500"
report.recommendation === 'Amber' ? "bg-red-500" : "bg-red-500"
)} />
{report.recommendation?.toUpperCase()} SIGNAL
</div>
@ -366,7 +366,7 @@ export function FinanceFddDetailPage({ applicationId, onBack }: FinanceFddDetail
<div className="space-y-5">
<Label className="text-[10px] text-slate-400 uppercase tracking-widest font-black mb-2 block">Available Documents</Label>
{report.reportDocument ? (
<div className="bg-white border-2 border-slate-100 rounded-2xl p-4 flex items-center justify-between hover:border-amber-400 transition-all hover:shadow-lg cursor-default" data-testid={`onboarding-finance-fdd-report-doc-${idx}-${reportIdx}`}>
<div className="bg-white border-2 border-slate-100 rounded-2xl p-4 flex items-center justify-between hover:border-red-300 transition-all hover:shadow-lg cursor-default" data-testid={`onboarding-finance-fdd-report-doc-${idx}-${reportIdx}`}>
<div className="flex items-center gap-4">
<div className="w-12 h-12 rounded-xl bg-red-50 flex items-center justify-center shadow-sm">
<FileText className="w-6 h-6 text-red-500" />
@ -380,7 +380,7 @@ export function FinanceFddDetailPage({ applicationId, onBack }: FinanceFddDetail
<Button
variant="ghost"
size="icon"
className="h-10 w-10 text-slate-400 hover:text-amber-600 hover:bg-amber-50 rounded-xl"
className="h-10 w-10 text-slate-400 hover:text-re-red hover:bg-red-50 rounded-xl"
onClick={() => window.open(`http://localhost:5000/${report.reportDocument.filePath}`, '_blank')}
data-testid={`onboarding-finance-fdd-report-download-${idx}-${reportIdx}`}
>
@ -389,7 +389,7 @@ export function FinanceFddDetailPage({ applicationId, onBack }: FinanceFddDetail
<Button
variant="ghost"
size="icon"
className="h-10 w-10 text-slate-400 hover:text-amber-600 hover:bg-amber-50 rounded-xl"
className="h-10 w-10 text-slate-400 hover:text-re-red hover:bg-red-50 rounded-xl"
onClick={() => {
setPreviewDoc(report.reportDocument);
setShowPreviewModal(true);
@ -413,9 +413,9 @@ export function FinanceFddDetailPage({ applicationId, onBack }: FinanceFddDetail
<span className="text-[10px] font-black text-green-700 uppercase tracking-widest">Audited & Verified</span>
</div>
) : (
<div className="flex items-center gap-2 bg-amber-50 border border-amber-100 px-4 py-2 rounded-full shadow-sm" data-testid={`onboarding-finance-fdd-report-pending-${idx}-${reportIdx}`}>
<Clock className="w-4 h-4 text-amber-600" />
<span className="text-[10px] font-black text-amber-700 uppercase tracking-widest">Pending Verification</span>
<div className="flex items-center gap-2 bg-red-50 border border-red-100 px-4 py-2 rounded-full shadow-sm" data-testid={`onboarding-finance-fdd-report-pending-${idx}-${reportIdx}`}>
<Clock className="w-4 h-4 text-re-red" />
<span className="text-[10px] font-black text-re-red-hover uppercase tracking-widest">Pending Verification</span>
</div>
)}
</div>
@ -437,7 +437,7 @@ export function FinanceFddDetailPage({ applicationId, onBack }: FinanceFddDetail
<Card className="border-slate-200 shadow-sm overflow-hidden rounded-2xl sticky top-6" data-testid="onboarding-finance-fdd-action-sidebar">
<CardHeader className="bg-slate-900 border-b border-slate-800 py-5">
<CardTitle className="text-white text-sm font-black uppercase tracking-widest flex items-center gap-2">
<CheckCircle className="w-4 h-4 text-amber-400" /> Finance Action
<CheckCircle className="w-4 h-4 text-re-red" /> Finance Action
</CardTitle>
</CardHeader>
<CardContent className="p-6 space-y-6">
@ -446,7 +446,7 @@ export function FinanceFddDetailPage({ applicationId, onBack }: FinanceFddDetail
<Textarea
id="remarks"
placeholder="Enter your assessment or audit sign-off remarks here..."
className="min-h-[150px] bg-slate-50 border-slate-200 rounded-xl focus:ring-amber-500 focus:border-amber-500 text-sm font-medium"
className="min-h-[150px] bg-slate-50 border-slate-200 rounded-xl focus:ring-re-red focus:border-re-red text-sm font-medium"
value={approvalRemark}
onChange={(e) => setApprovalRemark(e.target.value)}
disabled={hasMadeDecision || isSubmitting}
@ -458,7 +458,7 @@ export function FinanceFddDetailPage({ applicationId, onBack }: FinanceFddDetail
!isReadOnly ? (
<div className="space-y-3 pt-2">
<Button
className="w-full h-14 bg-amber-600 hover:bg-amber-700 text-white font-black uppercase tracking-widest rounded-xl shadow-lg shadow-amber-200/50 transition-all active:scale-95"
className="w-full h-14 bg-re-red hover:bg-re-red-hover text-white font-black uppercase tracking-widest rounded-xl shadow-lg shadow-red-200/50 transition-all active:scale-95"
onClick={() => handleDecision('Approved')}
disabled={isSubmitting}
data-testid="onboarding-finance-fdd-approve-btn"
@ -473,7 +473,7 @@ export function FinanceFddDetailPage({ applicationId, onBack }: FinanceFddDetail
</Button>
<Button
variant="outline"
className="w-full h-12 text-amber-600 border-amber-200 hover:bg-amber-50 font-black uppercase tracking-widest rounded-xl"
className="w-full h-12 text-re-red border-red-200 hover:bg-red-50 font-black uppercase tracking-widest rounded-xl"
onClick={() => toast.info('Clarification request functionality coming soon')}
disabled={isSubmitting}
data-testid="onboarding-finance-fdd-revision-btn"
@ -509,15 +509,15 @@ export function FinanceFddDetailPage({ applicationId, onBack }: FinanceFddDetail
<h5 className="text-[10px] text-slate-400 font-black uppercase tracking-widest mb-4">Verification Policy</h5>
<ul className="space-y-3">
<li className="flex items-start gap-3" data-testid="onboarding-finance-fdd-policy-1">
<div className="w-1.5 h-1.5 rounded-full bg-amber-500 mt-1.5 shrink-0" />
<div className="w-1.5 h-1.5 rounded-full bg-red-500 mt-1.5 shrink-0" />
<p className="text-[10px] text-slate-500 font-bold leading-tight">Must review PDF audit report for financial discrepancies before approval.</p>
</li>
<li className="flex items-start gap-3" data-testid="onboarding-finance-fdd-policy-2">
<div className="w-1.5 h-1.5 rounded-full bg-amber-500 mt-1.5 shrink-0" />
<div className="w-1.5 h-1.5 rounded-full bg-red-500 mt-1.5 shrink-0" />
<p className="text-[10px] text-slate-500 font-bold leading-tight">Approval triggers the progression to Security Deposit payment state.</p>
</li>
<li className="flex items-start gap-3" data-testid="onboarding-finance-fdd-policy-3">
<div className="w-1.5 h-1.5 rounded-full bg-amber-500 mt-1.5 shrink-0" />
<div className="w-1.5 h-1.5 rounded-full bg-red-500 mt-1.5 shrink-0" />
<p className="text-[10px] text-slate-500 font-bold leading-tight">Remarks are mandatory for audit trail and compliance tracking.</p>
</li>
</ul>

View File

@ -231,7 +231,7 @@ export function FinanceOnboardingPage({ onViewPaymentDetails }: FinanceOnboardin
className={
statusLabel === 'Verified' ? 'bg-emerald-50 text-emerald-700 border-emerald-100 px-3 py-1 rounded-full' :
statusLabel === 'Rejected' ? 'bg-rose-50 text-rose-700 border-rose-100 px-3 py-1 rounded-full' :
'bg-amber-50 text-amber-700 border-amber-100 px-3 py-1 rounded-full'
'bg-red-50 text-re-red-hover border-red-100 px-3 py-1 rounded-full'
}
variant="outline"
data-testid={`onboarding-finance-queue-status-${idx}`}

View File

@ -237,7 +237,7 @@ export function NonOpportunitiesPage({ onViewDetails }: NonOpportunitiesPageProp
</div>
<div className="bg-white rounded-lg border border-slate-200 p-4" data-testid="onboarding-non-opps-stat-exp">
<p className="text-slate-600">With Experience</p>
<p className="text-2xl text-amber-600 mt-1">
<p className="text-2xl text-re-red mt-1">
{paginationMeta?.stats?.withExperience || 0}
</p>
</div>
@ -355,7 +355,7 @@ export function NonOpportunitiesPage({ onViewDetails }: NonOpportunitiesPageProp
{selectedIds.length > 0 && (
<Button
className="bg-amber-600 hover:bg-amber-700 font-bold"
className="bg-re-red hover:bg-re-red-hover font-bold"
onClick={handleBulkConvert}
disabled={isBulkConverting}
>
@ -396,7 +396,7 @@ export function NonOpportunitiesPage({ onViewDetails }: NonOpportunitiesPageProp
{isGlobalLoading ? (
<TableRow>
<TableCell colSpan={11} className="text-center py-20">
<Loader2 className="w-8 h-8 mx-auto animate-spin text-amber-600 mb-2" />
<Loader2 className="w-8 h-8 mx-auto animate-spin text-re-red mb-2" />
<p className="text-slate-500 text-sm">Loading applications...</p>
</TableCell>
</TableRow>
@ -413,7 +413,7 @@ export function NonOpportunitiesPage({ onViewDetails }: NonOpportunitiesPageProp
<TableRow
key={lead.id}
data-testid={`onboarding-non-opps-row-${idx}`}
className={selectedIds.includes(lead.id) ? 'bg-amber-50/50' : ''}
className={selectedIds.includes(lead.id) ? 'bg-red-50/50' : ''}
>
<TableCell>
<Checkbox

View File

@ -48,7 +48,7 @@ import {
TableRow,
TableCell
} from '@/components/ui/table';
import { Progress } from '@/components/ui/progress';
import { ApplicationProgressBar } from '@/features/onboarding/components/ApplicationProgressBar';
import { Checkbox } from '@/components/ui/checkbox';
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog';
import { Label } from '@/components/ui/label';
@ -351,7 +351,7 @@ export function OpportunityRequestsPage({ onViewDetails }: OpportunityRequestsPa
'Level 2 Recommended': 'bg-teal-100 text-teal-800',
'Level 3 Interview Pending': 'bg-orange-100 text-orange-800',
'FDD Verification': 'bg-indigo-100 text-indigo-800',
'Payment Pending': 'bg-amber-100 text-amber-800',
'Payment Pending': 'bg-red-50 text-red-800',
'LOI Issued': 'bg-sky-100 text-sky-800',
'Dealer Code Generation': 'bg-purple-100 text-purple-800',
'Architecture Team Assigned': 'bg-blue-100 text-blue-800',
@ -401,7 +401,7 @@ export function OpportunityRequestsPage({ onViewDetails }: OpportunityRequestsPa
if (loading) {
return (
<div className="flex justify-center items-center h-96">
<Loader2 className="w-8 h-8 animate-spin text-amber-600" />
<Loader2 className="w-8 h-8 animate-spin text-re-red" />
</div>
);
}
@ -554,7 +554,7 @@ export function OpportunityRequestsPage({ onViewDetails }: OpportunityRequestsPa
variant={viewMode === 'grid' ? 'default' : 'outline'}
size="sm"
onClick={() => setViewMode('grid')}
className={viewMode === 'grid' ? 'bg-amber-600 hover:bg-amber-700' : ''}
className={viewMode === 'grid' ? 'bg-re-red hover:bg-re-red-hover' : ''}
data-testid="onboarding-opp-requests-view-grid-btn"
>
<Grid3x3 className="w-4 h-4 mr-2" />
@ -564,7 +564,7 @@ export function OpportunityRequestsPage({ onViewDetails }: OpportunityRequestsPa
variant={viewMode === 'table' ? 'default' : 'outline'}
size="sm"
onClick={() => setViewMode('table')}
className={viewMode === 'table' ? 'bg-amber-600 hover:bg-amber-700' : ''}
className={viewMode === 'table' ? 'bg-re-red hover:bg-re-red-hover' : ''}
data-testid="onboarding-opp-requests-view-table-btn"
>
<List className="w-4 h-4 mr-2" />
@ -706,10 +706,12 @@ export function OpportunityRequestsPage({ onViewDetails }: OpportunityRequestsPa
<Badge variant="outline" data-testid={`onboarding-opp-requests-shortlisted-badge-${idx}`}>No</Badge>
</TableCell>
<TableCell>
<div className="flex items-center gap-2" data-testid={`onboarding-opp-requests-progress-container-${idx}`}>
<Progress value={app.progress} className="w-20" />
<span className="text-slate-600">{app.progress}%</span>
</div>
<ApplicationProgressBar
value={app.progress}
status={app.status}
showPercent
data-testid={`onboarding-opp-requests-progress-${idx}`}
/>
</TableCell>
<TableCell>
<span className="text-slate-600" data-testid={`onboarding-opp-requests-date-${idx}`}>{formatDateTime(app.submissionDate)}</span>

View File

@ -211,7 +211,7 @@ export function WorkNotesPage(props: Partial<WorkNotesPageProps>) {
'bg-green-600',
'bg-blue-600',
'bg-purple-600',
'bg-amber-600',
'bg-re-red',
'bg-pink-600',
'bg-indigo-600',
'bg-teal-600',

View File

@ -14,18 +14,15 @@ import { toast } from 'sonner';
import { dealerService } from '@/services/dealer.service';
import { masterService } from '@/services/master.service';
import { formatDateTime } from '@/components/ui/utils';
import { getRequestStatusBadgeClass } from '@/lib/statusProgressTheme';
interface DealerRelocationPageProps {
currentUser: UserType | null;
onViewDetails?: (id: string) => void;
}
const getStatusColor = (status: string) => {
if (status === 'Completed') return 'bg-green-100 text-green-700 border-green-300';
if (status.includes('Review') || status.includes('Pending')) return 'bg-yellow-100 text-yellow-700 border-yellow-300';
if (status.includes('Rejected') || status.includes('Revoked')) return 'bg-red-100 text-red-700 border-red-300';
return 'bg-slate-100 text-slate-700 border-slate-300';
};
const getStatusColor = (status: string, currentStage?: string) =>
getRequestStatusBadgeClass(status, currentStage);
const getApiErrorMessage = (error: any, fallback: string) =>
error?.response?.data?.message || error?.data?.message || error?.message || fallback;
@ -203,7 +200,7 @@ export function DealerRelocationPage({ onViewDetails }: DealerRelocationPageProp
title: 'Pending',
value: requests.filter(r => r.status !== 'Completed' && r.status !== 'Rejected').length,
icon: Calendar,
color: 'bg-yellow-500',
color: 'bg-re-red',
},
{
title: 'Approved',

View File

@ -17,6 +17,14 @@ import { toast } from 'sonner';
import { API } from '@/api/API';
import { SlaBadge } from '@/components/sla/SlaBadge';
import { useSlaBatchStatus } from '@/hooks/useSlaBatchStatus';
import {
getCurrentStageBadgeClass,
getOffboardingRequestStatusBadgeClass,
getStatusLabelBadgeClass,
getStatusProgressBarClass,
isOffboardingTerminalNegative,
WORKFLOW_IN_PROGRESS_ACCENT,
} from '@/lib/offboardingDisplay';
interface RelocationRequestDetailsProps {
requestId: string;
@ -174,13 +182,12 @@ const requiredDocuments = [
'Water supply documents'
];
const getStatusColor = (status: string) => {
if (status === 'Completed' || status === 'Verified' || status === 'Closed') return 'bg-green-100 text-green-700 border-green-300';
if (status.includes('Review') || status.includes('Pending') || status === 'In Progress') return 'bg-yellow-100 text-yellow-700 border-yellow-300';
if (status.includes('Rejected') || status.includes('Revoked')) return 'bg-red-100 text-red-700 border-red-300';
if (status.includes('Collection') || status.includes('Completion') || status.includes('Infra')) return 'bg-blue-100 text-blue-700 border-blue-300';
return 'bg-slate-100 text-slate-700 border-slate-300';
};
const getStatusColor = (status: string) => getStatusLabelBadgeClass(status);
const getDocChecklistUploadButtonClass = (isRejected: boolean) =>
isRejected
? 'h-7 px-2 text-red-700 hover:bg-red-50 hover:text-red-800 flex-shrink-0'
: 'h-7 px-2 text-slate-700 hover:bg-slate-50 flex-shrink-0';
const getApiErrorMessage = (error: any, fallback: string) => {
const responseData = error?.response?.data || error?.data;
@ -224,6 +231,10 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
const [activeTab, setActiveTab] = useState('workflow');
const [isPreviewOpen, setIsPreviewOpen] = useState(false);
const [selectedDoc, setSelectedDoc] = useState<any>(null);
const [rejectDocDialogOpen, setRejectDocDialogOpen] = useState(false);
const [rejectDocId, setRejectDocId] = useState<string | null>(null);
const [rejectDocReason, setRejectDocReason] = useState('');
const [isRejectingDoc, setIsRejectingDoc] = useState(false);
useEffect(() => {
fetchRequestDetails();
@ -368,6 +379,15 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
? 100
: Math.min(100, Math.round((dbOrdinal / workflowStages.length) * 100));
const displayProgressPct = allWorkflowComplete ? 100 : timelineProgressPct;
const workflowTerminalNegative = request
? isOffboardingTerminalNegative(request.status, request.currentStage)
: false;
const statusProgressBarClass = request
? getStatusProgressBarClass(request.status, request.currentStage)
: 'bg-status-progress';
const requestStatusBadgeClass = request
? getOffboardingRequestStatusBadgeClass(request.status, request.currentStage)
: 'bg-re-red hover:bg-re-red-hover text-white border-transparent';
const missingRequiredDocs = request
? requiredDocuments.filter((doc) => !request.documents?.some((d: any) =>
@ -524,17 +544,31 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
}
};
const handleRejectDocument = async (documentId: string) => {
const submitRejectDocument = async () => {
if (!rejectDocId || !String(rejectDocReason).trim()) {
toast.error('Please enter a rejection reason.');
return;
}
try {
const response = await API.rejectRelocationDocument(requestId, documentId, { remarks: 'Rejected by reviewer' }) as any;
if (response.data.success) {
setIsRejectingDoc(true);
const response = await API.rejectRelocationDocument(requestId, rejectDocId, {
remarks: rejectDocReason.trim()
}) as any;
if (response.data?.success) {
toast.success('Document rejected successfully');
setRejectDocDialogOpen(false);
setRejectDocId(null);
setRejectDocReason('');
fetchRequestDetails(true);
fetchAuditLogs();
} else {
toast.error(response.data?.message || 'Failed to reject document');
}
} catch (error) {
console.error('Reject document error:', error);
toast.error(getApiErrorMessage(error, 'Failed to reject document'));
} finally {
setIsRejectingDoc(false);
}
};
@ -552,7 +586,7 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
if (isLoading) {
return (
<div className="flex flex-col items-center justify-center min-h-[400px] space-y-4">
<Loader2 className="w-10 h-10 text-amber-600 animate-spin" />
<Loader2 className="w-10 h-10 text-re-red animate-spin" />
<p className="text-slate-500 font-medium">Loading request details...</p>
</div>
);
@ -594,7 +628,7 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
<div className="flex items-center gap-3">
<Button
variant="outline"
className="relative hover:bg-amber-50 hover:border-amber-300 hover:text-amber-700 transition-all shadow-sm"
className="relative hover:bg-red-50 hover:border-red-300 hover:text-re-red-hover transition-all shadow-sm"
onClick={() => navigate(`/worknotes/relocation/${requestId}`, {
state: {
applicationName: request?.outlet?.name || 'Relocation',
@ -606,13 +640,13 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
<MessageSquare className="w-4 h-4 mr-2" />
View Work Notes
{request?.worknotes?.length > 0 && (
<Badge className="ml-2 bg-amber-600 hover:bg-amber-700 text-white h-5 px-2">
<Badge className="ml-2 bg-re-red hover:bg-re-red-hover text-white h-5 px-2">
{request.worknotes.length}
</Badge>
)}
</Button>
<Badge className={getStatusColor(request.status)}>
<Badge className={requestStatusBadgeClass}>
{request.status}
</Badge>
</div>
@ -642,7 +676,7 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
</div>
</div>
<div className="flex items-center gap-2">
<Navigation className="w-4 h-4 text-amber-600" />
<Navigation className="w-4 h-4 text-re-red" />
<div>
<p className="text-slate-600 text-xs">To (Proposed)</p>
<p className="text-slate-900 text-sm">{request.proposedLocation || `${request.newAddress}, ${request.newCity}`}</p>
@ -653,7 +687,7 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
Type: {request.relocationType}
</Badge>
{request.distance && (
<Badge variant="outline" className="border-amber-200 bg-amber-50 text-amber-700">
<Badge variant="outline" className="border-red-200 bg-red-50 text-re-red-hover">
Distance: {request.distance}
</Badge>
)}
@ -674,9 +708,12 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
<p className="text-slate-600 text-sm mb-1">Request Information</p>
<p className="text-slate-900 text-sm">Submitted: {formatDateTime(request.createdAt)}</p>
<p className="text-slate-600 text-sm">By: {request.dealer?.fullName}</p>
<p className="text-slate-900 text-sm mt-2">
Current Stage: {String(request.currentStage || '').replace(/_/g, ' ')}
</p>
<div className="mt-2 flex flex-wrap items-center gap-2">
<span className="text-slate-600 text-sm">Current Stage:</span>
<Badge className={getCurrentStageBadgeClass(request.currentStage, request.status)}>
{String(request.currentStage || '').replace(/_/g, ' ')}
</Badge>
</div>
</div>
</div>
@ -704,23 +741,32 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
<CardContent>
{/* Workflow Progress Tab */}
<TabsContent value="workflow" className="mt-0">
<TabsContent value="workflow" className="mt-0 status-progress-ui">
{/* Progress Bar */}
<div className="mb-8">
<div className="flex items-center justify-between mb-2">
<span className="text-slate-900">Overall Progress</span>
<span className="text-slate-600">{displayProgressPct}%</span>
<Badge className={`${statusProgressBarClass} text-white border-transparent hover:opacity-90`}>
{displayProgressPct}% Complete
</Badge>
</div>
<div className="h-3 bg-slate-200 rounded-full overflow-hidden">
<div
className="h-full bg-amber-600 transition-all duration-500"
className={`h-full transition-all duration-500 ${statusProgressBarClass}`}
style={{ width: `${displayProgressPct}%` }}
/>
</div>
</div>
{workflowProgressMismatch && (
<div className="mb-6 rounded-lg border border-amber-300 bg-amber-50 px-4 py-3 text-sm text-amber-900">
{workflowTerminalNegative && (
<div className="mb-6 rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-800">
This request is closed as <strong>{String(request.status)}</strong>. The approval path below is
for reference only.
</div>
)}
{workflowProgressMismatch && !workflowTerminalNegative && (
<div className="mb-6 rounded-lg border border-red-300 bg-red-50 px-4 py-3 text-sm text-red-900">
<span className="font-medium">Activity ahead of current stage:</span> Some timeline or audit
entries reference steps after the official current stage ({String(request.currentStage)}).
Per-step history below may include future steps; the highlighted step and approvals follow the
@ -737,7 +783,16 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
{/* Workflow stages + per-stage timeline (same pattern as ResignationDetails progress tab) */}
<div className="space-y-4">
{workflowStages.map((stage: any, index: number) => {
{workflowTerminalNegative ? (
<ul className="list-disc space-y-1 pl-5 text-sm text-slate-600">
{workflowStages.map((stage: any) => (
<li key={stage.id}>
<span className="text-slate-900">{stage.name}</span> {stage.role}
</li>
))}
</ul>
) : (
workflowStages.map((stage: any, index: number) => {
const isCompleted = allWorkflowComplete || index < dbOrdinal - 1;
const isCurrent = !allWorkflowComplete && index === dbOrdinal - 1;
const stageTimelineEntries = getRelocationTimelineEntriesForStage(
@ -758,7 +813,7 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
isCompleted
? 'bg-green-100 text-green-600'
: isCurrent
? 'bg-amber-100 text-amber-600'
? WORKFLOW_IN_PROGRESS_ACCENT.icon
: 'bg-slate-100 text-slate-400'
}`}
>
@ -779,9 +834,7 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
<div
className={`flex-1 pb-8 ${
isCurrent
? 'bg-amber-50 -ml-4 pl-4 pr-4 py-3 rounded-lg border border-amber-200'
: ''
isCurrent ? WORKFLOW_IN_PROGRESS_ACCENT.panel : ''
}`}
>
<div className="flex items-center justify-between mb-1 gap-2">
@ -791,7 +844,7 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
isCompleted
? 'text-green-700'
: isCurrent
? 'text-amber-900'
? WORKFLOW_IN_PROGRESS_ACCENT.title
: 'text-slate-900'
}
>
@ -799,7 +852,7 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
</h4>
<p
className={`text-sm ${
isCurrent ? 'text-amber-700' : 'text-slate-600'
isCurrent ? WORKFLOW_IN_PROGRESS_ACCENT.subtitle : 'text-slate-600'
}`}
>
Responsible: {stage.role}
@ -816,7 +869,7 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
isCompleted
? 'bg-green-100 text-green-700 border-green-300'
: isCurrent
? 'bg-amber-100 text-amber-700 border-amber-300'
? WORKFLOW_IN_PROGRESS_ACCENT.stageBadge
: 'bg-slate-100 text-slate-500 border-slate-300'
}
>
@ -862,7 +915,8 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
</div>
</div>
);
})}
})
)}
</div>
</TabsContent>
@ -893,7 +947,7 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
<DialogTrigger asChild>
<Button
size="sm"
className="bg-amber-600 hover:bg-amber-700"
className="bg-re-red hover:bg-re-red-hover"
onClick={() => {
setDocTypeLocked(false);
setSelectedDocType(requiredDocuments[0]);
@ -917,8 +971,8 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
{docTypeLocked ? (
<div>
<Label>Document</Label>
<div className="mt-1 flex items-center gap-2 bg-amber-50 border border-amber-200 rounded-md px-3 h-10">
<Badge className="bg-amber-600 text-white border-transparent">
<div className="mt-1 flex items-center gap-2 bg-red-50 border border-red-200 rounded-md px-3 h-10">
<Badge className="bg-re-red text-white border-transparent">
{selectedDocType}
</Badge>
</div>
@ -958,7 +1012,7 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
Cancel
</Button>
<Button
className="bg-amber-600 hover:bg-amber-700"
className="bg-re-red hover:bg-re-red-hover"
onClick={handleUploadDocument}
disabled={isUploading}
>
@ -982,27 +1036,40 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
const uploaded = request.documents?.find((d: any) =>
d.type === doc || d.name.toLowerCase().includes(doc.toLowerCase().split(' ')[0])
);
const isRejected = uploaded && String(uploaded.status) === 'Rejected';
const ok = uploaded && !isRejected;
return (
<div
key={index}
className={`flex items-center justify-between gap-2 p-2 rounded border text-sm ${uploaded ? 'bg-green-50 border-green-200' : 'bg-slate-50 border-slate-200'
}`}
className={`flex items-center justify-between gap-2 p-2 rounded border text-sm ${
isRejected
? 'bg-red-50 border-red-200'
: ok
? 'bg-green-50 border-green-200'
: 'bg-slate-50 border-slate-200'
}`}
>
<div className="flex items-center gap-2 min-w-0">
{uploaded ? (
{isRejected ? (
<AlertCircle className="w-4 h-4 text-red-600 flex-shrink-0" />
) : ok ? (
<CheckCircle2 className="w-4 h-4 text-green-600 flex-shrink-0" />
) : (
<AlertCircle className="w-4 h-4 text-slate-400 flex-shrink-0" />
)}
<span className={`truncate ${uploaded ? 'text-green-900' : 'text-slate-700'}`}>
<span
className={`truncate ${
isRejected ? 'text-red-900' : ok ? 'text-green-900' : 'text-slate-700'
}`}
>
{doc}
</span>
</div>
{!uploaded && (
{!ok && (
<Button
size="sm"
variant="ghost"
className="h-7 px-2 text-amber-700 hover:bg-amber-50 flex-shrink-0"
className={getDocChecklistUploadButtonClass(!!isRejected)}
onClick={() => {
setSelectedDocType(doc);
setSelectedFile(null);
@ -1011,7 +1078,7 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
}}
>
<Upload className="w-3.5 h-3.5 mr-1" />
Upload
{isRejected ? 'Re-upload' : 'Upload'}
</Button>
)}
</div>
@ -1040,7 +1107,10 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
</TableHeader>
<TableBody>
{request.documents.map((doc: any) => (
<TableRow key={doc.id}>
<TableRow
key={doc.id}
className={String(doc.status) === 'Rejected' ? 'bg-red-50/80' : undefined}
>
<TableCell className="text-slate-900">
{doc.name}
</TableCell>
@ -1101,7 +1171,11 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
size="sm"
variant="destructive"
className="h-8 gap-1"
onClick={() => handleRejectDocument(doc.id)}
onClick={() => {
setRejectDocId(doc.id);
setRejectDocReason('');
setRejectDocDialogOpen(true);
}}
title="Reject Document"
>
<AlertCircle className="w-4 h-4" />
@ -1148,7 +1222,7 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
<div className="bg-slate-50 border border-dashed border-slate-300 rounded-lg p-12 text-center">
{isEorLoading ? (
<div className="flex flex-col items-center gap-2">
<Loader2 className="w-8 h-8 text-amber-600 animate-spin" />
<Loader2 className="w-8 h-8 text-re-red animate-spin" />
<p className="text-slate-500">Fetching checklist...</p>
</div>
) : (
@ -1192,7 +1266,7 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
checked={item.isCompliant}
onChange={(e) => handleUpdateEorItem(item.description, e.target.checked, item.itemType)}
disabled={eorChecklist.status === 'Completed' || (currentUser?.role !== 'NBH' && currentUser?.role !== 'Super Admin')}
className="w-4 h-4 rounded border-slate-300 text-amber-600"
className="w-4 h-4 rounded border-slate-300 text-re-red"
/>
</TableCell>
<TableCell>
@ -1230,7 +1304,7 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
</Button>
</>
) : item.proofDocumentId ? (
<span className="text-xs text-amber-700">Proof linked (refresh if file details are missing)</span>
<span className="text-xs text-re-red-hover">Proof linked (refresh if file details are missing)</span>
) : (
<Button
type="button"
@ -1275,7 +1349,7 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
)}
{!eorChecklist.items?.every((i: any) => i.isCompliant) && (
<p className="text-right text-xs text-amber-600 italic">
<p className="text-right text-xs text-re-red italic">
All items must be marked as compliant before final submission.
</p>
)}
@ -1298,7 +1372,7 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
${(entry.description || entry.action || entry.details?.action || '').toLowerCase().includes('reject') || (entry.description || entry.action || entry.details?.action || '').toLowerCase().includes('revok')
? 'bg-red-100 text-red-700 border-red-200'
: (entry.description || entry.action || entry.details?.action || '').toLowerCase().includes('sent back') || (entry.description || entry.action || entry.details?.action || '').toLowerCase().includes('send back')
? 'bg-amber-100 text-amber-700 border-amber-200'
? 'bg-red-50 text-re-red-hover border-red-200'
: (entry.description || entry.action || entry.details?.action || '').toLowerCase().includes('approv') || (entry.description || entry.action || entry.details?.action || '').toLowerCase().includes('initi') || (entry.action || '').toLowerCase().includes('complete')
? 'bg-emerald-100 text-emerald-700 border-emerald-200'
: 'bg-slate-100 text-slate-700 border-slate-200'}
@ -1346,14 +1420,16 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
<CardContent className="space-y-4">
<div>
<p className="text-slate-600 text-sm">Current Stage</p>
<p className="text-slate-900">{request.currentStage}</p>
<Badge className={getCurrentStageBadgeClass(request.currentStage, request.status)}>
{request.currentStage}
</Badge>
</div>
<div>
<p className="text-slate-600 text-sm">Progress</p>
<div className="flex items-center gap-2 mt-2">
<div className="flex-1 h-2 bg-slate-200 rounded-full overflow-hidden">
<div
className="h-full bg-amber-600 transition-all duration-300"
className={`h-full transition-all duration-300 ${statusProgressBarClass}`}
style={{ width: `${displayProgressPct}%` }}
/>
</div>
@ -1388,9 +1464,9 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
Approve Request
</Button>
{!canApprove && (
<p className="text-xs text-amber-700">
<div className="rounded-md border border-red-200 bg-red-50 px-3 py-2 text-xs text-red-800">
Approval is blocked until mandatory documents are uploaded and verified for this stage.
</p>
</div>
)}
<Button
@ -1410,7 +1486,7 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
{canSendBack && (
<Button
variant="outline"
className="w-full border-amber-400 text-amber-900 hover:bg-amber-50"
className="w-full border-red-300 text-red-900 hover:bg-red-50"
onClick={() => handleAction('sendBack')}
disabled={isSubmitting}
>
@ -1460,34 +1536,6 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
</CardContent>
</Card>
{/* Assigned Evaluators Card */}
{request.participants && request.participants.length > 0 && (
<Card>
<CardHeader>
<CardTitle>Assigned Evaluators</CardTitle>
</CardHeader>
<CardContent className="space-y-3">
{request.participants.map((participant: any) => (
<div key={participant.id} className="flex items-center justify-between p-2 bg-slate-50 rounded-lg">
<div>
<p className="text-slate-900 text-sm font-medium">{participant.user?.fullName || 'Unknown'}</p>
<p className="text-slate-600 text-xs">{participant.user?.roleCode || 'User'}</p>
{participant.metadata?.stage && (
<Badge variant="outline" className="mt-1 text-xs">
{participant.metadata.stage.replace(/_/g, ' ')}
</Badge>
)}
</div>
{participant.metadata?.autoAssigned && (
<Badge className="bg-blue-100 text-blue-700 border-blue-300 text-xs">
Auto-assigned
</Badge>
)}
</div>
))}
</CardContent>
</Card>
)}
</div>
</div>
@ -1551,12 +1599,12 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
actionType === 'approve'
? 'bg-green-600 hover:bg-green-700'
: actionType === 'reject'
? 'bg-red-600 hover:bg-red-700'
? 'bg-re-red hover:bg-re-red-hover'
: actionType === 'sendBack'
? 'bg-amber-600 hover:bg-amber-700'
? 'bg-re-red hover:bg-re-red-hover'
: actionType === 'revoke'
? 'bg-red-700 hover:bg-red-800'
: 'bg-amber-600 hover:bg-amber-700'
? 'bg-re-red hover:bg-re-red-hover'
: 'bg-re-red hover:bg-re-red-hover'
}
disabled={isSubmitting}
>
@ -1582,6 +1630,40 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
{/* Worknotes Dialog */}
{/* Worknotes Dialog - handled in Header */}
<Dialog open={rejectDocDialogOpen} onOpenChange={setRejectDocDialogOpen}>
<DialogContent>
<DialogHeader>
<DialogTitle>Reject document</DialogTitle>
<DialogDescription>
Mark this upload as rejected and provide a reason. The action is recorded in the audit trail.
</DialogDescription>
</DialogHeader>
<div className="space-y-3">
<Label htmlFor="rejectDocReason">Rejection reason *</Label>
<Textarea
id="rejectDocReason"
rows={4}
value={rejectDocReason}
onChange={(e) => setRejectDocReason(e.target.value)}
placeholder="Explain what must be corrected or re-uploaded…"
/>
</div>
<DialogFooter>
<Button type="button" variant="outline" onClick={() => setRejectDocDialogOpen(false)}>
Cancel
</Button>
<Button
type="button"
variant="destructive"
disabled={isRejectingDoc}
onClick={() => void submitRejectDocument()}
>
{isRejectingDoc ? <Loader2 className="w-4 h-4 animate-spin" /> : 'Confirm reject'}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
<DocumentPreviewModal
isOpen={isPreviewOpen}
onClose={() => setIsPreviewOpen(false)}

View File

@ -17,6 +17,10 @@ import { API } from '@/api/API';
import { SlaBadge } from '@/components/sla/SlaBadge';
import { useSlaBatchStatus } from '@/hooks/useSlaBatchStatus';
import { formatDateTime } from '@/components/ui/utils';
import {
getCurrentStageBadgeClass,
getStatusProgressBarClass,
} from '@/lib/statusProgressTheme';
import {
Pagination,
PaginationContent,
@ -35,13 +39,8 @@ interface RelocationRequestPageProps {
const getApiErrorMessage = (error: any, fallback: string) =>
error?.response?.data?.message || error?.data?.message || error?.message || fallback;
const getStatusColor = (status: string) => {
if (status === 'Completed' || status === 'Closed') return 'bg-green-100 text-green-700 border-green-300';
if (status.includes('Review') || status.includes('Pending') || status === 'In Progress') return 'bg-yellow-100 text-yellow-700 border-yellow-300';
if (status.includes('Rejected') || status.includes('Revoked')) return 'bg-red-100 text-red-700 border-red-300';
if (status.includes('Collection') || status.includes('Completion') || status.includes('Infra')) return 'bg-blue-100 text-blue-700 border-blue-300';
return 'bg-slate-100 text-slate-700 border-slate-300';
};
const getStageBadgeClass = (stage: string, requestStatus?: string) =>
getCurrentStageBadgeClass(stage, requestStatus);
export function RelocationRequestPage({ currentUser, onViewDetails }: RelocationRequestPageProps) {
const [requests, setRequests] = useState<any[]>([]);
@ -251,7 +250,7 @@ export function RelocationRequestPage({ currentUser, onViewDetails }: Relocation
title: 'Pending Review',
value: requests.filter((r: any) => isPendingReviewRequest(r)).length,
icon: Calendar,
color: 'bg-yellow-500',
color: 'bg-re-red',
},
{
title: 'Completed',
@ -284,7 +283,7 @@ export function RelocationRequestPage({ currentUser, onViewDetails }: Relocation
{isSuperAdmin && (
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
<DialogTrigger asChild>
<Button className="bg-amber-600 hover:bg-amber-700 text-white">
<Button className="bg-re-red hover:bg-re-red-hover text-white">
<Plus className="w-4 h-4 mr-2" />
New Relocation Request
</Button>
@ -443,7 +442,7 @@ export function RelocationRequestPage({ currentUser, onViewDetails }: Relocation
</Button>
<Button
type="submit"
className="bg-amber-600 hover:bg-amber-700 text-white"
className="bg-re-red hover:bg-re-red-hover text-white"
disabled={submitting}
>
{submitting ? (
@ -522,7 +521,7 @@ export function RelocationRequestPage({ currentUser, onViewDetails }: Relocation
<TableRow>
<TableCell colSpan={8} className="h-32 text-center">
<div className="flex flex-col items-center justify-center space-y-2">
<Loader2 className="w-6 h-6 text-amber-600 animate-spin" />
<Loader2 className="w-6 h-6 text-re-red animate-spin" />
<p className="text-slate-500 text-sm">Loading requests...</p>
</div>
</TableCell>
@ -550,7 +549,7 @@ export function RelocationRequestPage({ currentUser, onViewDetails }: Relocation
<span>{request.currentLocation}</span>
</div>
<div className="flex items-center gap-1 text-slate-900 text-sm">
<Navigation className="w-3 h-3 text-amber-600" />
<Navigation className="w-3 h-3 text-re-red" />
<span className="text-slate-500">To:</span>
<span>{request.proposedLocation}</span>
</div>
@ -563,7 +562,7 @@ export function RelocationRequestPage({ currentUser, onViewDetails }: Relocation
</TableCell>
<TableCell>
<SlaBadge status={getSla('relocation', request.id || request.requestId)} compact />
<Badge className={`border ${getStatusColor(request.currentStage)}`}>
<Badge className={getStageBadgeClass(request.currentStage, request.status)}>
{request.currentStage}
</Badge>
</TableCell>
@ -571,7 +570,7 @@ export function RelocationRequestPage({ currentUser, onViewDetails }: Relocation
<div className="flex items-center gap-2">
<div className="flex-1 h-2 bg-slate-200 rounded-full overflow-hidden">
<div
className="h-full bg-amber-600 transition-all duration-300"
className={`h-full transition-all duration-300 ${getStatusProgressBarClass(request.status, request.currentStage)}`}
style={{ width: `${request.progressPercentage || 0}%` }}
/>
</div>
@ -616,7 +615,7 @@ export function RelocationRequestPage({ currentUser, onViewDetails }: Relocation
{isLoading ? (
<TableRow>
<TableCell colSpan={5} className="h-32 text-center">
<Loader2 className="w-6 h-6 text-amber-600 animate-spin mx-auto" />
<Loader2 className="w-6 h-6 text-re-red animate-spin mx-auto" />
</TableCell>
</TableRow>
) : (
@ -639,7 +638,7 @@ export function RelocationRequestPage({ currentUser, onViewDetails }: Relocation
</TableCell>
<TableCell>
<SlaBadge status={getSla('relocation', request.id || request.requestId)} compact />
<Badge className={`border ${getStatusColor(request.currentStage)}`}>
<Badge className={getStageBadgeClass(request.currentStage, request.status)}>
{request.currentStage}
</Badge>
</TableCell>
@ -678,7 +677,7 @@ export function RelocationRequestPage({ currentUser, onViewDetails }: Relocation
{isLoading ? (
<TableRow>
<TableCell colSpan={6} className="h-32 text-center">
<Loader2 className="w-6 h-6 text-amber-600 animate-spin mx-auto" />
<Loader2 className="w-6 h-6 text-re-red animate-spin mx-auto" />
</TableCell>
</TableRow>
) : (
@ -703,7 +702,7 @@ export function RelocationRequestPage({ currentUser, onViewDetails }: Relocation
<div className="flex items-center gap-2">
<div className="flex-1 h-2 bg-slate-200 rounded-full overflow-hidden">
<div
className="h-full bg-red-500 transition-all duration-300"
className={`h-full transition-all duration-300 ${getStatusProgressBarClass(request.status, request.currentStage)}`}
style={{ width: `${request.progressPercentage || 0}%` }}
/>
</div>
@ -712,7 +711,7 @@ export function RelocationRequestPage({ currentUser, onViewDetails }: Relocation
</TableCell>
<TableCell>
<SlaBadge status={getSla('relocation', request.id || request.requestId)} compact />
<Badge className={`border ${getStatusColor(request.currentStage)}`}>
<Badge className={getStageBadgeClass(request.currentStage, request.status)}>
{request.currentStage}
</Badge>
</TableCell>
@ -750,7 +749,7 @@ export function RelocationRequestPage({ currentUser, onViewDetails }: Relocation
{isLoading ? (
<TableRow>
<TableCell colSpan={5} className="h-32 text-center">
<Loader2 className="w-6 h-6 text-amber-600 animate-spin mx-auto" />
<Loader2 className="w-6 h-6 text-re-red animate-spin mx-auto" />
</TableCell>
</TableRow>
) : (

View File

@ -503,7 +503,7 @@ export function ResignationDetails({ resignationId, onBack, currentUser }: Resig
if (isLoading && !resignationData) {
return (
<div className="flex items-center justify-center min-h-[400px]">
<Loader2 className="w-8 h-8 animate-spin text-amber-600" />
<Loader2 className="w-8 h-8 animate-spin text-re-red" />
</div>
);
}
@ -692,7 +692,7 @@ export function ResignationDetails({ resignationId, onBack, currentUser }: Resig
<div className="flex flex-col items-center">
<div className={`w-10 h-10 rounded-full flex items-center justify-center ${
status === 'completed' ? 'bg-green-100 text-green-600' :
status === 'active' ? 'bg-blue-100 text-amber-600' :
status === 'active' ? 'bg-blue-100 text-re-red' :
'bg-slate-100 text-slate-400'
}`}>
{status === 'completed' ? (
@ -712,13 +712,13 @@ export function ResignationDetails({ resignationId, onBack, currentUser }: Resig
<div className="flex items-center gap-2">
<h3 className={
status === 'completed' ? 'text-green-600' :
status === 'active' ? 'text-amber-600' :
status === 'active' ? 'text-re-red' :
'text-slate-400'
}>{stage.name}</h3>
{stageDocumentCount > 0 && (
<button
onClick={() => handleViewStageDocuments(stage.name, stage.key)}
className="flex items-center gap-1 px-2 py-1 rounded-full bg-amber-100 hover:bg-amber-200 text-amber-700 text-xs transition-colors cursor-pointer"
className="flex items-center gap-1 px-2 py-1 rounded-full bg-red-50 hover:bg-red-100 text-re-red-hover text-xs transition-colors cursor-pointer"
>
<FileText className="w-3 h-3" />
<span>{stageDocumentCount} {stageDocumentCount === 1 ? 'doc' : 'docs'}</span>
@ -774,7 +774,7 @@ export function ResignationDetails({ resignationId, onBack, currentUser }: Resig
<CardTitle>Documents</CardTitle>
<CardDescription>View and manage resignation documents</CardDescription>
</div>
<Button size="sm" onClick={() => setShowUploadDialog(true)} className="bg-amber-600 hover:bg-amber-700">
<Button size="sm" onClick={() => setShowUploadDialog(true)} className="bg-re-red hover:bg-re-red-hover">
<Upload className="w-4 h-4 mr-2" />
Upload Document
</Button>
@ -899,7 +899,7 @@ export function ResignationDetails({ resignationId, onBack, currentUser }: Resig
${(log.description || log.action || log.details?.action || '').toLowerCase().includes('reject') || (log.description || log.action || log.details?.action || '').toLowerCase().includes('revok')
? 'bg-red-100 text-red-700 border-red-200'
: (log.description || log.action || log.details?.action || '').toLowerCase().includes('sent back') || (log.description || log.action || log.details?.action || '').toLowerCase().includes('send back')
? 'bg-amber-100 text-amber-700 border-amber-200'
? 'bg-red-50 text-re-red-hover border-red-200'
: (log.description || log.action || log.details?.action || '').toLowerCase().includes('approv') || (log.description || log.action || log.details?.action || '').toLowerCase().includes('initi')
? 'bg-emerald-100 text-emerald-700 border-emerald-200'
: 'bg-slate-100 text-slate-700 border-slate-200'}
@ -1006,7 +1006,7 @@ export function ResignationDetails({ resignationId, onBack, currentUser }: Resig
return (
<Button
variant="outline"
className="w-full text-amber-700 border-amber-300 hover:bg-amber-50"
className="w-full text-re-red-hover border-red-300 hover:bg-red-50"
onClick={() => {
setUploadDocType('PPT Presentation');
setUploadStage('DD Lead');
@ -1072,7 +1072,7 @@ export function ResignationDetails({ resignationId, onBack, currentUser }: Resig
<Button
variant="outline"
disabled={isSubmitting}
className="w-full text-amber-700 border-amber-300 hover:bg-amber-50"
className="w-full text-re-red-hover border-red-300 hover:bg-red-50"
onClick={() => handleAction('pushfnf')}
>
{isSubmitting && actionDialog.type === 'pushfnf' ? <Loader2 className="w-4 h-4 mr-2 animate-spin" /> : <Send className="w-4 h-4 mr-2" />}
@ -1108,7 +1108,7 @@ export function ResignationDetails({ resignationId, onBack, currentUser }: Resig
<MessageSquare className="w-4 h-4 mr-2" />
View Work Notes
{resignationData?.worknotes?.length > 0 && (
<Badge className="ml-auto bg-amber-600 hover:bg-amber-700 text-white h-5 px-2">
<Badge className="ml-auto bg-re-red hover:bg-re-red-hover text-white h-5 px-2">
{resignationData.worknotes.length}
</Badge>
)}
@ -1209,9 +1209,9 @@ export function ResignationDetails({ resignationId, onBack, currentUser }: Resig
</div>
) : actionDialog.type === 'pushfnf' ? (
<div className="space-y-4">
<div className="p-3 bg-amber-50 border border-amber-200 rounded-lg flex items-start gap-3">
<AlertCircle className="w-5 h-5 text-amber-600 mt-0.5" />
<div className="text-sm text-amber-800">
<div className="p-3 bg-red-50 border border-red-200 rounded-lg flex items-start gap-3">
<AlertCircle className="w-5 h-5 text-re-red mt-0.5" />
<div className="text-sm text-red-800">
<p className="font-bold">Manual Trigger Notice</p>
<p>
Normally F&F is triggered after the {LAST_WORKING_DAY_LABEL.toLowerCase()}. Use manual
@ -1264,7 +1264,7 @@ export function ResignationDetails({ resignationId, onBack, currentUser }: Resig
className={
actionDialog.type === 'approve' ? 'bg-green-600 hover:bg-green-700' :
actionDialog.type === 'withdrawal' ? 'bg-red-600 hover:bg-red-700' :
'bg-amber-600 hover:bg-amber-700'
'bg-re-red hover:bg-re-red-hover'
}
>
{isSubmitting ? (
@ -1292,7 +1292,7 @@ export function ResignationDetails({ resignationId, onBack, currentUser }: Resig
<DialogContent className={WIDE_DIALOG_CLASS}>
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
<FileText className="w-5 h-5 text-amber-600" />
<FileText className="w-5 h-5 text-re-red" />
Documents - {stageDocumentsDialog.stageName}
</DialogTitle>
<DialogDescription>
@ -1325,7 +1325,7 @@ export function ResignationDetails({ resignationId, onBack, currentUser }: Resig
<Button
size="sm"
variant="outline"
className="text-amber-600 hover:text-blue-700"
className="text-re-red hover:text-blue-700"
onClick={() => {
if (!doc.filePath) return;
const fullPath = doc.filePath.startsWith('/uploads/') && !doc.filePath.startsWith('/uploads/documents/')

View File

@ -146,8 +146,8 @@ export function ResignationPage({ onViewDetails }: ResignationPageProps) {
<CardContent className="pt-6">
<div className="flex items-start justify-between">
<div className="flex items-start gap-4 flex-1">
<div className="p-3 bg-amber-100 rounded-lg">
<FileText className="w-6 h-6 text-amber-600" />
<div className="p-3 bg-red-50 rounded-lg">
<FileText className="w-6 h-6 text-re-red" />
</div>
<div className="flex-1 text-left">
<div className="flex items-center gap-3 mb-2">

View File

@ -95,7 +95,7 @@ export function TerminationDetails({ terminationId, onBack, currentUser }: Termi
if (isLoading) {
return (
<div className="flex flex-col items-center justify-center min-h-[400px] space-y-4">
<Loader2 className="w-8 h-8 text-amber-600 animate-spin" />
<Loader2 className="w-8 h-8 text-re-red animate-spin" />
<p className="text-slate-600">Loading termination details...</p>
</div>
);
@ -751,9 +751,9 @@ export function TerminationDetails({ terminationId, onBack, currentUser }: Termi
</CardContent>
</Card>
<Card className="border-amber-200 bg-amber-50/30">
<Card className="border-red-200 bg-red-50/30">
<CardHeader>
<CardTitle className="text-amber-900 flex items-center gap-2">
<CardTitle className="text-red-900 flex items-center gap-2">
<AlertTriangle className="w-5 h-5" />
Termination Details
</CardTitle>
@ -762,7 +762,7 @@ export function TerminationDetails({ terminationId, onBack, currentUser }: Termi
<div className="space-y-4">
<div>
<Label className="text-slate-600">Termination Category</Label>
<p className="text-amber-900">{request.category}</p>
<p className="text-red-900">{request.category}</p>
</div>
<div>
<Label className="text-slate-600">Sub Category</Label>
@ -811,7 +811,7 @@ export function TerminationDetails({ terminationId, onBack, currentUser }: Termi
<div key={stage.id} className="flex gap-4">
<div className="flex flex-col items-center">
<div className={`w-10 h-10 rounded-full flex items-center justify-center ${stage.status === 'completed' ? 'bg-green-100 text-green-600' :
stage.status === 'active' ? 'bg-amber-100 text-amber-600' :
stage.status === 'active' ? 'bg-red-50 text-re-red' :
'bg-slate-100 text-slate-400'
}`}>
{stage.status === 'completed' ? (
@ -833,13 +833,13 @@ export function TerminationDetails({ terminationId, onBack, currentUser }: Termi
<div className="flex items-center gap-2">
<h3 className={
stage.status === 'completed' ? 'text-green-600' :
stage.status === 'active' ? 'text-amber-600' :
stage.status === 'active' ? 'text-re-red' :
'text-slate-400'
}>{stage.name}</h3>
{documentCount > 0 && (
<button
onClick={() => handleViewStageDocuments(stage.name)}
className="flex items-center gap-1 px-2 py-1 rounded-full bg-amber-100 hover:bg-amber-200 text-amber-700 text-xs transition-colors cursor-pointer"
className="flex items-center gap-1 px-2 py-1 rounded-full bg-red-50 hover:bg-red-100 text-re-red-hover text-xs transition-colors cursor-pointer"
>
<FileText className="w-3 h-3" />
<span>{documentCount} {documentCount === 1 ? 'doc' : 'docs'}</span>
@ -884,12 +884,12 @@ export function TerminationDetails({ terminationId, onBack, currentUser }: Termi
<div className={`
p-2.5 rounded-lg border text-sm
${isAttachment
? 'bg-amber-50/50 border-amber-100 text-amber-900'
? 'bg-red-50/50 border-red-100 text-red-900'
: 'bg-slate-50 border-slate-100 text-slate-700'}
`}>
{isAttachment ? (
<div className="flex items-center gap-2">
<FileText className="w-3.5 h-3.5 text-amber-600" />
<FileText className="w-3.5 h-3.5 text-re-red" />
<span className="font-medium truncate">{remarksContent}</span>
</div>
) : (
@ -918,7 +918,7 @@ export function TerminationDetails({ terminationId, onBack, currentUser }: Termi
<CardTitle>Documents</CardTitle>
<CardDescription>View and manage termination case documents</CardDescription>
</div>
<Button size="sm" onClick={() => setShowUploadDialog(true)} className="bg-amber-600 hover:bg-amber-700">
<Button size="sm" onClick={() => setShowUploadDialog(true)} className="bg-re-red hover:bg-re-red-hover">
<Upload className="w-4 h-4 mr-2" />
Upload Document
</Button>
@ -1012,7 +1012,7 @@ export function TerminationDetails({ terminationId, onBack, currentUser }: Termi
${(log.description || log.action || log.details?.action || '').toLowerCase().includes('reject') || (log.description || log.action || log.details?.action || '').toLowerCase().includes('revok')
? 'bg-red-100 text-red-700 border-red-200'
: (log.description || log.action || log.details?.action || '').toLowerCase().includes('sent back') || (log.description || log.action || log.details?.action || '').toLowerCase().includes('send back')
? 'bg-amber-100 text-amber-700 border-amber-200'
? 'bg-red-50 text-re-red-hover border-red-200'
: (log.description || log.action || log.details?.action || '').toLowerCase().includes('approv') || (log.description || log.action || log.details?.action || '').toLowerCase().includes('initi')
? 'bg-emerald-100 text-emerald-700 border-emerald-200'
: 'bg-slate-100 text-slate-700 border-slate-200'}
@ -1085,7 +1085,7 @@ export function TerminationDetails({ terminationId, onBack, currentUser }: Termi
)}
{permissions.canUploadSCNResponse && (
<Button
className="w-full bg-amber-600 hover:bg-amber-700"
className="w-full bg-re-red hover:bg-re-red-hover"
onClick={() => {
setScnFile(null);
setShowSCNDialog(true);
@ -1148,12 +1148,12 @@ export function TerminationDetails({ terminationId, onBack, currentUser }: Termi
request.status === OFFBOARDING_STATUS.AWAITING_FNF ||
request.status === OFFBOARDING_STATUS.AWAITING_FNF_LWD_PENDING) &&
!permissions.isLwdReached && (
<Alert className="border-amber-200 bg-amber-50">
<AlertTriangle className="h-4 w-4 text-amber-700" />
<AlertTitle className="text-amber-900">
<Alert className="border-red-200 bg-red-50">
<AlertTriangle className="h-4 w-4 text-re-red-hover" />
<AlertTitle className="text-red-900">
Push to F&F locked until {LAST_WORKING_DAY_LABEL}
</AlertTitle>
<AlertDescription className="text-amber-800 text-sm">
<AlertDescription className="text-red-800 text-sm">
{LAST_WORKING_DAY_LABEL} is{' '}
{request.proposedLwd
? new Date(request.proposedLwd).toLocaleDateString('en-IN', { dateStyle: 'medium' })
@ -1190,7 +1190,7 @@ export function TerminationDetails({ terminationId, onBack, currentUser }: Termi
<MessageSquare className="w-4 h-4 mr-2" />
View Work Notes
{workNotesCount > 0 && (
<Badge className="ml-auto bg-amber-600 hover:bg-amber-700 text-white h-5 px-2">
<Badge className="ml-auto bg-re-red hover:bg-re-red-hover text-white h-5 px-2">
{workNotesCount}
</Badge>
)}
@ -1308,7 +1308,7 @@ export function TerminationDetails({ terminationId, onBack, currentUser }: Termi
<DialogContent className={WIDE_DIALOG_CLASS}>
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
<FileText className="w-5 h-5 text-amber-600" />
<FileText className="w-5 h-5 text-re-red" />
Documents - {stageDocumentsDialog.stageName}
</DialogTitle>
<DialogDescription>
@ -1341,7 +1341,7 @@ export function TerminationDetails({ terminationId, onBack, currentUser }: Termi
<Button
size="sm"
variant="outline"
className="text-amber-600 hover:text-amber-700"
className="text-re-red hover:text-re-red-hover"
onClick={() => {
const path = doc.path;
if (!path) return;
@ -1426,7 +1426,7 @@ export function TerminationDetails({ terminationId, onBack, currentUser }: Termi
Cancel
</Button>
<Button
className={isScnStage ? 'bg-amber-600 hover:bg-amber-700' : 'bg-purple-600 hover:bg-purple-700'}
className={isScnStage ? 'bg-re-red hover:bg-re-red-hover' : 'bg-purple-600 hover:bg-purple-700'}
onClick={isScnStage ? handleUploadSCNResponse : handleIssueSCN}
disabled={isProcessing || (isScnStage && !scnFile)}
>
@ -1454,9 +1454,9 @@ export function TerminationDetails({ terminationId, onBack, currentUser }: Termi
<SelectValue placeholder="Select decision" />
</SelectTrigger>
<SelectContent className="bg-white border-slate-200 shadow-xl overflow-visible z-[9999]">
<SelectItem value="Approve" className="text-amber-700 focus:bg-amber-50">Confirm Termination</SelectItem>
<SelectItem value="Approve" className="text-re-red-hover focus:bg-red-50">Confirm Termination</SelectItem>
<SelectItem value="Reject" className="text-slate-600 focus:bg-slate-50">Reject Termination</SelectItem>
<SelectItem value="Reconsider" className="text-amber-600 focus:bg-amber-50">Reconsider / Give More Time</SelectItem>
<SelectItem value="Reconsider" className="text-re-red focus:bg-red-50">Reconsider / Give More Time</SelectItem>
</SelectContent>
</Select>
</div>

View File

@ -8,7 +8,6 @@ import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { Textarea } from '@/components/ui/textarea';
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
import { useState, useEffect } from 'react';
import { API } from '@/api/API';
import { slaService, SlaStatusSnapshot } from '@/services/sla.service';
@ -291,13 +290,7 @@ export function TerminationPage({ currentUser, onViewDetails }: TerminationPageP
return (
<div className="space-y-6">
{/* Warning Alert */}
{/* <Alert className="border-red-200 bg-red-50">
<AlertTriangle className="h-4 w-4 text-red-600" />
<AlertTitle className="text-red-900">Restricted Access</AlertTitle>
<AlertDescription className="text-red-700">
This section contains sensitive information. All termination actions are logged and require proper authorization.
</AlertDescription>
</Alert> */}
{/* Header Stats */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">

View File

@ -8,6 +8,31 @@ export const OFFBOARDING_STATUS = {
AWAITING_FNF_LWD_PENDING: 'Awaiting F&F (LWD Pending)'
} as const;
export {
isStatusTerminalNegative as isOffboardingTerminalNegative,
isStatusTerminalSuccess as isOffboardingTerminalSuccess,
resolveStatusProgressVariant,
STATUS_PROGRESS_THEME,
getStatusProgressBarClass,
getStatusProgressBarClass as getOffboardingProgressBarFillClass,
getStatusProgressBarClass as getWorkflowProgressBarFillClass,
getStatusProgressBadgeSolidClass,
getRequestStatusBadgeClass,
getRequestStatusBadgeSolidClass,
getRequestStatusBadgeSolidClass as getOffboardingRequestStatusBadgeClass,
getCurrentStageBadgeClass,
getStatusProgressBadgeClass,
getWorkflowActiveStageTheme,
getStatusLabelBadgeClass,
getPercentProgressBarClass,
getPercentProgressBadgeSolidClass,
} from './statusProgressTheme';
import { STATUS_PROGRESS_THEME } from './statusProgressTheme';
/** Active workflow step styling (amber) — Workflow Progress tab */
export const WORKFLOW_IN_PROGRESS_ACCENT = STATUS_PROGRESS_THEME.inProgress;
/** Maps backend status / stage strings to user-facing labels */
export function formatOffboardingStatusLabel(value: string | null | undefined): string {
if (!value) return 'Pending';
@ -17,3 +42,18 @@ export function formatOffboardingStatusLabel(value: string | null | undefined):
label = label.replace(/\bLWD\b/g, LAST_WORKING_DAY_LABEL);
return label;
}
/** @deprecated Use STATUS_PROGRESS_THEME from statusProgressTheme */
export const OFFBOARDING_BRAND_ACCENT = {
solid: 'bg-re-red hover:bg-re-red-hover text-white border-transparent',
progress: 'bg-re-red',
soft: 'bg-red-50 text-re-red border-red-200',
softHover: 'hover:bg-red-50 hover:text-re-red-hover',
text: 'text-re-red',
textMuted: 'text-re-red-hover',
icon: 'text-re-red',
ring: 'focus-visible:ring-re-red',
currentPanel: 'bg-red-50 border-red-200',
currentIcon: 'bg-red-100 text-re-red',
inProgressBadge: 'bg-red-100 text-re-red-hover border-red-300',
} as const;

View File

@ -0,0 +1,231 @@
/**
* Unified status & progress colors across the app.
* Uses CSS classes from globals.css (amber) NOT Tailwind bg-amber-* (remapped to re-red globally).
*
* - In progress / active: amber via .bg-status-progress / .bg-status-workflow-*
* - Completed / success: green
* - Rejected / revoked: re-red
*
* Primary buttons / brand UI: use bg-re-red (never amber).
*/
const TERMINAL_NEGATIVE = ['Rejected', 'Revoked', 'Withdrawn'] as const;
export type StatusProgressVariant = 'inProgress' | 'success' | 'negative' | 'neutral';
export function isStatusTerminalNegative(
status: string | null | undefined,
currentStage?: string | null | undefined
): boolean {
const s = String(status || '');
const stage = String(currentStage || '');
return TERMINAL_NEGATIVE.some((label) => s.includes(label) || stage === label);
}
export function isStatusTerminalSuccess(
status: string | null | undefined,
currentStage?: string | null | undefined
): boolean {
const s = String(status || '');
const stage = String(currentStage || '');
return (
s === 'Completed' ||
stage === 'Completed' ||
s === 'Settled' ||
s === 'Onboarded' ||
s === 'Verified' ||
s === 'Closed'
);
}
export function resolveStatusProgressVariant(
status: string | null | undefined,
currentStage?: string | null | undefined
): StatusProgressVariant {
if (isStatusTerminalSuccess(status, currentStage)) return 'success';
if (isStatusTerminalNegative(status, currentStage)) return 'negative';
return 'inProgress';
}
/** Semantic class sets — status/progress uses globals.css amber tokens */
export const STATUS_PROGRESS_THEME = {
inProgress: {
bar: 'bg-status-progress',
barBadge: 'bg-status-progress hover:bg-status-progress-hover text-white border-transparent',
badge: 'bg-status-progress-soft text-status-progress-strong border-status-progress',
badgeSolid: 'bg-status-progress hover:bg-status-progress-hover text-white border-transparent',
text: 'text-status-progress-muted',
textStrong: 'text-status-progress-strong',
icon: 'text-status-progress',
workflowIcon: 'bg-status-workflow-icon',
workflowPanel: 'bg-status-workflow-panel -ml-4 pl-4 pr-4 py-3 rounded-lg border border-status-workflow-panel',
workflowTitle: 'text-status-progress-strong',
workflowSubtitle: 'text-status-progress-muted',
workflowStageBadge: 'bg-status-workflow-badge border',
panel: 'bg-status-workflow-panel -ml-4 pl-4 pr-4 py-3 rounded-lg border border-status-workflow-panel',
title: 'text-status-progress-strong',
subtitle: 'text-status-progress-muted',
stageBadge: 'bg-status-workflow-badge border',
progress: 'bg-status-progress',
workflowActive: 'bg-status-workflow-active',
},
success: {
bar: 'bg-green-600',
barBadge: 'bg-green-600 hover:bg-green-700 text-white border-transparent',
badge: 'bg-green-100 text-green-800 border-green-200',
badgeSolid: 'bg-green-600 hover:bg-green-700 text-white border-transparent',
text: 'text-green-700',
textStrong: 'text-green-900',
icon: 'text-green-600',
workflowIcon: 'bg-green-100 text-green-600',
workflowPanel: 'bg-green-50 border border-green-200',
workflowTitle: 'text-green-900',
workflowSubtitle: 'text-green-700',
workflowStageBadge: 'bg-green-100 text-green-700 border-green-300',
panel: 'bg-green-50 border border-green-200',
title: 'text-green-900',
subtitle: 'text-green-700',
stageBadge: 'bg-green-100 text-green-700 border-green-300',
progress: 'bg-green-600',
workflowActive: 'bg-green-600 border-green-600',
},
negative: {
bar: 'bg-re-red',
barBadge: 'bg-re-red hover:bg-re-red-hover text-white border-transparent',
badge: 'bg-red-50 text-re-red border-red-200',
badgeSolid: 'bg-re-red hover:bg-re-red-hover text-white border-transparent',
text: 'text-re-red-hover',
textStrong: 'text-red-900',
icon: 'text-re-red',
workflowIcon: 'bg-red-100 text-re-red',
workflowPanel: 'bg-red-50 border border-red-200',
workflowTitle: 'text-red-900',
workflowSubtitle: 'text-re-red-hover',
workflowStageBadge: 'bg-red-50 text-re-red border-red-200',
panel: 'bg-red-50 border border-red-200',
title: 'text-red-900',
subtitle: 'text-re-red-hover',
stageBadge: 'bg-red-50 text-re-red border-red-200',
progress: 'bg-re-red',
workflowActive: 'bg-re-red border-re-red',
},
neutral: {
bar: 'bg-slate-300',
barBadge: 'bg-slate-500 text-white border-transparent',
badge: 'bg-slate-100 text-slate-700 border-slate-200',
badgeSolid: 'bg-slate-600 hover:bg-slate-700 text-white border-transparent',
text: 'text-slate-600',
textStrong: 'text-slate-900',
icon: 'text-slate-500',
workflowIcon: 'bg-slate-100 text-slate-400',
workflowPanel: '',
workflowTitle: 'text-slate-900',
workflowSubtitle: 'text-slate-600',
workflowStageBadge: 'bg-slate-100 text-slate-500 border-slate-300',
panel: '',
title: 'text-slate-900',
subtitle: 'text-slate-600',
stageBadge: 'bg-slate-100 text-slate-500 border-slate-300',
progress: 'bg-slate-300',
workflowActive: 'bg-slate-400 border-slate-400',
},
} as const;
function themeFor(status: string | null | undefined, currentStage?: string | null | undefined) {
return STATUS_PROGRESS_THEME[resolveStatusProgressVariant(status, currentStage)];
}
export function getStatusProgressBarClass(
status: string | null | undefined,
currentStage?: string | null | undefined
): string {
return themeFor(status, currentStage).bar;
}
export function getStatusProgressBadgeSolidClass(
status: string | null | undefined,
currentStage?: string | null | undefined
): string {
return themeFor(status, currentStage).barBadge;
}
export function getStatusProgressBadgeClass(
status: string | null | undefined,
currentStage?: string | null | undefined
): string {
return themeFor(status, currentStage).badge;
}
export function getWorkflowActiveStageTheme() {
return STATUS_PROGRESS_THEME.inProgress;
}
/** Progress bar / badge from completion % (EOR checklist, sub-trackers, etc.) */
export function getPercentProgressBarClass(percent: number): string {
if (percent >= 100) return STATUS_PROGRESS_THEME.success.bar;
return STATUS_PROGRESS_THEME.inProgress.bar;
}
export function getPercentProgressBadgeSolidClass(percent: number): string {
if (percent >= 100) return STATUS_PROGRESS_THEME.success.barBadge;
return STATUS_PROGRESS_THEME.inProgress.barBadge;
}
export function getStatusLabelBadgeClass(status: string | null | undefined): string {
const s = String(status || '');
const upper = s.toUpperCase();
if (s.includes('Rejected') || upper.includes('DOCUMENT_REJECTED')) {
return STATUS_PROGRESS_THEME.negative.badge;
}
if (s === 'Verified' || s === 'Completed' || upper.includes('DOCUMENT_VERIFIED')) {
return STATUS_PROGRESS_THEME.success.badge;
}
if (s.includes('Pending') || s.includes('Review') || s === 'In Progress') {
return STATUS_PROGRESS_THEME.inProgress.badge;
}
return STATUS_PROGRESS_THEME.neutral.badge;
}
/** Request status on listing/detail header — re-red while active (not amber). */
export function getRequestStatusBadgeClass(
status: string | null | undefined,
currentStage?: string | null | undefined
): string {
if (isStatusTerminalSuccess(status, currentStage)) {
return STATUS_PROGRESS_THEME.success.badge;
}
if (isStatusTerminalNegative(status, currentStage)) {
return STATUS_PROGRESS_THEME.negative.badge;
}
return STATUS_PROGRESS_THEME.negative.badge;
}
/** Solid request status pill (detail header). */
export function getRequestStatusBadgeSolidClass(
status: string | null | undefined,
currentStage?: string | null | undefined
): string {
if (isStatusTerminalSuccess(status, currentStage)) {
return STATUS_PROGRESS_THEME.success.badgeSolid;
}
if (isStatusTerminalNegative(status, currentStage)) {
return STATUS_PROGRESS_THEME.negative.badgeSolid;
}
return STATUS_PROGRESS_THEME.negative.badgeSolid;
}
/** Current stage chip (e.g. ASM Review) — brand re-red, not amber/yellow. */
export function getCurrentStageBadgeClass(
stage: string | null | undefined,
requestStatus?: string | null | undefined
): string {
const s = String(stage || '');
const status = String(requestStatus || '');
if (isStatusTerminalNegative(status, s) || /rejected|revoked/i.test(s)) {
return STATUS_PROGRESS_THEME.negative.badge;
}
if (s === 'Completed' || status === 'Completed' || s === 'Closed') {
return STATUS_PROGRESS_THEME.success.badge;
}
return STATUS_PROGRESS_THEME.negative.badge;
}

View File

@ -114,7 +114,7 @@ const PublicQuestionnairePage: React.FC = () => {
if (loading) return (
<div className="flex items-center justify-center h-screen bg-slate-50">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-amber-600"></div>
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-re-red"></div>
</div>
);
@ -210,8 +210,8 @@ const PublicQuestionnairePage: React.FC = () => {
<div className="flex items-center justify-center mb-7">
<img src="/assets/images/Re_Logo.png" alt="Royal Enfield" className="h-12 w-auto" />
</div>
<div className="h-1 w-24 bg-amber-600 mx-auto mb-4"></div>
<h2 className="text-amber-400 text-xl mb-4 font-light">Dealership Partner Application</h2>
<div className="h-1 w-24 bg-re-red mx-auto mb-4"></div>
<h2 className="text-re-red text-xl mb-4 font-light">Dealership Partner Application</h2>
<p className="text-slate-300 max-w-2xl mx-auto leading-relaxed text-sm">
Thank you for your interest in becoming a Royal Enfield business partner.
Please complete this questionnaire to help us understand your profile and aspirations.
@ -219,12 +219,12 @@ const PublicQuestionnairePage: React.FC = () => {
<div className="flex items-center justify-center gap-8 mt-8 border-t border-slate-700/50 pt-6 inline-flex mx-auto">
<div className="text-center px-4">
<div className="text-amber-400 text-2xl font-bold">{questions.length}</div>
<div className="text-re-red text-2xl font-bold">{questions.length}</div>
<div className="text-slate-400 text-xs uppercase tracking-wider">Questions</div>
</div>
<div className="h-10 w-px bg-slate-700"></div>
<div className="text-center px-4">
<div className="text-amber-400 text-2xl font-bold">{sections.length}</div>
<div className="text-re-red text-2xl font-bold">{sections.length}</div>
<div className="text-slate-400 text-xs uppercase tracking-wider">Sections</div>
</div>
</div>
@ -240,7 +240,7 @@ const PublicQuestionnairePage: React.FC = () => {
onClick={() => setActiveSection(section)}
className={`flex items-center gap-2 px-4 py-2 rounded-lg whitespace-nowrap transition-all text-sm font-medium
${activeSection === section
? 'bg-amber-600 text-white shadow-lg'
? 'bg-re-red text-white shadow-lg'
: 'bg-slate-700/50 text-slate-300 hover:bg-slate-700 hover:text-white'
}`}
>
@ -260,8 +260,8 @@ const PublicQuestionnairePage: React.FC = () => {
{/* Question Content */}
<div className="bg-white rounded-b-lg shadow-xl border border-slate-200 border-t-0 min-h-[400px]">
<div className="p-8">
<div className="flex items-start gap-4 pb-6 border-b-2 border-amber-100 mb-8">
<div className="w-12 h-12 bg-amber-50 rounded-lg flex items-center justify-center flex-shrink-0 text-amber-600">
<div className="flex items-start gap-4 pb-6 border-b-2 border-red-100 mb-8">
<div className="w-12 h-12 bg-red-50 rounded-lg flex items-center justify-center flex-shrink-0 text-re-red">
<Users className="w-6 h-6" />
</div>
<div className="flex-1">
@ -276,7 +276,7 @@ const PublicQuestionnairePage: React.FC = () => {
{activeQuestions.map((q, idx) => (
<div key={q.id} className="group animate-in fade-in duration-500" style={{ animationDelay: `${idx * 100}ms` }}>
<div className="flex items-start gap-5">
<div className="w-8 h-8 bg-slate-100 rounded-full flex items-center justify-center flex-shrink-0 group-hover:bg-amber-100 transition-colors text-slate-600 group-hover:text-amber-700 font-semibold text-sm">
<div className="w-8 h-8 bg-slate-100 rounded-full flex items-center justify-center flex-shrink-0 group-hover:bg-red-50 transition-colors text-slate-600 group-hover:text-re-red-hover font-semibold text-sm">
{idx + 1}
</div>
<div className="flex-1 space-y-3">
@ -291,7 +291,7 @@ const PublicQuestionnairePage: React.FC = () => {
{(q.inputType === 'text' || q.inputType === 'email' || q.inputType === 'number') && (
<input
type={q.inputType}
className="w-full h-10 px-3 rounded-lg border border-slate-300 focus:border-amber-500 focus:ring-2 focus:ring-amber-200 outline-none transition-all placeholder:text-slate-400"
className="w-full h-10 px-3 rounded-lg border border-slate-300 focus:border-re-red focus:ring-2 focus:ring-red-200 outline-none transition-all placeholder:text-slate-400"
placeholder="Type your answer here..."
value={responses[q.id] || ''}
onChange={(e) => handleInputChange(q.id, e.target.value)}
@ -300,7 +300,7 @@ const PublicQuestionnairePage: React.FC = () => {
{q.inputType === 'textarea' && (
<textarea
className="w-full h-32 p-3 rounded-lg border border-slate-300 focus:border-amber-500 focus:ring-2 focus:ring-amber-200 outline-none transition-all placeholder:text-slate-400"
className="w-full h-32 p-3 rounded-lg border border-slate-300 focus:border-re-red focus:ring-2 focus:ring-red-200 outline-none transition-all placeholder:text-slate-400"
placeholder="Type your answer here..."
value={responses[q.id] || ''}
onChange={(e) => handleInputChange(q.id, e.target.value)}
@ -316,7 +316,7 @@ const PublicQuestionnairePage: React.FC = () => {
<input
type="radio"
name={`q-${q.id}`}
className="w-4 h-4 text-amber-600 focus:ring-amber-500 border-slate-300"
className="w-4 h-4 text-re-red focus:ring-re-red border-slate-300"
checked={responses[q.id] === val}
onChange={() => handleInputChange(q.id, val)}
/>
@ -351,7 +351,7 @@ const PublicQuestionnairePage: React.FC = () => {
{currentSectionIndex < sections.length - 1 ? (
<button
onClick={handleNextSection}
className="px-6 py-2.5 rounded-lg text-sm font-medium bg-amber-600 text-white hover:bg-amber-700 flex items-center gap-2 transition-colors shadow-md hover:shadow-lg"
className="px-6 py-2.5 rounded-lg text-sm font-medium bg-re-red text-white hover:bg-re-red-hover flex items-center gap-2 transition-colors shadow-md hover:shadow-lg"
>
Next Section
<ChevronRight className="w-4 h-4" />

View File

@ -51,6 +51,14 @@
--re-black: #000000;
--re-white: #FFFFFF;
--re-gray: #717171;
/* Status / progress semantics (see statusProgressTheme.ts) */
--status-in-progress: #d97706;
--status-in-progress-hover: #b45309;
--status-success: #16a34a;
--status-success-hover: #15803d;
--status-negative: var(--re-red);
--status-negative-hover: var(--re-red-hover);
}
.dark {
@ -142,6 +150,9 @@
--color-re-red-hover: var(--re-red-hover);
--color-re-black: var(--re-black);
--color-re-gray: var(--re-gray);
--color-status-in-progress: var(--status-in-progress);
--color-status-success: var(--status-success);
--color-status-negative: var(--status-negative);
}
@layer base {
@ -155,12 +166,76 @@
}
@layer utilities {
button.bg-amber-600 {
/* ─── Status / progress only (amber) — use these instead of bg-amber-* for trackers ─── */
.bg-status-progress {
background-color: var(--status-in-progress);
}
.hover\:bg-status-progress-hover:hover {
background-color: var(--status-in-progress-hover);
}
.text-status-progress {
color: var(--status-in-progress);
}
.text-status-progress-muted {
color: var(--status-in-progress-hover);
}
.text-status-progress-strong {
color: #92400e;
}
.bg-status-progress-soft {
background-color: #fffbeb;
}
.border-status-progress {
border-color: #fde68a;
}
.bg-status-workflow-icon {
background-color: #fef3c7;
color: var(--status-in-progress);
}
.bg-status-workflow-panel {
background-color: #fffbeb;
border-color: #fde68a;
}
.border-status-workflow-panel {
border-color: #fde68a;
}
.bg-status-workflow-badge {
background-color: #fef3c7;
color: #b45309;
border-color: #fcd34d;
}
.bg-status-workflow-active {
background-color: var(--status-in-progress);
border-color: var(--status-in-progress);
}
.ring-status-workflow-active {
--tw-ring-color: var(--status-in-progress);
}
/* ─── Brand primary: legacy amber CTAs → re-red ─── */
.btn-re-primary,
button.bg-amber-600,
a.bg-amber-600,
[data-slot="button"].bg-amber-600 {
background-color: var(--color-re-red) !important;
color: #fff;
}
.btn-re-primary:hover,
button.bg-amber-600:hover,
button.hover\:bg-amber-700:hover,
a.bg-amber-600:hover,
a.hover\:bg-amber-700:hover {
background-color: var(--color-re-red-hover) !important;
}
/* Non-button amber-600 (badges, divs) → re-red unless using status-progress-* classes */
.bg-amber-600:not(.bg-status-progress):not(.bg-status-workflow-active):not(.bg-status-workflow-badge):not(.bg-status-workflow-icon) {
background-color: var(--color-re-red) !important;
}
button.hover\:bg-amber-700:hover {
background-color: var(--color-re-red) !important;
.hover\:bg-amber-700:hover:not(.hover\:bg-status-progress-hover) {
background-color: var(--color-re-red-hover) !important;
}
}