This commit finalizes Phase 4 of the Event & Session Overhaul: 1. Implements session pause logic across PostgreSQL schema, Valkey cache, and `auth_forward.ts` edge check (`is_paused`). 2. Implements non-destructive operational endpoints (`/api/events/:id/rotate-pin`, `/api/events/:id/expand`, `/api/events/:id/attendees`) with Zero-Trust Ownership verification. 3. Upgrades existing `end` and `extend` endpoints in `events.ts` to utilize robust Zero-Trust Ownership queries (created_by OR isGlobalAdmin). 4. Creates `EventAttendeesDrawer.tsx` to handle live participant inspection and individual session controls (Pause, Revoke). 5. Updates `EventCockpitDeck.tsx` and `SessionsScript.tsx` to mount and drive the new controls via vanilla JavaScript, respecting zero-framework guidelines. 6. Ensures `deno fmt`, `deno task lint`, `deno task check` and `deno test` execute successfully against the new schema and API guards. Co-authored-by: mrteye <1945243+mrteye@users.noreply.github.com>
181 lines
6.0 KiB
TypeScript
181 lines
6.0 KiB
TypeScript
import { AuthenticatedLayout } from "./AuthenticatedLayout.tsx";
|
||
import { EventCockpitDeck } from "./sessions/EventCockpitDeck.tsx";
|
||
import { EventAttendeesDrawer } from "./sessions/EventAttendeesDrawer.tsx";
|
||
import { DirectPassDrawer } from "./sessions/DirectPassDrawer.tsx";
|
||
import { WorkshopDrawer } from "./sessions/WorkshopDrawer.tsx";
|
||
import { ScopeModal } from "./sessions/ScopeModal.tsx";
|
||
import { SessionTable } from "./sessions/SessionTable.tsx";
|
||
import { SessionDeck } from "./sessions/SessionDeck.tsx";
|
||
import { SessionsScript } from "./sessions/SessionsScript.tsx";
|
||
|
||
export const SessionsPage = ({
|
||
sessions,
|
||
currentSessionId,
|
||
apps = [],
|
||
isAdmin = false,
|
||
eventPasses = [],
|
||
}: {
|
||
sessions: any[];
|
||
currentSessionId: string;
|
||
apps?: any[];
|
||
isAdmin?: boolean;
|
||
eventPasses?: any[];
|
||
}) => {
|
||
return (
|
||
<AuthenticatedLayout
|
||
title="Sessions & Delegations"
|
||
currentPath="/dashboard/sessions"
|
||
isAdmin={isAdmin}
|
||
>
|
||
<div
|
||
id="status-banner"
|
||
role="status"
|
||
aria-live="polite"
|
||
style="display: none; margin-bottom: 1rem; padding: 0.75rem 1rem; border-radius: var(--radius-md); font-size: 0.9rem;"
|
||
/>
|
||
|
||
<div style="display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 1.5rem; flex-wrap: wrap; gap: 1rem;">
|
||
<div>
|
||
<h1 style="font-size: 1.75rem; font-weight: 700; margin: 0 0 0.5rem 0; color: var(--text-primary);">
|
||
Active Sessions & Passes
|
||
</h1>
|
||
<p style="color: var(--text-secondary); margin: 0; font-size: 0.95rem;">
|
||
Manage logins, mint 1:1 ephemeral links & CLI tokens, or launch
|
||
multi-claim workshop event passes.
|
||
</p>
|
||
</div>
|
||
|
||
<div style="display: flex; gap: 0.75rem; flex-wrap: wrap;">
|
||
<button
|
||
type="button"
|
||
class="btn-primary"
|
||
style="min-height: 42px; box-shadow: var(--shadow-sm); display: inline-flex; align-items: center; gap: 0.4rem;"
|
||
onclick="openDelegateDrawer()"
|
||
>
|
||
<span style="font-size: 1.1rem;">🔑</span>
|
||
<span>+ Delegate Session</span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<EventCockpitDeck eventPasses={eventPasses} />
|
||
|
||
{/* Delegate Session Drawer */}
|
||
<div
|
||
id="delegateDrawer"
|
||
class="card"
|
||
style="display: none; border-left: 4px solid var(--primary); margin-bottom: 1.5rem;"
|
||
>
|
||
<div style="display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 0.5rem;">
|
||
<div style="width: 100%;">
|
||
<div style="display: flex; justify-content: space-between; align-items: center;">
|
||
<h3
|
||
id="delegateDrawerTitle"
|
||
style="margin: 0 0 0.25rem 0; color: var(--text-primary);"
|
||
>
|
||
Create New Pass / Session
|
||
</h3>
|
||
<button
|
||
type="button"
|
||
onclick="closeDelegateDrawer()"
|
||
style="background: none; border: none; font-size: 1.3rem; color: var(--text-muted); cursor: pointer;"
|
||
>
|
||
×
|
||
</button>
|
||
</div>
|
||
|
||
<div style="display: flex; background: var(--surface-muted); padding: 4px; border-radius: var(--radius-sm); border: 1px solid var(--border-subtle); gap: 4px; margin-top: 1rem; margin-bottom: 1rem;">
|
||
<button
|
||
type="button"
|
||
id="tabBtnDirectPass"
|
||
class="delegate-tab-btn active"
|
||
onclick="switchDelegateTab('tabDirectPass')"
|
||
>
|
||
🔑 1:1 Direct Pass
|
||
</button>
|
||
<button
|
||
type="button"
|
||
id="tabBtnWorkshopPass"
|
||
class="delegate-tab-btn"
|
||
onclick="switchDelegateTab('tabWorkshopPass')"
|
||
>
|
||
🎟️ Multi-Claim Workshop Pass
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<DirectPassDrawer apps={apps} isAdmin={isAdmin} />
|
||
<WorkshopDrawer apps={apps} />
|
||
</div>
|
||
|
||
<EventAttendeesDrawer />
|
||
|
||
<ScopeModal apps={apps} />
|
||
|
||
{/* Desktop Table View (≥ 768px) */}
|
||
<SessionTable
|
||
sessions={sessions}
|
||
currentSessionId={currentSessionId}
|
||
/>
|
||
|
||
{/* Mobile Adaptive Cards View (< 768px) */}
|
||
<SessionDeck
|
||
sessions={sessions}
|
||
currentSessionId={currentSessionId}
|
||
/>
|
||
|
||
<style>
|
||
{`
|
||
@media (min-width: 768px) {
|
||
.desktop-only { display: block !important; }
|
||
.mobile-only { display: none !important; }
|
||
}
|
||
@media (max-width: 767px) {
|
||
.desktop-only { display: none !important; }
|
||
.mobile-only { display: flex !important; }
|
||
}
|
||
.pill-btn {
|
||
background: var(--surface-card);
|
||
border: 1px solid var(--border-subtle);
|
||
border-radius: var(--radius-full);
|
||
padding: 0.4rem 0.85rem;
|
||
font-size: 0.85rem;
|
||
font-weight: 600;
|
||
color: var(--text-secondary);
|
||
cursor: pointer;
|
||
transition: all 0.15s ease;
|
||
}
|
||
.pill-btn:hover {
|
||
border-color: var(--primary);
|
||
color: var(--primary);
|
||
}
|
||
.pill-btn.active {
|
||
background: var(--primary-light);
|
||
border-color: var(--primary);
|
||
color: var(--primary);
|
||
}
|
||
.delegate-tab-btn {
|
||
flex: 1;
|
||
padding: 0.5rem 0.75rem;
|
||
border-radius: var(--radius-sm);
|
||
border: none;
|
||
font-weight: 600;
|
||
cursor: pointer;
|
||
transition: all 0.15s;
|
||
background: transparent;
|
||
color: var(--text-secondary);
|
||
}
|
||
.delegate-tab-btn.active {
|
||
background: var(--surface-card);
|
||
color: var(--primary);
|
||
box-shadow: var(--shadow-xs);
|
||
}
|
||
`}
|
||
</style>
|
||
|
||
<SessionsScript />
|
||
</AuthenticatedLayout>
|
||
);
|
||
};
|