- 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>
156 lines
7.6 KiB
Markdown
156 lines
7.6 KiB
Markdown
# 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`:
|
||
```sql
|
||
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.
|