Skip to main content

Components

shadcn/ui Components

The app uses shadcn/ui for standard UI components. These are installed directly into src/components/ui/ and can be customized:

ComponentUsage
ButtonPrimary actions, form submissions, destructive actions
CardContent containers for dashboard cards, detail sections
InputText inputs with label support
SelectDropdown selects for filters, form fields
BadgeTrip status badges (ACTIVE, COMPLETED, CANCELLED)
CheckboxToggle switches (GPS logging, auto-refresh)
LabelForm field labels
TextareaMulti-line text input (descriptions, notes)
SeparatorVisual dividers

Theme

shadcn/ui uses CSS variables for theming with automatic dark mode support via the dark class on <html>. The theme is configured in globals.css with oklch colors.


Shared Components (@elcto/ui)

Custom components that are shared across apps via the @elcto/ui workspace package.

Map

File: shared/ui/src/components/map.tsx

Interactive MapLibre GL map with composable children components. Supports multiple tile providers with automatic dark/light theme switching.

Map Props

PropTypeDefaultDescription
latitudenumberCenter latitude
longitudenumberCenter longitude
center[lng, lat]Center as GeoJSON array (alternative to lat/lng)
zoomnumber13Initial zoom level
tilesMapTileProvider'carto'Tile provider
themeOverride'light' | 'dark'Override auto theme detection
childrenReactNodeMap children (markers, routes, etc.)

Tile Providers

ProviderLightDark
cartoCarto VoyagerCarto Dark Matter
openfreemapOpenFreeMap BrightCarto Dark Matter
openfreemap3dOpenFreeMap Liberty (60° pitch)Carto Dark Matter
positronOpenFreeMap PositronCarto Dark Matter
libertyOpenFreeMap Liberty (flat)Carto Dark Matter
fiordOpenFreeMap FiordCarto Dark Matter

Child Components

ComponentDescription
MapMarkerPosition marker with React children (dot, arrow, badge)
StaticMarkerSimple colored dot marker
MapRouteGeoJSON line layer for routes
FitBoundsAuto-fit map to given points

Current Position Marker

The GPS page uses MapMarker with a CurrentPositionMarker child that renders:

  • Dot#14F5BF with beacon pulse animation
  • Heading arrow — SVG chevron pointing in direction of travel (only when speed > 2 km/h)
  • Speed badge — Spring-animated speed display with useSpring, positioned opposite to heading arrow using 8-point anchoring
  • Dark mode — Adapts border/stroke colors for dark map tiles

Usage

import { Map, MapMarker, StaticMarker, MapRoute, FitBounds } from '@elcto/ui';

<Map latitude={52.52} longitude={13.40} zoom={14} tiles="carto">
<MapMarker longitude={13.40} latitude={52.52}>
<CurrentPositionMarker speed={12.5} heading={90} />
</MapMarker>
<StaticMarker latitude={52.51} longitude={13.39} color="#22c55e" />
<MapRoute points={gpsHistory} />
<FitBounds points={allPoints} />
</Map>

Spinner

Loading indicator with configurable size (sm, md, lg).

import { Spinner } from '@elcto/ui';

<Spinner size="md" />

ThemeToggle

Dark/light mode toggle with system preference detection. Persists to localStorage.

import { ThemeToggle } from '@elcto/ui';

<ThemeToggle />

Speed Overlay

File: src/app/(overlay)/overlay/speed/page.tsx

A minimal, embeddable speed display designed for OBS Studio browser source.

Features

  • Real-time WebSocket — Subscribes to the speed channel via Rust API /ws
  • Spring animation — Speed transitions smoothly via custom useSpring hook (stiffness: 0.05, damping: 0.65)
  • Connection indicator — Green pulsing dot (connected), red (disconnected)
  • Three states — Loading (spinner), Active (animated speed), Error (message)
  • OBS compatible — Transparent html/body background, dark card design

Visual Design (hardcoded, not themed)

PropertyValue
Card backgroundrgba(39, 44, 50, 0.95)
Accent color#14F5BF
Text color#EDEDF3
Left border4px solid #14F5BF
Card height5.25rem

URL

Access at /overlay/speed — uses the overlay layout group (no navigation).


