- implemented universal ingress credential rotation (slug + pin) - fixed event extension logic (`GREATEST(expires_at, NOW())`) - added UI formatter logic for natural dates (`formatNaturalExpiry`, `formatNaturalJoinTime`) - updated event cards to bounded 2-row compact cards - consolidated CLI expanding snippets - overhauled WAI-ARIA support for delegation drawers - removed legacy "Dismiss" mock buttons for cleanly styled "OK" buttons - updated tests and ensured pure zero-dependency SSR JSX compatibility Co-authored-by: mrteye <1945243+mrteye@users.noreply.github.com>
212 lines
7.3 KiB
TypeScript
212 lines
7.3 KiB
TypeScript
import { AuthenticatedLayout } from "./AuthenticatedLayout.tsx";
|
|
import { EventCockpitDeck } from "./sessions/EventCockpitDeck.tsx";
|
|
import { EventGuestsDrawer } from "./sessions/EventGuestsDrawer.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);">
|
|
Sessions & Events
|
|
</h1>
|
|
<p style="color: var(--text-secondary); margin: 0; font-size: 0.95rem;">
|
|
Manage logins, mint 1:1 delegated tokens, or launch multi-claim
|
|
workshop events.
|
|
</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; justify-content: center;"
|
|
onclick="openDelegateDrawer()"
|
|
>
|
|
Delegate Session
|
|
</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;">
|
|
<h2
|
|
id="delegateDrawerTitle"
|
|
style="margin: 0 0 0.25rem 0; color: var(--text-primary); font-size: 1.25rem;"
|
|
>
|
|
Delegate Session
|
|
</h2>
|
|
<button
|
|
type="button"
|
|
onclick="closeDelegateDrawer()"
|
|
style="background: none; border: none; font-size: 1.3rem; color: var(--text-muted); cursor: pointer;"
|
|
>
|
|
×
|
|
</button>
|
|
</div>
|
|
<p style="color: var(--text-secondary); margin: 0; font-size: 0.9rem;">
|
|
Mint a 1:1 delegated token or launch a multi-seat workshop event.
|
|
</p>
|
|
|
|
<div
|
|
role="tablist"
|
|
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"
|
|
role="tab"
|
|
aria-selected="true"
|
|
id="tabBtnDirectPass"
|
|
class="delegate-tab-btn active"
|
|
onclick="switchDelegateTab('tabDirectPass')"
|
|
style="display: flex; flex-direction: column; align-items: center; gap: 2px;"
|
|
>
|
|
<span style="font-size: 0.95rem; font-weight: 700;">
|
|
Single Session
|
|
</span>
|
|
<span style="font-size: 0.75rem; font-weight: 400; opacity: 0.85;">
|
|
For agents, CI/CD, or 1:1 delegation
|
|
</span>
|
|
</button>
|
|
<button
|
|
type="button"
|
|
role="tab"
|
|
aria-selected="false"
|
|
id="tabBtnWorkshopPass"
|
|
class="delegate-tab-btn"
|
|
onclick="switchDelegateTab('tabWorkshopPass')"
|
|
style="display: flex; flex-direction: column; align-items: center; gap: 2px;"
|
|
>
|
|
<span style="font-size: 0.95rem; font-weight: 700;">
|
|
Multi-Claim Event
|
|
</span>
|
|
<span style="font-size: 0.75rem; font-weight: 400; opacity: 0.85;">
|
|
For workshops, teams & guest pools
|
|
</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<DirectPassDrawer apps={apps} isAdmin={isAdmin} />
|
|
<WorkshopDrawer apps={apps} />
|
|
</div>
|
|
|
|
<EventGuestsDrawer />
|
|
|
|
<ScopeModal apps={apps} />
|
|
|
|
<h2 style="font-size: 1.25rem; margin-top: 2rem; margin-bottom: 0.25rem; color: var(--text-primary);">
|
|
Sessions
|
|
</h2>
|
|
<p style="color: var(--text-secondary); margin-bottom: 1rem; font-size: 0.95rem;">
|
|
Direct device logins, passkey authentications, and delegated agent
|
|
tokens.
|
|
</p>
|
|
|
|
{/* 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);
|
|
opacity: 0.75;
|
|
}
|
|
.delegate-tab-btn.active {
|
|
background: var(--primary);
|
|
color: #ffffff;
|
|
box-shadow: var(--shadow-xs);
|
|
opacity: 1;
|
|
}
|
|
`}
|
|
</style>
|
|
|
|
<SessionsScript />
|
|
</AuthenticatedLayout>
|
|
);
|
|
};
|