Skip to main content
This page documents the TypeScript interfaces and types used throughout the Fleet Dashboard. Types are organized by domain: unified fleet, vehicles, and trailers.

unified-fleet.ts

Type definitions for the unified fleet management dashboard that combines drivers, vehicles, and trailers.

Import

import { 
  Driver, 
  UnifiedFleetData, 
  FleetStatistics,
  FleetConnectionState,
  UseUnifiedFleetDataResult 
} from "@/types/unified-fleet";

Driver Types

Core driver entity from /api/drivers/current endpoint.
interface Driver {
  driver_id: string;
  source_id: string;
  provider: string;
  status: string;
  
  // Personal info
  first_name: string;
  middle_name?: string | null;
  last_name: string;
  email?: string | null;
  phone?: string | null;
  username?: string | null;
  
  // License
  license?: DriverLicense | null;
  
  // Additional
  groups?: string[] | null;
  created_at?: string | null;
  updated_at?: string | null;
  metadata?: DriverMetadata | null;
}
FieldTypeNotes
driver_idstringPrimary identifier (may have drv_ prefix)
source_idstringID in the provider’s system
providerstringData source: "motive", "geotab", etc.
statusstring"active" or "inactive"
ID formats: Both driver_id and source_id can be used to match drivers. The useUnifiedFleetData hook creates mappings for both formats (with and without drv_ prefix).
Driver’s license information.
interface DriverLicense {
  state: string;   // e.g., "TN", "CA"
  number: string;  // License number
}
System metadata for driver records.
interface DriverMetadata {
  added_at: string;     // ISO timestamp
  modified_at: string;  // ISO timestamp
  visibility: string;   // Access level
}
API response from /api/drivers/current.
interface DriverBatchResponse {
  drivers_count: number;
  drivers: Driver[];
  polled_at: string;
  cache_status: "hit" | "miss";
  next_cursor?: string | null;
  prev_cursor?: string | null;
  has_next: boolean;
  has_prev: boolean;
  page_size: number;
  current_page: number;
  total_pages: number;
  connection_token: string;
}
Pagination fields: The response includes pagination metadata, but the useUnifiedFleetData hook currently fetches all drivers without pagination.
Compact driver representation for dashboard tables.
interface DriverSummary {
  driver_id: string;
  name: string;              // Combined "first_name last_name"
  status: string;
  vehicle_id: string | null; // Associated vehicle (if any)
  location: string;          // From associated vehicle
  provider: string;
  phone?: string | null;
  email?: string | null;
}
Derived fields: name is computed as ${first_name} ${last_name}. location comes from the associated vehicle’s address.

Fleet Data Types

Combined data from all three sources.
interface UnifiedFleetData {
  drivers: Driver[];
  vehicles: VehicleLocation[];
  trailers: TrailerLocation[];
  
  driversCount: number;
  vehiclesCount: number;
  trailersCount: number;
  
