Leaflet Fleet Vehicle Map
blocklogisticsLive fleet map on free OpenStreetMap/Esri tiles — no API key. Select a unit to fly the camera and read status.
Installation
$pnpm dlx shadcn@latest add https://uipkge.dev/r/react/leaflet-fleet-vehicle-map.json$npx shadcn@latest add https://uipkge.dev/r/react/leaflet-fleet-vehicle-map.json$yarn dlx shadcn@latest add https://uipkge.dev/r/react/leaflet-fleet-vehicle-map.json$bunx shadcn@latest add https://uipkge.dev/r/react/leaflet-fleet-vehicle-map.jsonNamed registry:
npx shadcn@latest add @uipkge-react/leaflet-fleet-vehicle-mapInstalls to:components/blocks/Variants
Loading interactive previews…
Loading interactive previews…
Props
| Name | Type / Values | Default | Required |
|---|---|---|---|
vehicles | FleetVehicle[] | — | optional |
Schema
Type aliases exported from this item's source. Use these to shape the data you pass in.
FleetVehicleinterface FleetVehicle {
id: string
identifier: string
model: string
driver: string
status: VehicleStatus
speed: number
lat: number
lng: number
}Includes
Files installed (1)
components/blocks/LeafletFleetVehicleMap.tsx5.3 kB
'use client' import * as React from 'react' import { cn } from '@/lib/utils' import { Badge } from '@/components/ui/badge' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { LeafletMap, LeafletMarker, type LeafletMapRef } from '@/components/ui/leaflet-map' export type VehicleStatus = 'moving' | 'idling' | 'parked' export interface FleetVehicle { id: string identifier: string model: string driver: string status: VehicleStatus speed: number lat: number lng: number } export interface LeafletFleetVehicleMapProps extends React.HTMLAttributes<HTMLDivElement> { vehicles?: FleetVehicle[] } const DEFAULT_VEHICLES: FleetVehicle[] = [ { id: 'v-104', identifier: 'Truck #104', model: 'Volvo VNL 860', driver: 'Marcus Vance', status: 'moving', speed: 64, lat: 41.5868, lng: -93.625, }, { id: 'v-208', identifier: 'Van #208', model: 'Ford E-Transit 350', driver: 'Elena Rostova', status: 'moving', speed: 48, lat: 41.6005, lng: -93.712, }, { id: 'v-109', identifier: 'Truck #109', model: 'Freightliner Cascadia', driver: 'Derrick Hayes', status: 'idling', speed: 0, lat: 41.984, lng: -93.582, }, { id: 'v-212', identifier: 'Van #212', model: 'Mercedes Sprinter', driver: 'Aaliyah Patel', status: 'moving', speed: 52, lat: 41.591, lng: -93.604, }, ] function statusVariant(status: VehicleStatus) { if (status === 'moving') return 'info' as const if (status === 'idling') return 'warning' as const return 'secondary' as const } export function LeafletFleetVehicleMap({ className, vehicles = DEFAULT_VEHICLES, ...props }: LeafletFleetVehicleMapProps) { const [selectedId, setSelectedId] = React.useState(vehicles[0]?.id ?? '') const mapRef = React.useRef<LeafletMapRef>(null) const selected = vehicles.find((v) => v.id === selectedId) ?? vehicles[0] const selectVehicle = (vehicle: FleetVehicle) => { setSelectedId(vehicle.id) mapRef.current?.flyTo?.({ center: [vehicle.lng, vehicle.lat], zoom: 12, duration: 800, }) } return ( <div data-slot="leaflet-fleet-vehicle-map" className={cn('flex h-full min-h-0 flex-col gap-4', className)} {...props} > <div className="flex flex-wrap items-center justify-between gap-3"> <h2 className="text-foreground text-lg font-semibold tracking-tight">Fleet</h2> <p className="text-muted-foreground text-xs">{vehicles.length} units</p> </div> <div className="grid min-h-0 flex-1 grid-cols-1 gap-4 lg:grid-cols-12"> <Card className="border-border flex min-h-0 flex-col overflow-hidden shadow-xs lg:col-span-8"> <div className="relative min-h-[360px] flex-1 overflow-hidden"> <LeafletMap ref={mapRef} variant="dark" center={selected ? [selected.lng, selected.lat] : [-93.625, 41.59]} zoom={11} className="absolute inset-0 size-full" > {vehicles.map((vehicle) => ( <LeafletMarker key={vehicle.id} lngLat={[vehicle.lng, vehicle.lat]} anchor="center"> <button type="button" className={ selectedId === vehicle.id ? 'border-primary bg-primary text-primary-foreground rounded-full border px-2 py-0.5 font-mono text-xs font-semibold shadow-xs' : 'border-border bg-card text-foreground rounded-full border px-2 py-0.5 font-mono text-xs font-semibold shadow-xs' } onClick={() => selectVehicle(vehicle)} > {vehicle.identifier.replace('Truck ', '').replace('Van ', '')} </button> </LeafletMarker> ))} </LeafletMap> </div> </Card> <Card className="border-border flex min-h-0 flex-col overflow-hidden shadow-xs lg:col-span-4"> <CardHeader className="border-border border-b p-4"> <CardTitle className="text-sm font-semibold">Vehicles</CardTitle> </CardHeader> <CardContent className="divide-border min-h-0 flex-1 divide-y overflow-auto p-0"> {vehicles.map((vehicle) => ( <button key={vehicle.id} type="button" className={cn( 'hover:bg-accent/50 flex w-full items-center justify-between gap-3 px-4 py-3 text-left transition-colors', selectedId === vehicle.id && 'bg-accent/60', )} onClick={() => selectVehicle(vehicle)} > <div className="min-w-0"> <p className="text-foreground truncate text-sm font-medium">{vehicle.identifier}</p> <p className="text-muted-foreground truncate text-xs"> {vehicle.driver} · {vehicle.model} </p> </div> <Badge variant={statusVariant(vehicle.status)} className="shrink-0 text-xs capitalize"> {vehicle.status === 'moving' ? `${vehicle.speed} mph` : vehicle.status} </Badge> </button> ))} </CardContent> </Card> </div> </div> ) } export default LeafletFleetVehicleMap
Raw manifest:https://uipkge.dev/r/react/leaflet-fleet-vehicle-map.json