auth-yes/tasks/complete/2026-0826.05.gem.feat.sessions-ui.sessions-and-guest-drawer-overhaul-2023.ph5.md
google-labs-jules[bot] d86642ce31 feat: Sessions UI overhaul and backend revocation fix
- Update `DELETE /api/sessions/:id` in `server/routes/sessions.ts` to allow event creators to delete guests' sessions.
- Update page hierarchy and top headings in `ui/components/SessionsPage.tsx`.
- Refactor `EventAttendeesDrawer.tsx` to `EventGuestsDrawer.tsx` as a fixed slide-over overlay.
- Add multi-event compact view toggle with `localStorage` persistence in `EventCockpitDeck.tsx`.
- Standardize dynamic countdown pills across `EventCockpitDeck.tsx`, `EventGuestsDrawer.tsx`, `SessionDeck.tsx`, and `SessionTable.tsx`.
- Optimize mobile session deck in `SessionDeck.tsx`.

Co-authored-by: mrteye <1945243+mrteye@users.noreply.github.com>
2026-08-27 04:06:19 +00:00

7.6 KiB
Raw Blame History

TASK METADATA

  • Target Files: server/routes/sessions.ts, server/routes/events.ts, ui/components/SessionsPage.tsx, ui/components/sessions/EventCockpitDeck.tsx, ui/components/sessions/EventAttendeesDrawer.tsx (refactored/renamed to EventGuestsDrawer.tsx), ui/components/sessions/SessionDeck.tsx, ui/components/sessions/SessionTable.tsx, ui/components/sessions/SessionsScript.tsx, server/tests/events.test.ts, server/tests/scopes.test.ts, ui/ui_scripts.test.ts
  • Core Objective: Implement Phase 5 Overhaul: Fix backend attendee session revocation permission checks, build a true fixed slide-over Guest Drawer, standardize dynamic countdown pills, add multi-event compact density toggle, and clean up page/session visual hierarchy.
  • Dependencies: None.
  • Additional Important Notes: Must remain 100% pure React-free Hono SSR JSX. All client interactions in SessionsScript.tsx must use native vanilla JavaScript DOM APIs.

2. Architectural Considerations & Risks

  • Risks:

    • Zero-Trust Revocation Leakage: When allowing event creators to delete/revoke attendee sessions via DELETE /api/sessions/:id, ensure the query strictly verifies that the session belongs to a guest user claimed under an event pass created by auth.userId (or that caller is global admin). Never allow arbitrary session deletions across different event creators.
    • DOM Stacking & Focus Trapping: A fixed slide-over drawer must properly layer (z-index: 1050) over the background page and support backdrop dismissal and Escape key capture without disrupting background table states.
    • Mobile Density Regressions: Ensure the mobile slide-up bottom sheet does not block necessary viewport scrolling or clip action buttons on small mobile viewports (320px–375px).
  • Alternatives:

    • In-Page Expandable Row vs. Fixed Slide-Over Panel: In-page expandable cards cause massive vertical jumping and layout disruption when 5–10 events are active. A dedicated fixed slide-over drawer (right panel on desktop, bottom sheet on mobile) provides isolated context, independent scrolling, and a pinned summary header without disturbing the background dashboard.

3. Proposed Implementation

Phase 1: Backend Revocation Permission Fix (server/routes/sessions.ts)

  1. Authorize Event Creators in DELETE /api/sessions/:id:
    • Update DELETE /api/sessions/:id to check whether the target session is owned by auth.userId, or if caller is isGlobalAdmin, OR if the session belongs to a guest user of an event pass created by auth.userId:
      SELECT s.id
      FROM sessions s
      JOIN users u ON s.user_id = u.id
      WHERE s.id = ${targetSessionId}
        AND (
          s.user_id = ${auth.userId}
          OR ${isAdmin}
          OR EXISTS (
            SELECT 1 FROM event_passes ep
            WHERE ep.created_by = ${auth.userId}
              AND u.username LIKE 'guest_' || ep.slug || '_%'
          )
        )
      
    • If found, delete the session from Valkey cache (valkey.del) and PostgreSQL (DELETE FROM sessions WHERE id = ${targetSessionId}), logging the audit event.
    • Return { success: true }.

