Real-time operator dispatch console for monitoring active deliveries in Porto Alegre Centro Histórico — live map, tracking states, and append-only event timelines.
Demonstration project. All couriers, restaurants, and delivery IDs are fictional. This is not a production system and is not affiliated with iFood, Uber, or any logistics operator.
- Live map — 15 simulated couriers moving across POA Centro (Brazil - RS) on OpenStreetMap (Leaflet).
- Active delivery list — restaurant, street, status, courier, and dynamic ETA.
- Courier detail — route polyline,
live/stale/offlinetracking state, last seen, event timeline. - Visibility gap demo — session plan rolls 2–4 stale/reconnect events on live couriers (varies per boot/reset); positions and routes stay deterministic (seed 42).
- Connection resilience — SSE stream with connected / reconnecting / disconnected indicator.
When courier GPS or connectivity drops, operators lose visibility — the map point freezes and disputes lack a timeline. DispatchLab shows last known position, stale duration, and an append-only event log instead of silent disappearance.
Map overview → select delivery / courier → inspect route and timeline → apply Surpresa de rede or wait for session events → observe stale/reconnect.
- Start backend and frontend (see Run locally).
- Open http://localhost:4200 (viewport ≥ 768px).
- Click Central da demo on the map to open the docked panel (tabs Controle, Cenários, Eventos).
Full walkthrough, dev controls, and E2E notes: docs/guides/demo-guide.md.
Angular UI (operator console)
↓ DispatchProvider / DispatchStreamService
Go REST + SSE (/api/scenario, /api/stream)
↓
Deterministic simulator (seed 42, poa_centro.json)
↓
In-memory store (couriers, deliveries, timelines)
- UI — presentational components in
frontend/src/app/components/; facade infeatures/dispatch/. - Domain — canonical types in
services/dispatch/types.ts. - Integration —
DispatchProviderfor snapshots;DispatchStreamServicefor SSE (separate lifecycle — ADR-0001). - Simulation — embedded POA Centro scenario with SMU/GIS bbox and real landmarks.
See docs/README.md for architecture notes and ADRs.
| Layer | Choice |
|---|---|
| Frontend | Angular 19, TypeScript strict, standalone components |
| Map | Leaflet + OpenStreetMap |
| Backend | Go 1.22, SSE hub, goroutine tick loop |
| Tests | Go test, Karma/Jasmine, Playwright |
Requirements: Node.js ≥ 22, Go ≥ 1.22, viewport ≥ 768px.
# Terminal 1 — backend
cd backend && go run ./cmd/server
# Terminal 2 — frontend (proxies /api → :8080)
cd frontend && npm install && npm startOr use ./scripts/dev.sh (bash).
| Command | Location | Description |
|---|---|---|
go run ./cmd/server |
backend/ |
Start API + SSE simulator |
npm start |
frontend/ |
Dev server with API proxy |
go test ./... |
backend/ |
Go unit tests |
npm run test -- --watch=false |
frontend/ |
Angular unit tests |
npm run test:e2e |
repo root | Playwright E2E (starts both servers) |
- Go: geo math, simulator stale/reconnect at ticks 45/90, SSE hub, REST handlers.
- Angular: stream merge logic, connection indicator labels.
- E2E: load map → open demo center → reset with confirmation → apply Surpresa de rede → manual stale/reconnect via dev trigger.
- No authentication, database, Kafka, or external routing engine (OSRM).
- Couriers follow dense L-shaped waypoint polylines (street-like, not road-snapped).
- Single-process in-memory state; restart resets simulation clock.
- Desktop/tablet layout only (≥768px).
- Five deliveries (DEL-016–020) are queued on couriers already on another active route.
v1 in-memory SSE → v2 Redis pub/sub → v3 Kafka → v4 Postgres + auth → v5 OSRM routing
| Layer | Target | Notes |
|---|---|---|
| Frontend | Cloudflare Pages | Build frontend/, output dist/frontend/browser |
| Backend | Fly.io | backend/fly.toml, set CORS_ORIGIN to Pages URL |
Update frontend/src/environments/environment.prod.ts with your Fly.io API URL.
MIT — see LICENSE.