feat: add hub tracking to damage and maintenance records with selection UI

This commit is contained in:
sazzadulalambd
2026-05-16 20:19:23 +06:00
parent 36b12772b7
commit ec487f6d27

View File

@@ -74,6 +74,8 @@ interface DamageRecord {
estimatedCost?: number; estimatedCost?: number;
actualCost?: number; actualCost?: number;
status: 'reported' | 'under_repair' | 'repaired' | 'claim_rejected'; status: 'reported' | 'under_repair' | 'repaired' | 'claim_rejected';
hubId?: string;
hubName?: string;
images?: string[]; images?: string[];
billImage?: string; billImage?: string;
resolvedAt?: string; resolvedAt?: string;
@@ -89,6 +91,8 @@ interface MaintenanceRecord {
cost: number; cost: number;
nextDueDate?: string; nextDueDate?: string;
status: 'scheduled' | 'in_progress' | 'completed'; status: 'scheduled' | 'in_progress' | 'completed';
hubId?: string;
hubName?: string;
notes?: string; notes?: string;
} }
@@ -149,13 +153,13 @@ const mockBikes: Bike[] = [
{ id: 'A005', action: 'Insurance Renewed', details: 'Insurance renewed for 1 year', date: '2024-01-15', by: 'Admin' }, { id: 'A005', action: 'Insurance Renewed', details: 'Insurance renewed for 1 year', date: '2024-01-15', by: 'Admin' },
], ],
damageHistory: [ damageHistory: [
{ id: 'DMG001', date: '2024-02-10', type: 'accident', description: 'Minor collision at Mirpur intersection', reportedBy: 'Jamal Khan', reportedAt: '2024-02-10 14:30', estimatedCost: 5000, actualCost: 4500, status: 'repaired', resolvedAt: '2024-02-15' }, { id: 'DMG001', date: '2024-02-10', type: 'accident', description: 'Minor collision at Mirpur intersection', reportedBy: 'Jamal Khan', reportedAt: '2024-02-10 14:30', estimatedCost: 5000, actualCost: 4500, status: 'repaired', resolvedAt: '2024-02-15', hubId: 'HUB-001', hubName: 'Gulshan Hub' },
{ id: 'DMG002', date: '2024-03-15', type: 'wear_tear', description: 'Front tire wear - replaced', reportedBy: 'Rahim Ahmed', reportedAt: '2024-03-15 09:00', estimatedCost: 2500, actualCost: 2200, status: 'repaired', resolvedAt: '2024-03-16' }, { id: 'DMG002', date: '2024-03-15', type: 'wear_tear', description: 'Front tire wear - replaced', reportedBy: 'Rahim Ahmed', reportedAt: '2024-03-15 09:00', estimatedCost: 2500, actualCost: 2200, status: 'repaired', resolvedAt: '2024-03-16', hubId: 'HUB-002', hubName: 'Banani Hub' },
], ],
maintenanceHistory: [ maintenanceHistory: [
{ id: 'MNT001', date: '2024-03-01', type: 'routine', description: 'Full service - oil change, brake check, tire rotation', performedBy: 'Service Center', cost: 1500, nextDueDate: '2024-04-01', status: 'completed' }, { id: 'MNT001', date: '2024-03-01', type: 'routine', description: 'Full service - oil change, brake check, tire rotation', performedBy: 'Service Center', cost: 1500, nextDueDate: '2024-04-01', status: 'completed', hubId: 'HUB-001', hubName: 'Gulshan Hub' },
{ id: 'MNT002', date: '2024-02-15', type: 'battery', description: 'Battery health check and terminal cleaning', performedBy: 'Service Center', cost: 500, nextDueDate: '2024-05-15', status: 'completed' }, { id: 'MNT002', date: '2024-02-15', type: 'battery', description: 'Battery health check and terminal cleaning', performedBy: 'Service Center', cost: 500, nextDueDate: '2024-05-15', status: 'completed', hubId: 'HUB-003', hubName: 'Uttara Hub' },
{ id: 'MNT003', date: '2024-01-20', type: 'tire', description: 'Tire pressure check and inflation', performedBy: 'Service Center', cost: 300, nextDueDate: '2024-04-20', status: 'completed' }, { id: 'MNT003', date: '2024-01-20', type: 'tire', description: 'Tire pressure check and inflation', performedBy: 'Service Center', cost: 300, nextDueDate: '2024-04-20', status: 'completed', hubId: 'HUB-004', hubName: 'Mirpur Hub' },
] ]
}, },
{ {
@@ -258,6 +262,13 @@ export default function FleetDetailPage({ params }: { params: Promise<{ id: stri
{ value: 'other', label: 'Other' }, { value: 'other', label: 'Other' },
]; ];
const mockHubs = [
{ id: 'HUB-001', name: 'Gulshan Hub' },
{ id: 'HUB-002', name: 'Banani Hub' },
{ id: 'HUB-003', name: 'Uttara Hub' },
{ id: 'HUB-004', name: 'Mirpur Hub' },
];
const handleAddDamage = (damage: DamageRecord) => { const handleAddDamage = (damage: DamageRecord) => {
setBikes(bikes.map(b => { setBikes(bikes.map(b => {
if (b.id === bike.id) { if (b.id === bike.id) {
@@ -415,6 +426,7 @@ export default function FleetDetailPage({ params }: { params: Promise<{ id: stri
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase">Date</th> <th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase">Date</th>
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase">Type</th> <th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase">Type</th>
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase">Description</th> <th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase">Description</th>
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase">Hub</th>
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase">Reported By</th> <th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase">Reported By</th>
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase">Est. Cost</th> <th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase">Est. Cost</th>
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase">Actual Cost</th> <th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase">Actual Cost</th>
@@ -430,6 +442,7 @@ export default function FleetDetailPage({ params }: { params: Promise<{ id: stri
<span className="text-sm text-slate-700 capitalize">{damage.type.replace('_', ' ')}</span> <span className="text-sm text-slate-700 capitalize">{damage.type.replace('_', ' ')}</span>
</td> </td>
<td className="px-4 py-3 text-sm text-slate-600 max-w-xs truncate">{damage.description}</td> <td className="px-4 py-3 text-sm text-slate-600 max-w-xs truncate">{damage.description}</td>
<td className="px-4 py-3 text-sm text-slate-600">{damage.hubName || '-'}</td>
<td className="px-4 py-3 text-sm text-slate-600">{damage.reportedBy}</td> <td className="px-4 py-3 text-sm text-slate-600">{damage.reportedBy}</td>
<td className="px-4 py-3 text-sm text-slate-600">{damage.estimatedCost || 0}</td> <td className="px-4 py-3 text-sm text-slate-600">{damage.estimatedCost || 0}</td>
<td className="px-4 py-3 text-sm font-medium text-slate-700">{damage.actualCost || '-'}</td> <td className="px-4 py-3 text-sm font-medium text-slate-700">{damage.actualCost || '-'}</td>
@@ -498,6 +511,7 @@ export default function FleetDetailPage({ params }: { params: Promise<{ id: stri
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase">Date</th> <th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase">Date</th>
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase">Type</th> <th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase">Type</th>
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase">Description</th> <th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase">Description</th>
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase">Hub</th>
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase">Performed By</th> <th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase">Performed By</th>
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase">Cost</th> <th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase">Cost</th>
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase">Next Due</th> <th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase">Next Due</th>
@@ -513,6 +527,7 @@ export default function FleetDetailPage({ params }: { params: Promise<{ id: stri
<span className="text-sm text-slate-700 capitalize">{maintenance.type}</span> <span className="text-sm text-slate-700 capitalize">{maintenance.type}</span>
</td> </td>
<td className="px-4 py-3 text-sm text-slate-600 max-w-xs truncate">{maintenance.description}</td> <td className="px-4 py-3 text-sm text-slate-600 max-w-xs truncate">{maintenance.description}</td>
<td className="px-4 py-3 text-sm text-slate-600">{maintenance.hubName || '-'}</td>
<td className="px-4 py-3 text-sm text-slate-600">{maintenance.performedBy}</td> <td className="px-4 py-3 text-sm text-slate-600">{maintenance.performedBy}</td>
<td className="px-4 py-3 text-sm font-medium text-slate-700">{maintenance.cost}</td> <td className="px-4 py-3 text-sm font-medium text-slate-700">{maintenance.cost}</td>
<td className="px-4 py-3 text-sm text-slate-600">{maintenance.nextDueDate || '-'}</td> <td className="px-4 py-3 text-sm text-slate-600">{maintenance.nextDueDate || '-'}</td>
@@ -1305,8 +1320,17 @@ function DamageModal({ bike, damage, onClose, onSave }: { bike: Bike; damage: Da
estimatedCost: damage?.estimatedCost || 0, estimatedCost: damage?.estimatedCost || 0,
actualCost: damage?.actualCost || 0, actualCost: damage?.actualCost || 0,
status: damage?.status || 'reported', status: damage?.status || 'reported',
hubId: damage?.hubId || '',
hubName: damage?.hubName || '',
}); });
const mockHubs = [
{ id: 'HUB-001', name: 'Gulshan Hub' },
{ id: 'HUB-002', name: 'Banani Hub' },
{ id: 'HUB-003', name: 'Uttara Hub' },
{ id: 'HUB-004', name: 'Mirpur Hub' },
];
const damageTypes = [ const damageTypes = [
{ value: 'accident', label: 'Accident' }, { value: 'accident', label: 'Accident' },
{ value: 'theft', label: 'Theft' }, { value: 'theft', label: 'Theft' },
@@ -1379,6 +1403,19 @@ function DamageModal({ bike, damage, onClose, onSave }: { bike: Bike; damage: Da
required required
/> />
</div> </div>
<div>
<label className="text-sm font-medium text-slate-600 mb-1 block">Hub</label>
<select
value={formData.hubId}
onChange={(e) => setFormData({ ...formData, hubId: e.target.value, hubName: mockHubs.find(h => h.id === e.target.value)?.name || '' })}
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
>
<option value="">Select Hub</option>
{mockHubs.map(hub => (
<option key={hub.id} value={hub.id}>{hub.name}</option>
))}
</select>
</div>
<div className="grid grid-cols-2 gap-3"> <div className="grid grid-cols-2 gap-3">
<div> <div>
<label className="text-sm font-medium text-slate-600 mb-1 block">Estimated Cost (৳)</label> <label className="text-sm font-medium text-slate-600 mb-1 block">Estimated Cost (৳)</label>
@@ -1435,8 +1472,17 @@ function MaintenanceModal({ bike, maintenance, onClose, onSave }: { bike: Bike;
cost: maintenance?.cost || 0, cost: maintenance?.cost || 0,
nextDueDate: maintenance?.nextDueDate || '', nextDueDate: maintenance?.nextDueDate || '',
status: maintenance?.status || 'completed', status: maintenance?.status || 'completed',
hubId: maintenance?.hubId || '',
hubName: maintenance?.hubName || '',
}); });
const mockHubs = [
{ id: 'HUB-001', name: 'Gulshan Hub' },
{ id: 'HUB-002', name: 'Banani Hub' },
{ id: 'HUB-003', name: 'Uttara Hub' },
{ id: 'HUB-004', name: 'Mirpur Hub' },
];
const maintenanceTypes = [ const maintenanceTypes = [
{ value: 'routine', label: 'Routine Service' }, { value: 'routine', label: 'Routine Service' },
{ value: 'battery', label: 'Battery' }, { value: 'battery', label: 'Battery' },
@@ -1510,6 +1556,19 @@ function MaintenanceModal({ bike, maintenance, onClose, onSave }: { bike: Bike;
required required
/> />
</div> </div>
<div>
<label className="text-sm font-medium text-slate-600 mb-1 block">Hub</label>
<select
value={formData.hubId}
onChange={(e) => setFormData({ ...formData, hubId: e.target.value, hubName: mockHubs.find(h => h.id === e.target.value)?.name || '' })}
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
>
<option value="">Select Hub</option>
{mockHubs.map(hub => (
<option key={hub.id} value={hub.id}>{hub.name}</option>
))}
</select>
</div>
<div className="grid grid-cols-2 gap-3"> <div className="grid grid-cols-2 gap-3">
<div> <div>
<label className="text-sm font-medium text-slate-600 mb-1 block">Cost (৳)</label> <label className="text-sm font-medium text-slate-600 mb-1 block">Cost (৳)</label>