File size: 5,161 Bytes
191b322 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
import React from 'react';
import { ProjectState, ProjectDocument, UserRole } from '../types';
import { AlertTriangle, Clock, Lock } from 'lucide-react';
import DocumentManager from './DocumentManager';
interface LiabilityTrackerProps {
data: ProjectState;
onAddDocument: (doc: ProjectDocument) => void;
userRole: UserRole;
}
const LiabilityTracker: React.FC<LiabilityTrackerProps> = ({ data, onAddDocument, userRole }) => {
const retentionTotal = data.liabilities.filter(l => l.type === 'RETENTION').reduce((s, l) => s + l.amount, 0);
const poTotal = data.liabilities.filter(l => l.type === 'PENDING_PO').reduce((s, l) => s + l.amount, 0);
const unbilledTotal = data.liabilities.filter(l => l.type === 'UNBILLED_WORK').reduce((s, l) => s + l.amount, 0);
const canEdit = userRole === 'DIRECTOR' || userRole === 'ACCOUNTANT';
return (
<div className="space-y-6">
<div className="flex flex-col md:flex-row md:items-center justify-between gap-4">
<div>
<h1 className="text-2xl font-bold text-slate-800">Liability Tracker</h1>
<p className="text-slate-500">Monitor Future Obligations & Risks</p>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<div className="bg-white p-6 rounded-xl border-l-4 border-indigo-500 shadow-sm">
<div className="flex items-center gap-3 mb-2">
<div className="p-2 bg-indigo-50 rounded-lg text-indigo-600">
<Lock className="w-5 h-5" />
</div>
<h3 className="font-semibold text-slate-800">Retention Money</h3>
</div>
<p className="text-2xl font-bold text-slate-900">৳{retentionTotal.toLocaleString()}</p>
<p className="text-sm text-slate-500 mt-1">Held by client, due after defect liability period.</p>
</div>
<div className="bg-white p-6 rounded-xl border-l-4 border-orange-500 shadow-sm">
<div className="flex items-center gap-3 mb-2">
<div className="p-2 bg-orange-50 rounded-lg text-orange-600">
<Clock className="w-5 h-5" />
</div>
<h3 className="font-semibold text-slate-800">Pending POs</h3>
</div>
<p className="text-2xl font-bold text-slate-900">৳{poTotal.toLocaleString()}</p>
<p className="text-sm text-slate-500 mt-1">Committed costs for materials ordered but not billed.</p>
</div>
<div className="bg-white p-6 rounded-xl border-l-4 border-red-500 shadow-sm">
<div className="flex items-center gap-3 mb-2">
<div className="p-2 bg-red-50 rounded-lg text-red-600">
<AlertTriangle className="w-5 h-5" />
</div>
<h3 className="font-semibold text-slate-800">Unbilled Liabilities</h3>
</div>
<p className="text-2xl font-bold text-slate-900">৳{unbilledTotal.toLocaleString()}</p>
<p className="text-sm text-slate-500 mt-1">Work done by subcontractors, not yet invoiced.</p>
</div>
</div>
<div className="bg-white rounded-xl border border-slate-200 shadow-sm overflow-hidden">
<div className="px-6 py-4 border-b border-slate-200">
<h3 className="font-semibold text-slate-800">Detailed Liability Ledger</h3>
</div>
<table className="w-full text-left text-sm">
<thead className="bg-slate-50 text-slate-600 font-medium border-b border-slate-200">
<tr>
<th className="px-6 py-3">ID</th>
<th className="px-6 py-3">Description</th>
<th className="px-6 py-3">Type</th>
<th className="px-6 py-3">Due Date</th>
<th className="px-6 py-3 text-right">Amount</th>
</tr>
</thead>
<tbody className="divide-y divide-slate-100">
{data.liabilities.map(liability => (
<tr key={liability.id} className="hover:bg-slate-50">
<td className="px-6 py-3 font-medium text-slate-700">{liability.id}</td>
<td className="px-6 py-3 text-slate-600">{liability.description}</td>
<td className="px-6 py-3">
<span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium
${liability.type === 'RETENTION' ? 'bg-indigo-100 text-indigo-800' :
liability.type === 'PENDING_PO' ? 'bg-orange-100 text-orange-800' :
'bg-red-100 text-red-800'}`}>
{liability.type.replace('_', ' ')}
</span>
</td>
<td className="px-6 py-3 text-slate-500">{liability.dueDate}</td>
<td className="px-6 py-3 text-right font-medium text-slate-900">৳{liability.amount.toLocaleString()}</td>
</tr>
))}
</tbody>
</table>
</div>
<DocumentManager
documents={data.documents}
onAddDocument={onAddDocument}
filterModule="LIABILITY"
compact={true}
allowUpload={canEdit}
/>
</div>
);
};
export default LiabilityTracker;
|