162 lines
5.6 KiB
TypeScript
162 lines
5.6 KiB
TypeScript
import { Layout } from "./Layout.tsx";
|
|
|
|
export const EventSplashPage = ({ event }: { event: any }) => {
|
|
const maxSeats = Number(event.max_seats) || 0;
|
|
const seatsClaimed = Number(event.seats_claimed) || 0;
|
|
const isFull = maxSeats > 0 && seatsClaimed >= maxSeats;
|
|
const seatsRemaining = maxSeats > 0
|
|
? Math.max(0, maxSeats - seatsClaimed)
|
|
: null;
|
|
|
|
return (
|
|
<Layout title={`Join ${event.name}`}>
|
|
<div>
|
|
<div class="brand-header">
|
|
<div
|
|
class="brand-logo"
|
|
style="background: var(--primary-light); color: var(--primary);"
|
|
>
|
|
<svg
|
|
width="26"
|
|
height="26"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-width="2.5"
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
>
|
|
<path d="M2 9a3 3 0 0 1 0 6v2a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-2a3 3 0 0 1 0-6V7a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2Z">
|
|
</path>
|
|
<path d="M13 5v2"></path>
|
|
<path d="M13 17v2"></path>
|
|
<path d="M13 11v2"></path>
|
|
</svg>
|
|
</div>
|
|
<h1>{event.name}</h1>
|
|
<p class="subtitle">
|
|
You've been invited to join this event sandbox session.
|
|
</p>
|
|
</div>
|
|
|
|
<div
|
|
class="card"
|
|
style="margin-bottom: 1.5rem; text-align: center; border: 1px solid var(--border-subtle); background: var(--surface-muted);"
|
|
>
|
|
<div style="display: flex; justify-content: center; gap: 0.5rem; margin-bottom: 0.75rem; flex-wrap: wrap;">
|
|
{isFull
|
|
? (
|
|
<span class="badge badge-danger">
|
|
Workshop Full ({seatsClaimed}/{maxSeats})
|
|
</span>
|
|
)
|
|
: seatsRemaining !== null
|
|
? (
|
|
<span class="badge badge-success">
|
|
{seatsRemaining} seats remaining ({seatsClaimed}/{maxSeats})
|
|
</span>
|
|
)
|
|
: (
|
|
<span class="badge badge-success">
|
|
{seatsClaimed} attendees active (Open Access)
|
|
</span>
|
|
)}
|
|
<span class="badge badge-info">
|
|
⏱️ {event.lifespan_hours || 3}h Session
|
|
</span>
|
|
</div>
|
|
|
|
{event.pin_code && (
|
|
<div style="font-size: 0.85rem; color: var(--text-secondary); margin-top: 0.5rem;">
|
|
Event PIN:{" "}
|
|
<code style="font-weight: 700; color: var(--primary); font-size: 0.95rem;">
|
|
{event.pin_code}
|
|
</code>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<div
|
|
id="splashNotice"
|
|
style="display: none; margin-bottom: 1rem; padding: 0.75rem 1rem; border-radius: var(--radius-md); font-size: 0.9rem;"
|
|
/>
|
|
|
|
{!isFull
|
|
? (
|
|
<button
|
|
type="button"
|
|
id="joinSplashBtn"
|
|
class="btn-primary"
|
|
style="width: 100%; min-height: 52px; font-size: 1.05rem; box-shadow: var(--shadow-sm);"
|
|
onclick={`handleSplashJoin('${event.slug}')`}
|
|
>
|
|
⚡ Enter Workshop & Claim Seat
|
|
</button>
|
|
)
|
|
: (
|
|
<button
|
|
type="button"
|
|
class="btn-outline"
|
|
disabled
|
|
style="width: 100%; min-height: 52px; opacity: 0.6; cursor: not-allowed;"
|
|
>
|
|
Workshop at Capacity
|
|
</button>
|
|
)}
|
|
|
|
<div style="margin-top: 1.5rem; text-align: center; font-size: 0.85rem; color: var(--text-muted);">
|
|
Standard account login?{" "}
|
|
<a
|
|
href="/login"
|
|
style="color: var(--primary); text-decoration: none; font-weight: 600;"
|
|
>
|
|
Sign in with Passkey
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<script
|
|
dangerouslySetInnerHTML={{
|
|
__html: `
|
|
async function handleSplashJoin(slug) {
|
|
const btn = document.getElementById('joinSplashBtn');
|
|
const notice = document.getElementById('splashNotice');
|
|
btn.disabled = true;
|
|
btn.textContent = 'Claiming Seat...';
|
|
notice.style.display = 'none';
|
|
|
|
try {
|
|
const res = await fetch('/api/join', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ code: slug }),
|
|
});
|
|
const data = await res.json();
|
|
if (res.ok) {
|
|
window.location.href = data.redirectUrl || '/dashboard';
|
|
} else {
|
|
notice.textContent = data.error || 'Failed to join event';
|
|
notice.style.display = 'block';
|
|
notice.style.background = 'var(--danger-bg)';
|
|
notice.style.color = 'var(--danger-text)';
|
|
notice.style.border = '1px solid var(--danger-border)';
|
|
btn.disabled = false;
|
|
btn.textContent = '⚡ Enter Workshop & Claim Seat';
|
|
}
|
|
} catch (err) {
|
|
notice.textContent = 'Network error connecting to event';
|
|
notice.style.display = 'block';
|
|
notice.style.background = 'var(--danger-bg)';
|
|
notice.style.color = 'var(--danger-text)';
|
|
notice.style.border = '1px solid var(--danger-border)';
|
|
btn.disabled = false;
|
|
btn.textContent = '⚡ Enter Workshop & Claim Seat';
|
|
}
|
|
}
|
|
`,
|
|
}}
|
|
/>
|
|
</Layout>
|
|
);
|
|
};
|