Map Overlay

File: src/app/(overlay)/overlay/map/page.tsx

Fullpage live GPS map for OBS Studio browser source with heading arrow, speed badge, and compass.

URL Parameters

ParamDefaultDescription
tilesopenfreemapTile provider (carto, openfreemap, openfreemap3d, positron, liberty, fiord)
themelightlight or dark (manual override)
zoom16Initial zoom level
compassoff1 = show compass, 1.5 = larger, 0 or absent = hidden
speedonfalse or 0 = hide speed badge
aisonfalse or 0 = hide AIS vessel markers
aisnamesonfalse or 0 = hide vessel name labels
aisinfoonfalse or 0 = hide vessel info badges (speed, length, beam, type)
zoomcycleoffComma-separated zoom levels (e.g. 16,12,10)
zoominterval30Seconds between zoom cycle steps
autotimeofftrue = auto dark/light based on Berlin time (20:00–06:00 = night)

Example

/overlay/map?tiles=carto&theme=dark&zoom=15&compass=1&speed=true&ais=true&aisnames=true&zoomcycle=16,12,10&zoominterval=15

Compass Overlay

File: src/app/(overlay)/overlay/compass/page.tsx

Standalone compass widget for OBS. Shows heading needle, degree value, and 16-point cardinal direction.

ParamDefaultDescription
scale1Compass size multiplier

URL

Access at /overlay/compass or /overlay/compass?scale=1.5.


AIS Vessel Markers

The map overlay displays AIS (Automatic Identification System) vessel data received via the vessels WebSocket channel. The Rust API connects to AISstream.io via tokio-tungstenite and broadcasts vessel positions.

Vessel Display

Vessel StateMarkerArrowBadge
Moving (Unterwegs)Orange dotOrange heading arrowSpeed, name, info rotation
Stationary (Vor Anker, Festgemacht, Auf Grund)Gray dotNoneName only, no speed

Badge Info Rotation

Moving vessels rotate through info badges every 10 seconds with a fade animation:

  • Speed (knots)
  • Length (meters, from ShipStaticData: A+B)
  • Beam (meters, from ShipStaticData: C+D)
  • Ship type

Distance-Based Opacity

Badge opacity scales with distance from own ship:

  • ≤500m: 100% opacity
  • 500m–5km: Linear fade
  • ≥5km: 15% minimum opacity

z-index Layering

Layerz-index
Own ship marker100
Moving vessels10
Stationary vessels1

Unterwegs, Vor Anker, Festgemacht, Nicht unter Kommando, Tiefgangbeschränkt, Auf Grund, Fischend, Unter Segel, Manövrierunfähig.

Configuration

AIS is configured in the Rust API via the [ais] TOML section:

KeyDescription
api_keyAISstream.io API key
enabledEnable/disable AIS tracking
radius_kmRadius around GPS position for vessel subscription
ignore_mmsiArray of MMSI numbers to blacklist

GPS Tracker Page

File: src/app/(app)/gps/page.tsx

Displays real-time GPS position via WebSocket with interactive map.

  • Subscribes to speed and gps WebSocket channels for live updates
  • Dead reckoning interpolation for smooth marker movement between GPS updates
  • Heading arrow + spring-animated speed badge on current position marker
  • Beacon pulse animation on dot
  • Tile provider selector (Carto / OpenStreetMap) with auto dark/light mode
  • Map auto-centers on position via LiveTracker
  • Manual refresh button as REST fallback

Trip Management

Full CRUD with filtering by status, client, category, date range, GPS tracking, and search.

Pages

PagePathDescription
Trip List/tripsFilterable list with status badges
New Trip/trips/newCreate with sender/receiver, cargo, GPS toggle
Trip Detail/trips/[id]Map with route, complete/delete actions
Edit Trip/trips/[id]/editUpdate all trip fields
Categories/trips/categoriesInline CRUD for cargo categories

Client Management

Client directory with address management, contact persons, and trip associations.

Pages

PagePathDescription
Client List/clientsNames, emails, trip counts
New Client/clients/newCompany info, address, dynamic contacts
Client Detail/clients/[id]Full info with trips as sender/receiver