diff --git a/src/routes/events/[event_id]/(leads)/README.md b/src/routes/events/[event_id]/(leads)/README.md index c9437df1..8857138a 100644 --- a/src/routes/events/[event_id]/(leads)/README.md +++ b/src/routes/events/[event_id]/(leads)/README.md @@ -109,13 +109,22 @@ Two scan modes (toggled per exhibit): ## Known Gaps / Not Yet Implemented -- **`allow_tracking` gate** — spec says badge must have `allow_tracking = true` before a lead can - be added. Neither the QR scanner nor manual search enforce this yet. - **Payment / Stripe** — `ae_comp__exhibit_payment.svelte` is a stub. The payment tab can be hidden via the "Show Payment Tab" toggle in the Manage tab's App Settings. -- **Export endpoint** — `download_export__event_exhibit_tracking` calls a legacy V1 endpoint - (`/event/exhibit/{id}/tracking/export`). Verify it's live on the backend before demoing export. -- **PWA install prompt** — spec calls for nudging exhibitors to install as a PWA on the Start tab. +- **License management access for shared-passcode users** — spec says booth staff signed in via + the shared passcode should be able to manage licenses. Currently gated behind + `$ae_loc.administrator_access` only (in `ae_tab__manage.svelte`). + +## Implemented (previously listed as gaps) + +- **`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 endpoint** — `download_export__event_exhibit_tracking` uses V3 action endpoint + `/v3/action/event_exhibit/{id}/tracking_export`. Gated on `leads_api_access === true`. +- **PWA install prompt** — `element_pwa_install_prompt.svelte` placed on the Start tab. + Handles Chrome/Android native install and iOS Safari manual instructions. --- @@ -123,4 +132,4 @@ Two scan modes (toggled per exhibit): `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_functions.ts`. +Both are aggregated into `events_func` via `src/lib/ae_events/ae_events_functions.ts`. diff --git a/src/routes/events/[event_id]/(leads)/leads/exhibit/[exhibit_id]/+page.svelte b/src/routes/events/[event_id]/(leads)/leads/exhibit/[exhibit_id]/+page.svelte index d2ba69ee..88ce0f8e 100644 --- a/src/routes/events/[event_id]/(leads)/leads/exhibit/[exhibit_id]/+page.svelte +++ b/src/routes/events/[event_id]/(leads)/leads/exhibit/[exhibit_id]/+page.svelte @@ -33,6 +33,8 @@ $events_loc.leads.tracking__qry__sort_order = 'created_desc'; if (typeof $events_loc.leads.refresh_interval_sec === 'undefined') $events_loc.leads.refresh_interval_sec = 25; + if (typeof $events_loc.leads.show_hidden === 'undefined') + $events_loc.leads.show_hidden = false; } // --- Sign-In State (Derived) --- @@ -74,9 +76,12 @@ // (API or IDB), it MUST match the selected licensee. let filtered_lead_li = $derived.by(() => { const licensee_filter = search_params.licensee_email; - if (licensee_filter === 'all') return raw_lead_li; - + const show_hidden = search_params.show_hidden; + return raw_lead_li.filter(lead => { + // Exclude hidden leads unless show_hidden is toggled on + if (!show_hidden && lead.hide) return false; + if (licensee_filter === 'all') return true; const capturer = lead.external_person_id || lead.group; return capturer === licensee_filter; }); @@ -138,7 +143,8 @@ sort: $events_loc.leads.tracking__qry__sort_order, licensee_email: licensee_email, exhibit_id: page.params.exhibit_id, - remote_first: $events_loc.leads.tracking__qry__remote_first + remote_first: $events_loc.leads.tracking__qry__remote_first, + show_hidden: $events_loc.leads.show_hidden ?? false }; }); @@ -205,7 +211,10 @@ .where('event_exhibit_id') .equals(target_exhibit_id) .filter((tracking) => { - // 1. Licensee Email Filter + // 1. Hide filter — exclude hidden records unless show_hidden is on + if (!params.show_hidden && tracking.hide) return false; + + // 2. Licensee Email Filter if (target_licensee_email !== 'all') { if (tracking.external_person_id !== target_licensee_email) return false; } @@ -313,6 +322,7 @@ event_exhibit_id: q_exhibit_id, fulltext_search_qry_str: qry_str || null, qry_external_person_id: q_licensee_email, + hidden: params.show_hidden ? 'all' : 'not_hidden', order_by_li, limit: 150 }); diff --git a/src/routes/events/[event_id]/(leads)/leads/exhibit/[exhibit_id]/ae_comp__exhibit_tracking_search.svelte b/src/routes/events/[event_id]/(leads)/leads/exhibit/[exhibit_id]/ae_comp__exhibit_tracking_search.svelte index a0bc1613..6b228bb9 100644 --- a/src/routes/events/[event_id]/(leads)/leads/exhibit/[exhibit_id]/ae_comp__exhibit_tracking_search.svelte +++ b/src/routes/events/[event_id]/(leads)/leads/exhibit/[exhibit_id]/ae_comp__exhibit_tracking_search.svelte @@ -7,7 +7,7 @@ let { exhibit_id, log_lvl = 0 }: Props = $props(); // *** Import other supporting libraries - import { Library, LoaderCircle, RemoveFormatting, Search } from '@lucide/svelte'; + import { Eye, EyeOff, Library, LoaderCircle, RemoveFormatting, Search } from '@lucide/svelte'; import { untrack } from 'svelte'; import { ae_loc } from '$lib/stores/ae_stores'; import { events_loc, events_sess } from '$lib/stores/ae_events_stores'; @@ -165,6 +165,27 @@