Commit Graph
3 Commits
Author SHA1 Message Date
Logan CusanoandClaude Opus 5.5 c836986b89 Click-to-listen on the live wall + HTTP Range on /media (server-26#233)
Backend: /media/calls/{id}/audio now honours a single HTTP Range
(bytes=a-b, a-, -n) with 206 + Content-Range, 416 for unsatisfiable,
and a 200 full body (Accept-Ranges: bytes) otherwise. The body is
streamed from GCS in 512 KiB span reads (storage.audio_object_size +
download_audio_range) instead of downloading the whole object first,
so a 600 s FLAC starts after one span and seeks fetch only what they
need. HMAC check and Firestore lookup unchanged and still first.

Frontend: InlinePlayer moved to components/InlinePlayer.tsx and now
drives lib/audioQueue.ts, a useSyncExternalStore store over ONE audio
element (one call plays at a time). Wall spine rows get play buttons
and a "Play incident" queue (oldest first) with the playing row
highlighted; map path-stop popups get a play button and the playing
stop gets an accent ring; rail cards get "play latest". Playback is
gated by canPlayAudio() (BUSINESS_MODEL section 3 / Gate A hook).
Nothing plays until clicked. Spine truncation is now stated
("showing latest 30 of M", show-all toggle, count outside the loaded
window) for server-26#239.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 02:33:25 -04:00
Logan CusanoandClaude Sonnet 5 d67b2057e6 frontend: safe fixes from the #109 punch-list
- CallSpineEntry.tsx: drop the dead `hasAudio` prop + the early `return null`
  that sat between hooks in InlinePlayer (React #310 risk). Parent already
  gates the mount on audio presence.
- NodeCard.tsx + nodes/page.tsx: pending-node card no longer double-fires.
  NodeCard gains `linkToDetail` (default true); the pending branch passes
  false so the wrapping onClick (open config modal) isn't swallowed by the
  inner <Link> navigation. List view unchanged.
- trips/page.tsx: TripCard badge now buckets on end_date >= today, matching
  the list's own upcoming/past split — an in-progress trip no longer shows a
  "Past" badge under "Upcoming".
- trips/page.tsx, NodeConfigModal.tsx, nodes/[id]/page.tsx: tall modals get
  `p-4` on the overlay + `max-h-[90vh] overflow-y-auto` on the panel so they
  don't clip on short viewports (incidents' CreateModal pattern).
- lib/types.ts: IncidentRecord.units / vehicles are optional now, matching
  Firestore (older docs omit them); incidents/[id] gains a `?? []` guard.

Untypechecked (no node/npm locally). next build in deploy.yml gates it.
Full list of remaining items in server-26 #109.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-07 00:07:54 -04:00
Logan Cusano 4b5cf1971e Frontend redesign chunk 7: incident detail rebuild
Build & Deploy / Build & push images (push) Successful in 4m32s
Build & Deploy / Deploy to VM (push) Failing after 3m47s
Rewrite app/incidents/[id]/page.tsx to UI_REDESIGN.md §5.2. Header is now
type glyph + SeverityMark + active/resolved chip + a 27px title, with
elapsed time, path length (haversine sum over geocoded calls) and call
count as a single subline. Summary is promoted out of the old tab into a
first-class prose block (16.5px/1.58) — it's the artifact the product
sells, so it gets the best position instead of competing with Units/
Details behind a click. Units/Details tabs are gone; On scene / Cleared
render directly from units_active/units_cleared (chunk 3), Vehicles below.

New components/CallSpineEntry.tsx replaces CallRow for this page (CallRow
stays for the Archive table until chunk 12): time-ordered entries with a
numbered stop marker that matches the map's path stops via the same
sort-by-started_at-over-geocoded-calls index MapView's IncidentPathLayer
uses — the "shared index" from §2.4. Includes an inline play/scrub audio
player (lazy-fetches the signed URL on first play, same pattern CallRow
already used), transcript in sans prose instead of a font-mono <pre>, unit/
cleared-unit chips, and a paginating "N earlier calls" control. Thin/
status-only calls collapse to one line.

The incident map keeps the location_coords guard and now passes `calls`
through to MapView so its path polyline (chunk 5) renders here too.

Per UI_REDESIGN.md chunk 7.
2026-08-19 23:07:28 -04:00