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

224 lines
8.8 KiB
TypeScript

/**
* ActivityInformationCard Component
* Displays activity details for Claim Management requests
*/
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Calendar, MapPin, DollarSign, Receipt } from 'lucide-react';
import { ClaimActivityInfo } from '@/pages/RequestDetail/types/claimManagement.types';
import { format } from 'date-fns';
import { formatDateTime } from '@/utils/dateFormatter';
import { FormattedDescription } from '@/components/common/FormattedDescription';
interface ActivityInformationCardProps {
activityInfo: ClaimActivityInfo;
className?: string;
// Plug-and-play: Pass timestamps from module's business logic
createdAt?: string | Date;
updatedAt?: string | Date;
}
export function ActivityInformationCard({
activityInfo,
className,
createdAt,
updatedAt
}: ActivityInformationCardProps) {
// Defensive check: Ensure activityInfo exists
if (!activityInfo) {
console.warn('[ActivityInformationCard] activityInfo is missing');
return (
<Card className={className}>
<CardContent className="py-8 text-center text-gray-500">
<p>Activity information not available</p>
</CardContent>
</Card>
);
}
const formatCurrency = (amount: string | number) => {
const numAmount = typeof amount === 'string' ? parseFloat(amount) : amount;
if (isNaN(numAmount)) return 'N/A';
return `${numAmount.toLocaleString('en-IN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
};
const formatDate = (dateString?: string) => {
if (!dateString) return 'N/A';
try {
return format(new Date(dateString), 'MMM d, yyyy');
} catch {
return dateString;
}
};
return (
<Card className={className}>
<CardHeader>
<CardTitle className="flex items-center gap-2 text-base">
<Calendar className="w-5 h-5 text-blue-600" />
Activity Information
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid grid-cols-2 gap-4">
{/* Activity Name */}
<div>
<label className="text-xs font-medium text-gray-500 uppercase tracking-wide">
Activity Name
</label>
<p className="text-sm text-gray-900 font-medium mt-1">
{activityInfo.activityName}
</p>
</div>
{/* Activity Type */}
<div>
<label className="text-xs font-medium text-gray-500 uppercase tracking-wide">
Activity Type
</label>
<p className="text-sm text-gray-900 font-medium mt-1">
{activityInfo.activityType}
</p>
</div>
{/* Location */}
<div>
<label className="text-xs font-medium text-gray-500 uppercase tracking-wide">
Location
</label>
<p className="text-sm text-gray-900 font-medium mt-1 flex items-center gap-2">
<MapPin className="w-4 h-4 text-gray-400" />
{activityInfo.location}
</p>
</div>
{/* Requested Date */}
<div>
<label className="text-xs font-medium text-gray-500 uppercase tracking-wide">
Requested Date
</label>
<p className="text-sm text-gray-900 font-medium mt-1">
{formatDate(activityInfo.requestedDate)}
</p>
</div>
{/* Estimated Budget */}
<div>
<label className="text-xs font-medium text-gray-500 uppercase tracking-wide">
Estimated Budget
</label>
<p className="text-sm text-gray-900 font-medium mt-1 flex items-center gap-2">
<DollarSign className="w-4 h-4 text-green-600" />
{activityInfo.estimatedBudget
? formatCurrency(activityInfo.estimatedBudget)
: 'TBD'}
</p>
</div>
{/* Closed Expenses - Show if value exists (including 0) */}
{activityInfo.closedExpenses !== undefined && activityInfo.closedExpenses !== null && (
<div>
<label className="text-xs font-medium text-gray-500 uppercase tracking-wide">
Closed Expenses
</label>
<p className="text-sm text-gray-900 font-medium mt-1 flex items-center gap-2">
<Receipt className="w-4 h-4 text-blue-600" />
{formatCurrency(activityInfo.closedExpenses)}
</p>
</div>
)}
{/* Period */}
{activityInfo.period && (
<div className="col-span-2">
<label className="text-xs font-medium text-gray-500 uppercase tracking-wide">
Period
</label>
<p className="text-sm text-gray-900 font-medium mt-1">
{formatDate(activityInfo.period.startDate)} - {formatDate(activityInfo.period.endDate)}
</p>
</div>
)}
</div>
{/* Closed Expenses Breakdown */}
{activityInfo.closedExpensesBreakdown && activityInfo.closedExpensesBreakdown.length > 0 && (
<div className="pt-4 border-t">
<label className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-3 block">
Closed Expenses Breakdown
</label>
<div className="bg-blue-50 border border-blue-200 rounded-lg overflow-hidden">
<table className="w-full text-xs sm:text-sm">
<thead className="bg-blue-100/50">
<tr>
<th className="px-3 py-2 text-left font-semibold text-blue-900">Description</th>
<th className="px-3 py-2 text-right font-semibold text-blue-900 w-24">Base</th>
<th className="px-3 py-2 text-right font-semibold text-blue-900 w-24">GST</th>
<th className="px-3 py-2 text-right font-semibold text-blue-900 w-28">Total</th>
</tr>
</thead>
<tbody className="divide-y divide-blue-200/50">
{activityInfo.closedExpensesBreakdown.map((item: any, index: number) => (
<tr key={index} className="hover:bg-blue-100/30">
<td className="px-3 py-2 text-gray-700">
{item.description}
{item.gstRate ? <span className="text-[10px] text-gray-400 block">{item.gstRate}% GST</span> : null}
</td>
<td className="px-3 py-2 text-right text-gray-900">{formatCurrency(item.amount)}</td>
<td className="px-3 py-2 text-right text-gray-900">{formatCurrency(item.gstAmt || 0)}</td>
<td className="px-3 py-2 text-right font-medium text-gray-900">
{formatCurrency(item.totalAmt || (item.amount + (item.gstAmt || 0)))}
</td>
</tr>
))}
<tr className="bg-blue-100/50 font-bold">
<td colSpan={3} className="px-3 py-2 text-blue-900">Final Claim Amount</td>
<td className="px-3 py-2 text-right text-blue-700">
{formatCurrency(
activityInfo.closedExpensesBreakdown.reduce((sum: number, item: any) => sum + (item.totalAmt || (item.amount + (item.gstAmt || 0))), 0)
)}
</td>
</tr>
</tbody>
</table>
</div>
</div>
)}
{/* Description */}
{activityInfo.description && (
<div className="pt-4 border-t">
<label className="text-xs font-medium text-gray-500 uppercase tracking-wide">
Description
</label>
<div className="mt-2 bg-gray-50 p-3 rounded-lg border border-gray-200">
<FormattedDescription
content={activityInfo.description || ''}
className="text-sm"
/>
</div>
</div>
)}
{/* Timestamps - Similar to Request Details Card */}
{(createdAt || updatedAt) && (
<div className="grid grid-cols-2 gap-4 pt-4 border-t border-gray-300">
{createdAt && (
<div>
<label className="text-xs font-medium text-gray-500 uppercase tracking-wide">Created</label>
<p className="text-sm text-gray-900 font-medium mt-1">{formatDateTime(createdAt)}</p>
</div>
)}
{updatedAt && (
<div>
<label className="text-xs font-medium text-gray-500 uppercase tracking-wide">Last Updated</label>
<p className="text-sm text-gray-900 font-medium mt-1">{formatDateTime(updatedAt)}</p>
</div>
)}
</div>
)}
</CardContent>
</Card>
);
}