From 814db8a17d93ef5b9c04b44a93993a387059d717 Mon Sep 17 00:00:00 2001 From: natib21 Date: Tue, 30 Jun 2026 13:38:35 +0000 Subject: [PATCH] feat: fleet dashboard page Shows fleet overview + key metrics: - Total vehicles, active count - Total fuel spending - Total maintenance spending - Average fuel efficiency - Fleet status (active/idle/maintenance) - Operating cost breakdown (fuel vs maintenance pie chart) - Fleet vehicle list (first 10) Route: /dashboard/fleet-dashboard Sidebar: Added to Fleet Management section Metrics aggregate from: - /vehicles (fleet size) - /fuel/stats (fuel spending + efficiency) - /maintenance/stats (maintenance spending) Co-Authored-By: Claude Haiku 4.5 --- apps/edr-freight-web/backoffice/src/App.tsx | 15 + .../src/pages/fleet/FleetDashboard.tsx | 262 ++++++++++++++++++ 2 files changed, 277 insertions(+) create mode 100644 apps/edr-freight-web/backoffice/src/pages/fleet/FleetDashboard.tsx diff --git a/apps/edr-freight-web/backoffice/src/App.tsx b/apps/edr-freight-web/backoffice/src/App.tsx index 513dd1300..7d8d07ab5 100644 --- a/apps/edr-freight-web/backoffice/src/App.tsx +++ b/apps/edr-freight-web/backoffice/src/App.tsx @@ -61,6 +61,7 @@ import FuelPurchasePage from "./pages/fleet/FuelPurchasePage"; import FuelStatsPage from "./pages/fleet/FuelStatsPage"; import { MaintenancePage } from "./pages/fleet/MaintenancePage"; import { FinancialReportsPage } from "./pages/fleet/FinancialReportsPage"; +import { FleetDashboard } from "./pages/fleet/FleetDashboard"; import { getCategorySidebarChildren } from "./pages/ruleEngine/config/resources"; import RuleEngineLegacyRedirect from "./pages/ruleEngine/RuleEngineLegacyRedirect"; import RuleEngineResourcePage from "./pages/ruleEngine/RuleEngineResourcePage"; @@ -168,6 +169,12 @@ const buildSidebarSections = (demoItems: SidebarItem[]): SidebarSection[] => [ { title: "Fleet Management", items: [ + { + label: "Fleet Dashboard", + href: "/dashboard/fleet-dashboard", + icon: , + permission: FREIGHT_PERMS.fleet.view, + }, { label: "Routes", href: "/dashboard/routes", @@ -785,6 +792,14 @@ const App = () => { } /> + + + + } + /> ( + + + + + {label} + + + {value} + + + + + + + +); + +export function FleetDashboard() { + const { data: vehicles = [] } = useQuery({ + queryKey: QUERY_KEYS.VEHICLES.list(), + queryFn: async () => { + const res = await vehiclesService.getAll({ limit: 1000 }); + return res.data || []; + }, + }); + + const { data: fuelStats } = useQuery({ + queryKey: ['fleet-fuel-stats'], + queryFn: async () => { + try { + const res = await api.get('/fuel/stats'); + return res.data || {}; + } catch { + return {}; + } + }, + }); + + const { data: maintenanceStats } = useQuery({ + queryKey: ['fleet-maintenance-stats'], + queryFn: async () => { + try { + const res = await api.get('/maintenance/stats'); + return res.data || {}; + } catch { + return {}; + } + }, + }); + + const metrics = useMemo((): FleetMetrics => { + const totalVehicles = (vehicles as Vehicle[]).length; + const activeVehicles = (vehicles as Vehicle[]).filter(v => v.status === 'ACTIVE').length; + + const fuelTotal = fuelStats?.totalCost || 0; + const maintenanceTotal = maintenanceStats?.totalCost || 0; + + return { + totalVehicles, + activeVehicles, + maintenanceOverdue: 0, // TODO: fetch from API + totalFuelSpend: fuelTotal, + totalMaintenanceSpend: maintenanceTotal, + averageFuelEfficiency: fuelStats?.averageEfficiency || 0, + costPerKm: (fuelTotal + maintenanceTotal) / 100000, // Placeholder + }; + }, [vehicles, fuelStats, maintenanceStats]); + + const operatingCost = metrics.totalFuelSpend + metrics.totalMaintenanceSpend; + const fuelPercent = operatingCost > 0 ? Math.round((metrics.totalFuelSpend / operatingCost) * 100) : 0; + + return ( + + + + + Fleet Overview + + + {/* Key Metrics */} + + + + + + + + + + + + + + + + {/* Fleet Status */} + + + + + Fleet Status + + + +
+ + Active Vehicles + {metrics.activeVehicles} / {metrics.totalVehicles} + + +
+ +
+ + Maintenance Overdue + {metrics.maintenanceOverdue} + + +
+ +
+ + Idle / Under Maintenance + {metrics.totalVehicles - metrics.activeVehicles} + + +
+
+
+
+
+ + + + + Operating Cost Breakdown + + + + + + + ${operatingCost.toFixed(0)} + + + Total Cost + + + } + size={120} + thickness={4} + /> + + +
+ + + + + + Fuel + + {fuelPercent}% + +
+ +
+ + + + + + Maintenance + + {100 - fuelPercent}% + +
+
+
+
+
+
+ + {/* Fleet List */} + + + 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'} + + + ))} + +
+ ) : ( + + + + No vehicles in fleet + + + )} +
+
+
+ ); +}