242 lines
7 KiB
Svelte
242 lines
7 KiB
Svelte
|
|
<script>
|
||
|
|
import { onMount } from 'svelte'
|
||
|
|
import { api } from '../api.js'
|
||
|
|
|
||
|
|
let loading = $state(true)
|
||
|
|
let submitting = $state(false)
|
||
|
|
let error = $state('')
|
||
|
|
let submitted = $state(false)
|
||
|
|
|
||
|
|
let config = $state(null)
|
||
|
|
let preferredName = $state('')
|
||
|
|
let ticketName = $state('')
|
||
|
|
let email = $state('')
|
||
|
|
let pronouns = $state('')
|
||
|
|
let phone = $state('')
|
||
|
|
let departmentId = $state('')
|
||
|
|
let note = $state('')
|
||
|
|
|
||
|
|
onMount(async () => {
|
||
|
|
try {
|
||
|
|
config = await api.signup.config()
|
||
|
|
} catch (err) {
|
||
|
|
error = err.message
|
||
|
|
} finally {
|
||
|
|
loading = false
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
async function submit(e) {
|
||
|
|
e.preventDefault()
|
||
|
|
submitting = true
|
||
|
|
error = ''
|
||
|
|
try {
|
||
|
|
const data = {
|
||
|
|
preferred_name: preferredName.trim(),
|
||
|
|
email: email.trim(),
|
||
|
|
}
|
||
|
|
if (ticketName.trim()) data.ticket_name = ticketName.trim()
|
||
|
|
if (pronouns.trim()) data.pronouns = pronouns.trim()
|
||
|
|
if (phone.trim()) data.phone = phone.trim()
|
||
|
|
if (departmentId) data.department_id = Number(departmentId)
|
||
|
|
if (note.trim()) data.note = note.trim()
|
||
|
|
await api.signup.submit(data)
|
||
|
|
submitted = true
|
||
|
|
} catch (err) {
|
||
|
|
error = err.message
|
||
|
|
} finally {
|
||
|
|
submitting = false
|
||
|
|
}
|
||
|
|
}
|
||
|
|
</script>
|
||
|
|
|
||
|
|
<div class="kiosk">
|
||
|
|
<div class="kiosk-header">
|
||
|
|
<div class="kiosk-brand">Turn<span>pike</span> <span class="kiosk-role">Volunteer Signup</span></div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="kiosk-body">
|
||
|
|
{#if loading}
|
||
|
|
<div class="kiosk-center">Loading...</div>
|
||
|
|
{:else if submitted}
|
||
|
|
<div class="kiosk-card" style="text-align:center">
|
||
|
|
<h2 style="font-size:1.3rem;font-weight:700;margin-bottom:0.75rem">Thank you!</h2>
|
||
|
|
<p style="color:var(--c-muted);line-height:1.6;margin:0">
|
||
|
|
We've sent a confirmation email to <strong style="color:var(--c-text)">{email}</strong>.
|
||
|
|
Please check your inbox and click the link to confirm your signup.
|
||
|
|
</p>
|
||
|
|
</div>
|
||
|
|
{:else}
|
||
|
|
{#if config?.event_name && config.event_name !== 'the event'}
|
||
|
|
<h2 class="signup-event-name">{config.event_name}</h2>
|
||
|
|
{/if}
|
||
|
|
|
||
|
|
{#if error}
|
||
|
|
<div class="kiosk-alert">{error}</div>
|
||
|
|
{/if}
|
||
|
|
|
||
|
|
<form onsubmit={submit}>
|
||
|
|
<div class="kiosk-card">
|
||
|
|
<div class="signup-field">
|
||
|
|
<label for="s-name">Preferred Name <span class="req">*</span></label>
|
||
|
|
<input id="s-name" bind:value={preferredName} required placeholder="What should we call you?" />
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="signup-field">
|
||
|
|
<label for="s-ticket">Ticket Name</label>
|
||
|
|
<input id="s-ticket" bind:value={ticketName} placeholder="Name on your ticket (if different)" />
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="signup-field">
|
||
|
|
<label for="s-email">Email <span class="req">*</span></label>
|
||
|
|
<input id="s-email" type="email" bind:value={email} required placeholder="you@example.com" />
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="signup-row">
|
||
|
|
<div class="signup-field">
|
||
|
|
<label for="s-pronouns">Pronouns</label>
|
||
|
|
<input id="s-pronouns" bind:value={pronouns} placeholder="e.g. she/her" />
|
||
|
|
</div>
|
||
|
|
<div class="signup-field">
|
||
|
|
<label for="s-phone">Phone</label>
|
||
|
|
<input id="s-phone" type="tel" bind:value={phone} placeholder="Optional" />
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{#if config?.departments?.length > 0}
|
||
|
|
<div class="signup-field">
|
||
|
|
<label for="s-dept">Department Preference</label>
|
||
|
|
<select id="s-dept" bind:value={departmentId}>
|
||
|
|
<option value="">No preference</option>
|
||
|
|
{#each config.departments as dept}
|
||
|
|
<option value={dept.id}>{dept.name}</option>
|
||
|
|
{/each}
|
||
|
|
</select>
|
||
|
|
</div>
|
||
|
|
{/if}
|
||
|
|
|
||
|
|
<div class="signup-field">
|
||
|
|
<label for="s-note">
|
||
|
|
{config?.volunteer_note_label ?? 'Additional note'}
|
||
|
|
{#if config?.volunteer_note_required}<span class="req">*</span>{/if}
|
||
|
|
</label>
|
||
|
|
<textarea id="s-note" bind:value={note} rows="3"
|
||
|
|
required={config?.volunteer_note_required}
|
||
|
|
placeholder=""></textarea>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<button type="submit" class="kbtn kbtn-primary" style="width:100%;margin-top:0.5rem" disabled={submitting}>
|
||
|
|
{submitting ? 'Submitting...' : 'Sign Up'}
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
</form>
|
||
|
|
{/if}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<style>
|
||
|
|
.kiosk {
|
||
|
|
min-height: 100vh;
|
||
|
|
background: var(--c-bg);
|
||
|
|
color: var(--c-text);
|
||
|
|
font-family: var(--font);
|
||
|
|
display: flex;
|
||
|
|
flex-direction: column;
|
||
|
|
}
|
||
|
|
.kiosk-header {
|
||
|
|
background: var(--c-surface);
|
||
|
|
border-bottom: 1px solid var(--c-border);
|
||
|
|
padding: 1rem 1.5rem;
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
}
|
||
|
|
.kiosk-brand {
|
||
|
|
font-size: 1.1rem;
|
||
|
|
font-weight: 700;
|
||
|
|
letter-spacing: -0.02em;
|
||
|
|
color: var(--c-text);
|
||
|
|
}
|
||
|
|
.kiosk-brand span:first-of-type { color: var(--c-accent); }
|
||
|
|
.kiosk-role {
|
||
|
|
font-size: 0.8rem;
|
||
|
|
font-weight: 400;
|
||
|
|
color: var(--c-muted);
|
||
|
|
letter-spacing: 0;
|
||
|
|
}
|
||
|
|
.kiosk-body {
|
||
|
|
max-width: 540px;
|
||
|
|
margin: 0 auto;
|
||
|
|
padding: 1.5rem 1rem;
|
||
|
|
width: 100%;
|
||
|
|
}
|
||
|
|
.kiosk-center { display: flex; align-items: center; justify-content: center; }
|
||
|
|
.kiosk-alert {
|
||
|
|
background: rgba(239,68,68,0.1);
|
||
|
|
border: 1px solid rgba(239,68,68,0.25);
|
||
|
|
color: #fca5a5;
|
||
|
|
padding: 0.75rem 1rem;
|
||
|
|
border-radius: 6px;
|
||
|
|
margin-bottom: 1rem;
|
||
|
|
font-size: 0.875rem;
|
||
|
|
}
|
||
|
|
.kiosk-card {
|
||
|
|
background: var(--c-surface);
|
||
|
|
border: 1px solid var(--c-border);
|
||
|
|
border-radius: 10px;
|
||
|
|
padding: 1.25rem;
|
||
|
|
}
|
||
|
|
.signup-event-name {
|
||
|
|
font-size: 1.15rem;
|
||
|
|
font-weight: 700;
|
||
|
|
text-align: center;
|
||
|
|
margin-bottom: 1rem;
|
||
|
|
color: var(--c-text);
|
||
|
|
}
|
||
|
|
.signup-field {
|
||
|
|
margin-bottom: 1rem;
|
||
|
|
}
|
||
|
|
.signup-field label {
|
||
|
|
display: block;
|
||
|
|
font-size: 0.8rem;
|
||
|
|
font-weight: 600;
|
||
|
|
color: var(--c-muted);
|
||
|
|
margin-bottom: 0.3rem;
|
||
|
|
}
|
||
|
|
.signup-field input,
|
||
|
|
.signup-field select,
|
||
|
|
.signup-field textarea {
|
||
|
|
width: 100%;
|
||
|
|
padding: 0.55rem 0.75rem;
|
||
|
|
border: 1px solid var(--c-border);
|
||
|
|
border-radius: 6px;
|
||
|
|
background: var(--c-bg);
|
||
|
|
color: var(--c-text);
|
||
|
|
font-family: var(--font);
|
||
|
|
font-size: 0.875rem;
|
||
|
|
box-sizing: border-box;
|
||
|
|
}
|
||
|
|
.signup-field input:focus,
|
||
|
|
.signup-field select:focus,
|
||
|
|
.signup-field textarea:focus {
|
||
|
|
outline: none;
|
||
|
|
border-color: var(--c-accent);
|
||
|
|
}
|
||
|
|
.signup-row {
|
||
|
|
display: grid;
|
||
|
|
grid-template-columns: 1fr 1fr;
|
||
|
|
gap: 1rem;
|
||
|
|
}
|
||
|
|
.req { color: var(--c-accent); }
|
||
|
|
.kbtn {
|
||
|
|
display: inline-flex; align-items: center; justify-content: center;
|
||
|
|
padding: 0.55rem 1rem; border-radius: 6px;
|
||
|
|
border: 1px solid transparent;
|
||
|
|
font-size: 0.875rem; font-weight: 500; cursor: pointer;
|
||
|
|
font-family: var(--font);
|
||
|
|
transition: background 150ms;
|
||
|
|
}
|
||
|
|
.kbtn:disabled { opacity: 0.5; cursor: not-allowed; }
|
||
|
|
.kbtn-primary { background: var(--c-accent); color: #fff; }
|
||
|
|
.kbtn-primary:hover:not(:disabled) { background: var(--c-accent-h); }
|
||
|
|
</style>
|