  driversPolledAt: string | null;
  vehiclesPolledAt: string | null;
  trailersPolledAt: string | null;
}
FieldTypeNotes
*CountnumberCount from API response
*PolledAtstring | nullISO timestamp of last fetch/update
Timestamps: The *PolledAt fields are useful for displaying “Last updated X seconds ago” and detecting stale data.
Aggregated statistics for KPI display.
interface FleetStatistics {
  drivers: DriverStatistics;
  vehicles: VehicleStatistics;
  trailers: TrailerStatistics;
}
Driver-related statistics.
interface DriverStatistics {
  total: number;
  active: number;
  inactive: number;
  onDuty: number;       // Currently 0 (requires HOS data)
  offDuty: number;      // Currently 0 (requires HOS data)
  hosWarnings: number;  // Drivers with <4 hours remaining (currently 0)
}
Unimplemented fields: onDuty, offDuty, and hosWarnings are always 0. They require HOS (Hours of Service) data integration which is not yet implemented.
Vehicle/tractor statistics.
interface VehicleStatistics {
  total: number;
  engineOn: number;
  engineOff: number;
  available: number;   // Vehicles without assigned driver
  lowFuel: number;     // Fuel < 25%
  avgSpeed: number;    // Average of moving vehicles
  withDrivers: number; // Vehicles with assigned driver
}
StatCalculation
available!driver_id (falsy)
lowFuelfuel.primary_percentage < 25
avgSpeedAverage where engine_state === "on" AND speed != null
withDriversdriver_id is truthy
Trailer statistics.
interface TrailerStatistics {
  total: number;
  moving: number;         // Speed ≥ 5 mph
  stationary: number;     // Speed < 5 mph
  recentUpdates: number;  // Updated within 5 minutes
  staleData: number;      // Not updated in 10+ minutes
  avgSpeed: number;       // Average of moving trailers
  providerCounts: Record<string, number>;
}
Speed threshold: The 5 mph threshold prevents GPS drift from being counted as movement.Speed conversion: Motive speeds are in km/h and converted to mph before comparison.

Connection State Types

Connection status for all three data sources.
interface FleetConnectionState {
  drivers: {
    status: "connecting" | "connected" | "disconnected" | "error";
    lastUpdate: string | null;
    error: string | null;
  };
  vehicles: SSEConnectionState;  // From vehicle-locations.ts
  trailers: SSEConnectionState;  // From trailer-locations.ts
}
Drivers don’t have SSE: Only vehicles and trailers have SSE connections. Drivers use polling-only REST API.

Summary Types

Compact vehicle representation for dashboard tables.
interface TractorSummary {
  vehicle_id: string;
  driver_id: string | null;
  location: string;           // Formatted address
  fuelLevel: number | null;   // Percentage 0-100
  engineState: "on" | "off" | "unknown" | null;
  speed: number | null;
  provider: string;
  located_at: string;         // ISO timestamp
}
Compact trailer representation for dashboard tables.
interface TrailerSummary {
  trailer_id: string;
  speed: number | null;
  location: string;        // Formatted address
  provider: string;
  located_at: string;      // ISO timestamp
  heading: number | null;  // Degrees 0-360
}
Return type of the useUnifiedFleetData hook.
interface UseUnifiedFleetDataResult {
  data: UnifiedFleetData;
  statistics: FleetStatistics;
  connectionState: FleetConnectionState;
  isLoading: boolean;
  refresh: () => Promise<void>;
  topDrivers: DriverSummary[];
  topVehicles: TractorSummary[];
  topTrailers: TrailerSummary[];
}

vehicle-locations.ts

Type definitions for real-time vehicle location data from backend SSE/REST APIs.

Import

import { 
  VehicleLocation,
  VehicleLocationsResponse,
  SSEConnectionState,
  UseVehicleLocationsResult 
} from "@/types/vehicle-locations";
Note: This file defines its own Coordinates and Address interfaces, identical to those in trailer-locations.ts. They are not shared/imported to keep each domain self-contained.

Core Types

Primary vehicle location entity.
interface VehicleLocation {
  vehicle_id: string;
  tractor_number?: string | null;      // Legacy McLeod ID (e.g., "107")
  mcleod_tractor_id?: string | null;   // McLeod ID from API (e.g., "556")
  driver_id: string | null;
  provider: string;
  located_at: string;                   // ISO 8601 timestamp
  location: Coordinates;
  address: Address;
  heading: number | null;               // 0-360 degrees
  speed: number | null;                 // Units vary by provider!
  odometer: number | null;              // Total mileage
  fuel: FuelData | null;
  engine_state: EngineState | null;
  engine_runtime: number | null;
}
Speed units vary by provider:
  • Motive: km/h
  • Most others: mph
Always convert before display or comparison!
Two tractor ID fields:
  • tractor_number: Legacy field from database mapping
  • mcleod_tractor_id: Current field from /locations/current endpoint
