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 }) => updateConstitutionalChange: (id: string, action: ConstitutionalChangeAction, data?: { comments?: string; remarks?: string }) =>
client.post(`/constitutional-change/${id}/action`, { action, ...data }), client.post(`/constitutional-change/${id}/action`, { action, ...data }),
uploadConstitutionalDocuments: (id: string, documents: any[]) => client.post(`/constitutional-change/${id}/documents`, { documents }), 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 // SLA
getSlaConfigs: () => client.get('/master/sla-configs'), 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 [flyout, setFlyout] = useState<FlyoutState | null>(null);
const hoverTimeout = useRef<ReturnType<typeof setTimeout> | null>(null); const hoverTimeout = useRef<ReturnType<typeof setTimeout> | null>(null);
const currentRole = currentUser?.role || currentUser?.roleCode || '';
const hasRole = (roles: string[]) => { const hasRole = (roles: string[]) => {
const normalizedTargetRoles = roles.map((r) => r.toLowerCase()); const normalizedTargetRoles = roles.map((r) => r.toLowerCase());
const userRole = String(currentUser?.role || '').toLowerCase(); const userRole = String(currentUser?.role || '').toLowerCase();

View File

@ -1,10 +1,9 @@
import React from 'react';
import { Badge } from '@/components/ui/badge'; import { Badge } from '@/components/ui/badge';
import { SlaBucket, SlaStatusSnapshot } from '@/services/sla.service'; import { SlaBucket, SlaStatusSnapshot } from '@/services/sla.service';
const BUCKET_CLASS: Record<SlaBucket, string> = { const BUCKET_CLASS: Record<SlaBucket, string> = {
healthy: 'bg-emerald-100 text-emerald-800 border-emerald-200', 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', critical: 'bg-orange-100 text-orange-800 border-orange-200',
breached: 'bg-red-100 text-red-800 border-red-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 justify-between p-4 border-b bg-slate-50">
<div className="flex items-center gap-3"> <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"> <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-amber-600" /> <Eye className="w-5 h-5 text-re-red" />
</div> </div>
<div> <div>
<DialogTitle className="text-sm font-bold text-slate-900 leading-none mb-1"> <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({ function Progress({
className, className,
indicatorClassName,
value, value,
...props ...props
}: React.ComponentProps<typeof ProgressPrimitive.Root>) { }: React.ComponentProps<typeof ProgressPrimitive.Root> & {
indicatorClassName?: string;
}) {
return ( return (
<ProgressPrimitive.Root <ProgressPrimitive.Root
data-slot="progress" data-slot="progress"
@ -21,7 +24,7 @@ function Progress({
> >
<ProgressPrimitive.Indicator <ProgressPrimitive.Indicator
data-slot="progress-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)}%)` }} style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
/> />
</ProgressPrimitive.Root> </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', DELETED: 'bg-rose-100 text-rose-700 border-rose-200',
ACTIVATED: 'bg-emerald-100 text-emerald-700 border-emerald-200', ACTIVATED: 'bg-emerald-100 text-emerald-700 border-emerald-200',
DEACTIVATED: 'bg-slate-200 text-slate-700 border-slate-300', 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', SUBMITTED: 'bg-indigo-100 text-indigo-700 border-indigo-200',
ASSIGNED: 'bg-violet-100 text-violet-700 border-violet-200', ASSIGNED: 'bg-violet-100 text-violet-700 border-violet-200',
UNASSIGNED: 'bg-slate-200 text-slate-700 border-slate-300', UNASSIGNED: 'bg-slate-200 text-slate-700 border-slate-300',

View File

@ -94,13 +94,13 @@ export function ProspectiveLoginPage() {
return ( 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="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 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 -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-amber-600/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>
<div className="relative w-full max-w-md"> <div className="relative w-full max-w-md">
<div className="text-center mb-8"> <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"> <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> <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> <circle cx="12" cy="12" r="3"></circle>
@ -122,8 +122,8 @@ export function ProspectiveLoginPage() {
</Button> </Button>
<div className="flex items-center gap-3 mb-2"> <div className="flex items-center gap-3 mb-2">
<div className="p-2 bg-amber-100 rounded-lg"> <div className="p-2 bg-red-50 rounded-lg">
<Smartphone className="w-6 h-6 text-amber-600" /> <Smartphone className="w-6 h-6 text-re-red" />
</div> </div>
<div> <div>
<h2 className="text-slate-900 text-lg font-semibold">Dealer Login</h2> <h2 className="text-slate-900 text-lg font-semibold">Dealer Login</h2>
@ -158,7 +158,7 @@ export function ProspectiveLoginPage() {
<Button <Button
type="submit" 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} disabled={isLoading || phone.length < 10}
> >
{isLoading ? 'Sending...' : 'Send OTP'} {isLoading ? 'Sending...' : 'Send OTP'}
@ -198,7 +198,7 @@ export function ProspectiveLoginPage() {
<Button <Button
type="submit" 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} disabled={isLoading || otp.length < 6}
> >
{isLoading ? 'Verifying...' : 'Verify OTP'} {isLoading ? 'Verifying...' : 'Verify OTP'}
@ -207,7 +207,7 @@ export function ProspectiveLoginPage() {
<div className="text-center text-sm"> <div className="text-center text-sm">
<button <button
type="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')} onClick={() => setStep('PHONE')}
> >
Change Phone Number Change Phone Number
@ -215,7 +215,7 @@ export function ProspectiveLoginPage() {
<span className="mx-2 text-slate-400">|</span> <span className="mx-2 text-slate-400">|</span>
<button <button
type="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} onClick={handleSendOtp}
disabled={isLoading} disabled={isLoading}
> >

View File

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

View File

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

View File

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

View File

@ -42,7 +42,7 @@ export function Dashboard({ onNavigate }: DashboardProps) {
title: 'Level 1 Pending', title: 'Level 1 Pending',
value: dashboardStats.level1Pending, value: dashboardStats.level1Pending,
icon: Clock, icon: Clock,
color: 'bg-amber-500', color: 'bg-red-500',
trend: { value: 3, isPositive: false }, trend: { value: 3, isPositive: false },
filter: 'Level 1 Pending' 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="w-24 text-slate-700">{item.location}</div>
<div className="flex-1 bg-slate-200 rounded-full h-8 relative overflow-hidden"> <div className="flex-1 bg-slate-200 rounded-full h-8 relative overflow-hidden">
<div <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}%` }} style={{ width: `${(item.count / maxLocationCount) * 100}%` }}
> >
<span className="text-white">{item.count}</span> <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" className="flex items-start gap-4 p-3 hover:bg-slate-50 rounded-lg cursor-pointer transition-colors"
onClick={() => onNavigate('applications')} 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 === '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 === 'Interview Scheduled' && <Clock className="w-5 h-5 text-blue-600" />}
{activity.action === 'Document Uploaded' && <FileText className="w-5 h-5 text-purple-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"> <td className="px-6 py-4">
<Badge className={`px-3 py-1 rounded-full text-[10px] uppercase font-bold tracking-wider ${ <Badge className={`px-3 py-1 rounded-full text-[10px] uppercase font-bold tracking-wider ${
app.overallStatus === 'Completed' ? 'bg-green-100 text-green-700' : 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} {app.overallStatus === 'Active' ? 'FDD Pending' : app.overallStatus}
</Badge> </Badge>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -64,7 +64,7 @@ export const SLAConfigPage: React.FC = () => {
<div className="space-y-6 max-w-7xl mx-auto"> <div className="space-y-6 max-w-7xl mx-auto">
<div> <div>
<h1 className="text-2xl font-bold text-slate-900 flex items-center gap-2"> <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 SLA & Escalation
</h1> </h1>
<p className="text-slate-500">Configure TAT rules and monitor live queue, breaches, and schedulers</p> <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"> <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-start justify-between">
<div className="flex items-center gap-3"> <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"> <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-amber-600" /> <Clock className="w-5 h-5 text-re-red" />
</div> </div>
<div> <div>
<CardTitle className="text-lg">{sla.activityName}</CardTitle> <CardTitle className="text-lg">{sla.activityName}</CardTitle>
<CardDescription className="flex items-center gap-1.5 mt-0.5"> <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} Target TAT: {sla.tatHours} {sla.tatUnit}
</span> </span>
</CardDescription> </CardDescription>
@ -133,7 +133,7 @@ export const SLAConfigPage: React.FC = () => {
variant="ghost" variant="ghost"
size="icon" size="icon"
onClick={() => handleEdit(sla)} 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" /> <Pen className="w-4 h-4" />
</Button> </Button>

View File

@ -1,6 +1,6 @@
import { Badge } from '@/components/ui/badge'; import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button'; 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 { Application } from '@/lib/mock-data';
import { MapPin, Phone, Mail, Award, Calendar, Building } from 'lucide-react'; import { MapPin, Phone, Mail, Award, Calendar, Building } from 'lucide-react';
import { formatDateTime } from '@/components/ui/utils'; import { formatDateTime } from '@/components/ui/utils';
@ -17,7 +17,7 @@ export function ApplicationCard({ application, onViewDetails }: ApplicationCardP
'Questionnaire Pending': 'bg-orange-500', 'Questionnaire Pending': 'bg-orange-500',
'Questionnaire Completed': 'bg-blue-500', 'Questionnaire Completed': 'bg-blue-500',
'Shortlisted': 'bg-cyan-500', 'Shortlisted': 'bg-cyan-500',
'Level 1 Pending': 'bg-amber-500', 'Level 1 Pending': 'bg-red-500',
'Level 1 Approved': 'bg-green-500', 'Level 1 Approved': 'bg-green-500',
'Level 2 Pending': 'bg-purple-500', 'Level 2 Pending': 'bg-purple-500',
'Level 2 Approved': 'bg-green-600', '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-600">Progress</span>
<span className="text-slate-900" data-testid="onboarding-application-card-progress-text">{application.progress}%</span> <span className="text-slate-900" data-testid="onboarding-application-card-progress-text">{application.progress}%</span>
</div> </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> </div>
{/* Deadline Warning */} {/* Deadline Warning */}
@ -138,7 +143,7 @@ export function ApplicationCard({ application, onViewDetails }: ApplicationCardP
<Button <Button
onClick={() => onViewDetails(application.id)} 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" data-testid="onboarding-application-card-view-button"
> >
View Details 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" id="isPrimaryModal"
name="isPrimary" name="isPrimary"
defaultChecked={editingBank?.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" data-testid="onboarding-is-primary-checkbox"
/> />
<Label htmlFor="isPrimaryModal" className="text-xs font-medium cursor-pointer">Set as primary account</Label> <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> </div>
<DialogFooter> <DialogFooter>
<Button type="button" variant="outline" size="sm" onClick={onClose} data-testid="onboarding-bank-details-cancel">Cancel</Button> <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} {isSubmitting ? <Loader2 className="w-4 h-4 animate-spin mr-2" /> : null}
{editingBank ? 'Update Account' : 'Save Bank Details'} {editingBank ? 'Update Account' : 'Save Bank Details'}
</Button> </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="space-y-6" data-testid="onboarding-questionnaire-view">
<div className="flex items-center justify-between mb-4"> <div className="flex items-center justify-between mb-4">
<div className="flex items-center gap-3"> <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> <h3 className="text-slate-900">Questionnaire Responses</h3>
</div> </div>
{totalScore !== undefined && ( {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> </div>
@ -62,12 +62,12 @@ const QuestionnaireResponseView: React.FC<QuestionnaireResponseViewProps> = ({ a
return ( return (
<div <div
key={resp.id} 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}`} data-testid={`onboarding-questionnaire-item-${index}`}
> >
<div className="flex items-start gap-3 mb-3"> <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"> <div className="w-8 h-8 rounded-full bg-red-50 flex items-center justify-center flex-shrink-0">
<span className="text-amber-600">{index + 1}</span> <span className="text-re-red">{index + 1}</span>
</div> </div>
<div className="flex-1"> <div className="flex-1">
<div className="flex items-center gap-2 mb-2"> <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" data-testid="onboarding-applicant-info-edit-firm-type"
> >
Proposed 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>
<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'} {application.constitutionType || 'Not Provided'}
</p> </p>
</div> </div>
@ -214,14 +214,14 @@ export function ApplicantInformationCard({
<div className="pt-6 border-t mt-6"> <div className="pt-6 border-t mt-6">
<div className="flex items-center justify-between mb-4"> <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"> <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> </h3>
{canEditStatutory && !isEditingStatutory && ( {canEditStatutory && !isEditingStatutory && (
<Button <Button
variant="ghost" variant="ghost"
size="sm" size="sm"
onClick={onEditStatutory} 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" data-testid="onboarding-applicant-info-edit-statutory"
> >
<Pencil className="w-3.5 h-3.5" /> <Pencil className="w-3.5 h-3.5" />
@ -232,7 +232,7 @@ export function ApplicantInformationCard({
{isEditingStatutory ? ( {isEditingStatutory ? (
<div <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" data-testid="onboarding-applicant-info-statutory-edit-form"
> >
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> <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" size="sm"
onClick={onSaveStatutory} onClick={onSaveStatutory}
disabled={isSavingStatutory} 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" data-testid="onboarding-applicant-info-statutory-save"
> >
{isSavingStatutory ? <Loader2 className="w-4 h-4 animate-spin" /> : 'Save Details'} {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}> <Select value={interviewType} onValueChange={setInterviewType}>
<SelectTrigger className="mt-2" data-testid="onboarding-schedule-type-select"><SelectValue placeholder="Select interview type" /></SelectTrigger> <SelectTrigger className="mt-2" data-testid="onboarding-schedule-type-select"><SelectValue placeholder="Select interview type" /></SelectTrigger>
<SelectContent> <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="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-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-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-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-re-red ml-2 inline" />}</div></SelectItem>
</SelectContent> </SelectContent>
</Select> </Select>
</div> </div>

View File

@ -69,15 +69,12 @@ export function ApplicationDetailsExtendedModals(props: ApplicationDetailsExtend
getDocumentsForStage, getDocumentsForStage,
setPreviewDoc, setPreviewDoc,
setShowPreviewModal, setShowPreviewModal,
flattenedStages,
setSelectedStage,
uploadDocType, uploadDocType,
setUploadDocType, setUploadDocType,
setUploadFile, setUploadFile,
isUploading, isUploading,
handleUpload, handleUpload,
uploadFile, uploadFile,
documentConfigs,
showPreviewModal, showPreviewModal,
previewDoc, previewDoc,
showFddFinalizeModal, 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="custom-scrollbar-slim min-h-0 flex-1 overflow-y-auto px-5 py-5">
<div className="space-y-6"> <div className="space-y-6">
{ktCriteria.length === 0 && ( {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. KT Matrix configuration is not available. Configure it in Master &gt; Interview Configurations.
</div> </div>
)} )}
@ -211,7 +208,7 @@ export function ApplicationDetailsExtendedModals(props: ApplicationDetailsExtend
<Separator /> <Separator />
{l2Fields.length === 0 && ( {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. Level 2 feedback configuration is not available. Configure it in Master &gt; Interview Configurations.
</div> </div>
)} )}
@ -315,7 +312,7 @@ export function ApplicationDetailsExtendedModals(props: ApplicationDetailsExtend
<Separator /> <Separator />
{l3Fields.length === 0 && ( {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. Level 3 feedback configuration is not available. Configure it in Master &gt; Interview Configurations.
</div> </div>
)} )}
@ -371,7 +368,7 @@ export function ApplicationDetailsExtendedModals(props: ApplicationDetailsExtend
<Dialog open={showDocumentsModal} onOpenChange={(open) => { setShowDocumentsModal(open); if (!open) setShowUploadForm(false); }}> <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"> <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"> <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> <DialogDescription className="text-slate-500">View and manage documents uploaded for this stage.</DialogDescription>
</DialogHeader> </DialogHeader>
{!showUploadForm ? ( {!showUploadForm ? (
@ -398,7 +395,7 @@ export function ApplicationDetailsExtendedModals(props: ApplicationDetailsExtend
<TableCell className="text-right py-3"> <TableCell className="text-right py-3">
<div className="flex gap-1 justify-end"> <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-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> </div>
</TableCell> </TableCell>
</TableRow> </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-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"> <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> <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>
</div> </div>
@ -425,7 +422,7 @@ export function ApplicationDetailsExtendedModals(props: ApplicationDetailsExtend
placeholder="Enter document name" placeholder="Enter document name"
value={uploadDocType} value={uploadDocType}
onChange={(e) => setUploadDocType(e.target.value)} 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" data-testid="onboarding-documents-name-input"
/> />
</div> </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> <Label className="text-slate-700 font-semibold px-1">Select File <span className="text-red-500">*</span></Label>
<Input <Input
type="file" 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) => { onChange={(e) => {
const file = e.target.files ? e.target.files[0] : null; const file = e.target.files ? e.target.files[0] : null;
setUploadFile(file); setUploadFile(file);
@ -449,7 +446,7 @@ export function ApplicationDetailsExtendedModals(props: ApplicationDetailsExtend
</div> </div>
<div className="flex flex-col sm:flex-row gap-3 pt-4"> <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-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>} {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> </Button>
</div> </div>
@ -461,11 +458,11 @@ export function ApplicationDetailsExtendedModals(props: ApplicationDetailsExtend
<Dialog open={showFddFinalizeModal} onOpenChange={setShowFddFinalizeModal}> <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"> <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"> <div className="p-8 space-y-6 bg-white">
<DialogHeader> <DialogHeader>
<DialogTitle className="text-2xl font-black text-slate-900 text-center tracking-tight">Finalize FDD Audit</DialogTitle> <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> </DialogHeader>
<div className="space-y-4"> <div className="space-y-4">
{(currentUser?.role !== 'FDD' && currentUser?.roleCode !== 'FDD') && ( {(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> <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"> <div className="flex gap-2">
{['Recommended', 'Qualified with Observations', 'Not Recommended'].map((rec) => ( {['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>
</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> <Label className="text-[10px] font-black uppercase tracking-widest text-slate-400">Findings Summary</Label>
<Textarea <Textarea
placeholder="Summarize key financial findings or discrepancies..." 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} value={fddAuditFindings}
onChange={(e) => setFddAuditFindings(e.target.value)} onChange={(e) => setFddAuditFindings(e.target.value)}
data-testid="onboarding-fdd-findings-textarea" data-testid="onboarding-fdd-findings-textarea"
/> />
</div> </div>
</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"> <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 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 <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} disabled={isFinalizingFdd || !fddAuditFindings}
data-testid="onboarding-fdd-finalize-submit" data-testid="onboarding-fdd-finalize-submit"
onClick={async () => { onClick={async () => {
@ -569,16 +566,16 @@ export function ApplicationDetailsExtendedModals(props: ApplicationDetailsExtend
<Dialog open={showFirmTypeModal} onOpenChange={setShowFirmTypeModal}> <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"> <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> <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> <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>
<div className="p-8 space-y-6 bg-white"> <div className="p-8 space-y-6 bg-white">
<div className="space-y-2"> <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> <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}> <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> <SelectContent>
<SelectItem value="Proprietorship" data-testid="onboarding-firm-type-proprietorship">Proprietorship</SelectItem> <SelectItem value="Proprietorship" data-testid="onboarding-firm-type-proprietorship">Proprietorship</SelectItem>
<SelectItem value="Partnership" data-testid="onboarding-firm-type-partnership">Partnership</SelectItem> <SelectItem value="Partnership" data-testid="onboarding-firm-type-partnership">Partnership</SelectItem>
@ -590,7 +587,7 @@ export function ApplicationDetailsExtendedModals(props: ApplicationDetailsExtend
</div> </div>
<div className="flex gap-3 pt-2"> <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 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>
</div> </div>
</DialogContent> </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 { toast } from 'sonner';
import { onboardingService } from '@/services/onboarding.service'; import { onboardingService } from '@/services/onboarding.service';
import { cn, formatDateTime } from '@/components/ui/utils'; import { cn, formatDateTime } from '@/components/ui/utils';
@ -8,13 +8,7 @@ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
interface Props { interface Props {
application: any; application: any;
currentUser: any;
documents: any[]; documents: any[];
fddAgencies: any[];
selectedAgencyId: string;
setSelectedAgencyId: (v: string) => void;
isAssigningAgency: boolean;
handleAssignAgency: () => void;
setPreviewDoc: (d: any) => void; setPreviewDoc: (d: any) => void;
setShowPreviewModal: (v: boolean) => void; setShowPreviewModal: (v: boolean) => void;
setIsUploading: (v: boolean) => void; setIsUploading: (v: boolean) => void;
@ -24,13 +18,7 @@ interface Props {
export function ApplicationDetailsFddAuditContent({ export function ApplicationDetailsFddAuditContent({
application, application,
currentUser,
documents, documents,
fddAgencies,
selectedAgencyId,
setSelectedAgencyId,
isAssigningAgency,
handleAssignAgency,
setPreviewDoc, setPreviewDoc,
setShowPreviewModal, setShowPreviewModal,
setIsUploading, setIsUploading,
@ -82,47 +70,6 @@ export function ApplicationDetailsFddAuditContent({
The Financial Due Diligence process has not been initiated for this application yet. The Financial Due Diligence process has not been initiated for this application yet.
</p> </p>
</div> </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> </div>
); );
} }
@ -132,8 +79,8 @@ export function ApplicationDetailsFddAuditContent({
{hasAssignment && ( {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 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="flex items-center gap-3">
<div className="p-2 bg-amber-100 rounded-lg"> <div className="p-2 bg-red-50 rounded-lg">
<ShieldCheck className="w-5 h-5 text-amber-600" /> <ShieldCheck className="w-5 h-5 text-re-red" />
</div> </div>
<div> <div>
<h4 className="text-sm font-bold text-slate-900">FDD Assignment Active</h4> <h4 className="text-sm font-bold text-slate-900">FDD Assignment Active</h4>
@ -230,7 +177,7 @@ export function ApplicationDetailsFddAuditContent({
</div> </div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4" data-testid="onboarding-fdd-support-docs-grid"> <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) => ( {(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="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="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"> <div className="overflow-hidden">
@ -239,13 +186,13 @@ export function ApplicationDetailsFddAuditContent({
</div> </div>
</div> </div>
<div className="flex gap-1"> <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'; const baseUrl = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:5000';
window.open(`${baseUrl}/${doc.filePath}`, '_blank'); window.open(`${baseUrl}/${doc.filePath}`, '_blank');
}} data-testid={`onboarding-fdd-support-doc-download-${index}`}> }} data-testid={`onboarding-fdd-support-doc-download-${index}`}>
<Download className="w-4 h-4" /> <Download className="w-4 h-4" />
</Button> </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" /> <Eye className="w-4 h-4" />
</Button> </Button>
</div> </div>

View File

@ -72,7 +72,7 @@ export function ApplicationDetailsHeader({
<div className="flex flex-wrap gap-2"> <div className="flex flex-wrap gap-2">
<Button <Button
variant="outline" 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} onClick={onOpenWorknotes}
data-testid="onboarding-details-view-work-notes" data-testid="onboarding-details-view-work-notes"
> >

View File

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

View File

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

View File

@ -40,7 +40,7 @@ import {
TableHeader, TableHeader,
TableRow, TableRow,
} from '@/components/ui/table'; } 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 { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog';
import { Label } from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import { Textarea } from '@/components/ui/textarea'; 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', 'In Review': 'bg-slate-100 text-slate-800',
'Level 3 Approved': 'bg-green-100 text-green-800', 'Level 3 Approved': 'bg-green-100 text-green-800',
'FDD Verification': 'bg-indigo-100 text-indigo-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 In Progress': 'bg-sky-100 text-sky-800',
'LOI Issued': 'bg-sky-100 text-sky-800', 'LOI Issued': 'bg-sky-100 text-sky-800',
'Dealer Code Generation': 'bg-purple-100 text-purple-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 In Progress': 'bg-violet-100 text-violet-800',
'EOR Complete': 'bg-violet-100 text-violet-800', 'EOR Complete': 'bg-violet-100 text-violet-800',
'LOA Pending': 'bg-pink-100 text-pink-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', 'Approved': 'bg-green-100 text-green-800',
'Rejected': 'bg-red-100 text-red-800', 'Rejected': 'bg-red-100 text-red-800',
'Disqualified': 'bg-gray-100 text-gray-800', 'Disqualified': 'bg-gray-100 text-gray-800',
'Onboarded': 'bg-emerald-100 text-emerald-800', 'Onboarded': 'bg-emerald-100 text-emerald-800',
'LOI Approved': 'bg-sky-100 text-sky-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 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 */ /** 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 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', 'LOA Issued': 'bg-pink-100 text-pink-800',
}; };
return colors[status] || 'bg-gray-100 text-gray-800'; return colors[status] || 'bg-gray-100 text-gray-800';
@ -257,12 +257,12 @@ export function AllApplicationsPage({ onViewDetails, initialFilter = 'all' }: Al
return ( return (
<div className="space-y-6"> <div className="space-y-6">
{/* Info Banner */} {/* 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"> <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> <div>
<h3 className="text-amber-900 mb-1">DD Workflow - Initial Application Review</h3> <h3 className="text-red-900 mb-1">DD Workflow - Initial Application Review</h3>
<p className="text-amber-800"> <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. 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. Once shortlisted, applications will be removed from here and moved to the <strong>Dealership Requests</strong> page for further processing.
</p> </p>
@ -331,7 +331,7 @@ export function AllApplicationsPage({ onViewDetails, initialFilter = 'all' }: Al
variant={viewMode === 'grid' ? 'default' : 'outline'} variant={viewMode === 'grid' ? 'default' : 'outline'}
size="sm" size="sm"
onClick={() => setViewMode('grid')} 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" data-testid="onboarding-all-apps-grid-view-btn"
> >
<Grid3x3 className="w-4 h-4 mr-2" /> <Grid3x3 className="w-4 h-4 mr-2" />
@ -341,7 +341,7 @@ export function AllApplicationsPage({ onViewDetails, initialFilter = 'all' }: Al
variant={viewMode === 'table' ? 'default' : 'outline'} variant={viewMode === 'table' ? 'default' : 'outline'}
size="sm" size="sm"
onClick={() => setViewMode('table')} 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" data-testid="onboarding-all-apps-table-view-btn"
> >
<List className="w-4 h-4 mr-2" /> <List className="w-4 h-4 mr-2" />
@ -380,7 +380,7 @@ export function AllApplicationsPage({ onViewDetails, initialFilter = 'all' }: Al
{/* Applications Grid/Table */} {/* Applications Grid/Table */}
{loading ? ( {loading ? (
<div className="flex justify-center items-center h-96 bg-white rounded-lg border border-slate-200"> <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> </div>
) : viewMode === 'grid' ? ( ) : 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"> <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>
<TableCell> <TableCell>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Progress value={app.progress} className="w-20" data-testid={`onboarding-all-apps-progress-bar-${idx}`} /> <ApplicationProgressBar
<span className="text-slate-600" data-testid={`onboarding-all-apps-progress-text-${idx}`}>{app.progress}%</span> value={app.progress}
status={app.status}
showPercent
data-testid={`onboarding-all-apps-progress-bar-${idx}`}
/>
</div> </div>
</TableCell> </TableCell>
<TableCell> <TableCell>

View File

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

View File

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

View File

@ -408,20 +408,20 @@ export function FDDApplicationDetails() {
{/* SECTION 2: MY SUBMISSIONS */} {/* SECTION 2: MY SUBMISSIONS */}
<div data-testid="onboarding-fdd-details-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"> <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 My Uploaded Reports
</p> </p>
<div className="space-y-2"> <div className="space-y-2">
{application.uploadedDocuments?.filter((d: any) => d.uploader?.roleCode === 'FDD').map((doc: any, i: number) => ( {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="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" /> <FileText className="w-4 h-4" />
</div> </div>
<div> <div>
<div className="flex items-center gap-2"> <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> <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> </div>
<p className="text-[10px] text-slate-400 font-medium" data-testid={`onboarding-fdd-details-my-report-meta-${i}`}> <p className="text-[10px] text-slate-400 font-medium" data-testid={`onboarding-fdd-details-my-report-meta-${i}`}>
{doc.documentType} {formatDateTime(doc.createdAt)} {doc.documentType} {formatDateTime(doc.createdAt)}
@ -433,7 +433,7 @@ export function FDDApplicationDetails() {
<button <button
type="button" type="button"
onClick={() => handlePreview(doc)} 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}`} data-testid={`onboarding-fdd-details-my-report-preview-${i}`}
> >
<Eye className="w-4 h-4" /> <Eye className="w-4 h-4" />
@ -576,22 +576,22 @@ export function FDDApplicationDetails() {
<Dialog open={showFinalizeModal} onOpenChange={setShowFinalizeModal}> <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"> <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="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="absolute inset-0 bg-gradient-to-br from-re-red/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"> <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-amber-500" /> <ShieldCheck className="w-8 h-8 text-re-red" />
</div> </div>
</div> </div>
<div className="p-8 space-y-4"> <div className="p-8 space-y-4">
<DialogHeader> <DialogHeader>
<DialogTitle className="text-2xl font-bold text-slate-900 text-center" data-testid="onboarding-fdd-details-finalize-title">Submit Audit Report</DialogTitle> <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"> <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> </DialogDescription>
</DialogHeader> </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"> <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-amber-600 shrink-0 mt-0.5" /> <Info className="w-5 h-5 text-re-red shrink-0 mt-0.5" />
<p className="text-xs text-amber-800 leading-normal"> <p className="text-xs text-red-800 leading-normal">
Once submitted, you cannot edit the findings. Ensure all documents are uploaded. Once submitted, you cannot edit the findings. Ensure all documents are uploaded.
</p> </p>
</div> </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> <Label className="text-[10px] font-black uppercase tracking-widest text-slate-400">Detailed Audit Findings & Remarks</Label>
<Textarea <Textarea
placeholder="Enter detailed financial observations..." 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} value={fddAuditFindings}
onChange={(e) => setFddAuditFindings(e.target.value)} onChange={(e) => setFddAuditFindings(e.target.value)}
data-testid="onboarding-fdd-details-finalize-remarks" data-testid="onboarding-fdd-details-finalize-remarks"
@ -621,7 +621,7 @@ export function FDDApplicationDetails() {
Cancel Cancel
</Button> </Button>
<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" data-testid="onboarding-fdd-details-finalize-confirm"
onClick={async () => { onClick={async () => {
try { try {

View File

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

View File

@ -231,7 +231,7 @@ export function FinanceOnboardingPage({ onViewPaymentDetails }: FinanceOnboardin
className={ className={
statusLabel === 'Verified' ? 'bg-emerald-50 text-emerald-700 border-emerald-100 px-3 py-1 rounded-full' : 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' : 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" variant="outline"
data-testid={`onboarding-finance-queue-status-${idx}`} data-testid={`onboarding-finance-queue-status-${idx}`}

View File

@ -237,7 +237,7 @@ export function NonOpportunitiesPage({ onViewDetails }: NonOpportunitiesPageProp
</div> </div>
<div className="bg-white rounded-lg border border-slate-200 p-4" data-testid="onboarding-non-opps-stat-exp"> <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-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} {paginationMeta?.stats?.withExperience || 0}
</p> </p>
</div> </div>
@ -355,7 +355,7 @@ export function NonOpportunitiesPage({ onViewDetails }: NonOpportunitiesPageProp
{selectedIds.length > 0 && ( {selectedIds.length > 0 && (
<Button <Button
className="bg-amber-600 hover:bg-amber-700 font-bold" className="bg-re-red hover:bg-re-red-hover font-bold"
onClick={handleBulkConvert} onClick={handleBulkConvert}
disabled={isBulkConverting} disabled={isBulkConverting}
> >
@ -396,7 +396,7 @@ export function NonOpportunitiesPage({ onViewDetails }: NonOpportunitiesPageProp
{isGlobalLoading ? ( {isGlobalLoading ? (
<TableRow> <TableRow>
<TableCell colSpan={11} className="text-center py-20"> <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> <p className="text-slate-500 text-sm">Loading applications...</p>
</TableCell> </TableCell>
</TableRow> </TableRow>
@ -413,7 +413,7 @@ export function NonOpportunitiesPage({ onViewDetails }: NonOpportunitiesPageProp
<TableRow <TableRow
key={lead.id} key={lead.id}
data-testid={`onboarding-non-opps-row-${idx}`} 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> <TableCell>
<Checkbox <Checkbox

View File

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

View File

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

View File

@ -17,6 +17,14 @@ import { toast } from 'sonner';
import { API } from '@/api/API'; import { API } from '@/api/API';
import { SlaBadge } from '@/components/sla/SlaBadge'; import { SlaBadge } from '@/components/sla/SlaBadge';
import { useSlaBatchStatus } from '@/hooks/useSlaBatchStatus'; import { useSlaBatchStatus } from '@/hooks/useSlaBatchStatus';
import {
getCurrentStageBadgeClass,
getOffboardingRequestStatusBadgeClass,
getStatusLabelBadgeClass,
getStatusProgressBarClass,
isOffboardingTerminalNegative,
WORKFLOW_IN_PROGRESS_ACCENT,
} from '@/lib/offboardingDisplay';
interface RelocationRequestDetailsProps { interface RelocationRequestDetailsProps {
requestId: string; requestId: string;
@ -174,13 +182,12 @@ const requiredDocuments = [
'Water supply documents' 'Water supply documents'
]; ];
const getStatusColor = (status: string) => { const getStatusColor = (status: string) => getStatusLabelBadgeClass(status);
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'; const getDocChecklistUploadButtonClass = (isRejected: boolean) =>
if (status.includes('Rejected') || status.includes('Revoked')) return 'bg-red-100 text-red-700 border-red-300'; isRejected
if (status.includes('Collection') || status.includes('Completion') || status.includes('Infra')) return 'bg-blue-100 text-blue-700 border-blue-300'; ? 'h-7 px-2 text-red-700 hover:bg-red-50 hover:text-red-800 flex-shrink-0'
return 'bg-slate-100 text-slate-700 border-slate-300'; : 'h-7 px-2 text-slate-700 hover:bg-slate-50 flex-shrink-0';
};
const getApiErrorMessage = (error: any, fallback: string) => { const getApiErrorMessage = (error: any, fallback: string) => {
const responseData = error?.response?.data || error?.data; const responseData = error?.response?.data || error?.data;
@ -224,6 +231,10 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
const [activeTab, setActiveTab] = useState('workflow'); const [activeTab, setActiveTab] = useState('workflow');
const [isPreviewOpen, setIsPreviewOpen] = useState(false); const [isPreviewOpen, setIsPreviewOpen] = useState(false);
const [selectedDoc, setSelectedDoc] = useState<any>(null); 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(() => { useEffect(() => {
fetchRequestDetails(); fetchRequestDetails();
@ -368,6 +379,15 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
? 100 ? 100
: Math.min(100, Math.round((dbOrdinal / workflowStages.length) * 100)); : Math.min(100, Math.round((dbOrdinal / workflowStages.length) * 100));
const displayProgressPct = allWorkflowComplete ? 100 : timelineProgressPct; 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 const missingRequiredDocs = request
? requiredDocuments.filter((doc) => !request.documents?.some((d: any) => ? 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 { try {
const response = await API.rejectRelocationDocument(requestId, documentId, { remarks: 'Rejected by reviewer' }) as any; setIsRejectingDoc(true);
if (response.data.success) { const response = await API.rejectRelocationDocument(requestId, rejectDocId, {
remarks: rejectDocReason.trim()
}) as any;
if (response.data?.success) {
toast.success('Document rejected successfully'); toast.success('Document rejected successfully');
setRejectDocDialogOpen(false);
setRejectDocId(null);
setRejectDocReason('');
fetchRequestDetails(true); fetchRequestDetails(true);
fetchAuditLogs(); fetchAuditLogs();
} else {
toast.error(response.data?.message || 'Failed to reject document');
} }
} catch (error) { } catch (error) {
console.error('Reject document error:', error); console.error('Reject document error:', error);
toast.error(getApiErrorMessage(error, 'Failed to reject document')); toast.error(getApiErrorMessage(error, 'Failed to reject document'));
} finally {
setIsRejectingDoc(false);
} }
}; };
@ -552,7 +586,7 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
if (isLoading) { if (isLoading) {
return ( return (
<div className="flex flex-col items-center justify-center min-h-[400px] space-y-4"> <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> <p className="text-slate-500 font-medium">Loading request details...</p>
</div> </div>
); );
@ -594,7 +628,7 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<Button <Button
variant="outline" 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}`, { onClick={() => navigate(`/worknotes/relocation/${requestId}`, {
state: { state: {
applicationName: request?.outlet?.name || 'Relocation', applicationName: request?.outlet?.name || 'Relocation',
@ -606,13 +640,13 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
<MessageSquare className="w-4 h-4 mr-2" /> <MessageSquare className="w-4 h-4 mr-2" />
View Work Notes View Work Notes
{request?.worknotes?.length > 0 && ( {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} {request.worknotes.length}
</Badge> </Badge>
)} )}
</Button> </Button>
<Badge className={getStatusColor(request.status)}> <Badge className={requestStatusBadgeClass}>
{request.status} {request.status}
</Badge> </Badge>
</div> </div>
@ -642,7 +676,7 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
</div> </div>
</div> </div>
<div className="flex items-center gap-2"> <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> <div>
<p className="text-slate-600 text-xs">To (Proposed)</p> <p className="text-slate-600 text-xs">To (Proposed)</p>
<p className="text-slate-900 text-sm">{request.proposedLocation || `${request.newAddress}, ${request.newCity}`}</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} Type: {request.relocationType}
</Badge> </Badge>
{request.distance && ( {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} Distance: {request.distance}
</Badge> </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-600 text-sm mb-1">Request Information</p>
<p className="text-slate-900 text-sm">Submitted: {formatDateTime(request.createdAt)}</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-600 text-sm">By: {request.dealer?.fullName}</p>
<p className="text-slate-900 text-sm mt-2"> <div className="mt-2 flex flex-wrap items-center gap-2">
Current Stage: {String(request.currentStage || '').replace(/_/g, ' ')} <span className="text-slate-600 text-sm">Current Stage:</span>
</p> <Badge className={getCurrentStageBadgeClass(request.currentStage, request.status)}>
{String(request.currentStage || '').replace(/_/g, ' ')}
</Badge>
</div>
</div> </div>
</div> </div>
@ -704,23 +741,32 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
<CardContent> <CardContent>
{/* Workflow Progress Tab */} {/* Workflow Progress Tab */}
<TabsContent value="workflow" className="mt-0"> <TabsContent value="workflow" className="mt-0 status-progress-ui">
{/* Progress Bar */} {/* Progress Bar */}
<div className="mb-8"> <div className="mb-8">
<div className="flex items-center justify-between mb-2"> <div className="flex items-center justify-between mb-2">
<span className="text-slate-900">Overall Progress</span> <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>
<div className="h-3 bg-slate-200 rounded-full overflow-hidden"> <div className="h-3 bg-slate-200 rounded-full overflow-hidden">
<div <div
className="h-full bg-amber-600 transition-all duration-500" className={`h-full transition-all duration-500 ${statusProgressBarClass}`}
style={{ width: `${displayProgressPct}%` }} style={{ width: `${displayProgressPct}%` }}
/> />
</div> </div>
</div> </div>
{workflowProgressMismatch && ( {workflowTerminalNegative && (
<div className="mb-6 rounded-lg border border-amber-300 bg-amber-50 px-4 py-3 text-sm text-amber-900"> <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 <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)}). 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 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) */} {/* Workflow stages + per-stage timeline (same pattern as ResignationDetails progress tab) */}
<div className="space-y-4"> <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 isCompleted = allWorkflowComplete || index < dbOrdinal - 1;
const isCurrent = !allWorkflowComplete && index === dbOrdinal - 1; const isCurrent = !allWorkflowComplete && index === dbOrdinal - 1;
const stageTimelineEntries = getRelocationTimelineEntriesForStage( const stageTimelineEntries = getRelocationTimelineEntriesForStage(
@ -758,7 +813,7 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
isCompleted isCompleted
? 'bg-green-100 text-green-600' ? 'bg-green-100 text-green-600'
: isCurrent : isCurrent
? 'bg-amber-100 text-amber-600' ? WORKFLOW_IN_PROGRESS_ACCENT.icon
: 'bg-slate-100 text-slate-400' : 'bg-slate-100 text-slate-400'
}`} }`}
> >
@ -779,9 +834,7 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
<div <div
className={`flex-1 pb-8 ${ className={`flex-1 pb-8 ${
isCurrent isCurrent ? WORKFLOW_IN_PROGRESS_ACCENT.panel : ''
? 'bg-amber-50 -ml-4 pl-4 pr-4 py-3 rounded-lg border border-amber-200'
: ''
}`} }`}
> >
<div className="flex items-center justify-between mb-1 gap-2"> <div className="flex items-center justify-between mb-1 gap-2">
@ -791,7 +844,7 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
isCompleted isCompleted
? 'text-green-700' ? 'text-green-700'
: isCurrent : isCurrent
? 'text-amber-900' ? WORKFLOW_IN_PROGRESS_ACCENT.title
: 'text-slate-900' : 'text-slate-900'
} }
> >
@ -799,7 +852,7 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
</h4> </h4>
<p <p
className={`text-sm ${ className={`text-sm ${
isCurrent ? 'text-amber-700' : 'text-slate-600' isCurrent ? WORKFLOW_IN_PROGRESS_ACCENT.subtitle : 'text-slate-600'
}`} }`}
> >
Responsible: {stage.role} Responsible: {stage.role}
@ -816,7 +869,7 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
isCompleted isCompleted
? 'bg-green-100 text-green-700 border-green-300' ? 'bg-green-100 text-green-700 border-green-300'
: isCurrent : isCurrent
? 'bg-amber-100 text-amber-700 border-amber-300' ? WORKFLOW_IN_PROGRESS_ACCENT.stageBadge
: 'bg-slate-100 text-slate-500 border-slate-300' : 'bg-slate-100 text-slate-500 border-slate-300'
} }
> >
@ -862,7 +915,8 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
</div> </div>
</div> </div>
); );
})} })
)}
</div> </div>
</TabsContent> </TabsContent>
@ -893,7 +947,7 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
<DialogTrigger asChild> <DialogTrigger asChild>
<Button <Button
size="sm" size="sm"
className="bg-amber-600 hover:bg-amber-700" className="bg-re-red hover:bg-re-red-hover"
onClick={() => { onClick={() => {
setDocTypeLocked(false); setDocTypeLocked(false);
setSelectedDocType(requiredDocuments[0]); setSelectedDocType(requiredDocuments[0]);
@ -917,8 +971,8 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
{docTypeLocked ? ( {docTypeLocked ? (
<div> <div>
<Label>Document</Label> <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"> <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-amber-600 text-white border-transparent"> <Badge className="bg-re-red text-white border-transparent">
{selectedDocType} {selectedDocType}
</Badge> </Badge>
</div> </div>
@ -958,7 +1012,7 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
Cancel Cancel
</Button> </Button>
<Button <Button
className="bg-amber-600 hover:bg-amber-700" className="bg-re-red hover:bg-re-red-hover"
onClick={handleUploadDocument} onClick={handleUploadDocument}
disabled={isUploading} disabled={isUploading}
> >
@ -982,27 +1036,40 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
const uploaded = request.documents?.find((d: any) => const uploaded = request.documents?.find((d: any) =>
d.type === doc || d.name.toLowerCase().includes(doc.toLowerCase().split(' ')[0]) d.type === doc || d.name.toLowerCase().includes(doc.toLowerCase().split(' ')[0])
); );
const isRejected = uploaded && String(uploaded.status) === 'Rejected';
const ok = uploaded && !isRejected;
return ( return (
<div <div
key={index} 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"> <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" /> <CheckCircle2 className="w-4 h-4 text-green-600 flex-shrink-0" />
) : ( ) : (
<AlertCircle className="w-4 h-4 text-slate-400 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} {doc}
</span> </span>
</div> </div>
{!uploaded && ( {!ok && (
<Button <Button
size="sm" size="sm"
variant="ghost" variant="ghost"
className="h-7 px-2 text-amber-700 hover:bg-amber-50 flex-shrink-0" className={getDocChecklistUploadButtonClass(!!isRejected)}
onClick={() => { onClick={() => {
setSelectedDocType(doc); setSelectedDocType(doc);
setSelectedFile(null); setSelectedFile(null);
@ -1011,7 +1078,7 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
}} }}
> >
<Upload className="w-3.5 h-3.5 mr-1" /> <Upload className="w-3.5 h-3.5 mr-1" />
Upload {isRejected ? 'Re-upload' : 'Upload'}
</Button> </Button>
)} )}
</div> </div>
@ -1040,7 +1107,10 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
</TableHeader> </TableHeader>
<TableBody> <TableBody>
{request.documents.map((doc: any) => ( {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"> <TableCell className="text-slate-900">
{doc.name} {doc.name}
</TableCell> </TableCell>
@ -1101,7 +1171,11 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
size="sm" size="sm"
variant="destructive" variant="destructive"
className="h-8 gap-1" className="h-8 gap-1"
onClick={() => handleRejectDocument(doc.id)} onClick={() => {
setRejectDocId(doc.id);
setRejectDocReason('');
setRejectDocDialogOpen(true);
}}
title="Reject Document" title="Reject Document"
> >
<AlertCircle className="w-4 h-4" /> <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"> <div className="bg-slate-50 border border-dashed border-slate-300 rounded-lg p-12 text-center">
{isEorLoading ? ( {isEorLoading ? (
<div className="flex flex-col items-center gap-2"> <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> <p className="text-slate-500">Fetching checklist...</p>
</div> </div>
) : ( ) : (
@ -1192,7 +1266,7 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
checked={item.isCompliant} checked={item.isCompliant}
onChange={(e) => handleUpdateEorItem(item.description, e.target.checked, item.itemType)} onChange={(e) => handleUpdateEorItem(item.description, e.target.checked, item.itemType)}
disabled={eorChecklist.status === 'Completed' || (currentUser?.role !== 'NBH' && currentUser?.role !== 'Super Admin')} 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>
<TableCell> <TableCell>
@ -1230,7 +1304,7 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
</Button> </Button>
</> </>
) : item.proofDocumentId ? ( ) : 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 <Button
type="button" type="button"
@ -1275,7 +1349,7 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
)} )}
{!eorChecklist.items?.every((i: any) => i.isCompliant) && ( {!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. All items must be marked as compliant before final submission.
</p> </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') ${(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' ? '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') : (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') : (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-emerald-100 text-emerald-700 border-emerald-200'
: 'bg-slate-100 text-slate-700 border-slate-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"> <CardContent className="space-y-4">
<div> <div>
<p className="text-slate-600 text-sm">Current Stage</p> <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>
<div> <div>
<p className="text-slate-600 text-sm">Progress</p> <p className="text-slate-600 text-sm">Progress</p>
<div className="flex items-center gap-2 mt-2"> <div className="flex items-center gap-2 mt-2">
<div className="flex-1 h-2 bg-slate-200 rounded-full overflow-hidden"> <div className="flex-1 h-2 bg-slate-200 rounded-full overflow-hidden">
<div <div
className="h-full bg-amber-600 transition-all duration-300" className={`h-full transition-all duration-300 ${statusProgressBarClass}`}
style={{ width: `${displayProgressPct}%` }} style={{ width: `${displayProgressPct}%` }}
/> />
</div> </div>
@ -1388,9 +1464,9 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
Approve Request Approve Request
</Button> </Button>
{!canApprove && ( {!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. Approval is blocked until mandatory documents are uploaded and verified for this stage.
</p> </div>
)} )}
<Button <Button
@ -1410,7 +1486,7 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
{canSendBack && ( {canSendBack && (
<Button <Button
variant="outline" 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')} onClick={() => handleAction('sendBack')}
disabled={isSubmitting} disabled={isSubmitting}
> >
@ -1460,34 +1536,6 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
</CardContent> </CardContent>
</Card> </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>
</div> </div>
@ -1551,12 +1599,12 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
actionType === 'approve' actionType === 'approve'
? 'bg-green-600 hover:bg-green-700' ? 'bg-green-600 hover:bg-green-700'
: actionType === 'reject' : actionType === 'reject'
? 'bg-red-600 hover:bg-red-700' ? 'bg-re-red hover:bg-re-red-hover'
: actionType === 'sendBack' : actionType === 'sendBack'
? 'bg-amber-600 hover:bg-amber-700' ? 'bg-re-red hover:bg-re-red-hover'
: actionType === 'revoke' : actionType === 'revoke'
? 'bg-red-700 hover:bg-red-800' ? 'bg-re-red hover:bg-re-red-hover'
: 'bg-amber-600 hover:bg-amber-700' : 'bg-re-red hover:bg-re-red-hover'
} }
disabled={isSubmitting} disabled={isSubmitting}
> >
@ -1582,6 +1630,40 @@ export function RelocationRequestDetails({ requestId, onBack, currentUser }: Rel
{/* Worknotes Dialog */} {/* Worknotes Dialog */}
{/* Worknotes Dialog - handled in Header */} {/* 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 <DocumentPreviewModal
isOpen={isPreviewOpen} isOpen={isPreviewOpen}
onClose={() => setIsPreviewOpen(false)} onClose={() => setIsPreviewOpen(false)}

View File

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

View File

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

View File

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

View File

@ -8,7 +8,6 @@ import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { Textarea } from '@/components/ui/textarea'; import { Textarea } from '@/components/ui/textarea';
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { API } from '@/api/API'; import { API } from '@/api/API';
import { slaService, SlaStatusSnapshot } from '@/services/sla.service'; import { slaService, SlaStatusSnapshot } from '@/services/sla.service';
@ -291,13 +290,7 @@ export function TerminationPage({ currentUser, onViewDetails }: TerminationPageP
return ( return (
<div className="space-y-6"> <div className="space-y-6">
{/* Warning Alert */} {/* 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 */} {/* Header Stats */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-4"> <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)' AWAITING_FNF_LWD_PENDING: 'Awaiting F&F (LWD Pending)'
} as const; } 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 */ /** Maps backend status / stage strings to user-facing labels */
export function formatOffboardingStatusLabel(value: string | null | undefined): string { export function formatOffboardingStatusLabel(value: string | null | undefined): string {
if (!value) return 'Pending'; 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); label = label.replace(/\bLWD\b/g, LAST_WORKING_DAY_LABEL);
return 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 ( if (loading) return (
<div className="flex items-center justify-center h-screen bg-slate-50"> <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> </div>
); );
@ -210,8 +210,8 @@ const PublicQuestionnairePage: React.FC = () => {
<div className="flex items-center justify-center mb-7"> <div className="flex items-center justify-center mb-7">
<img src="/assets/images/Re_Logo.png" alt="Royal Enfield" className="h-12 w-auto" /> <img src="/assets/images/Re_Logo.png" alt="Royal Enfield" className="h-12 w-auto" />
</div> </div>
<div className="h-1 w-24 bg-amber-600 mx-auto mb-4"></div> <div className="h-1 w-24 bg-re-red mx-auto mb-4"></div>
<h2 className="text-amber-400 text-xl mb-4 font-light">Dealership Partner Application</h2> <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"> <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. Thank you for your interest in becoming a Royal Enfield business partner.
Please complete this questionnaire to help us understand your profile and aspirations. 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="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-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 className="text-slate-400 text-xs uppercase tracking-wider">Questions</div>
</div> </div>
<div className="h-10 w-px bg-slate-700"></div> <div className="h-10 w-px bg-slate-700"></div>
<div className="text-center px-4"> <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 className="text-slate-400 text-xs uppercase tracking-wider">Sections</div>
</div> </div>
</div> </div>
@ -240,7 +240,7 @@ const PublicQuestionnairePage: React.FC = () => {
onClick={() => setActiveSection(section)} onClick={() => setActiveSection(section)}
className={`flex items-center gap-2 px-4 py-2 rounded-lg whitespace-nowrap transition-all text-sm font-medium className={`flex items-center gap-2 px-4 py-2 rounded-lg whitespace-nowrap transition-all text-sm font-medium
${activeSection === section ${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' : 'bg-slate-700/50 text-slate-300 hover:bg-slate-700 hover:text-white'
}`} }`}
> >
@ -260,8 +260,8 @@ const PublicQuestionnairePage: React.FC = () => {
{/* Question Content */} {/* Question Content */}
<div className="bg-white rounded-b-lg shadow-xl border border-slate-200 border-t-0 min-h-[400px]"> <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="p-8">
<div className="flex items-start gap-4 pb-6 border-b-2 border-amber-100 mb-8"> <div className="flex items-start gap-4 pb-6 border-b-2 border-red-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="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" /> <Users className="w-6 h-6" />
</div> </div>
<div className="flex-1"> <div className="flex-1">
@ -276,7 +276,7 @@ const PublicQuestionnairePage: React.FC = () => {
{activeQuestions.map((q, idx) => ( {activeQuestions.map((q, idx) => (
<div key={q.id} className="group animate-in fade-in duration-500" style={{ animationDelay: `${idx * 100}ms` }}> <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="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} {idx + 1}
</div> </div>
<div className="flex-1 space-y-3"> <div className="flex-1 space-y-3">
@ -291,7 +291,7 @@ const PublicQuestionnairePage: React.FC = () => {
{(q.inputType === 'text' || q.inputType === 'email' || q.inputType === 'number') && ( {(q.inputType === 'text' || q.inputType === 'email' || q.inputType === 'number') && (
<input <input
type={q.inputType} 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..." placeholder="Type your answer here..."
value={responses[q.id] || ''} value={responses[q.id] || ''}
onChange={(e) => handleInputChange(q.id, e.target.value)} onChange={(e) => handleInputChange(q.id, e.target.value)}
@ -300,7 +300,7 @@ const PublicQuestionnairePage: React.FC = () => {
{q.inputType === 'textarea' && ( {q.inputType === 'textarea' && (
<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..." placeholder="Type your answer here..."
value={responses[q.id] || ''} value={responses[q.id] || ''}
onChange={(e) => handleInputChange(q.id, e.target.value)} onChange={(e) => handleInputChange(q.id, e.target.value)}
@ -316,7 +316,7 @@ const PublicQuestionnairePage: React.FC = () => {
<input <input
type="radio" type="radio"
name={`q-${q.id}`} 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} checked={responses[q.id] === val}
onChange={() => handleInputChange(q.id, val)} onChange={() => handleInputChange(q.id, val)}
/> />
@ -351,7 +351,7 @@ const PublicQuestionnairePage: React.FC = () => {
{currentSectionIndex < sections.length - 1 ? ( {currentSectionIndex < sections.length - 1 ? (
<button <button
onClick={handleNextSection} 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 Next Section
<ChevronRight className="w-4 h-4" /> <ChevronRight className="w-4 h-4" />

View File

@ -51,6 +51,14 @@
--re-black: #000000; --re-black: #000000;
--re-white: #FFFFFF; --re-white: #FFFFFF;
--re-gray: #717171; --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 { .dark {
@ -142,6 +150,9 @@
--color-re-red-hover: var(--re-red-hover); --color-re-red-hover: var(--re-red-hover);
--color-re-black: var(--re-black); --color-re-black: var(--re-black);
--color-re-gray: var(--re-gray); --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 { @layer base {
@ -155,12 +166,76 @@
} }
@layer utilities { @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; background-color: var(--color-re-red) !important;
} }
button.hover\:bg-amber-700:hover { .hover\:bg-amber-700:hover:not(.hover\:bg-status-progress-hover) {
background-color: var(--color-re-red) !important; background-color: var(--color-re-red-hover) !important;
} }
} }