Skip to content

fix(web,api): soft-delete filter, StrictMode hub, one-click Stop, quick timer#3

Merged
jeroenniesen merged 1 commit into
mainfrom
fix/post-merge-followups
May 11, 2026
Merged

fix(web,api): soft-delete filter, StrictMode hub, one-click Stop, quick timer#3
jeroenniesen merged 1 commit into
mainfrom
fix/post-merge-followups

Conversation

@jeroenniesen
Copy link
Copy Markdown
Contributor

Summary

Follow-up to #2, addressing issues found during in-browser testing of the apple-ux-pass plus one small new feature.

Server

  • Soft-delete filterAppDbContext query filters for Event / Room / ScheduleItem now also exclude rows whose DeletedAtUtc is set. Before this, deletes were soft-deletes but the list endpoints still returned the same rows, so the UI showed "deleted" items reappearing immediately. Callers that need to see tombstones (audit, undo, purge jobs) must use IgnoreQueryFilters(), per the existing convention.

Hub / connection

  • StrictMode hub fix (useTimerHub) — moved hub instantiation back inside the effect (it had been hoisted into useMemo). With React 18 StrictMode the same hub object survived the dev-mode double-mount, so .start() ran twice on the same connection and rejected with Cannot start a HubConnection that is not in the 'Disconnected' state. A fresh per-mount hub fixes it.
  • Silent per-room rejections (useEventRoomSnapshots) — each per-room resync is caught individually, and the underlying connection runs at LogLevel.Critical so expected per-room Forbidden rejections on the dashboard (RoomOperator scoped to a subset, Viewer with no hub access) don't surface as red errors. Presence fetch falls back to empty presence on Forbidden.
  • TimerHub now accepts an optional LogLevel so consumers can opt down to Critical for read-only multi-room dashboards.

Operator console

  • Stop is one-click again. Hold-to-confirm was too strict for the normal "speaker finished, end the session" flow. Skip and Reset remain hold-to-confirm in the More menu where the destructive risk actually lives.
  • Quick timer for empty-schedule rooms. New QuickTimerSheet (title + MM:SS duration + 5/10/15/30 min presets); creates a one-off schedule item (scheduledStartUtc = now, preRoll = 0) and starts it via hub.startItem. OperatorHero shows the CTA when the room has no current item and no upcoming item.

Dashboard / surroundings (already on this branch)

  • Presence pill on LobbyCard (lobby displays connected).
  • EventIdentity status pill now reads Live · N/M rooms running.
  • Show-mode entry — RoomShowModePage + Show mode button.
  • RehearsalControls + useRehearsalClock + lib/rehearsal for offline pacing.
  • PreflightPanel + lib/preflight for pre-show checks.
  • useToast moved into its own toastContext.ts file (fast-refresh hygiene).

Test plan

  • tsc -b clean
  • npm test — 16 test files, 73 tests pass (4 new: preflight + rehearsal)
  • npm run build succeeds
  • Manual: deleting a schedule item actually removes it from the list (no reappearance)
  • Manual: signing in on a fresh tab no longer fires StrictMode "cannot start HubConnection"
  • Manual: Stop ends the running session on a single click; Skip/Reset still require hold
  • Manual: Quick timer appears in OperatorHero empty state and starts an ad-hoc countdown
  • Manual on staging/prod: same set, with multiple concurrent operators

Deploy notes

This includes a backend change (AppDbContext). The Docker image needs rebuilding; docker compose build app && docker compose up -d app is sufficient locally. No DB migration required — the DeletedAtUtc columns already exist.

🤖 Generated with Claude Code

…ck timer, dashboard polish

Follow-up on the apple-ux-pass branch addressing issues found during in-browser
testing, plus a small new feature.

Server
- AppDbContext: query filters for Event / Room / ScheduleItem now also exclude
  rows whose DeletedAtUtc is set. Before this fix, deletes were soft-deletes but
  the list/get endpoints kept returning the same rows, so the UI showed
  "deleted" items reappearing. Callers that need to see tombstones must use
  IgnoreQueryFilters() (audit, undo, purge jobs).

Hub / connection
- useTimerHub: move hub instantiation back inside the effect (was hoisted into
  useMemo). With React 18 StrictMode the same hub object survived the dev-mode
  double-mount, so .start() ran twice on the same connection and rejected with
  "Cannot start a HubConnection that is not in the 'Disconnected' state."
  A fresh per-mount hub fixes it.
- useEventRoomSnapshots: catch each per-room resync individually and run the
  hub at LogLevel.Critical so expected per-room Forbidden rejections on the
  dashboard (RoomOperator scoped to a subset, Viewer with no hub access) don't
  surface as red errors. Presence fetch falls back to empty on Forbidden.
- TimerHub: accept an optional LogLevel so consumers can opt down to Critical
  for read-only dashboards.

Operator console
- Stop is a one-click Button again. The hold-to-confirm pattern was too strict
  for the normal "speaker finished, end the session" flow; Skip and Reset
  remain hold-to-confirm in the More menu where the destructive risk lives.
- OperatorHero gains an empty-state "Quick timer" CTA when the room has no
  current item and no upcoming item.
- New QuickTimerSheet: title + MM:SS duration + 5/10/15/30 min presets. Creates
  a one-off schedule item (scheduledStartUtc=now, preRoll=0) and starts it via
  hub.startItem. Lets operators run an ad-hoc countdown without authoring a
  schedule first.

Misc dashboard
- Picks up the surrounding edits already on the branch: presence pill on
  LobbyCard, Live · N/M rooms running in EventIdentity, Show mode entry,
  RehearsalControls + useRehearsalClock + lib/rehearsal, PreflightPanel +
  lib/preflight, useToast moved to its own toastContext file, plus the
  matching new tests.

Verification
- tsc clean, 73/73 tests pass (4 new — preflight + rehearsal), build succeeds.
- Manual: delete a schedule item now actually removes it; Stop ends the session
  on a single click; signing in on a fresh tab no longer fires StrictMode
  "cannot start HubConnection" errors; Quick timer starts immediately.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@jeroenniesen jeroenniesen merged commit ce7b822 into main May 11, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant