- Created the `ui/components/sessions/` directory. - Extracted `EventCockpitDeck.tsx`, `DirectPassDrawer.tsx`, `WorkshopDrawer.tsx`, and `ScopeModal.tsx` from `ui/components/SessionsPage.tsx`. - Refactored `SessionsPage.tsx` to import and assemble these subcomponents cleanly. - Preserved zero React dependencies, keeping it pure Hono SSR JSX. - Preserved all inline client-side JavaScript, DOM IDs, and form submission handlers. - Formatted and linted all code via `deno fmt` and `deno task lint`. - Verified that all tests are passing. Let me know if you need any further adjustments! Co-authored-by: mrteye <1945243+mrteye@users.noreply.github.com>
302 lines
12 KiB
XML
302 lines
12 KiB
XML
export const WorkshopDrawer = ({ apps = [] }: { apps?: any[] }) => {
|
|
return (
|
|
<div id="tabWorkshopPass" style="display: none;">
|
|
<p style="color: var(--text-secondary); font-size: 0.9rem; margin: 0 0 1rem 0;">
|
|
Generate a shared event pass with a universal 6-digit PIN, vanity
|
|
link (<code style="font-size: 0.8rem;">/e/slug</code>), and CLI
|
|
1-liner for workshops, hackathons, and multi-user kiosks.
|
|
</p>
|
|
|
|
<form
|
|
id="eventForm"
|
|
onsubmit="handleCreateEvent(event)"
|
|
style="margin-top: 1rem;"
|
|
>
|
|
{/* Event Name */}
|
|
<div style="margin-bottom: 1.25rem;">
|
|
<label style="display: block; font-weight: 600; margin-bottom: 0.35rem; font-size: 0.875rem; color: var(--text-secondary);">
|
|
Workshop / Event Name *
|
|
</label>
|
|
<input
|
|
type="text"
|
|
id="eventName"
|
|
placeholder="e.g. Deno & SPIRE Zero-Trust Workshop, Elite Fleet Live Demo"
|
|
required
|
|
style="width: 100%;"
|
|
/>
|
|
</div>
|
|
|
|
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 1rem; margin-bottom: 1.25rem;">
|
|
{/* Target App */}
|
|
<div>
|
|
<label style="display: block; font-weight: 600; margin-bottom: 0.35rem; font-size: 0.875rem; color: var(--text-secondary);">
|
|
Target App Destination
|
|
</label>
|
|
<select
|
|
id="eventAppId"
|
|
style="width: 100%; padding: 0.5rem 0.75rem; background: var(--surface-card); border: 1px solid var(--border-subtle); border-radius: var(--radius-sm); color: var(--text-primary); font-size: 0.9rem;"
|
|
>
|
|
<option value="">Universal Sandbox (Default)</option>
|
|
{apps.map((app) => (
|
|
<option value={app.id} key={app.id}>
|
|
{app.name} ({app.domain || "Internal"})
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
|
|
{/* Guest Role */}
|
|
<div>
|
|
<label style="display: block; font-weight: 600; margin-bottom: 0.35rem; font-size: 0.875rem; color: var(--text-secondary);">
|
|
Attendee Access Role
|
|
</label>
|
|
<select
|
|
id="eventRole"
|
|
style="width: 100%; padding: 0.5rem 0.75rem; background: var(--surface-card); border: 1px solid var(--border-subtle); border-radius: var(--radius-sm); color: var(--text-primary); font-size: 0.9rem;"
|
|
>
|
|
<option value="viewer">Viewer (Read-Only Demo)</option>
|
|
<option value="operator">Operator (Workshop Actions)</option>
|
|
<option value="user">Standard User</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Seat Capacity Presets */}
|
|
<div style="margin-bottom: 1.25rem;">
|
|
<label style="display: block; font-weight: 600; margin-bottom: 0.45rem; font-size: 0.875rem; color: var(--text-secondary);">
|
|
Seat Capacity Limit
|
|
</label>
|
|
<div style="display: flex; gap: 0.5rem; flex-wrap: wrap; align-items: center;">
|
|
<button
|
|
type="button"
|
|
class="pill-btn seat-pill"
|
|
onclick="selectSeats(this, 25)"
|
|
>
|
|
25 Seats
|
|
</button>
|
|
<button
|
|
type="button"
|
|
class="pill-btn seat-pill active"
|
|
onclick="selectSeats(this, 50)"
|
|
>
|
|
50 Seats
|
|
</button>
|
|
<button
|
|
type="button"
|
|
class="pill-btn seat-pill"
|
|
onclick="selectSeats(this, 100)"
|
|
>
|
|
100 Seats
|
|
</button>
|
|
<button
|
|
type="button"
|
|
class="pill-btn seat-pill"
|
|
onclick="selectSeats(this, 0)"
|
|
>
|
|
∞ Unlimited
|
|
</button>
|
|
<input
|
|
type="number"
|
|
id="eventMaxSeats"
|
|
value="50"
|
|
min="0"
|
|
max="10000"
|
|
style="width: 100px; padding: 0.35rem 0.5rem; text-align: center;"
|
|
title="Max seats (0 = unlimited)"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Duration Presets */}
|
|
<div style="margin-bottom: 1.25rem;">
|
|
<label style="display: block; font-weight: 600; margin-bottom: 0.45rem; font-size: 0.875rem; color: var(--text-secondary);">
|
|
Event Duration & Expiration
|
|
</label>
|
|
<div style="display: flex; gap: 0.5rem; flex-wrap: wrap;">
|
|
<button
|
|
type="button"
|
|
class="pill-btn event-lifespan-pill"
|
|
onclick="selectEventLifespan(this, 1)"
|
|
>
|
|
⚡ 1 Hour
|
|
</button>
|
|
<button
|
|
type="button"
|
|
class="pill-btn event-lifespan-pill active"
|
|
onclick="selectEventLifespan(this, 3)"
|
|
>
|
|
🛠️ 3 Hours (Standard Workshop)
|
|
</button>
|
|
<button
|
|
type="button"
|
|
class="pill-btn event-lifespan-pill"
|
|
onclick="selectEventLifespan(this, 12)"
|
|
>
|
|
📅 12 Hours (All-Day Hackathon)
|
|
</button>
|
|
<button
|
|
type="button"
|
|
class="pill-btn event-lifespan-pill"
|
|
onclick="selectEventLifespan(this, 24)"
|
|
>
|
|
🗓️ 24 Hours
|
|
</button>
|
|
</div>
|
|
<input type="hidden" id="eventLifespanHours" value="3" />
|
|
</div>
|
|
|
|
{/* Custom Codes Accordion (Optional) */}
|
|
<details style="margin-bottom: 1.5rem; background: var(--surface-muted); padding: 0.75rem 1rem; border-radius: var(--radius-sm); border: 1px solid var(--border-subtle);">
|
|
<summary style="font-size: 0.875rem; font-weight: 600; color: var(--text-primary); cursor: pointer;">
|
|
▸ Custom Vanity Slug & Custom PIN Code (Optional)
|
|
</summary>
|
|
<div style="margin-top: 0.75rem; display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem;">
|
|
<div>
|
|
<label style="display: block; font-size: 0.8rem; font-weight: 600; color: var(--text-secondary); margin-bottom: 0.25rem;">
|
|
Vanity Slug (e.g. /e/deno-lab)
|
|
</label>
|
|
<input
|
|
type="text"
|
|
id="eventSlug"
|
|
placeholder="auto-generated if blank"
|
|
style="width: 100%; font-size: 0.85rem;"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label style="display: block; font-size: 0.8rem; font-weight: 600; color: var(--text-secondary); margin-bottom: 0.25rem;">
|
|
Custom 6-Digit PIN (e.g. 749-123)
|
|
</label>
|
|
<input
|
|
type="text"
|
|
id="eventPinCode"
|
|
placeholder="auto-generated if blank"
|
|
style="width: 100%; font-size: 0.85rem;"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</details>
|
|
|
|
<div style="display: flex; gap: 0.75rem;">
|
|
<button
|
|
type="submit"
|
|
id="submitEventBtn"
|
|
class="btn-primary"
|
|
style="min-height: 42px; background: var(--success); border-color: var(--success);"
|
|
>
|
|
🎟️ Launch Workshop Pass
|
|
</button>
|
|
<button
|
|
type="button"
|
|
class="btn-outline"
|
|
onclick="closeDelegateDrawer()"
|
|
style="min-height: 42px;"
|
|
>
|
|
Cancel
|
|
</button>
|
|
</div>
|
|
</form>
|
|
|
|
{/* Event Launch Success Modal / Card */}
|
|
<div
|
|
id="eventHandoffModal"
|
|
style="display: none; margin-top: 1.5rem; padding: 1.25rem; background: var(--surface-card); border: 2px solid var(--success); border-radius: var(--radius-md); box-shadow: var(--shadow-md);"
|
|
>
|
|
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.75rem;">
|
|
<div style="display: flex; align-items: center; gap: 0.5rem;">
|
|
<span style="font-size: 1.3rem;">🎟️</span>
|
|
<strong
|
|
id="createdEventTitle"
|
|
style="color: var(--text-primary); font-size: 1.1rem;"
|
|
>
|
|
Workshop Pass Live!
|
|
</strong>
|
|
</div>
|
|
<span class="badge badge-success">Active Now</span>
|
|
</div>
|
|
|
|
<p style="font-size: 0.85rem; color: var(--text-secondary); margin: 0 0 1rem 0;">
|
|
Share the 6-digit PIN, 1-click link, or CLI 1-liner with
|
|
attendees. You can monitor live claimed seats and trigger the
|
|
master kill switch from the Event Cockpit below.
|
|
</p>
|
|
|
|
<div style="display: flex; flex-direction: column; gap: 0.75rem; margin-bottom: 1.25rem;">
|
|
{/* PIN Code */}
|
|
<div>
|
|
<label style="display: block; font-size: 0.75rem; font-weight: 700; text-transform: uppercase; color: var(--text-muted); margin-bottom: 0.25rem;">
|
|
6-Digit Universal PIN (for /join or Kiosk Entry)
|
|
</label>
|
|
<div style="display: flex; gap: 0.5rem;">
|
|
<code
|
|
id="eventHandoffPin"
|
|
style="flex: 1; padding: 0.5rem 0.75rem; background: var(--surface-muted); border: 1px solid var(--border-subtle); border-radius: var(--radius-sm); font-family: monospace; font-size: 1.1rem; text-align: center; letter-spacing: 3px; font-weight: 700; color: var(--primary);"
|
|
>
|
|
</code>
|
|
<button
|
|
type="button"
|
|
class="btn-primary"
|
|
style="min-height: 36px; padding: 0 0.85rem; font-size: 0.8rem;"
|
|
onclick="copyEventHandoff('pin')"
|
|
>
|
|
Copy PIN
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 1-Click Vanity Link */}
|
|
<div>
|
|
<label style="display: block; font-size: 0.75rem; font-weight: 700; text-transform: uppercase; color: var(--text-muted); margin-bottom: 0.25rem;">
|
|
Direct 1-Click Workshop Entrance URL
|
|
</label>
|
|
<div style="display: flex; gap: 0.5rem;">
|
|
<code
|
|
id="eventHandoffLink"
|
|
style="flex: 1; padding: 0.5rem 0.75rem; background: var(--surface-muted); border: 1px solid var(--border-subtle); border-radius: var(--radius-sm); font-family: monospace; font-size: 0.85rem; overflow-x: auto; white-space: nowrap; color: var(--success);"
|
|
>
|
|
</code>
|
|
<button
|
|
type="button"
|
|
class="btn-outline"
|
|
style="min-height: 36px; padding: 0 0.85rem; font-size: 0.8rem;"
|
|
onclick="copyEventHandoff('link')"
|
|
>
|
|
Copy URL
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
{/* CLI 1-Liner */}
|
|
<div>
|
|
<label style="display: block; font-size: 0.75rem; font-weight: 700; text-transform: uppercase; color: var(--text-muted); margin-bottom: 0.25rem;">
|
|
Terminal 1-Liner (Environment Injector)
|
|
</label>
|
|
<div style="display: flex; gap: 0.5rem;">
|
|
<code
|
|
id="eventHandoffCli"
|
|
style="flex: 1; padding: 0.5rem 0.75rem; background: var(--surface-muted); border: 1px solid var(--border-subtle); border-radius: var(--radius-sm); font-family: monospace; font-size: 0.8rem; overflow-x: auto; white-space: nowrap; color: var(--text-primary);"
|
|
>
|
|
</code>
|
|
<button
|
|
type="button"
|
|
class="btn-outline"
|
|
style="min-height: 36px; padding: 0 0.85rem; font-size: 0.8rem;"
|
|
onclick="copyEventHandoff('cli')"
|
|
>
|
|
Copy 1-Liner
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<button
|
|
type="button"
|
|
class="btn-outline"
|
|
style="width: 100%; min-height: 38px; justify-content: center; font-size: 0.85rem;"
|
|
onclick="closeEventHandoffModal()"
|
|
>
|
|
Done & Refresh Cockpit
|
|
</button>
|
|
</div>
|
|
</div>
|
|
);
|
|
}; |