SSE updates may not include these fields, so the hook preserves existing values during merges.
engine_runtime: This field contains total engine hours. The value may be in thousandths (divide by 1000 to get actual hours). Check the source code comment: “Prefer engine_hours (backend-processed) over raw engine_runtime”.
Geographic coordinates.
interface Coordinates {
  latitude: number;   // -90 to 90
  longitude: number;  // -180 to 180
}
Validation: Always validate coordinates before use:
const isValid = lat >= -90 && lat <= 90 && lng >= -180 && lng <= 180;
Geocoded address information.
interface Address {
  formatted: string;               // Full address string
  street_address?: string | null;
  locality?: string | null;        // City
  region?: string | null;          // State/province
  postal_code?: string | null;
  country?: string | null;
}
Fallback display: If formatted is missing, components typically show coordinates in "lat, lng" format.
Fuel level information.
interface FuelData {
  primary_percentage: number | null;    // 0-100
  secondary_percentage?: number | null; // For dual-tank vehicles
}
Low fuel threshold: The dashboard uses < 25% as the low fuel threshold.
Engine state type.
type EngineState = "on" | "off" | "unknown";
The API may also return "running" which should be treated as "on".

Response Types

API response from /locations/current and SSE events.
interface VehicleLocationsResponse {
  vehicles_count: number;
  polled_at: string;           // ISO 8601 timestamp
  cache_status: "hit" | "miss";
  vehicles: VehicleLocation[];
}
cache_status: Indicates whether the response came from backend cache ("hit") or fresh fetch ("miss"). Useful for debugging latency issues.
SSE connection status.
interface SSEConnectionState {
  status: "connecting" | "connected" | "disconnected" | "error";
  lastUpdate: string | null;  // ISO timestamp
  error: string | null;       // Error message
}
StatusMeaning
connectingInitial state, establishing connection
connectedReceiving real-time updates
disconnectedConnection lost (auto-reconnecting)
errorREST fetch failed (requires manual action)
Return type of the useVehicleLocations hook.
interface UseVehicleLocationsResult {
  vehicles: VehicleLocation[];
  connectionState: SSEConnectionState;
  vehiclesCount: number;
  polledAt: string | null;
  cacheStatus: "hit" | "miss" | null;
  refresh: () => Promise<void>;
  isLoading: boolean;
}

Filter & Sort Types

Types for filtering vehicle lists.
type EngineStateFilter = "all" | "on" | "off";
type FuelLevelFilter = "all" | "low" | "medium" | "high";
type ProviderFilter = "all" | string;

interface VehicleFilters {
  engineState: EngineStateFilter;
  fuelLevel: FuelLevelFilter;
  provider: ProviderFilter;
  searchTerm: string;
}
Fuel level thresholds (implied):
  • low: < 25%
  • medium: 25-75%
  • high: > 75%
Types for sorting vehicle lists.
type SortColumn = 
  | "vehicle_id"
  | "driver_id"
  | "provider"
  | "located_at"
  | "engine_state"
  | "fuel"
  | "speed"
  | "location";

type SortDirection = "asc" | "desc";

interface SortConfig {
  column: SortColumn | null;
  direction: SortDirection;
}

trailer-locations.ts

Type definitions for real-time trailer location data with SSE integration.

Import

import { 
  TrailerLocation,
  TrailerInfo,
  CombinedTrailerData,
  TrailerLocationBatch,
  SSEConnectionState 
} from "@/types/trailer-locations";
Note: This file defines its own Coordinates and Address interfaces (same as vehicle-locations.ts). This keeps each domain self-contained but may cause issues if you try to mix types directly.

Core Types

Real-time trailer location from location_update SSE events.
interface TrailerLocation {
  provider: string;
  trailer_id: string;
  terminal_trailer_id?: string;  // "trl_xxx" format
  name?: string | null;          // Short name/number
  located_at: string;            // ISO 8601 timestamp
  location: Coordinates;
  address: Address | null;       // May be null!
  heading: number | null;        // Degrees 0-360
  speed: number | null;          // Units vary by provider
  raw: any[];                    // Raw data from provider
}
Speed units vary:
  • Motive: km/h
  • Other providers: mph
