auth-yes/tasks/complete/2026-0826.06.gem.feat.ui.sessions-and-events-final-polish-2305.ph6.md

12 KiB
Raw Permalink Blame History

TASK METADATA

  • Target Files:
    • server/routes/events.ts
    • ui/components/SessionsPage.tsx
    • ui/components/sessions/EventCockpitDeck.tsx
    • ui/components/sessions/WorkshopDrawer.tsx
    • ui/components/sessions/EventGuestsDrawer.tsx
    • ui/components/sessions/SessionDeck.tsx
    • ui/components/sessions/SessionTable.tsx
    • ui/components/sessions/SessionsScript.tsx
    • server/tests/events.test.ts
    • ui/ui_scripts.test.ts
  • Core Objective: Implement Phase 6 Final Polish & Hardening:
    1. Unify nomenclature & IA across pages and drawers (Sessions & Events, Delegate Session, Events, Sessions).
    2. Implement universal ingress credential rotation (rotates both 6-digit PIN and event slug suffix simultaneously).
    3. Fix expired event extend math bug using GREATEST(expires_at, NOW()) + interval.
    4. Replace broken compact mode with strictly bounded, 2-Row Compact Cards with inline copy pills and zero text/border collisions.
    5. Implement standardized natural ExpiryBadge format ([Date] · [Time] · [Urgency Badge]) and symmetrical inverted start-time telemetry in the Guest Drawer.
    6. Upgrade segmented view mode toggles and WAI-ARIA delegation tabs with high-contrast active states.
    7. Reset drawer state machine on open and replace verbose/mock buttons with clean [ OK ].
  • 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:

    • Ingress Rotation Leakage & Active Session Disruption: When rotating event ingress credentials (pin_code and slug), existing active guest sessions must remain valid and uninterrupted. Only subsequent unauthenticated join attempts using the old PIN, old Direct Link, or old CLI command must be rejected (404 / Invalid Code).
    • Expired Event Extending Edge Cases: When an organizer extends an event that expired in the past, expires_at + interval '1 hour' would leave the expiry in the past. Using GREATEST(expires_at, NOW()) + interval '${extendHours} hours' guarantees the new expiration time is set relative to the current moment.
    • DOM Boundary & Overflow Bleed: The previous compact mode forced elements into a single unconstrained horizontal flex line, causing text collisions and input boxes bleeding off the desktop screen. The new 2-Row Compact Card must enforce strict CSS bounding (box-sizing: border-box; max-width: 100%; overflow: hidden; text-overflow: ellipsis;).
    • State Machine Staleness: Reopening the delegation drawer must unconditionally reset the form back to State 1 (clean creation form with Single Session default tab) rather than leaving the stale success screen visible.
  • Alternatives:

    • Single-Line vs. 2-Row Compact Cards: Single-line cards inevitably drop essential copy actions or clip text on viewports under 1200px. A structured 2-Row Compact Card (~70px height) preserves 100% of copy handoffs (PIN, Link, CLI) and key metrics (Seats, Status, Expiry) while preventing all layout collisions.

3. Proposed Implementation

