auth-yes/tasks/new/2026-0827.06.gem.feat.ui.sessions-docking-and-creation-flow-1130.md

7.3 KiB

TASK METADATA

  • Target Files:
    • ui/components/SessionsPage.tsx
    • ui/components/sessions/DirectPassDrawer.tsx
    • ui/components/sessions/WorkshopDrawer.tsx
    • ui/components/sessions/SessionsScript.tsx
    • ui/ui_scripts.test.ts
  • Core Objective: Resolve Round 4 UI defects by docking the sticky sub-header beneath the main app top bar, standardizing single session creation to full parity with workshop passes, eliminating <summary> double arrows while preserving (Optional) labels, and implementing a rapid creation flow ([ Create Another ] + [ OK ]) with tab-locking during handoff states.
  • Dependencies: None.
  • Additional Important Notes: Must remain 100% pure React-free Hono SSR JSX. All state transitions in SessionsScript.tsx must use native vanilla JavaScript DOM APIs.

2. Architectural Considerations & Risks

Risks

  1. Navbar Collision on Mobile Viewports: On narrow screens, the top app bar height might vary depending on wrapped elements.
    • Mitigation: Use top: var(--top-bar-height, 57px); z-index: 30; with a solid background (var(--surface-bg)) to prevent cards from scrolling through transparent gaps or overlapping the fixed .top-bar (which sits at z-index: 40).
  2. State Leakage Across Tabs: If a user mints a Workshop Pass, the handoff screen appears. If they click the "Single Pass" tab while the event handoff is still visible, the form displays a dirty or stale state.
    • Mitigation: Lock the tab switcher (pointer-events: none; opacity: 0.5; or visually disable the buttons) while in either handoffModal or eventHandoffState. Tab navigation is automatically re-enabled when the user clicks [ Create Another ] or closes the drawer.
  3. Double Arrow Rendering: Native <details><summary> elements automatically render disclosure triangles in modern browsers.
    • Mitigation: Purge all explicit characters from <summary> text strings across both drawer templates.

Alternatives

  • Closing the Drawer Immediately on Creation: We considered closing the drawer right after minting and letting the user copy links from the main page deck. However, showing the 1-click link, PIN, and CLI export immediately upon creation in a dedicated handoff card provides essential visual confirmation and zero-friction copying before returning to the dashboard.

3. Proposed Implementation

Phase 1: Viewport Docking (ui/components/SessionsPage.tsx)

  1. Locate the Sessions & Events sub-header block in SessionsPage.tsx.
  2. Update the sticky styling from top: 0; z-index: 40; to:
    position: sticky;
    top: var(--top-bar-height, 57px);
    z-index: 30;
    background: var(--surface-bg, #0f172a);
    padding: 0.75rem 0;
    margin-bottom: 1.5rem;
    border-bottom: 1px solid var(--border-subtle);
    

Phase 2: Single Pass Drawer Parity & Form Hiding (ui/components/sessions/DirectPassDrawer.tsx)

  1. Wrap the Single Pass creation form in <div id="delegateCreateState" style="display: block;">.
  2. Update the submit button:
    • Label: [ Create Session ] (replace "Mint & Delegate Session").
    • Standardize styling to match btn-primary.
  3. In the Accordion <summary>:
    • Remove the character.
    • Label as: "Customize App Permissions & Scopes (Optional)".
  4. In #handoffModal:
    • Change the 1-Click Link copy button from btn-primary to uniform btn-outline.
    • Update footer actions:
      • Replace the single "Done (Session is Active)" button with a dual-action flex container:
        • [ Create Another ] (type="button" class="btn-outline" onclick="createAnotherSession()")
        • [ OK ] (type="button" class="btn-primary" onclick="closeDelegateDrawer()")

Phase 3: Workshop Drawer Parity & Accordion Cleanliness (ui/components/sessions/WorkshopDrawer.tsx)

  1. Update the submit button:
    • Label: [ Create Event ] (replace "🎟️ Launch Workshop Pass").
  2. In the Custom Vanity Slug & PIN <summary>:
    • Remove the character.
    • Label as: "Custom Vanity Slug & PIN Code (Optional)".
  3. In #eventHandoffState:
    • Update footer actions to match Single Pass:
      • [ Create Another ] (type="button" class="btn-outline" onclick="createAnotherEvent()")
      • [ OK ] (type="button" class="btn-primary" onclick="closeDelegateDrawer()")

Phase 4: Client State Machine & Tab Locking (ui/components/sessions/SessionsScript.tsx)

  1. Single Session Submit Handler (handleDelegateSession):
    • On success, set #delegateCreateState.style.display = 'none' and #handoffModal.style.display = 'block'.
    • Call lockDelegationTabs(true) to prevent switching tabs while viewing the handoff token.
  2. Event Creation Submit Handler (handleCreateEvent):
    • On success, set #eventCreateState.style.display = 'none' and #eventHandoffState.style.display = 'block'.
    • Call lockDelegationTabs(true).
  3. Tab Locking Helpers:
    • lockDelegationTabs(locked): Toggles disabled state and opacity: 0.5; pointer-events: none; on #tabBtnDirectPass and #tabBtnWorkshopPass.
  4. "Create Another" Handlers:
    • createAnotherSession():
      • Reset #delegateForm input values (default back to 1 Hour and Read-Only).
      • #handoffModal.style.display = 'none'.
      • #delegateCreateState.style.display = 'block'.
      • lockDelegationTabs(false).
      • Focus #delegateLabel.
    • createAnotherEvent():
      • Reset #eventForm input values (default back to 50 seats and 3 hours).
      • #eventHandoffState.style.display = 'none'.
      • #eventCreateState.style.display = 'block'.
      • lockDelegationTabs(false).
      • Focus #eventName.
  5. Drawer Open/Close Reset:
    • When calling openDelegateDrawer(), ensure lockDelegationTabs(false) is invoked and both form states are reset to visible State 1.

Phase 5: Expired Events Accordion & 1-Click Reopen (ui/components/sessions/EventCockpitDeck.tsx)

  1. Active vs. Expired Separation:
    • Filter eventPasses into activeEvents and expiredEvents (expires_at <= NOW() or is_active = FALSE).
  2. Collapsible Accordion:
    • Below the active deck, render a native <details> block:
      <details
        style="margin-top: 1.5rem; background: var(--surface-card); border: 1px solid var(--border-subtle); border-radius: var(--radius-md); padding: 0.75rem 1rem;">
        <summary style="font-weight: 600; font-size: 0.95rem; cursor: pointer; color: var(--text-secondary);">
          📁 Expired Events ({expiredEvents.length})
        </summary>
        ...
      </details>
      
  3. 1-Click Reopen Action:
    • For naturally expired events (is_active = TRUE), render a dedicated [ 🔄 Reopen (+1h) ] button calling POST /api/events/:id/extend with extendHours: 1.

Phase 6: Quality Gates & Network/Error Mock Testing

  1. Run deno test -A --no-check server/tests/events.test.ts to assert all extend, attendees, and 404/403 failure paths pass.
  2. Run deno test -A --no-check ui/ui_scripts.test.ts to verify zero JavaScript syntax or parsing errors.
  3. Run deno fmt, deno task lint, deno task check, and deno test -A --no-check.