feat: add hub tracking to damage and maintenance records with selection UI
This commit is contained in:
@@ -74,6 +74,8 @@ interface DamageRecord {
|
||||
estimatedCost?: number;
|
||||
actualCost?: number;
|
||||
status: 'reported' | 'under_repair' | 'repaired' | 'claim_rejected';
|
||||
hubId?: string;
|
||||
hubName?: string;
|
||||
images?: string[];
|
||||
billImage?: string;
|
||||
resolvedAt?: string;
|
||||
@@ -89,6 +91,8 @@ interface MaintenanceRecord {
|
||||
cost: number;
|
||||
nextDueDate?: string;
|
||||
status: 'scheduled' | 'in_progress' | 'completed';
|
||||
hubId?: string;
|
||||
hubName?: 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' },
|
||||
],
|
||||
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: '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: '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', hubId: 'HUB-002', hubName: 'Banani Hub' },
|
||||
],
|
||||
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: '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: '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: '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', 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', hubId: 'HUB-004', hubName: 'Mirpur Hub' },
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -258,6 +262,13 @@ export default function FleetDetailPage({ params }: { params: Promise<{ id: stri
|
||||
{ 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) => {
|
||||
setBikes(bikes.map(b => {
|
||||
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">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">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">Est. 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>
|
||||
</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.estimatedCost || 0}</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">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">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">Cost</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>
|
||||
</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 font-medium text-slate-700">৳{maintenance.cost}</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,
|
||||
actualCost: damage?.actualCost || 0,
|
||||
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 = [
|
||||
{ value: 'accident', label: 'Accident' },
|
||||
{ value: 'theft', label: 'Theft' },
|
||||
@@ -1379,6 +1403,19 @@ function DamageModal({ bike, damage, onClose, onSave }: { bike: Bike; damage: Da
|
||||
required
|
||||
/>
|
||||
</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>
|
||||
<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,
|
||||
nextDueDate: maintenance?.nextDueDate || '',
|
||||
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 = [
|
||||
{ value: 'routine', label: 'Routine Service' },
|
||||
{ value: 'battery', label: 'Battery' },
|
||||
@@ -1510,6 +1556,19 @@ function MaintenanceModal({ bike, maintenance, onClose, onSave }: { bike: Bike;
|
||||
required
|
||||
/>
|
||||
</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>
|
||||
<label className="text-sm font-medium text-slate-600 mb-1 block">Cost (৳)</label>
|
||||
|
||||
Reference in New Issue
Block a user