Phase 1: Universal Ingress Credential Rotation & Backend Math Fix (server/routes/events.ts)

  1. Universal Ingress Rotation (POST /api/events/:id/rotate-pin):

    • Update endpoint to rotate BOTH pin_code AND the random slug suffix:
      const randPin = Math.floor(100000 + Math.random() * 900000).toString();
      const newPinCode = randPin.substring(0, 3) + "-" + randPin.substring(3);
      
      // Extract base slug prefix and generate fresh random 4-char suffix
      const event = await sqlWrapper
        .sql`SELECT slug, name FROM event_passes WHERE id = ${eventId}...`;
      const baseSlug = event[0].slug.replace(/-[a-z0-9]{4}$/, "");
      const newSuffix = Math.random().toString(36).substring(2, 6);
      const newSlug = `${baseSlug}-${newSuffix}`;
      
    • Update database: UPDATE event_passes SET pin_code = ${newPinCode}, slug = ${newSlug} WHERE id = ${eventId}...
    • Audit log event_ingress_rotated.
    • Return { success: true, pinCode: newPinCode, slug: newSlug }.
    • Note: Existing active attendee sessions (username LIKE 'guest_...') authenticate via session cookies/Valkey tokens and are unaffected.
  2. Resilient Event Extension Math (POST /api/events/:id/extend):

    • Fix SQL to calculate new expiry from GREATEST(expires_at, NOW()):
      UPDATE event_passes
      SET expires_at = GREATEST(expires_at, NOW()) + interval '${extendHours} hours'
      WHERE id = ${eventId} AND (created_by = ${user.userId} OR ${isAdmin}) AND is_active = TRUE
      RETURNING slug, expires_at
      
  3. Guest Attendee Telemetry (GET /api/events/:id/attendees):

    • Ensure query returns s.id, s.label, s.is_paused, s.created_at, s.expires_at, s.last_activity_at, s.last_activity_action, u.username, u.display_name.

Phase 2: Page Hierarchy, Nomenclature & Heading Cleanup (SessionsPage.tsx)

  1. Page Title:
    • Set top <h1> in SessionsPage.tsx to Sessions & Events with subtitle "Manage logins, mint 1:1 delegated tokens, or launch multi-claim workshop events."
  2. Remove Duplicate Headings:
    • Remove redundant <h2>Event Passes</h2> from SessionsPage.tsx. The section header is exclusively rendered inside EventCockpitDeck.tsx as <h2>Events</h2> alongside the view toggle.
  3. Sessions Section:
    • Retain <h2>Sessions</h2> heading with subtitle "Direct device logins, passkey authentications, and delegated agent tokens."

Phase 3: Bounded 2-Row Compact Cards & Grid Card Polish (EventCockpitDeck.tsx)

  1. Section Header & High-Contrast View Toggle:

    • Header: <h2>Events</h2>.
    • Toggle buttons ([ 🗂️ Grid ] and [ 📋 Compact ]):
      • Active style: background: var(--primary); color: #ffffff; font-weight: 700; box-shadow: 0 1px 3px rgba(0,0,0,0.3); border-radius: var(--radius-sm);
      • Inactive style: background: transparent; color: var(--text-muted); opacity: 0.75;
      • Include aria-pressed="true/false".
  2. 2-Row Compact Card Layout (.compact-view .event-card):

    • Container: Strictly bounded flex/grid (~70px height), box-sizing: border-box; overflow: hidden; padding: 0.75rem 1rem;.
    • Row 1 (Metadata Header):
      • Left: Event title with ellipsis (max-width: 280px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-weight: 700; color: var(--text-primary);).
      • Center/Right: Subtle dot · + renderExpiryPill(expiresAt) + Subtle dot · + [ N/Max Seats ] + [ Status Badge ].
    • Row 2 (1-Click Handoffs & Action Pinned Right):
      • Left (Handoff Pills): [ PIN: 241-881 (Copy) ], [ Link (Copy) ], [ CLI (Copy) ] using compact inline pills (padding: 2px 6px; font-size: 0.75rem;).
      • Right (Action Buttons): Compact [ 👥 Guests (N) ] and [ 🔄 +1h ] (or [ 🔄 Reopen (+1h) ] if expired).
    • Delete Mock Code: Completely remove the [ ▸ Details ] button and its alert(...) placeholder.
  3. Grid Card Polish:

    • Replace detached CLI <details> box with a unified Integrated Expanding CLI Component:
      • Single-line snippet when collapsed with [ Copy ] and [ ▾ ] toggle.
      • Expands downward into multiline highlighted command block on toggle click.
      • Fix double arrow marker bug (list-style: none;).
    • Standardize [ 🔄 Rotate Credentials ] button to invoke multi-field rotation.

