110 lines
4.8 KiB
Markdown
110 lines
4.8 KiB
Markdown
# TASK METADATA
|
|
|
|
- **Target Files:**
|
|
- `server/routes/events.ts`
|
|
- `ui/components/sessions/EventCockpitDeck.tsx`
|
|
- `ui/components/sessions/EventGuestsDrawer.tsx`
|
|
- `ui/components/sessions/SessionsScript.tsx`
|
|
- `ui/components/SessionsPage.tsx`
|
|
- `server/tests/events.test.ts`
|
|
- `ui/ui_scripts.test.ts`
|
|
- **Core Objective:** Clean up the Sessions & Events dashboard by archiving
|
|
expired passes into a collapsible `<details>` accordion with a 1-click
|
|
`[ 🔄 Reopen (+1h) ]` action, and enrich the Guest Drawer with real-time
|
|
attendee telemetry and activity logs.
|
|
- **Dependencies:** None.
|
|
- **Additional Important Notes:**
|
|
- Must remain 100% pure React-free Hono SSR JSX.
|
|
- Distinguish between naturally expired events (`expires_at < NOW()`,
|
|
`is_active = TRUE`, reopenable) vs manually terminated events
|
|
(`is_active = FALSE`, permanent archive).
|
|
|
|
---
|
|
|
|
## 2. Architectural Considerations & Risks
|
|
|
|
### Risks
|
|
|
|
1. **Dashboard Clutter & Information Overload:** As organizers host dozens of
|
|
workshops over weeks, active and dead events pile up on the main screen,
|
|
pushing active management tools below the fold.
|
|
- _Mitigation:_ Partition the SQL query and SSR view into two decks:
|
|
`activePasses` (top priority grid/compact cards) and `expiredPasses`
|
|
(collapsed under a clean `<details>` drawer showing count
|
|
`[ 📁 Expired Passes (N) ]`).
|
|
2. **Re-opening State Ambiguity:** If an organizer re-opens an event that
|
|
expired 3 days ago, adding 1 hour via `expires_at + interval '1h'` leaves it
|
|
in the past.
|
|
- _Mitigation:_ Use
|
|
`SET expires_at = GREATEST(expires_at, NOW()) + interval '1 hour'` in
|
|
PostgreSQL to guarantee the event opens immediately for 60 minutes from the
|
|
moment clicked.
|
|
3. **Telemetry Performance in Large Guest Drawers:** Querying full session
|
|
activity logs for 100+ attendees could slow down
|
|
`GET /api/events/:id/attendees`.
|
|
- _Mitigation:_ Select only `last_activity_action`, `last_activity_at`, and
|
|
`is_agent` joined directly on `sessions` indexed by `user_id`.
|
|
|
|
### Alternatives
|
|
|
|
- **Permanent Deletion of Expired Events:** We considered automatically dropping
|
|
expired events from the database. However, organizers frequently need to audit
|
|
past workshop attendees or re-open events that ran over schedule. Keeping
|
|
expired passes in an archived state maintains audit integrity.
|
|
|
|
---
|
|
|
|
## 3. Proposed Implementation
|
|
|
|
### Phase 1: Split Active vs. Expired Events Query (`ui/components/SessionsPage.tsx`)
|
|
|
|
1. Update `event_passes` query in `SessionsPage.tsx`:
|
|
- Split passes into
|
|
`activeEvents = eventPasses.filter(e => e.is_active && (!e.expires_at || new Date(e.expires_at) > new Date()))`.
|
|
- `expiredEvents = eventPasses.filter(e => !e.is_active || (e.expires_at && new Date(e.expires_at) <= new Date()))`.
|
|
2. Pass both arrays into `EventCockpitDeck.tsx`.
|
|
|
|
### Phase 2: Collapsible Expired Deck & 1-Click Reopen (`ui/components/sessions/EventCockpitDeck.tsx`)
|
|
|
|
1. **Active Deck:**
|
|
- Render active events in standard Grid or 2-Row Compact view.
|
|
2. **Archived Deck (`[ 📁 Expired Passes (N) ]`):**
|
|
- Render a native `<details>` element below the active deck.
|
|
- For each expired pass with `is_active = TRUE`, display an amber/gray card
|
|
with an instant `[ 🔄 Reopen (+1h) ]` button.
|
|
- For manually ended events (`is_active = FALSE`), show a muted
|
|
`[ Terminated ]` badge without a reopen button.
|
|
3. **Client Script Handler (`SessionsScript.tsx`):**
|
|
- Add `reopenEvent(eventId)` calling `POST /api/events/:id/extend` with
|
|
`{ extendHours: 1 }`.
|
|
- Dynamically transition the card or reload to place it back into the active
|
|
deck.
|
|
|
|
### Phase 3: Rich Attendee Telemetry in Guest Drawer (`ui/components/sessions/EventGuestsDrawer.tsx`)
|
|
|
|
1. **Backend Payload Enrichment (`server/routes/events.ts`):**
|
|
- In `GET /api/events/:id/attendees`, select:
|
|
- `s.last_activity_at`
|
|
- `s.last_activity_action` (e.g. `ForwardAuth Ingress (ed-droid)`,
|
|
`API Token Read`)
|
|
- `s.is_agent`
|
|
2. **UI Card Formatting:**
|
|
- On each attendee card in the drawer, render:
|
|
- **Header:** `Seat #[N]` + `🟢 Active / ⏸️ Paused` badge + inline
|
|
`[ 🗑️ Revoke ]`.
|
|
- **Line 1 (Inverted Join Time):**
|
|
`Joined Today · 2:15 PM · [ Active 1h 45m ]`.
|
|
- **Line 2 (Action Telemetry):**
|
|
`Last Action: ForwardAuth (ed-droid) · 2m ago · 💻 Web` (with device icon
|
|
or agent robot emoji if `is_agent = true`).
|
|
|
|
### Phase 4: Quality Gates & Verification
|
|
|
|
1. Unit tests in `server/tests/events.test.ts`:
|
|
- Test reopening naturally expired events with
|
|
`GREATEST(expires_at, NOW()) + 1h`.
|
|
- Test telemetry fields in `/attendees` endpoint.
|
|
2. UI syntax tests in `ui/ui_scripts.test.ts`.
|
|
3. Run `deno fmt`, `deno task lint`, `deno task check`, and
|
|
`deno test -A --no-check`.
|