Frontend punch-list (drb.cusano.net live review, 2026-09-07) #109

Open
opened 2026-09-06 23:44:02 -04:00 by logan · 2 comments
Owner

From a full route audit of drb-frontend (all app/ routes except the fenced /, /features, /pricing, /faq, /settings/billing, and the map internals). P1/P2 items in this list are NOT yet fixed unless noted; the honest-error-state items for /incidents and /watch shipped in the fix/frontend-map-and-error-states PR.

P1 — broken / crash-risk

  1. Zero-node org lands on a blank map. app/page.tsx:147 renders <LiveView/> for any user && orgId; LiveView.tsx:37 only has a "configured but quiet" empty state. The UI_REDESIGN.md Activation screen (chunk 10) was never built — no components/Activation.tsx. A new customer sees an empty map with no "mint a token" path.
  2. components/CallSpineEntry.tsx InlinePlayer — conditional hook. if (!hasAudio) return null (~line 33) sits between useState/useRef calls and a later useEffect → React error #310 if ever mounted with hasAudio={false}. Masked only because the parent mounts it exclusively when hasAudio is true (dead guard). Fix: delete the dead hasAudio prop+guard, or hoist the return above all hooks.
  3. /incidents raw Firestore index error — FIXED in fix/frontend-map-and-error-states (friendlyIncidentsError). Root cause still server-26 #13/#51.

P2 — misleading state / wrong data

  1. /watch alerts events tab swallowed query failures → false "No alerts triggered yet." — FIXED in the same PR. Also: RulesTab (~line 36) calls load() from the render body — move to useEffect.
  2. app/nodes/[id]/page.tsx:123 "Recent Calls" usually wrongly empty. useCalls(20) fetches 20 most-recent org-wide then filters c.node_id === id client-side. Busy multi-node org → "No calls recorded from this node" for nodes that have plenty. Needs a server-side node_id filter.
  3. app/nodes/page.tsx:44 pending-node card double-fires. <NodeCard> is a <Link>; the pending branch wraps it in a <div onClick={setConfigNode}>. Click → navigates AND tries to open the config modal; navigation wins, modal never shows. Fix: pending card not a Link, or stopPropagation.
  4. app/trips/page.tsx upcoming/past mismatch. List buckets on end_date >= today (line 176); TripCard badge uses start_date >= today (line 25). In-progress trip shows under "Upcoming" with a "Past" badge. Align both on end_date.
  5. DEFERRED drift — settings/organization. DEFERRED says the org-name Save button is disabled/no backend, but c2api.getOrg/updateOrg are wired and the button is live (page.tsx:80). Confirm PATCH /org exists on c2-core; update DEFERRED or the button 403s raw.
  6. app/calls/page.tsx (Archive) ahead of its doc. Built against GET /calls/search; UI_REDESIGN.md lists the route as BLOCKED on backend. If /calls/search isn't deployed → "Couldn't load calls: C2 API error 404". Verify the endpoint. (Possibly the user's reported /archive CORS symptom.)

