From 6a4a3d464a4d73d9eb251e327dceae50cbca8ebd Mon Sep 17 00:00:00 2001 From: natib21 Date: Tue, 30 Jun 2026 13:46:58 +0000 Subject: [PATCH] improve: fleet dashboard UI + add drivers UI Improvements: - Better card design with gradient icons - Added title + description section - Improved stat card layout (gradient backgrounds) - Added change percentage badges - Better color scheme (blue, cyan, orange, red) - Striped + highlight on hover for tables - Tab interface for vehicles/drivers Driver Management: - Fetch drivers from /drivers API - Display driver list in table - Show driver name, license, contact info - Display assigned/unassigned status - Count total drivers + assigned drivers in metrics Metrics now show: 1. Total Vehicles 2. Total Drivers (NEW) 3. Fuel Spend 4. Maintenance Spend 5. Status breakdowns 6. Cost breakdown pie chart Tabs show: - Vehicles with registration, plate, model, status - Drivers with name, license, contact, assignment Co-Authored-By: Claude Haiku 4.5 --- .../src/pages/fleet/FleetDashboard.tsx | 216 +++++++++++++----- 1 file changed, 157 insertions(+), 59 deletions(-) diff --git a/apps/edr-freight-web/backoffice/src/pages/fleet/FleetDashboard.tsx b/apps/edr-freight-web/backoffice/src/pages/fleet/FleetDashboard.tsx index 7eee0e657..a977c592d 100644 --- a/apps/edr-freight-web/backoffice/src/pages/fleet/FleetDashboard.tsx +++ b/apps/edr-freight-web/backoffice/src/pages/fleet/FleetDashboard.tsx @@ -1,7 +1,7 @@ import { useMemo } from 'react'; import { useQuery } from '@tanstack/react-query'; -import { Card, Stack, Group, Grid, Text, ThemeIcon, Progress, Badge, Table, RingProgress, Container } from '@mantine/core'; -import { Truck, Fuel, Wrench, TrendingUp, AlertCircle } from 'lucide-react'; +import { Card, Stack, Group, Grid, Text, ThemeIcon, Progress, Badge, Table, RingProgress, Container, Title, Box, Tabs } from '@mantine/core'; +import { Truck, Fuel, Wrench, TrendingUp, AlertCircle, Users, User, MapPin, Calendar } from 'lucide-react'; import Breadcrumbs from '@/components/ui/Breadcrumbs'; import { QUERY_KEYS } from '@/constants/QUERY_KEYS'; import { api } from '@/auth/http'; @@ -16,6 +16,16 @@ interface Vehicle { status?: string; } +interface Driver { + id: string; + firstName: string; + lastName: string; + licenseNumber?: string; + email?: string; + phone?: string; + assignedVehicle?: string; +} + interface FleetMetrics { totalVehicles: number; activeVehicles: number; @@ -24,23 +34,28 @@ interface FleetMetrics { totalMaintenanceSpend: number; averageFuelEfficiency: number; costPerKm: number; + totalDrivers: number; + assignedDrivers: number; } -const StatCard = ({ icon: Icon, label, value, color = 'blue' }: any) => ( - - - - - {label} - - - {value} - - - - +const StatCard = ({ icon: Icon, label, value, color = 'blue', change }: any) => ( + + + + + + + {label} + + + + {value} + + {change && 0 ? 'green' : 'red'} size="lg">{change > 0 ? '+' : ''}{change}%} + + ); @@ -53,6 +68,18 @@ export function FleetDashboard() { }, }); + const { data: drivers = [] } = useQuery({ + queryKey: ['drivers'], + queryFn: async () => { + try { + const res = await api.get('/drivers'); + return res.data || []; + } catch { + return []; + } + }, + }); + const { data: fuelStats } = useQuery({ queryKey: ['fleet-fuel-stats'], queryFn: async () => { @@ -80,6 +107,8 @@ export function FleetDashboard() { const metrics = useMemo((): FleetMetrics => { const totalVehicles = (vehicles as Vehicle[]).length; const activeVehicles = (vehicles as Vehicle[]).filter(v => v.status === 'ACTIVE').length; + const totalDrivers = (drivers as Driver[]).length; + const assignedDrivers = (drivers as Driver[]).filter(d => d.assignedVehicle).length; const fuelTotal = fuelStats?.totalCost || 0; const maintenanceTotal = maintenanceStats?.totalCost || 0; @@ -92,8 +121,10 @@ export function FleetDashboard() { totalMaintenanceSpend: maintenanceTotal, averageFuelEfficiency: fuelStats?.averageEfficiency || 0, costPerKm: (fuelTotal + maintenanceTotal) / 100000, // Placeholder + totalDrivers, + assignedDrivers, }; - }, [vehicles, fuelStats, maintenanceStats]); + }, [vehicles, drivers, fuelStats, maintenanceStats]); const operatingCost = metrics.totalFuelSpend + metrics.totalMaintenanceSpend; const fuelPercent = operatingCost > 0 ? Math.round((metrics.totalFuelSpend / operatingCost) * 100) : 0; @@ -102,24 +133,29 @@ export function FleetDashboard() { - - Fleet Overview - + + + Fleet Management Dashboard + + + Real-time fleet overview, vehicle & driver management + + - {/* Key Metrics */} - + {/* Primary Metrics */} + + + + - - - {/* Fleet Status */} @@ -216,46 +252,108 @@ export function FleetDashboard() { - {/* Fleet List */} + {/* Vehicles & Drivers Tabs */} - - Fleet Vehicles - - - {(vehicles as Vehicle[]).length > 0 ? ( - - - - Registration - Plate - Model - Status - - - - {(vehicles as Vehicle[]).slice(0, 10).map(v => ( - - {v.registrationNumber} - {v.plateNumber} - - {v.manufacturer} {v.model} - - - {v.status || 'UNKNOWN'} - + + + }> + Vehicles ({(vehicles as Vehicle[]).length}) + + }> + Drivers ({(drivers as Driver[]).length}) + + + + + {(vehicles as Vehicle[]).length > 0 ? ( +
+ + + Registration + Plate + Model + Status - ))} - -
- ) : ( - - + + + {(vehicles as Vehicle[]).slice(0, 15).map(v => ( + + {v.registrationNumber} + {v.plateNumber} + + {v.manufacturer} {v.model} + + + + {v.status || 'UNKNOWN'} + + + + ))} + + + ) : ( + No vehicles in fleet - - )} -
+ )} + + + + {(drivers as Driver[]).length > 0 ? ( + + + + Name + License + Contact + Assigned Vehicle + + + + {(drivers as Driver[]).slice(0, 15).map(d => ( + + + + + + + {d.firstName} {d.lastName} + + + {d.licenseNumber || 'N/A'} + + + {d.phone && ( + + + {d.phone} + + + )} + {d.email && {d.email}} + + + + {d.assignedVehicle ? ( + Assigned + ) : ( + Unassigned + )} + + + ))} + +
+ ) : ( + + + No drivers in system + + )} +
+
);