Shift
Map up, manifest down
Overview
The top of the screen is the assignment header — eyebrow `SHIFT · YYYY-MM-DD · HH:MM`, the route name in Instrument Serif, the vehicle name and plate underneath. Below: a LiveMap rendered with the same component the rider uses, but in a denser 16:10 aspect ratio and with the driver's own marker pinned. The three stat cells — PASSENGERS BOOKED, CAPACITY, STATUS — sit in a card row with hairline dividers between. The COMPLETE SHIFT button is full-width pill, only enabled once the vehicle reaches the dropoff. The MANIFEST list below it shows each reservation with the passenger name, party size, notes, and a CONFIRMED pill.
How it works
On mount, `GET /api/driver/today` returns the assignment (route, vehicle, scheduled start) plus the manifest. The driver's user record is read from `/auth/me`.
Background location: the screen requests ACCESS_BACKGROUND_LOCATION plus Location.startLocationUpdatesAsync on mount, after the OS permission flow.
Position push: `POST /api/driver/ping` with assignmentId, lat, lng, speedMps, headingDeg every 5 seconds. The API broadcasts via the pub/sub to every SSE subscriber.
Map shows the route polyline plus every stop plus the driver's own marker, which doesn't push positions — the driver sees the same data the dispatcher sees, including their own movement.
The status cell is computed from the assignment record's status field — planned, in_progress, completed. The COMPLETE SHIFT button flips status to completed via `PATCH /api/driver/assignments/[id]`.
Manifest is a scrollable FlatList below the map. Tapping a passenger row opens their reservation detail (notes, party size, contact).
Key decisions
Background location, not just foreground
Drivers lock their phones constantly. Foreground-only location stops broadcasting the moment the screen sleeps, which is exactly when riders most want the ETA. Background location with a foreground-service notification on Android (and the iOS background mode) keeps the feed alive. The trade-off is the permission ask is heavier; the win is the entire core feature works.
Same map component as the rider
The driver's map is literally the rider's LiveMap with a denser aspect. One component, one set of bugs, one source of truth for how a route renders. The driver's marker comes from their own SSE feed — they see themselves on the map exactly the way every rider sees them.
5-second cadence, not 1-second
Pinging every second would be smoother but burns battery and bandwidth. 5 seconds is fast enough that the rider feels live and slow enough that an 8-hour shift doesn't kill the phone. We considered adaptive cadence (slower when stopped) and may revisit, but the current uniform tick is debuggable and predictable.
