diff --git a/src/lib/ae_events/ae_events__exhibit.ts b/src/lib/ae_events/ae_events__exhibit.ts index 7ec0f34f..42d2de46 100644 --- a/src/lib/ae_events/ae_events__exhibit.ts +++ b/src/lib/ae_events/ae_events__exhibit.ts @@ -362,6 +362,7 @@ export async function search__exhibit({ fulltext_search_qry_str = null, enabled = 'enabled', hidden = 'not_hidden', + priority = 'all', view = 'default', order_by_li = { name: 'ASC' }, limit = 100, @@ -374,6 +375,7 @@ export async function search__exhibit({ fulltext_search_qry_str?: string | null; enabled?: 'enabled' | 'all' | 'not_enabled'; hidden?: 'hidden' | 'all' | 'not_hidden'; + priority?: 'all' | 'priority' | 'not_priority'; view?: string; order_by_li?: any; limit?: number; @@ -398,6 +400,9 @@ export async function search__exhibit({ if (hidden === 'hidden') search_query.and.push({ field: 'hide', op: 'eq', value: 1 }); else if (hidden === 'not_hidden') search_query.and.push({ field: 'hide', op: 'eq', value: 0 }); + if (priority === 'priority') search_query.and.push({ field: 'priority', op: 'eq', value: 1 }); + else if (priority === 'not_priority') search_query.and.push({ field: 'priority', op: 'eq', value: 0 }); + try { const result_li = await api.search_ae_obj_v3({ api_cfg, diff --git a/src/lib/stores/ae_events_stores.ts b/src/lib/stores/ae_events_stores.ts index 7f2291e3..91ecf58b 100644 --- a/src/lib/stores/ae_events_stores.ts +++ b/src/lib/stores/ae_events_stores.ts @@ -477,6 +477,9 @@ const events_session_data_struct: key_val = { submit_status__search: null, // 'searching', 'complete' + last_refresh_time: null as string | null, + next_refresh_countdown: 0, + // The entered_passcode is the exhibit booths shared passcode for staff. This is used to initially access the lead retrieval service. entered_passcode: null, diff --git a/src/routes/events/[event_id]/(leads)/leads/+page.svelte b/src/routes/events/[event_id]/(leads)/leads/+page.svelte index 0287927a..fc668cd9 100644 --- a/src/routes/events/[event_id]/(leads)/leads/+page.svelte +++ b/src/routes/events/[event_id]/(leads)/leads/+page.svelte @@ -85,17 +85,26 @@ const current_search_id = ++last_search_id; const event_id = params.event_id; const remote_first = params.remote_first; + const qry_str = params.str; if (!event_id) return; + // --- Search Constraint: Min 3 characters for non-trusted users --- + if (!$ae_loc.trusted_access && qry_str.length < 3) { + if (log_lvl) console.log('🛑 [Trace] Search string too short for public user.'); + untrack(() => { + exhibit_id_li = []; + $events_sess.leads.submit_status__search = 'idle'; + }); + return; + } + if (log_lvl) console.log(`🔎 [Trace] Exhibit Search #${current_search_id}: START (remote=${remote_first}, event=${event_id}, str=${params.str})`); untrack(() => { $events_sess.leads.submit_status__search = 'searching'; }); - const qry_str = params.str; - // 1. FAST PATH: Local IDB Search if (!remote_first) { try { @@ -103,6 +112,9 @@ .where('event_id') .equals(event_id) .filter((exhibit) => { + // Priority Filter for Public + if (!$ae_loc.manager_access && !exhibit.priority) return false; + if (qry_str) { const name = (exhibit.name ?? '').toLowerCase(); const code = (exhibit.code ?? '').toLowerCase(); @@ -111,6 +123,9 @@ !code.includes(qry_str) ) return false; + } else if (!$ae_loc.trusted_access) { + // Don't show default results to public if no search string + return false; } return true; }) @@ -178,6 +193,7 @@ api_cfg: $ae_api, event_id: event_id, fulltext_search_qry_str: qry_str || null, + priority: $ae_loc.manager_access ? 'all' : 'priority', order_by_li, limit: 100 }); 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 5436804b..06f47720 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 @@ -123,6 +123,8 @@ search_debounce_timer = setTimeout(() => { untrack(() => { handle_search_refresh(params); + // Reset countdown on manual search + $events_sess.leads.next_refresh_countdown = $events_loc.leads.refresh_interval_sec || 25; }); }, 300); return () => { @@ -130,6 +132,25 @@ }; }); + // --- Auto-Refresh Timer Logic --- + $effect(() => { + if (!is_signed_in) return; + + const interval = setInterval(() => { + untrack(() => { + if ($events_sess.leads.next_refresh_countdown > 0) { + $events_sess.leads.next_refresh_countdown--; + } else { + // Trigger refresh + $events_loc.leads.tracking__search_version++; + $events_sess.leads.next_refresh_countdown = $events_loc.leads.refresh_interval_sec || 25; + } + }); + }, 1000); + + return () => clearInterval(interval); + }); + async function handle_search_refresh(params: any) { const qry_key = JSON.stringify(params); if (qry_key === last_executed_key) return; @@ -268,6 +289,7 @@ untrack(() => { tracking_id_li = api_ids; $events_sess.leads.submit_status__search = 'done'; + $events_sess.leads.last_refresh_time = new Date().toISOString(); }); } } catch (error) { diff --git a/src/routes/events/[event_id]/(leads)/leads/exhibit/[exhibit_id]/ae_tab__manage.svelte b/src/routes/events/[event_id]/(leads)/leads/exhibit/[exhibit_id]/ae_tab__manage.svelte index 259e618c..3cd867a4 100644 --- a/src/routes/events/[event_id]/(leads)/leads/exhibit/[exhibit_id]/ae_tab__manage.svelte +++ b/src/routes/events/[event_id]/(leads)/leads/exhibit/[exhibit_id]/ae_tab__manage.svelte @@ -7,7 +7,7 @@ import { liveQuery } from 'dexie'; import { db_events } from '$lib/ae_events/db_events'; import { ae_api, ae_loc } from '$lib/stores/ae_stores'; - import { events_loc } from '$lib/stores/ae_events_stores'; + import { events_loc, events_sess } from '$lib/stores/ae_events_stores'; import { events_func } from '$lib/ae_events_functions'; import Element_ae_crud_v2 from '$lib/elements/element_ae_crud_v2.svelte'; import Comp_exhibit_license_list from './ae_comp__exhibit_license_list.svelte'; @@ -340,9 +340,25 @@
-
Data Synchronization
+
+
Data Synchronization
+
+ + {#if $events_sess.leads.last_refresh_time} + Last: {new Date($events_sess.leads.last_refresh_time).toLocaleTimeString()} + {:else} + Waiting... + {/if} +
+
+
- Refresh Interval (sec) +
+ Refresh Interval (sec) +
+ Next Sync in {$events_sess.leads.next_refresh_countdown}s +
+