Phase 4: Standardized Natural Expiry & Start-Time Telemetry (SessionsScript.tsx & Drawers)

  1. Natural Expiry Formatter Helper (formatNaturalExpiry(expiresAt)):

    • Natural Date String: Today (if $<24$h), Tomorrow (if $<48$h), MMM D (if same year), MMM D, YYYY (if different year).
    • Exact Time: h:mm A (e.g. 10:39 PM).
    • Scaled Urgency Badge:
      • $<1$h: Amber [ 45m left ]
      • $<24$h: Amber/Green [ 2h 45m left ]
      • 160d: Green [ 28d left ]
      • 212 mos: Green [ 4.5 mos left ] (no 142d)
      • >1 yr: Green [ 1.2 yrs left ] (no 420d)
      • Expired: Red [ Expired ]
    • Tooltip: title="${fullISODate}" on hover/long-press.
    • Format: [Date] · [Time] · [ Colored Urgency Badge ].
  2. Inverted Symmetrical Start-Time Formatter (formatNaturalJoinTime(createdAt)):

    • In EventGuestsDrawer.tsx, render:
      • Header: Bold Seat #[N] + 🟢 Active / ⏸️ Paused badge + [ ⏸️ Pause ] + [ 🗑️ Revoke ].
      • Line 1: Joined Today · 2:15 PM · Active 7h 54m.
      • Line 2: Last Action: ${lastAction || 'ForwardAuth Ingress'} · ${timeAgo} · 💻 Web (or 📟 CLI).
      • Remove redundant guest_slug_seat visual text (keep in tooltip only).

Phase 5: Delegation Drawer Overhaul (WorkshopDrawer.tsx & SessionsScript.tsx)

  1. Drawer Nomenclature & WAI-ARIA High-Contrast Tabs:

    • Drawer Header: <h2>Delegate Session</h2> with subtitle "Mint a 1:1 delegated token or launch a multi-seat workshop event."
    • Tabs (role="tablist"):
      • Tab 1 (role="tab"): Single Session
        • Subtitle hint: For agents, CI/CD, or 1:1 delegation
      • Tab 2 (role="tab"): Multi-Claim Event
        • Subtitle hint: For workshops, teams & guest pools
      • High-Contrast Active State: Solid var(--primary) background with white text (#ffffff), aria-selected="true".
      • Inactive State: Translucent muted background (opacity: 0.75), aria-selected="false".
  2. Handoff State & Button Minimalist Polish:

    • Standardize all 3 copy buttons to uniform btn-outline (Copy PIN, Copy URL, Copy 1-Liner).
    • Replace "Dismiss" with a clean, minimal button [ OK ].
  3. State Machine Reset on Open:

    • In SessionsScript.tsx:openDelegateDrawer():
      • Reset #eventCreateState to display: block and #eventHandoffState to display: none.
      • Clear form inputs (eventName, eventSlug, eventPinCode, etc.).
      • Reset tab selection to Single Session.

4. Verification Plan

Automated Tests

  1. Universal Credential Rotation Test (server/tests/events.test.ts):
    • Create event pass -> Call POST /api/events/:id/rotate-pin.
    • Verify response returns new pinCode AND new slug.
    • Verify old PIN and old slug are rejected at /api/events/join with 404.
    • Verify new PIN and new slug succeed at /api/events/join.
    • Verify existing authenticated guest sessions remain valid.
  2. Expired Event Extend Math Test (server/tests/events.test.ts):
    • Insert expired event with expires_at = NOW() - interval '3 hours'.
    • Call POST /api/events/:id/extend with extendHours: 1.
    • Verify expires_at > NOW() (approximately NOW() + 1 hour).
  3. UI Script & Formatter Tests (ui/ui_scripts.test.ts):
    • Unit test formatNaturalExpiry across all time horizons (<1h, <24h, 28d, 142d -> 4.5 mos, 420d -> 1.2 yrs, expired).
    • Test formatNaturalJoinTime inverted duration math.
    • Test drawer state machine reset logic.

Quality Gate Commands

deno fmt --check
deno task lint
deno task check
deno test -A --no-check server/tests/events.test.ts ui/ui_scripts.test.ts