P3 — visual / polish

  1. Two-tier visual seam. Token-migrated: Live, Incidents, Incident detail, Archive, Network hub. NOT migrated (hardcoded bg-gray-*, depend on the globals.css !important block): /nodes, /systems, /alerts body, /admin, /profile, /settings/*, /trips, /onboarding, /login, /signup, /waitlist. Known (DEFERRED globals.css row) but jars visibly. This is the remaining migration backlog.
  2. app/network/page.tsx is a 3-card hub, not UI_REDESIGN.md §5.6 (no Enrollment tab, still at /settings/nodes; "Bot Tokens" shows —). Scope decision.
  3. components/StatusBadge.tsx:6 offline state + admin Toggle off-state → pale-gray-on-near-white in light mode. Borderline contrast; off-toggle reads as "no control".
  4. app/trips/page.tsx:100 CreateModal has no p-4 / no inner scroll → clips on short viewports. Same for NodeConfigModal, DiscordJoinModal. Add p-4 overlay + max-h-[90vh] overflow-y-auto panel (incidents CreateModal already does).
  5. lib/types.ts — IncidentRecord.units/vehicles non-optional but Firestore omits them on older docs; incidents/[id]/page.tsx:216 already ?.-guards, contradicting the type. Make them optional.
  6. /settings/members + /settings/nodes 403 for a pure org-owner (both call /admin/users, require_admin_token; B7 gate lets them reach the page). Known (SAAS_PLAN B7); error surface is at least honest.

Generated by a frontend-audit agent.

From a full route audit of `drb-frontend` (all `app/` routes except the fenced `/`, `/features`, `/pricing`, `/faq`, `/settings/billing`, and the map internals). P1/P2 items in this list are NOT yet fixed unless noted; the honest-error-state items for `/incidents` and `/watch` shipped in the `fix/frontend-map-and-error-states` PR. ## P1 — broken / crash-risk 1. **Zero-node org lands on a blank map.** `app/page.tsx:147` renders `<LiveView/>` for any `user && orgId`; `LiveView.tsx:37` only has a "configured but quiet" empty state. The `UI_REDESIGN.md` Activation screen (chunk 10) was never built — no `components/Activation.tsx`. A new customer sees an empty map with no "mint a token" path. 2. **`components/CallSpineEntry.tsx` `InlinePlayer` — conditional hook.** `if (!hasAudio) return null` (~line 33) sits between `useState`/`useRef` calls and a later `useEffect` → React error #310 if ever mounted with `hasAudio={false}`. Masked only because the parent mounts it exclusively when `hasAudio` is true (dead guard). Fix: delete the dead `hasAudio` prop+guard, or hoist the return above all hooks. 3. **`/incidents` raw Firestore index error** — FIXED in `fix/frontend-map-and-error-states` (friendlyIncidentsError). Root cause still server-26 #13/#51. ## P2 — misleading state / wrong data 4. **`/watch` alerts events tab swallowed query failures** → false "No alerts triggered yet." — FIXED in the same PR. Also: `RulesTab` (~line 36) calls `load()` from the render body — move to `useEffect`. 5. **`app/nodes/[id]/page.tsx:123` "Recent Calls" usually wrongly empty.** `useCalls(20)` fetches 20 most-recent org-wide then filters `c.node_id === id` client-side. Busy multi-node org → "No calls recorded from this node" for nodes that have plenty. Needs a server-side `node_id` filter. 6. **`app/nodes/page.tsx:44` pending-node card double-fires.** `<NodeCard>` is a `<Link>`; the pending branch wraps it in a `<div onClick={setConfigNode}>`. Click → navigates AND tries to open the config modal; navigation wins, modal never shows. Fix: pending card not a Link, or stopPropagation. 7. **`app/trips/page.tsx` upcoming/past mismatch.** List buckets on `end_date >= today` (line 176); `TripCard` badge uses `start_date >= today` (line 25). In-progress trip shows under "Upcoming" with a "Past" badge. Align both on `end_date`. 8. **DEFERRED drift — `settings/organization`.** DEFERRED says the org-name Save button is disabled/no backend, but `c2api.getOrg`/`updateOrg` are wired and the button is live (`page.tsx:80`). Confirm `PATCH /org` exists on c2-core; update DEFERRED or the button 403s raw. 9. **`app/calls/page.tsx` (Archive) ahead of its doc.** Built against `GET /calls/search`; `UI_REDESIGN.md` lists the route as BLOCKED on backend. If `/calls/search` isn't deployed → "Couldn't load calls: C2 API error 404". Verify the endpoint. (Possibly the user's reported `/archive` CORS symptom.) ## P3 — visual / polish 10. **Two-tier visual seam.** Token-migrated: Live, Incidents, Incident detail, Archive, Network hub. NOT migrated (hardcoded `bg-gray-*`, depend on the `globals.css` `!important` block): `/nodes`, `/systems`, `/alerts` body, `/admin`, `/profile`, `/settings/*`, `/trips`, `/onboarding`, `/login`, `/signup`, `/waitlist`. Known (DEFERRED globals.css row) but jars visibly. This is the remaining migration backlog. 11. `app/network/page.tsx` is a 3-card hub, not `UI_REDESIGN.md` §5.6 (no Enrollment tab, still at `/settings/nodes`; "Bot Tokens" shows `—`). Scope decision. 12. `components/StatusBadge.tsx:6` offline state + `admin` `Toggle` off-state → pale-gray-on-near-white in light mode. Borderline contrast; off-toggle reads as "no control". 13. `app/trips/page.tsx:100` CreateModal has no `p-4` / no inner scroll → clips on short viewports. Same for `NodeConfigModal`, `DiscordJoinModal`. Add `p-4` overlay + `max-h-[90vh] overflow-y-auto` panel (incidents CreateModal already does). 14. `lib/types.ts` — `IncidentRecord.units`/`vehicles` non-optional but Firestore omits them on older docs; `incidents/[id]/page.tsx:216` already `?.`-guards, contradicting the type. Make them optional. 15. `/settings/members` + `/settings/nodes` 403 for a pure org-owner (both call `/admin/users`, `require_admin_token`; B7 gate lets them reach the page). Known (SAAS_PLAN B7); error surface is at least honest. Generated by a frontend-audit agent.
Author
Owner

Live re-walk 2026-09-07 ~18:38 ET. Items 1/2/3 in this list confirmed still broken in prod — all three are backend/infra deploys, no frontend PR closed them:

  • Map (P1 #1 area): every map (/, /incidents/[id]) renders full-screen API KEY REQUIRED Carto watermarks. MAP_TILE_URL override merged in code but NEXT_PUBLIC_MAP_TILE_URL was never set in the CI build env, so prod still uses the dead Carto URL. Filed separately.
  • /watch Triggered Alerts: dead — alert_events composite index (acknowledged, org_id, triggered_at desc) never deployed. Same index throws useUnacknowledgedAlerts: FirebaseError: The query requires an index in layout.js console on every page. Blocks the whole tab + the site-wide alert bell. -> #51.
  • /calls (Archive): TypeError: Failed to fetch — CORS preflight failure, root-caused in #110.

New, lower severity (added to Version 5C/UI_AUDIT_0907b.md):

  • Stray unstyled clock (18:37:23) bottom-left of the Leaflet map on / and /incidents/[id] — no label, no styling. Leftover debug element.
  • Incident mini-card overlay collides with the leaflet zoom controls + severity legend (top-left of the map).
  • Possible transcript-block leak: incident 4d49e09c call transcript shows 1. ... 2. 10-4. 3. 5, I'm out. 4. 10-4. — looks like _build_transcript_block's {i}. segment numbering bleeding into the user-visible corrected transcript. Unconfirmed (could be Whisper). Worth checking the #102 0-based change.

Item 2 (CallSpineEntry conditional hook) and item 14 (IncidentRecord.units optional) from this list were fixed in the punch-list PRs this session.

Live re-walk 2026-09-07 ~18:38 ET. Items 1/2/3 in this list **confirmed still broken in prod** — all three are backend/infra deploys, no frontend PR closed them: - **Map (P1 #1 area):** every map (`/`, `/incidents/[id]`) renders full-screen `API KEY REQUIRED` Carto watermarks. `MAP_TILE_URL` override merged in code but `NEXT_PUBLIC_MAP_TILE_URL` was never set in the CI build env, so prod still uses the dead Carto URL. Filed separately. - **/watch Triggered Alerts:** dead — `alert_events` composite index (`acknowledged`, `org_id`, `triggered_at` desc) never deployed. Same index throws `useUnacknowledgedAlerts: FirebaseError: The query requires an index` in `layout.js` console on **every page**. Blocks the whole tab + the site-wide alert bell. -> #51. - **/calls (Archive):** `TypeError: Failed to fetch` — CORS preflight failure, root-caused in #110. New, lower severity (added to `Version 5C/UI_AUDIT_0907b.md`): - **Stray unstyled clock** (`18:37:23`) bottom-left of the Leaflet map on `/` and `/incidents/[id]` — no label, no styling. Leftover debug element. - **Incident mini-card overlay collides** with the leaflet zoom controls + severity legend (top-left of the map). - **Possible transcript-block leak:** incident `4d49e09c` call transcript shows `1. ... 2. 10-4. 3. 5, I'm out. 4. 10-4.` — looks like `_build_transcript_block`'s `{i}.` segment numbering bleeding into the user-visible corrected transcript. Unconfirmed (could be Whisper). Worth checking the `#102` 0-based change. Item 2 (`CallSpineEntry` conditional hook) and item 14 (`IncidentRecord.units` optional) from this list were fixed in the punch-list PRs this session.
Author
Owner

