Re_Figma_Code/src/dealer-claim/components/request-detail/claim-cards/ProcessDetailsCard.tsx

292 lines
11 KiB
TypeScript

/**
* ProcessDetailsCard Component
* Displays process-related details: IO Number, DMS Number, Claim Amount, and Budget Breakdowns
* Visibility controlled by user role
*/
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Label } from '@/components/ui/label';
import { Activity, Receipt, DollarSign, Pen } from 'lucide-react';
import { format } from 'date-fns';
// Local minimal types to avoid external dependency issues
interface IODetails {
ioNumber?: string;
remarks?: string;
availableBalance?: number;
blockedAmount?: number;
remainingBalance?: number;
blockedByName?: string;
blockedAt?: string;
}
interface DMSDetails {
dmsNumber?: string;
remarks?: string;
createdByName?: string;
createdAt?: string;
}
interface ClaimAmountDetails {
amount: number;
lastUpdatedBy?: string;
lastUpdatedAt?: string;
}
interface CostBreakdownItem {
description: string;
amount: number;
}
interface RoleBasedVisibility {
showIODetails: boolean;
showDMSDetails: boolean;
showClaimAmount: boolean;
canEditClaimAmount: boolean;
}
interface ProcessDetailsCardProps {
ioDetails?: IODetails;
dmsDetails?: DMSDetails;
claimAmount?: ClaimAmountDetails;
estimatedBudgetBreakdown?: CostBreakdownItem[];
closedExpensesBreakdown?: CostBreakdownItem[];
visibility: RoleBasedVisibility;
onEditClaimAmount?: () => void;
className?: string;
}
export function ProcessDetailsCard({
ioDetails,
dmsDetails,
claimAmount,
estimatedBudgetBreakdown,
closedExpensesBreakdown,
visibility,
onEditClaimAmount,
className,
}: ProcessDetailsCardProps) {
const formatCurrency = (amount?: number | null) => {
if (amount === undefined || amount === null || Number.isNaN(amount)) {
return '₹0.00';
}
return `${amount.toLocaleString('en-IN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
};
const formatDate = (dateString?: string | null) => {
if (!dateString) return '';
try {
return format(new Date(dateString), 'MMM d, yyyy, h:mm a');
} catch {
return dateString || '';
}
};
const calculateTotal = (items?: CostBreakdownItem[]) => {
if (!items || items.length === 0) return 0;
return items.reduce((sum, item) => sum + (item.amount ?? 0), 0);
};
// Don't render if nothing to show
const hasContent =
(visibility.showIODetails && ioDetails) ||
(visibility.showDMSDetails && dmsDetails) ||
(visibility.showClaimAmount && claimAmount && claimAmount.amount !== undefined && claimAmount.amount !== null) ||
(estimatedBudgetBreakdown && estimatedBudgetBreakdown.length > 0) ||
(closedExpensesBreakdown && closedExpensesBreakdown.length > 0);
if (!hasContent) {
return null;
}
return (
<Card className={`bg-gradient-to-br from-blue-50 to-purple-50 border-2 border-blue-200 ${className}`}>
<CardHeader>
<CardTitle className="text-base flex items-center gap-2">
<Activity className="w-4 h-4 text-blue-600" />
Process Details
</CardTitle>
<CardDescription>Workflow reference numbers</CardDescription>
</CardHeader>
<CardContent className="space-y-3">
{/* IO Details - Only visible to internal RE users */}
{visibility.showIODetails && ioDetails && (
<div className="bg-white rounded-lg p-3 border border-blue-200">
<div className="flex items-center gap-2 mb-2">
<Receipt className="w-4 h-4 text-blue-600" />
<Label className="text-xs font-semibold text-blue-900 uppercase tracking-wide">
IO Number
</Label>
</div>
<p className="font-bold text-gray-900 mb-2">{ioDetails.ioNumber}</p>
{ioDetails.remarks && (
<div className="pt-2 border-t border-blue-100">
<p className="text-xs text-gray-600 mb-1">Remark:</p>
<p className="text-xs text-gray-900">{ioDetails.remarks}</p>
</div>
)}
{/* Budget Details */}
{(ioDetails.availableBalance !== undefined || ioDetails.blockedAmount !== undefined) && (
<div className="pt-2 border-t border-blue-100 mt-2 space-y-1">
{ioDetails.availableBalance !== undefined && (
<div className="flex justify-between text-xs">
<span className="text-gray-600">Available Balance:</span>
<span className="font-medium text-gray-900">
{formatCurrency(ioDetails.availableBalance)}
</span>
</div>
)}
{ioDetails.blockedAmount !== undefined && (
<div className="flex justify-between text-xs">
<span className="text-gray-600">Blocked Amount:</span>
<span className="font-medium text-blue-700">
{formatCurrency(ioDetails.blockedAmount)}
</span>
</div>
)}
{ioDetails.remainingBalance !== undefined && (
<div className="flex justify-between text-xs">
<span className="text-gray-600">Remaining Balance:</span>
<span className="font-medium text-green-700">
{formatCurrency(ioDetails.remainingBalance)}
</span>
</div>
)}
</div>
)}
<div className="pt-2 border-t border-blue-100 mt-2">
<p className="text-xs text-gray-500">By {ioDetails.blockedByName}</p>
<p className="text-xs text-gray-500">{formatDate(ioDetails.blockedAt)}</p>
</div>
</div>
)}
{/* DMS Details */}
{visibility.showDMSDetails && dmsDetails && (
<div className="bg-white rounded-lg p-3 border border-purple-200">
<div className="flex items-center gap-2 mb-2">
<Activity className="w-4 h-4 text-purple-600" />
<Label className="text-xs font-semibold text-purple-900 uppercase tracking-wide">
DMS Number
</Label>
</div>
<p className="font-bold text-gray-900 mb-2">{dmsDetails.dmsNumber}</p>
{dmsDetails.remarks && (
<div className="pt-2 border-t border-purple-100">
<p className="text-xs text-gray-600 mb-1">Remarks:</p>
<p className="text-xs text-gray-900">{dmsDetails.remarks}</p>
</div>
)}
<div className="pt-2 border-t border-purple-100 mt-2">
<p className="text-xs text-gray-500">By {dmsDetails.createdByName}</p>
<p className="text-xs text-gray-500">{formatDate(dmsDetails.createdAt)}</p>
</div>
</div>
)}
{/* Claim Amount */}
{visibility.showClaimAmount && claimAmount && (
<div className="bg-white rounded-lg p-3 border border-green-200">
<div className="flex items-center justify-between gap-2 mb-2">
<div className="flex items-center gap-2">
<DollarSign className="w-4 h-4 text-green-600" />
<Label className="text-xs font-semibold text-green-900 uppercase tracking-wide">
Claim Amount
</Label>
</div>
{visibility.canEditClaimAmount && onEditClaimAmount && (
<Button
variant="outline"
size="sm"
onClick={onEditClaimAmount}
className="h-7 px-2 text-xs border-green-300 hover:bg-green-50"
>
<Pen className="w-3 h-3 mr-1 text-green-700" />
Edit
</Button>
)}
</div>
<p className="text-2xl font-bold text-green-700">
{formatCurrency(claimAmount.amount)}
</p>
{claimAmount.lastUpdatedBy && (
<div className="mt-2 pt-2 border-t border-green-100">
<p className="text-xs text-gray-500">
Last updated by {claimAmount.lastUpdatedBy}
</p>
{claimAmount.lastUpdatedAt && (
<p className="text-xs text-gray-500">
{formatDate(claimAmount.lastUpdatedAt)}
</p>
)}
</div>
)}
</div>
)}
{/* Estimated Budget Breakdown */}
{estimatedBudgetBreakdown && estimatedBudgetBreakdown.length > 0 && (
<div className="bg-white rounded-lg p-3 border border-amber-200">
<div className="flex items-center gap-2 mb-2">
<Receipt className="w-4 h-4 text-amber-600" />
<Label className="text-xs font-semibold text-amber-900 uppercase tracking-wide">
Estimated Budget Breakdown
</Label>
</div>
<div className="space-y-1.5 pt-1">
{estimatedBudgetBreakdown.map((item, index) => (
<div key={index} className="flex justify-between items-center text-xs">
<span className="text-gray-700">{item.description}</span>
<span className="font-medium text-gray-900">
{formatCurrency(item.amount)}
</span>
</div>
))}
<div className="pt-2 border-t border-amber-200 flex justify-between items-center">
<span className="font-semibold text-gray-900 text-xs">Total</span>
<span className="font-bold text-amber-700">
{formatCurrency(calculateTotal(estimatedBudgetBreakdown))}
</span>
</div>
</div>
</div>
)}
{/* Closed Expenses Breakdown */}
{closedExpensesBreakdown && closedExpensesBreakdown.length > 0 && (
<div className="bg-white rounded-lg p-3 border border-indigo-200">
<div className="flex items-center gap-2 mb-2">
<Receipt className="w-4 h-4 text-indigo-600" />
<Label className="text-xs font-semibold text-indigo-900 uppercase tracking-wide">
Closed Expenses Breakdown
</Label>
</div>
<div className="space-y-1.5 pt-1">
{closedExpensesBreakdown.map((item, index) => (
<div key={index} className="flex justify-between items-center text-xs">
<span className="text-gray-700">{item.description}</span>
<span className="font-medium text-gray-900">
{formatCurrency(item.amount)}
</span>
</div>
))}
<div className="pt-2 border-t border-indigo-200 flex justify-between items-center">
<span className="font-semibold text-gray-900 text-xs">Total</span>
<span className="font-bold text-indigo-700">
{formatCurrency(calculateTotal(closedExpensesBreakdown))}
</span>
</div>
</div>
</div>
)}
</CardContent>
</Card>
);
}