Creates ae_events_stores__auth.svelte.ts with PersistedState keyed 'ae_events_auth_loc' for auth__person and auth__kv (presenter/session sign-in state). Migrated 10 component files from $events_loc.auth__* to events_auth_loc.current.auth__*. Also fixed stale pres_mgmt stragglers: $events_loc.pres_mgmt.* refs in presenter_obj_li.svelte, presenter_page_menu.svelte, and [presenter_id]/ +page.svelte now use pres_mgmt_loc.current.* directly. show_details boolean moved from events_loc to leads_loc (it belongs in the leads module — one bind in ae_tab__manage.svelte). auth__person, auth__kv, show_details, events_cfg_json, event_id removed from events_local_data_struct. events_loc now only carries ver, title, and qry__* prefs. svelte-check: 0 errors, 0 warnings. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Aether Exhibitor Leads Module (v3)
Status: Substantially implemented. Core lead capture flow works end-to-end. PWA only — no Electron involvement. The Electron app is exclusively for the Launcher.
Spec docs:
documentation/PROJECT__AE_Events_Exhibitor_Leads_v3.md— overviewdocumentation/PROJECT__AE_Events_Exhibitor_Leads_v3_detail.md— tab-level detail
Overview
Exhibitors sign in at their booth page, then scan or search attendee badges to capture leads.
Leads are stored as event_exhibit_tracking records linked to the exhibit and the badge.
All data is cached in IndexedDB (Dexie.js) for offline use, with background API revalidation (SWR).
Route Structure
/events/[event_id]/leads/ — Exhibit search / landing
/events/[event_id]/leads/exhibit/[exhibit_id]/ — Exhibit detail (4 tabs)
/events/[event_id]/leads/exhibit/[exhibit_id]/lead/[exhibit_tracking_id]/ — Lead detail
Key Files
Routes
| File | Role |
|---|---|
leads/+page.svelte |
Exhibit search/landing — find your booth |
leads/+page.ts |
Layout data load |
leads/exhibit/[exhibit_id]/+page.svelte |
Main exhibitor view — orchestrates all tabs |
leads/exhibit/[exhibit_id]/+layout.svelte / +layout.ts |
Exhibit layout / data load |
leads/exhibit/[exhibit_id]/lead/[exhibit_tracking_id]/+page.svelte |
Lead detail view/edit |
leads/exhibit/[exhibit_id]/lead/[exhibit_tracking_id]/+page.ts |
Lead data load |
Components (within exhibit/[exhibit_id]/)
| File | Role |
|---|---|
ae_tab__start.svelte |
Tab 1 — welcome + sign-in |
ae_tab__add.svelte |
Tab 2 — QR/search toggle + scan mode toggle |
ae_tab__manage.svelte |
Tab 4 — admin tools, booth config, app settings |
ae_comp__exhibit_signin.svelte |
Sign-in: shared passcode + licensed user |
ae_comp__lead_qr_scanner.svelte |
QR scanner (rapid vs. qualify mode) |
ae_comp__lead_manual_search.svelte |
Manual badge search + add |
ae_comp__exhibit_tracking_search.svelte |
Lead list search/filter bar |
ae_comp__exhibit_tracking_obj_li.svelte |
Lead list item renderer |
ae_comp__exhibit_license_list.svelte |
License slot manager (admin) |
ae_comp__exhibit_custom_questions.svelte |
Custom question config editor (admin) |
ae_comp__exhibit_payment.svelte |
Stripe payment component (fully implemented) |
ae_comp__exhibit_search.svelte |
Exhibit search input on the landing page |
Lead detail components (within lead/[exhibit_tracking_id]/)
| File | Role |
|---|---|
ae_comp__lead_detail_form.svelte |
Custom question response editor |
Data Model
event_exhibit
Represents one exhibitor's presence at an event.
Key fields: event_exhibit_id, name, code (booth #), staff_passcode, priority (paid flag),
license_max, license_li_json (array of {full_name, email, passcode}),
leads_custom_questions_json (array of question defs), leads_device_sm_qty, leads_device_lg_qty.
event_exhibit_tracking
One captured lead — links an exhibit to a badge.
Key fields: event_exhibit_tracking_id, event_exhibit_id, event_badge_id,
external_person_id (capturer's email), exhibitor_notes (HTML),
responses_json ({ [question_code]: { response: value } }),
priority, enable, hide.
Denormalized badge fields: event_badge_full_name, event_badge_email,
event_badge_affiliations, event_badge_professional_title.
Sign-In Model
Three auth levels in this module:
- Aether platform auth (manager_access / trusted_access) — full admin bypass
- Shared exhibit passcode (
event_exhibit.staff_passcode) — grants booth management access - Licensed user (email + passcode from
license_li_json) — grants lead capture access
Auth state stored in $events_loc.leads.auth_exhibit_kv[exhibit_id] (persisted to localStorage).
QR Scan Flow
Badge QR encodes: { type: 'event_badge', id: '<id_random>' }
Scanner reads this, checks for duplicate in IDB, loads badge info, then creates an
event_exhibit_tracking record via events_func.create_ae_obj__exhibit_tracking.
Two scan modes (toggled per exhibit):
- Rapid — auto-resets after 2 seconds to scan the next person
- Qualify — navigates to lead detail immediately to fill in notes/responses
Known Gaps / Not Yet Implemented
- None currently. See
TODO__Agents.mdfor the remaining smoke test checklist.
Implemented (previously listed as gaps)
-
Payment / Stripe —
ae_comp__exhibit_payment.svelteis fully implemented. Three states: paid (priority=truegreen card), Stripe not configured (admin hint), payment form. Visibility is controlled event-wide byevent.mod_exhibits_json.leads_require_payment. Set totruein event settings JSON to enable Stripe for an event; defaultfalsehides all billing UI (header CreditCard button + Manage tab "Licenses & Billing" accordion). -
allow_trackinggate — enforced in bothae_comp__lead_qr_scanner.svelteandae_comp__lead_manual_search.svelte. Badges withoutallow_tracking = trueare blocked. -
Show/hide hidden records — toggle in
ae_comp__exhibit_tracking_search.svelte; filters local IDB search, API search (hiddenparam), andfiltered_lead_liin+page.svelte. -
Export endpoint —
download_export__event_exhibit_trackinguses V3 action endpoint/v3/action/event_exhibit/{id}/tracking_export. Gated onleads_api_access === true. -
PWA install prompt —
element_pwa_install_prompt.svelteplaced on the Start tab. Handles Chrome/Android native install and iOS Safari manual instructions. -
License management access for shared-passcode users —
ae_tab__manage.sveltelicense section now visible toadministrator_accessORauth_exhibit_kv[id].type === 'shared'. Also addedtypefield toLeadsLocState.auth_exhibit_kvinterface.
Recent Changes (2026-04-03)
-
Migrated Leads persisted state to Svelte‑5 PersistedState: new
leads_locstore implemented atsrc/lib/stores/ae_events_stores__leads.svelte.tsand the store version constantAE_LEADS_LOC_VERSIONadded tosrc/lib/stores/store_versions.ts. -
Payment UI updates:
ae_comp__exhibit_payment.sveltenow accepts aleads_require_paymentprop and honors the event-levelevent.mod_exhibits_json.leads_require_paymentflag to hide billing UI when payment is not required for the event.- Added a loading guard so the payment component shows a loader until the exhibit record resolves from IndexedDB (Dexie
liveQuery), preventing the payment form from appearing prematurely.
-
Tests: some test seeds need updating — update
tests/_helpers/leads_helpers.tsto seedleads_locdefaults and the expected__versionwhere tests rely on pre-seeded localStorage.
Lib Functions
src/lib/ae_events/ae_events__exhibit.ts — exhibit load, search, create, update
src/lib/ae_events/ae_events__exhibit_tracking.ts — tracking load, search, create, update, export
Both are aggregated into events_func via src/lib/ae_events/ae_events_functions.ts.