Phase 2: Page Hierarchy & Section Titles (ui/components/SessionsPage.tsx)

  1. Page Title:
    • Update top <h1> in SessionsPage.tsx from Active Sessions & Passes to Sessions & Passes.
  2. Missing Section Headings:
    • Section 1: <h2>Event Passes</h2> (with compact density toggle).
    • Section 2: Add a prominent <h2>Sessions</h2> heading directly above SessionTable and SessionDeck with subtitle "Direct device logins, passkey authentications, and delegated agent tokens."

Phase 3: True Fixed Slide-Over Panel (EventGuestsDrawer.tsx & SessionsScript.tsx)

  1. Drawer Component Overhaul (ui/components/sessions/EventAttendeesDrawer.tsx -> EventGuestsDrawer.tsx):
    • Refactor the component from an in-line <div class="card"> into a fixed slide-over overlay:
      • Desktop: position: fixed; top: 0; right: 0; width: 420px; height: 100vh; background: var(--surface-card); box-shadow: var(--shadow-lg); z-index: 1050; display: flex; flex-direction: column;
      • Mobile: Full-width slide-up bottom sheet (width: 100vw; height: 80vh; bottom: 0; right: 0; border-radius: 16px 16px 0 0;).
      • Backdrop: Dimmed backdrop overlay (position: fixed; inset: 0; background: rgba(0,0,0,0.5); z-index: 1040;) closing on click.
  2. Pinned Contextual Header:
    • Title: <h2 id="guestDrawerTitle" style="margin: 0; font-size: 1.25rem;">[Event Name] Guests</h2>
    • Subheader Context Bar: <div id="guestDrawerContext" style="font-size: 0.8rem; color: var(--text-secondary); margin-top: 0.25rem;">Event Pass · [N] / [Max] Claimed Seats · ⏳ [Xh Ym left] · (Expires [Time])</div>
    • Close Button: Accessible close button in top right.
  3. Streamlined Roster Rows (SessionsScript.tsx):
    • Discard repeated static expiration timestamps from individual rows.
    • Render clean, distinct cards for each guest seat:
      • Left: Seat #[N] (guest_<slug>_<N>) + relative join timestamp (Joined 5m ago).
      • Status Badge: 🟢 Active / ⏸️ Paused.
      • Right Actions: Compact [ ⏸️ Pause ] / [ ▶️ Resume ] toggle and [ 🗑️ Revoke ] trigger.
  4. Standardize Terminology:
    • Use Event Guests and Claimed Seats across all drawer titles, buttons ([ 👥 Manage Guests (N) ]), and notices.

Phase 4: Dynamic Countdown Standardization & Density Polish (EventCockpitDeck.tsx, SessionDeck.tsx, SessionTable.tsx)

  1. Standardized Countdown Pill:
    • Create a reusable countdown formatter rendering: ⏳ 2h 45m left · (Expires 10:39 PM) (or ⏳ 29d left · (Expires Sep 25)).
    • Dynamic status coloring: Green (healthy) -> Amber (<1h remaining) -> Red (expired).
    • Apply consistently across Event Pass Cards and the Sessions Table/Deck.
  2. Multi-Event Compact Density Toggle (EventCockpitDeck.tsx):
    • Add a [ 🗂️ Grid ] / [ 📋 Compact ] density toggle at the top of the Event Passes section.
    • In Compact Mode, collapse each event card into a sleek 1-row summary strip (Title · 🟢 Active · ⏳ 2h 45m left · 12/50 Seats · PIN: 749-123 · [ 👥 Guests (12) ] · [ ▸ Details ]), fitting 10+ active events in a single screen.
  3. Mobile Session Card Optimization (SessionDeck.tsx):
    • Remove the dedicated full-width bottom row on remote session cards.
    • Move [ 🗑️ Revoke ] inline into the top card header row next to the status badge as a compact button (btn-outline-danger), reducing card height by 50% and fitting 4–5 more sessions per screen.

Phase 5: Quality Gates & Testing

  1. Unit Tests (server/tests/events.test.ts & scopes.test.ts):
    • Test event host revoking a guest attendee session via DELETE /api/sessions/:id succeeds (200 OK).
    • Test non-owner unauthorized user attempting to revoke an attendee session is rejected (404/403).
    • Test guest list API returns active claimed seats.
  2. Hermetic UI Validation (ui/ui_scripts.test.ts):
    • Verify all vanilla JavaScript drawer controllers, density toggles, and countdown scripts in SessionsScript.tsx parse and evaluate cleanly with zero syntax errors.
  3. Formatting & Linting:
    • Ensure deno fmt, deno task lint, deno task check, and deno test --allow-all execute with 100% green status.