Files
OSIT-AE-App-Svelte/src/routes/events/[event_id]/(leads)
Scott Idem 83c9b9fd4f feat(stores): promote events auth state to Svelte 5 PersistedState
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>
2026-06-11 16:19:49 -04:00
..

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 — overview
  • documentation/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:

  1. Aether platform auth (manager_access / trusted_access) — full admin bypass
  2. Shared exhibit passcode (event_exhibit.staff_passcode) — grants booth management access
  3. 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.md for the remaining smoke test checklist.

Implemented (previously listed as gaps)

  • Payment / Stripeae_comp__exhibit_payment.svelte is fully implemented. Three states: paid (priority=true green card), Stripe not configured (admin hint), payment form. Visibility is controlled event-wide by event.mod_exhibits_json.leads_require_payment. Set to true in event settings JSON to enable Stripe for an event; default false hides all billing UI (header CreditCard button + Manage tab "Licenses & Billing" accordion).

  • allow_tracking gate — enforced in both ae_comp__lead_qr_scanner.svelte and ae_comp__lead_manual_search.svelte. Badges without allow_tracking = true are blocked.

  • Show/hide hidden records — toggle in ae_comp__exhibit_tracking_search.svelte; filters local IDB search, API search (hidden param), and filtered_lead_li in +page.svelte.

  • Export endpointdownload_export__event_exhibit_tracking uses V3 action endpoint /v3/action/event_exhibit/{id}/tracking_export. Gated on leads_api_access === true.

  • PWA install promptelement_pwa_install_prompt.svelte placed on the Start tab. Handles Chrome/Android native install and iOS Safari manual instructions.

  • License management access for shared-passcode usersae_tab__manage.svelte license section now visible to administrator_access OR auth_exhibit_kv[id].type === 'shared'. Also added type field to LeadsLocState.auth_exhibit_kv interface.


Recent Changes (2026-04-03)

  • Migrated Leads persisted state to Svelte5 PersistedState: new leads_loc store implemented at src/lib/stores/ae_events_stores__leads.svelte.ts and the store version constant AE_LEADS_LOC_VERSION added to src/lib/stores/store_versions.ts.

  • Payment UI updates:

    • ae_comp__exhibit_payment.svelte now accepts a leads_require_payment prop and honors the event-level event.mod_exhibits_json.leads_require_payment flag 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.ts to seed leads_loc defaults and the expected __version where 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.