The useUnifiedFleetData hook handles conversion. Components should do the same.
address can be null: Unlike vehicles, trailers may not have geocoded addresses. Always check for null before accessing address fields.
raw field: Contains the original unprocessed data from the provider’s API. Useful for debugging or accessing provider-specific fields not in the normalized interface.
Static trailer information from trailer_info SSE events.
interface TrailerInfo {
  id: string;              // "trl_xxx" format
  source_id: string;       // ID in provider's system
  provider: string;
  status: "active" | "inactive";
  
  // Equipment details
  name: string | null;     // "Trailer #02323"
  vin: string | null;
  serial_number: string | null;
  make: string | null;     // "Great Dane Trailers"
  model: string | null;    // "Champion CP Plate Van"
  year: number | null;
  
  license_plate: LicensePlate | null;
  groups: string[];
  
  created_at: string | null;
  updated_at: string | null;
  metadata: {
    added_at: string;
    modified_at: string;
    deleted_at: string | null;
    visibility: string;
  } | null;
  raw: any[];
}
Metadata is inline: Unlike DriverMetadata, the trailer metadata type is defined inline (not a separate interface). It includes deleted_at for soft-delete tracking.
Joining location and info: Use terminal_trailer_id from TrailerLocation to match with id from TrailerInfo.
License plate information.
interface LicensePlate {
  state: string;   // e.g., "TN", "CA"
  number: string;  // e.g., "ABC-1234"
}
Merged location + static info for complete trailer view.
interface CombinedTrailerData extends TrailerLocation {
  // All TrailerLocation fields plus:
  name?: string | null;
  vin?: string | null;
  make?: string | null;
  model?: string | null;
  year?: number | null;
  serial_number?: string | null;
  license_plate?: LicensePlate | null;
  status?: "active" | "inactive";
  groups?: string[];
}
This type extends TrailerLocation with optional static fields from TrailerInfo. Used when you need complete trailer data in one object.

Response Types

API response from /api/trailers/current and SSE location_update events.
interface TrailerLocationBatch {
  trailers_count: number;
  polled_at: string;                // ISO 8601 timestamp
  cache_status?: "hit" | "miss";
  trailers: TrailerLocation[];
  next_cursor?: string | null;
  connection_token?: string | null;
}
Response for static trailer information.
interface TrailerInfoBatch {
  trailers_count: number;
  fetched_at: string;              // ISO 8601 timestamp
  trailers: TrailerInfo[];
  next_cursor?: string | null;
  connection_token?: string | null;
}
Payload of the SSE connected event.
interface SSEConnectedEvent {
  client_id: string;  // Unique client identifier
}
Trailer SSE connection status (extends base with clientId).
interface SSEConnectionState {
  status: "connecting" | "connected" | "disconnected" | "error";
  lastUpdate: string | null;
  error: string | null;
  clientId?: string;  // From SSE connected event
}
clientId: Unique identifier assigned by the server. Useful for debugging multiple connections.

Hook Result Types

Return type for a basic trailer locations hook.
interface UseTrailerLocationsResult {
  trailers: TrailerLocation[];
  connectionState: SSEConnectionState;
  trailersCount: number;
  polledAt: string | null;
  cacheStatus: "hit" | "miss" | null;
  refresh: () => Promise<void>;
  isLoading: boolean;
}
Return type for full SSE integration with both location and info events.
interface UseTrailerSSEResult {
  locations: TrailerLocation[];
  staticInfo: TrailerInfo[];
  combined: CombinedTrailerData[];
  connectionState: SSEConnectionState;
  trailersCount: number;
  lastLocUpdate: string | null;
  lastInfoUpdate: string | null;
  refresh: () => Promise<void>;
  isLoading: boolean;
}
Three data arrays:
  • locations: Real-time location data
  • staticInfo: Equipment details (VIN, make, model)
  • combined: Merged view of both