P2 punch-list pass on branch fix/109-punchlist-p2 (local commit only, not pushed).

Item 4 — FIXED. RulesTab in app/alerts/page.tsx (not /watch) called load() from the render body. Moved to useEffect(() => { load(); }, []); load() already self-guards on its loaded state flag, so [] deps are honest. Added useEffect to the react import.

Item 6 — ALREADY FIXED (verified). components/NodeCard.tsx already has a linkToDetail?: boolean prop (defaults true) that drops the <Link> wrapper when false, and app/nodes/page.tsx:45 already passes linkToDetail={false} on the pending branch. The wrapping <div onClick={() => setConfigNode(n)}> now opens the config modal without navigating. No change needed.

Item 8 — ENDPOINT EXISTS; button is correctly wired. drb-c2-core/app/routers/org.py:48 @router.patch("") on prefix /org → update_org(OrgUpdateBody, Depends(require_org_owner_token)). Frontend c2api.updateOrg (lib/c2api.ts:306) does PATCH /org with {name}. app/settings/organization/page.tsx Save button is live and guarded (!canEdit / empty / unchanged disable it). No frontend change made. The stale entry is DEFERRED.md:35 ("no backend to save to — save button is disabled"), which lives at the Version 5C root, outside the Server repo and this worktree — cannot commit it here. Flagging for someone with repo-root access to correct that row.

