Fix/post merge followups#5
Merged
Merged
Conversation
…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>
…om slide-in panel + README screenshots
Five themed features stacked on the apple-ux-pass foundation, plus the
screenshots that go into the README.
1. Offline mode (display + safe-command queue, ≤5 min outage)
- LocalStorage-backed FIFO queue of operator commands (Pause / Resume /
Adjust / Set message / Clear message). Optimistic updates while offline;
silent best-effort drain + "N synced, M skipped" toast on reconnect.
- SignalR connection state surfaced via TimerHub.onConnectionChange and a
three-state LiveIndicator (Live · Xs / Reconnecting · N queued / Offline).
- Audience surfaces (Speaker / Door / Lobby) hold the last snapshot through
reconnects with a non-blocking "Reconnecting · display may drift" banner
after 30s. Destructive ops (Stop / Skip / Reset / Start / Quick timer)
remain gated by `online`.
- StrictMode hub fix: useTimerHub builds the hub inside the effect so the
dev-mode double-mount produces two independent connections.
2. Member admin (admin password reset + profile + lock)
- MembersController gains send-reset-link (token-emailed), force-set
temporary password (audit-logged), profile edit (display name + email
with duplicate guard), lock / unlock (Identity LockoutEnd).
- PasswordController gains the public `POST /api/auth/password/reset` for
the token-emailed reset link with opaque error mapping for bad token vs
bad user id.
- Frontend: ⋯ row menu, EditMemberSheet, SetTempPasswordSheet (two-phase
with copy-to-clipboard once committed), public /reset-password/:userId/:token
page, Locked chip on member rows.
3. Door display configuration (per-room)
- Room gains DoorDisplayConfigJson (opaque to server, 4 KB cap, audit-logged).
- PublicInfoController returns config so DoorView reads it without auth.
- New DoorDisplaySettingsSheet (opened from room slide-in): orientation
toggle (landscape / portrait) + checkboxes for every element (event name,
room name, logo, now-playing section + speaker + countdown, up-next +
start time). Child checkboxes auto-disable when their parent unchecks.
- DoorPanel renders two layout templates (landscape vs portrait), each
respecting the toggles.
4. Programmes (event-level time schedules)
- New Programme + ProgrammeSlot entities; nullable refs from Room.ProgrammeId
and ScheduleItem.ProgrammeSlotId.
- ProgrammesController CRUD + nested slot CRUD + reorder. Slot edits accept
`cascade: "update" | "detach"` for re-syncing or detaching linked sessions.
- RoomFormSheet gains a Programme dropdown. ScheduleItemForm shows a slot
picker when the room has a bound programme; picking a slot fills + locks
start + duration. Cascade confirm dialog when editing a slot timing with
linked sessions: "Update linked" vs "Detach instead".
- SetupNav gains a "Programmes" tab. New /events/:eventId/programmes page.
5. Room slide-in panel (replaces cramped ⋯ dropdown)
- 480px right-anchored drawer with sections: Live state (current item +
tabular countdown), Audience displays (inline 64px QR codes for speaker
+ door, Configure layout button for door), Operate (control / show mode /
schedule), Configure (room settings / door layout / reset code), Danger.
- drawer-panel-enter + drawer-backdrop-enter keyframes via --ease-out.
- RoomTile shrinks: ⋯ button + secondary "Details" button both open the
panel; Control button stays as the primary CTA.
Misc
- vite.config.ts gains a VITE_API_TARGET env override so devs can choose
localhost:5050 (dotnet run) or localhost:8080 (docker compose) without
editing the file. .env.local stays gitignored.
- Soft-delete query filters now apply to programmes too.
- localStorage shim in test/setup.ts so the offline-queue unit tests run
under vitest/jsdom regardless of Node experimental-storage state.
Screenshots
- docs/screenshots/ added with 5 PNGs covering the event dashboard, room
control, room slide-in panel, speaker view, and branding setup. README
gains a Screenshots section between Status and Docker Compose.
Verification
- dotnet build clean; 88/88 frontend tests pass (16 new for offline queue +
temp-password sheet flow); vite build succeeds.
- Migrations applied on Docker container restart: DoorDisplayConfig (Room
column) and Programmes (Programme + ProgrammeSlot tables, Room.ProgrammeId,
ScheduleItem.ProgrammeSlotId).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.