Filter & Sort Types

Types for filtering trailer lists.
type ProviderFilter = "all" | string;
type MotionStateFilter = "all" | "moving" | "stationary";
type SpeedFilter = "all" | "stationary" | "slow" | "moderate" | "fast";
type HeadingFilter = "all" | "north" | "east" | "south" | "west";
type FreshnessFilter = "all" | "recent" | "normal" | "stale";

interface TrailerFilters {
  provider: ProviderFilter;
  motionState: MotionStateFilter;
  speed: SpeedFilter;
  heading: HeadingFilter;
  freshness: FreshnessFilter;
  searchTerm: string;
}
Note: The thresholds below are NOT defined in the types file - they are typical implementation values based on how the hooks calculate statistics. The types only define the filter names.
Typical implementation thresholds:
FilterTypical Condition
movingSpeed ≥ 5 mph
stationarySpeed < 5 mph
recentUpdated < 5 min ago
staleUpdated > 10 min ago
northHeading 315-45° (implementation-specific)
eastHeading 45-135° (implementation-specific)
southHeading 135-225° (implementation-specific)
westHeading 225-315° (implementation-specific)
Types for sorting trailer lists.
type SortColumn = 
  | "trailer_id"
  | "provider"
  | "located_at"
  | "speed"
  | "location"
  | "name"
  | "vin"
  | "make";

type SortDirection = "asc" | "desc";

interface SortConfig {
  column: SortColumn | null;
  direction: SortDirection;
}
Type alias for trailer motion state.
type MotionState = "moving" | "stationary";

Type Relationships

Visual overview of how types relate across files:
unified-fleet.ts
├── imports VehicleLocation from vehicle-locations.ts
├── imports TrailerLocation from trailer-locations.ts
├── imports SSEConnectionState from both

├── Driver (own type)
├── UnifiedFleetData
│   ├── drivers: Driver[]
│   ├── vehicles: VehicleLocation[]
│   └── trailers: TrailerLocation[]

├── FleetStatistics
│   ├── drivers: DriverStatistics
│   ├── vehicles: VehicleStatistics
│   └── trailers: TrailerStatistics

└── FleetConnectionState
    ├── drivers: inline type
    ├── vehicles: SSEConnectionState (from vehicle-locations)
    └── trailers: SSEConnectionState (from trailer-locations)
Note: Both vehicle-locations.ts and trailer-locations.ts define their own SSEConnectionState interface. The trailer version has an additional clientId field.

Common Gotchas

Problem: Different providers return speed in different units.Solution: Always check the provider and convert if necessary:
const speedMph = provider === "motive" 
  ? speed * 0.621371  // km/h to mph
  : speed;            // already mph
Problem: Some fields use null, others use undefined, some use both.Solution: Use nullish coalescing for safe access:
const address = trailer.address?.formatted ?? "Location unavailable";
const fuel = vehicle.fuel?.primary_percentage ?? null;
Problem: IDs may have prefixes (drv_, trl_) or not.Solution: The hooks create mappings for both formats. When matching manually:
const normalizedId = id.replace(/^(drv_|trl_)/i, "");
Problem: All timestamps are ISO 8601 strings, not Date objects.Solution: Parse before using:
const date = new Date(located_at);
const isRecent = Date.now() - date.getTime() < 5 * 60 * 1000; // 5 min
Problem: Coordinates may be null, NaN, or out of valid range.Solution: Always validate before use:
function isValidCoord(lat: number | null, lng: number | null): boolean {
  if (lat == null || lng == null) return false;
  if (Number.isNaN(lat) || Number.isNaN(lng)) return false;
  return lat >= -90 && lat <= 90 && lng >= -180 && lng <= 180;
}