feat(leads): persistent tab states and unified search UI

- Implemented sticky tab persistence using local storage via events_loc store.
- Aligned Manual Search form styling with Lead List search for UI consistency.
- Updated tab switching logic to support historical navigation within exhibits.
- Center-aligned and stabilized Add Lead content area.
This commit is contained in:
Scott Idem
2026-02-07 18:08:23 -05:00
parent f4a34e4ad7
commit 677571dde2
3 changed files with 49 additions and 29 deletions

View File

@@ -41,15 +41,21 @@
$events_loc.leads.tracking__qry__sort_order = 'created_desc';
}
// --- Tab State ---
let active_tab = $state('list'); // 'start', 'add', 'list', 'manage'
let previous_main_tab = $state('list'); // To remember if we were on 'add' or 'list' before going to 'manage'
// Mock sign-in state for now
let is_signed_in = $state(true);
let tracking_id_li: Array<string> = $state([]);
let search_debounce_timer: any = null;
// --- Tab State (Sticky via Store) ---
let active_tab = $derived($events_loc.leads.tab?.[page.params.exhibit_id ?? ''] ?? 'list');
let previous_main_tab = $state('list'); // To remember if we were on 'add' or 'list' before going to 'manage'
function set_active_tab(new_tab: string) {
const exhibit_id = page.params.exhibit_id;
if (!exhibit_id) return;
if (!$events_loc.leads.tab) $events_loc.leads.tab = {};
$events_loc.leads.tab[exhibit_id] = new_tab;
}
// Mock sign-in state for now
let is_signed_in = $state(true);
let tracking_id_li: Array<string> = $state([]); let search_debounce_timer: any = null;
let last_search_id = 0;
let last_executed_key = '';
let log_lvl = 1;
@@ -257,28 +263,28 @@
function toggle_main_tab() {
if (active_tab === 'add') {
active_tab = 'list';
set_active_tab('list');
previous_main_tab = 'list';
} else {
active_tab = 'add';
set_active_tab('add');
previous_main_tab = 'add';
}
}
function toggle_manage_tab() {
if (active_tab === 'manage') {
active_tab = previous_main_tab;
set_active_tab(previous_main_tab);
} else {
active_tab = 'manage';
set_active_tab('manage');
}
}
</script>
<section
class="ae_events_leads_tracking_new h-full min-w-lg md:min-w-md w-full flex flex-col items-center justify-center overflow-x-hidden outline-2 outline-red-600"
class="ae_events_leads_tracking_new h-full w-full flex flex-col items-center overflow-x-hidden"
>
<!-- Header -->
<header class="grow-w w-full bg-surface-100-900 border-b border-surface-500/20 px-4 py-2 sticky top-0 z-10 flex items-center justify-between gap-4 shadow-sm">
<header class="w-full bg-surface-100-900 border-b border-surface-500/20 px-4 py-2 sticky top-0 z-10 flex items-center justify-between gap-4 shadow-sm">
<div class="flex flex-col min-w-0">
<h1 class="text-base sm:text-lg font-bold truncate leading-tight">
{$lq__exhibit_obj?.name ?? 'Exhibitor'}

View File

@@ -68,24 +68,32 @@
<div class="lead-manual-search space-y-4 w-full">
<form
class="flex gap-2"
class="search_form flex flex-row flex-wrap gap-1 items-center justify-center w-full px-2 py-2 preset-tonal-primary rounded-lg shadow-sm"
onsubmit={(e) => { e.preventDefault(); handle_search(); }}
>
<div class="relative grow">
<Search class="absolute left-3 top-1/2 -translate-y-1/2 opacity-50" size="1.2em" />
<div class="flex flex-col md:flex-row items-center justify-center gap-1 grow">
<input
type="search"
bind:value={search_query}
placeholder="Name, email, or badge ID..."
class="input pl-10 w-full"
placeholder="Attendee name, email, or badge ID..."
class="input text-lg font-mono grow transition-all w-full"
/>
</div>
<button type="submit" class="btn preset-filled-primary" disabled={searching}>
{#if searching}
<LoaderCircle class="animate-spin mr-2" size="1.2em" />
{/if}
Search
</button>
<div class="flex flex-row items-center justify-center gap-1">
<button
type="submit"
class="btn btn-lg preset-tonal-primary border border-primary-500 hover:preset-tonal-primary text-2xl font-bold w-48 transition-all"
disabled={searching}
>
{#if searching}
<LoaderCircle class="animate-spin mx-1" size="1.2em" />
{:else}
<Search class="mx-1" size="1.2em" />
{/if}
Search
</button>
</div>
</form>
{#if results.length > 0}

View File

@@ -14,7 +14,13 @@
let { exhibit_id }: Props = $props();
let mode = $state('qr'); // 'qr' or 'search'
// Use store for persistence (Stickiness)
let mode = $derived($events_loc.leads.tab_add_mode?.[exhibit_id] ?? 'qr');
function set_mode(new_mode: string) {
if (!$events_loc.leads.tab_add_mode) $events_loc.leads.tab_add_mode = {};
$events_loc.leads.tab_add_mode[exhibit_id] = new_mode;
}
function handle_lead_added(badge: any) {
console.log('Lead successfully added:', badge.full_name);
@@ -30,7 +36,7 @@
type="button"
class="flex-1 btn btn-sm py-3 flex items-center justify-center gap-2 rounded-lg transition-all duration-200"
class:preset-filled-primary={mode === 'qr'}
onclick={() => mode = 'qr'}
onclick={() => set_mode('qr')}
>
<QrCode size="1.2em" />
<span class="font-bold">Scan QR</span>
@@ -39,7 +45,7 @@
type="button"
class="flex-1 btn btn-sm py-3 flex items-center justify-center gap-2 rounded-lg transition-all duration-200"
class:preset-filled-primary={mode === 'search'}
onclick={() => mode = 'search'}
onclick={() => set_mode('search')}
>
<Search size="1.2em" />
<span class="font-bold">Search</span>