312 lines
11 KiB
TypeScript
312 lines
11 KiB
TypeScript
/**
|
|
* ClaimManagementWorkflowTab Component
|
|
* Displays the 8-step workflow process specific to Claim Management requests
|
|
*/
|
|
|
|
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';
|
|
import { Badge } from '@/components/ui/badge';
|
|
import { Button } from '@/components/ui/button';
|
|
import {
|
|
TrendingUp,
|
|
CircleCheckBig,
|
|
Clock,
|
|
Mail,
|
|
Download,
|
|
Receipt,
|
|
Activity,
|
|
AlertCircle,
|
|
} from 'lucide-react';
|
|
import { format } from 'date-fns';
|
|
|
|
interface WorkflowStep {
|
|
stepNumber: number;
|
|
stepName: string;
|
|
stepDescription: string;
|
|
assignedTo: string;
|
|
assignedToType: 'dealer' | 'requestor' | 'department_lead' | 'finance' | 'system';
|
|
status: 'pending' | 'in_progress' | 'approved' | 'rejected' | 'skipped';
|
|
tatHours: number;
|
|
elapsedHours?: number;
|
|
remarks?: string;
|
|
approvedAt?: string;
|
|
approvedBy?: string;
|
|
ioDetails?: {
|
|
ioNumber: string;
|
|
ioRemarks: string;
|
|
organisedBy: string;
|
|
organisedAt: string;
|
|
};
|
|
dmsDetails?: {
|
|
dmsNumber: string;
|
|
dmsRemarks: string;
|
|
pushedBy: string;
|
|
pushedAt: string;
|
|
};
|
|
hasEmailNotification?: boolean;
|
|
hasDownload?: boolean;
|
|
downloadUrl?: string;
|
|
}
|
|
|
|
interface ClaimManagementWorkflowTabProps {
|
|
steps: WorkflowStep[];
|
|
currentStep: number;
|
|
totalSteps?: number;
|
|
onViewEmailTemplate?: (stepNumber: number) => void;
|
|
onDownloadDocument?: (stepNumber: number, url: string) => void;
|
|
className?: string;
|
|
}
|
|
|
|
export function ClaimManagementWorkflowTab({
|
|
steps,
|
|
currentStep,
|
|
totalSteps = 8,
|
|
onViewEmailTemplate,
|
|
onDownloadDocument,
|
|
className = '',
|
|
}: ClaimManagementWorkflowTabProps) {
|
|
|
|
const formatDate = (dateString?: string) => {
|
|
if (!dateString) return 'N/A';
|
|
try {
|
|
return format(new Date(dateString), 'MMM d, yyyy, h:mm a');
|
|
} catch {
|
|
return dateString;
|
|
}
|
|
};
|
|
|
|
const getStepBorderColor = (status: string) => {
|
|
switch (status) {
|
|
case 'approved':
|
|
return 'border-green-500 bg-green-50';
|
|
case 'in_progress':
|
|
return 'border-blue-500 bg-blue-50';
|
|
case 'rejected':
|
|
return 'border-red-500 bg-red-50';
|
|
case 'pending':
|
|
return 'border-gray-300 bg-white';
|
|
case 'skipped':
|
|
return 'border-gray-400 bg-gray-50';
|
|
default:
|
|
return 'border-gray-300 bg-white';
|
|
}
|
|
};
|
|
|
|
const getStepIconBg = (status: string) => {
|
|
switch (status) {
|
|
case 'approved':
|
|
return 'bg-green-100';
|
|
case 'in_progress':
|
|
return 'bg-blue-100';
|
|
case 'rejected':
|
|
return 'bg-red-100';
|
|
case 'pending':
|
|
return 'bg-gray-100';
|
|
case 'skipped':
|
|
return 'bg-gray-200';
|
|
default:
|
|
return 'bg-gray-100';
|
|
}
|
|
};
|
|
|
|
const getStepIcon = (status: string) => {
|
|
switch (status) {
|
|
case 'approved':
|
|
return <CircleCheckBig className="w-5 h-5 text-green-600" />;
|
|
case 'in_progress':
|
|
return <Clock className="w-5 h-5 text-blue-600" />;
|
|
case 'rejected':
|
|
return <AlertCircle className="w-5 h-5 text-red-600" />;
|
|
case 'pending':
|
|
return <Clock className="w-5 h-5 text-gray-400" />;
|
|
default:
|
|
return <Clock className="w-5 h-5 text-gray-400" />;
|
|
}
|
|
};
|
|
|
|
const getStatusBadgeColor = (status: string) => {
|
|
switch (status) {
|
|
case 'approved':
|
|
return 'bg-green-100 text-green-800 border-green-200';
|
|
case 'in_progress':
|
|
return 'bg-blue-100 text-blue-800 border-blue-200';
|
|
case 'rejected':
|
|
return 'bg-red-100 text-red-800 border-red-200';
|
|
case 'pending':
|
|
return 'bg-gray-100 text-gray-600 border-gray-200';
|
|
case 'skipped':
|
|
return 'bg-gray-200 text-gray-700 border-gray-300';
|
|
default:
|
|
return 'bg-gray-100 text-gray-600 border-gray-200';
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Card className={className}>
|
|
<CardHeader>
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<CardTitle className="leading-none flex items-center gap-2">
|
|
<TrendingUp className="w-5 h-5 text-purple-600" />
|
|
Claim Management Workflow
|
|
</CardTitle>
|
|
<CardDescription className="mt-2">
|
|
8-Step approval process for dealer claim management
|
|
</CardDescription>
|
|
</div>
|
|
<Badge variant="outline" className="font-medium">
|
|
Step {currentStep} of {totalSteps}
|
|
</Badge>
|
|
</div>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="space-y-4">
|
|
{steps.map((step, index) => (
|
|
<div
|
|
key={step.stepNumber}
|
|
className={`relative p-5 rounded-lg border-2 transition-all ${getStepBorderColor(step.status)}`}
|
|
>
|
|
{/* Step Content */}
|
|
<div className="flex items-start gap-4">
|
|
{/* Icon */}
|
|
<div className={`p-3 rounded-xl ${getStepIconBg(step.status)}`}>
|
|
{getStepIcon(step.status)}
|
|
</div>
|
|
|
|
{/* Step Details */}
|
|
<div className="flex-1 min-w-0">
|
|
{/* Header Row */}
|
|
<div className="flex items-start justify-between gap-4 mb-2">
|
|
<div className="flex-1">
|
|
<div className="flex items-center gap-2 mb-1">
|
|
<h4 className="font-semibold text-gray-900">
|
|
Step {step.stepNumber}: {step.stepName}
|
|
</h4>
|
|
<Badge className={getStatusBadgeColor(step.status)}>
|
|
{step.status}
|
|
</Badge>
|
|
|
|
{/* Action Buttons */}
|
|
{step.hasEmailNotification && onViewEmailTemplate && (
|
|
<Button
|
|
variant="ghost"
|
|
size="sm"
|
|
className="h-6 w-6 p-0 hover:bg-blue-100"
|
|
onClick={() => onViewEmailTemplate(step.stepNumber)}
|
|
title="View email template"
|
|
>
|
|
<Mail className="w-3.5 h-3.5 text-blue-600" />
|
|
</Button>
|
|
)}
|
|
|
|
{step.hasDownload && onDownloadDocument && step.downloadUrl && (
|
|
<Button
|
|
variant="ghost"
|
|
size="sm"
|
|
className="h-6 w-6 p-0 hover:bg-green-100"
|
|
onClick={() => onDownloadDocument(step.stepNumber, step.downloadUrl!)}
|
|
title="Download E-Invoice"
|
|
>
|
|
<Download className="w-3.5 h-3.5 text-green-600" />
|
|
</Button>
|
|
)}
|
|
</div>
|
|
|
|
<p className="text-sm text-gray-600">{step.assignedTo}</p>
|
|
<p className="text-sm text-gray-500 mt-2 italic">
|
|
{step.stepDescription}
|
|
</p>
|
|
</div>
|
|
|
|
{/* TAT Info */}
|
|
<div className="text-right">
|
|
<p className="text-xs text-gray-500">TAT: {step.tatHours}h</p>
|
|
{step.elapsedHours !== undefined && (
|
|
<p className="text-xs text-gray-600 font-medium">
|
|
Elapsed: {step.elapsedHours}h
|
|
</p>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Remarks */}
|
|
{step.remarks && (
|
|
<div className="mt-3 p-3 bg-white rounded-lg border border-gray-200">
|
|
<p className="text-sm text-gray-700">{step.remarks}</p>
|
|
</div>
|
|
)}
|
|
|
|
{/* IO Details */}
|
|
{step.ioDetails && (
|
|
<div className="mt-3 p-3 bg-blue-50 rounded-lg border border-blue-200">
|
|
<div className="flex items-center gap-2 mb-2">
|
|
<Receipt className="w-4 h-4 text-blue-600" />
|
|
<p className="text-xs font-semibold text-blue-900 uppercase tracking-wide">
|
|
IO Organisation Details
|
|
</p>
|
|
</div>
|
|
<div className="space-y-1.5">
|
|
<div className="flex items-center justify-between">
|
|
<span className="text-xs text-gray-600">IO Number:</span>
|
|
<span className="text-sm font-semibold text-gray-900">
|
|
{step.ioDetails.ioNumber}
|
|
</span>
|
|
</div>
|
|
<div className="pt-1.5 border-t border-blue-100">
|
|
<p className="text-xs text-gray-600 mb-1">IO Remark:</p>
|
|
<p className="text-sm text-gray-900">{step.ioDetails.ioRemarks}</p>
|
|
</div>
|
|
<div className="pt-1.5 border-t border-blue-100 text-xs text-gray-500">
|
|
Organised by {step.ioDetails.organisedBy} on{' '}
|
|
{formatDate(step.ioDetails.organisedAt)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* DMS Details */}
|
|
{step.dmsDetails && (
|
|
<div className="mt-3 p-3 bg-purple-50 rounded-lg border border-purple-200">
|
|
<div className="flex items-center gap-2 mb-2">
|
|
<Activity className="w-4 h-4 text-purple-600" />
|
|
<p className="text-xs font-semibold text-purple-900 uppercase tracking-wide">
|
|
DMS Processing Details
|
|
</p>
|
|
</div>
|
|
<div className="space-y-1.5">
|
|
<div className="flex items-center justify-between">
|
|
<span className="text-xs text-gray-600">DMS Number:</span>
|
|
<span className="text-sm font-semibold text-gray-900">
|
|
{step.dmsDetails.dmsNumber}
|
|
</span>
|
|
</div>
|
|
<div className="pt-1.5 border-t border-purple-100">
|
|
<p className="text-xs text-gray-600 mb-1">DMS Remarks:</p>
|
|
<p className="text-sm text-gray-900">{step.dmsDetails.dmsRemarks}</p>
|
|
</div>
|
|
<div className="pt-1.5 border-t border-purple-100 text-xs text-gray-500">
|
|
Pushed by {step.dmsDetails.pushedBy} on{' '}
|
|
{formatDate(step.dmsDetails.pushedAt)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Approval Timestamp */}
|
|
{step.approvedAt && (
|
|
<p className="text-xs text-gray-500 mt-2">
|
|
{step.status === 'approved' ? 'Approved' : 'Updated'} on{' '}
|
|
{formatDate(step.approvedAt)}
|
|
</p>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|
|
|
|
|