diff --git a/frontend/src/pages/GateKiosk.svelte b/frontend/src/pages/GateKiosk.svelte index 6c7dc71..d1eeaa1 100644 --- a/frontend/src/pages/GateKiosk.svelte +++ b/frontend/src/pages/GateKiosk.svelte @@ -7,6 +7,8 @@ let { session, onLogout } = $props() let search = $state('') + let manuallySelectedId = $state(null) + let showAll = $state(false) let error = $state('') let scannerMsg = $state('') let qrSupported = $state(false) @@ -44,22 +46,44 @@ return ($tickets ?? []).find(t => t.external_id?.toLowerCase() === sl) ?? null }) - // Name/email search across participants + const allParticipantsSorted = $derived.by(() => + ($participants ?? []) + .filter(p => !p.deleted_at) + .sort((a, b) => (a.preferred_name || a.email || '').localeCompare(b.preferred_name || b.email || '')) + ) + + // Clear manual selection whenever search text changes + $effect(() => { + search + manuallySelectedId = null + }) + + // Name/email/ticket-name search across participants const filteredParticipants = $derived.by(() => { if (matchedTicket) return [] const s = search.trim().toLowerCase() if (!s || s.length < 2) return [] + const byTicketName = new Set( + ($tickets ?? []) + .filter(t => t.name?.toLowerCase().includes(s)) + .map(t => t.participant_id) + .filter(Boolean) + ) return ($participants ?? []) .filter(p => p.preferred_name?.toLowerCase().includes(s) || - p.email?.toLowerCase().includes(s) + p.email?.toLowerCase().includes(s) || + byTicketName.has(p.id) ) .sort((a, b) => (a.preferred_name || '').localeCompare(b.preferred_name || '')) .slice(0, 8) }) - // Auto-select when exactly one participant matches + // Manual selection takes priority; fall back to auto-select on single match const selectedParticipant = $derived.by(() => { + if (manuallySelectedId) { + return ($participants ?? []).find(p => p.id === manuallySelectedId) ?? null + } if (filteredParticipants.length === 1) return filteredParticipants[0] return null }) @@ -165,6 +189,9 @@ {scanning ? '■ Stop' : '⊡ Scan QR'} {/if} + {#if scanning} @@ -211,7 +238,13 @@ {:else if selectedParticipant} {@const pts = ticketsFor(selectedParticipant.id)}
-
{selectedParticipant.preferred_name || selectedParticipant.email || '(unknown)'}
+
+
{selectedParticipant.preferred_name || selectedParticipant.email || '(unknown)'}
+ {#if manuallySelectedId} + + {/if} +
{#if selectedParticipant.email}
{selectedParticipant.email}
{/if} @@ -243,7 +276,7 @@ {#each filteredParticipants as p} {@const pts = ticketsFor(p.id)} {@const ci = pts.filter(t => t.checked_in_at).length} - + {/each} +
+ {/if} +
Recent Check-ins
@@ -385,7 +439,8 @@ padding: 1.25rem; margin-bottom: 1rem; } - .gate-match-name { font-size: 1.4rem; font-weight: 700; margin-bottom: 0.2rem; } + .gate-match-name-row { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; margin-bottom: 0.2rem; } + .gate-match-name { font-size: 1.4rem; font-weight: 700; } .gate-match-sub { color: var(--c-muted); font-size: 0.875rem; } .gate-party { margin: 0.5rem 0;