Item 5 — deferred with TODO (limit raised). useCalls in lib/useCalls.ts is a direct Firestore query (where org_id ==, optional started_at range, orderBy started_at desc, limit). Adding where('node_id','==', id) alongside the existing org_id equality + started_at orderBy would require a brand-new composite index, so took the safe path: app/nodes/[id]/page.tsx:123 now calls useCalls(200) (was 20) and keeps the client-side c.node_id === id filter, with a // TODO(server-26#109 item5) comment. Proper fix is a server-side node_id filter.

Item 7 — ALREADY FIXED (verify only). app/trips/page.tsx: TripCard badge (line 27) uses trip.end_date >= today; list buckets (lines 178-179) use end_date. Consistent. No change.

Items 1-3, 9-15 out of scope for this pass. Files changed: drb-frontend/app/alerts/page.tsx, drb-frontend/app/nodes/[id]/page.tsx.

P2 punch-list pass on branch `fix/109-punchlist-p2` (local commit only, not pushed). **Item 4 — FIXED.** `RulesTab` in `app/alerts/page.tsx` (not `/watch`) called `load()` from the render body. Moved to `useEffect(() => { load(); }, [])`; `load()` already self-guards on its `loaded` state flag, so `[]` deps are honest. Added `useEffect` to the react import. **Item 6 — ALREADY FIXED (verified).** `components/NodeCard.tsx` already has a `linkToDetail?: boolean` prop (defaults true) that drops the `<Link>` wrapper when false, and `app/nodes/page.tsx:45` already passes `linkToDetail={false}` on the pending branch. The wrapping `<div onClick={() => setConfigNode(n)}>` now opens the config modal without navigating. No change needed. **Item 8 — ENDPOINT EXISTS; button is correctly wired.** `drb-c2-core/app/routers/org.py:48` `@router.patch("")` on prefix `/org` → `update_org(OrgUpdateBody, Depends(require_org_owner_token))`. Frontend `c2api.updateOrg` (`lib/c2api.ts:306`) does `PATCH /org` with `{name}`. `app/settings/organization/page.tsx` Save button is live and guarded (`!canEdit` / empty / unchanged disable it). No frontend change made. The stale entry is `DEFERRED.md:35` ("no backend to save to — save button is disabled"), which lives at the Version 5C root, outside the Server repo and this worktree — cannot commit it here. Flagging for someone with repo-root access to correct that row. **Item 5 — deferred with TODO (limit raised).** `useCalls` in `lib/useCalls.ts` is a direct Firestore query (`where org_id ==`, optional `started_at` range, `orderBy started_at desc`, `limit`). Adding `where('node_id','==', id)` alongside the existing `org_id` equality + `started_at` orderBy would require a brand-new composite index, so took the safe path: `app/nodes/[id]/page.tsx:123` now calls `useCalls(200)` (was 20) and keeps the client-side `c.node_id === id` filter, with a `// TODO(server-26#109 item5)` comment. Proper fix is a server-side node_id filter. **Item 7 — ALREADY FIXED (verify only).** `app/trips/page.tsx`: `TripCard` badge (line 27) uses `trip.end_date >= today`; list buckets (lines 178-179) use `end_date`. Consistent. No change. Items 1-3, 9-15 out of scope for this pass. Files changed: `drb-frontend/app/alerts/page.tsx`, `drb-frontend/app/nodes/[id]/page.tsx`.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: logan/server-26#109