import { Vehicle, VehicleGroup } from '@/lib/api'; import { getVehicleDisplayStatus } from '@/lib/utils'; import { Car, Clock, Signal, Layers, ChevronDown, ChevronRight, ChevronsDown, ChevronsUp } from 'lucide-react'; import { clsx } from 'clsx'; import { useMemo, useState } from 'react'; interface VehicleListProps { vehicles: Vehicle[]; groups?: VehicleGroup[]; } export function VehicleList({ vehicles, groups = [] }: VehicleListProps) { // State for tracking collapsed groups. If a key is present and true, it is collapsed. // Default (empty object) means all are expanded. const [collapsedGroups, setCollapsedGroups] = useState>({}); const groupedVehicles = useMemo(() => { const grouped: Record = { 'unassigned': [] }; // Initialize groups groups.forEach(g => { grouped[g.id] = []; }); // Distribute vehicles vehicles.forEach(v => { if (v.groupId && grouped[v.groupId]) { grouped[v.groupId].push(v); } else { grouped['unassigned'].push(v); } }); return grouped; }, [vehicles, groups]); const getGroupStats = (groupVehicles: Vehicle[]) => { const online = groupVehicles.filter(v => getVehicleDisplayStatus(v) === 'Online').length; const total = groupVehicles.length; const versions = groupVehicles.reduce((acc, v) => { acc[v.currentVersion] = (acc[v.currentVersion] || 0) + 1; return acc; }, {} as Record); const versionStr = Object.entries(versions) .map(([v, count]) => `v${v} (${Math.round((count / total) * 100)}%)`) .join(', '); return { online, total, versionStr }; }; // Helper to get group details const getGroupDetails = (key: string) => { if (key === 'unassigned') return { name: 'Unassigned', description: 'Vehicles without a group' }; const group = groups.find(g => g.id === Number(key)); return group || { name: 'Unknown Group', description: '' }; }; const toggleGroup = (key: string) => { setCollapsedGroups(prev => ({ ...prev, [key]: !prev[key] })); }; const groupKeys = Object.keys(groupedVehicles).filter(k => k === 'unassigned' ? groupedVehicles[k].length > 0 : true ).sort((a, b) => { if (a === 'unassigned') return 1; if (b === 'unassigned') return -1; return 0; // Keep order }); const expandAll = () => setCollapsedGroups({}); const collapseAll = () => { const allCollapsed = groupKeys.reduce((acc, key) => { acc[key] = true; return acc; }, {} as Record); setCollapsedGroups(allCollapsed); }; return (

Fleet Overview

{groupKeys.length === 0 && (
No groups or vehicles found.
)} {groupKeys.map(key => { const groupVs = groupedVehicles[key as any]; if (key !== 'unassigned' && groupVs.length === 0) { // Show empty groups too? Requirement implies showing group info. // If 0 vehicles, stats are 0/0 and versions empty. } const { name, description } = getGroupDetails(key); const stats = getGroupStats(groupVs); const isCollapsed = collapsedGroups[key]; return (
toggleGroup(key)} >
{isCollapsed ? : }

{name}

{description &&

{description}

}
{stats.online}/{stats.total} Online {stats.total > 0 && ( {stats.versionStr} )}
{!isCollapsed && (
{groupVs.length > 0 ? (
{groupVs.map((vehicle) => { const displayStatus = getVehicleDisplayStatus(vehicle); return ( ) })}
VIN Status Version Last Heartbeat
{vehicle.vin} {displayStatus} v{vehicle.currentVersion} {new Date(vehicle.lastHeartbeat).toLocaleTimeString()}
) : (
No vehicles in this group.
)}
)}
); })}
); }