auth-yes/tasks/new/2026-0826.04.jul.feat.event-controls.attendees-drawer-and-live-controls-0029.ph4.md

5.9 KiB

TASK METADATA

  • Target Files: server/db.ts, server/routes/events.ts, server/routes/sessions.ts, server/auth-session.ts, server/middleware.ts (if needed for auth_forward.ts), ui/components/sessions/EventCockpitDeck.tsx, ui/components/sessions/EventAttendeesDrawer.tsx, ui/components/sessions/SessionsScript.tsx
  • Core Objective: Implement Phase 4 of the Event & Session Overhaul by adding live attendee management controls, an expandable EventAttendeesDrawer, individual session pausing, and operational card controls (Rotate PIN, +5 Seats).
  • Dependencies: None.
  • Additional Important Notes: Client scripts in SessionsScript.tsx must use pure vanilla JavaScript for DOM manipulation. is_paused logic must apply both to Postgres and the Valkey caching layer for immediate 403 Forbidden evaluation on active sessions.

2. Architectural Considerations & Risks

  • Risks:

    • Caching Desync: When pausing a session, updating PostgreSQL is insufficient; the corresponding Valkey session object must immediately have is_paused: true merged into its JSON string payload to enforce real-time blocks.
    • Zero-Migration Query Fragility: The attendee lookup relies on the deterministic username format guest_<slug>_<seatNumber>. If the slug format changes in the future, this lookup may fail. This is accepted as a feature to avoid schema migrations for ephemeral associations.
    • Schema Evolution: We are adding an is_paused BOOLEAN DEFAULT FALSE column to both the sessions and event_passes tables. The migration logic in server/db.ts needs soft-ignores via try-catch to avoid crashing if run repeatedly.
    • DOM Complexity: We are introducing an EventAttendeesDrawer which slides out over the interface. Existing strict DOM bindings must not be broken. Use accessible semantic markup and clear IDs.
  • Alternatives:

    • Adding event_id to sessions: While more relational, it introduces a schema migration and foreign key complexity for an ephemeral guest. Leveraging the existing deterministic username prefix for attendee querying keeps the schema decoupled and is highly efficient via standard SQL LIKE.

3. Proposed Implementation

Phase 1: Database & Backend Endpoints

  1. Schema Updates (server/db.ts):
    • Inject ALTER TABLE sessions ADD COLUMN IF NOT EXISTS is_paused BOOLEAN DEFAULT FALSE.
    • Inject ALTER TABLE event_passes ADD COLUMN IF NOT EXISTS is_paused BOOLEAN DEFAULT FALSE.
  2. Session Pausing Logic (server/routes/sessions.ts & Auth/Middleware Layer):
    • Add POST /api/sessions/:id/pause to toggle is_paused.
    • Update the PostgreSQL sessions record.
    • Fetch the session from Valkey, merge is_paused: true|false, and re-serialize to Valkey.
    • Ensure the edge auth check (server/auth-session.ts or forward-auth middleware) immediately returns 403 Forbidden: Session Paused by Host if is_paused === true.
  3. Event Operational Controls (server/routes/events.ts):
    • Add POST /api/events/:id/rotate-pin: Generate a new 6-digit PIN, update event_passes.pin_code, and return the new PIN. Existing sessions remain untouched.
    • Add POST /api/events/:id/expand: Accept { addSeats }, execute UPDATE event_passes SET max_seats = max_seats + $1 WHERE id = $2, and return the updated capacity.
  4. Attendee List Endpoint (server/routes/events.ts):
    • Add GET /api/events/:id/attendees.
    • Perform a deterministic SQL JOIN: SELECT s.*, u.username, u.display_name FROM sessions s JOIN users u ON s.user_id = u.id WHERE u.username LIKE ${'guest_' + event.slug + '_%'} AND s.expires_at > NOW() ORDER BY s.created_at DESC.

Phase 2: Event Deck Polish (ui/components/sessions/EventCockpitDeck.tsx)

  1. Countdown & UI Cleanup:
    • Add dynamic ⏳ Xh Ym left logic or badge for active passes.
    • Introduce an accessible <details> expander for the CLI 1-liner handoff string to clean up the card's visual footprint.
  2. New Controls:
    • Add a [ 🔄 Rotate PIN ] button.
    • Add a [ 👥 Manage Attendees (N) ] button to trigger the new slide-out drawer, replacing static numbers with actionable links.
    • Add a [ +5 Seats ] capacity expansion button.
    • Ensure [ End Event ] retains clear visual destructive styling and aria-label.

Phase 3: Slide-Out Attendee Drawer (ui/components/sessions/EventAttendeesDrawer.tsx)

  1. Drawer Component:
    • Create a new reusable drawer <div id="attendeesDrawer" class="drawer-overlay"> following existing drawer patterns (e.g., WorkshopDrawer.tsx).
    • The drawer content container (#attendeesDrawerContent) will display the fetched real-time list of attendees for a targeted event.
    • Each row will show username, seat_number, time active, and individual controls for [ Pause ] and [ Revoke ].

Phase 4: Client Logic Updates (ui/components/sessions/SessionsScript.tsx)

  1. Action Handlers:
    • Implement rotatePin(eventId) utilizing the new backend endpoint and updating the DOM instantly.
    • Implement expandSeats(eventId, count) to hit the endpoint and update the progress bar/capacity max.
    • Implement openAttendeesDrawer(eventId, slug) to trigger the fetch of attendees, render the HTML string into #attendeesDrawerContent, and slide the drawer into view.
    • Implement toggleSessionPause(sessionId) to trigger the pause endpoint and visually update the attendee row (e.g., strikethrough or badge update).

Phase 5: Quality Gates

  1. Run deno fmt and deno task lint across all changed files.
  2. Verify all API behaviors via isolated unit tests if applicable, ensuring deno test -A --unstable-ffi passes.
  3. Validate client-side UI script syntax using the hermetic validation present in ui/ui_scripts.test.ts.