From bd39fd3061bc3764f29a26d1f879b930b0e71e6c Mon Sep 17 00:00:00 2001 From: Scott Idem Date: Wed, 4 Feb 2026 19:32:17 -0500 Subject: [PATCH] Restore Event Session search stability and advance platform-wide string ID standardization - Restored Event Session search by standardizing on 'event_id' for Dexie queries and implementing dual-layer filtering (local + API guard) to prevent broad results from clobbering filtered views. - Advanced String-Only ID Standardization (Phase 2) by updating generic object processors across all event library modules to support both base IDs and legacy '_random' variants. - Refactored Event Presenter and Presentation components to support standardized '_id_li' props while maintaining backward compatibility. - Standardized common helper identifiers to snake_case (e.g., 'prevent_default') in the Events module. - Verified Staff and Poster email notification logic in the Bulletin Board module. - Updated .gitignore and cleaned up test artifacts. --- .gitignore | 4 +- GEMINI.md | 78 ++++++++++++++++--- TODO.md | 8 ++ .../ae_events/ae_events__event_location.ts | 2 + .../ae_events__event_presentation.ts | 2 + .../ae_events/ae_events__event_presenter.ts | 2 + src/lib/ae_events/ae_events__event_session.ts | 8 ++ .../events/[event_id]/(leads)/README.md | 2 +- ...omp__event_presenter_obj_li_wrapper.svelte | 10 ++- src/routes/events/[event_id]/+page.svelte | 42 ++++++---- .../ae_comp__event_presentation_obj_li.svelte | 4 +- test-results/.last-run.json | 4 - 12 files changed, 134 insertions(+), 32 deletions(-) delete mode 100644 test-results/.last-run.json diff --git a/.gitignore b/.gitignore index e5ee0583..4eb799f8 100644 --- a/.gitignore +++ b/.gitignore @@ -30,4 +30,6 @@ backups/ # Temporary files tmp/ temp/ -*.kate-swp \ No newline at end of file +*.kate-swp +test_results +test-results \ No newline at end of file diff --git a/GEMINI.md b/GEMINI.md index cb4bd9a9..88000bf9 100644 --- a/GEMINI.md +++ b/GEMINI.md @@ -123,7 +123,15 @@ -- **Last Action:** Committed a platform-wide refactor of button types and file operation security (81 files). + + + + +- **Last Action:** Restored Event Session search functionality and standardized ID mapping across core event library modules. + + + + @@ -131,15 +139,31 @@ - - **Multi-Line Attribute Hazards:** Bulk regex tools often miss or duplicate attributes in multi-line Svelte tags; surgical manual edits or specialized parsers are required for clean tag handling. - - **V3 Action Pattern:** Authenticated file downloads now strictly require the `key=${account_id}` parameter via the `/v3/action/` endpoint path. + + - **Dual-Layer Filtering:** API search revalidation can clobber correctly filtered local results if the API has a minimum character limit (e.g., 3 chars); implementing a backup client-side filter on the API response is essential for "sticky" search results. + + + + + + + + - **ID Standardization:** Phasing out the `_random` suffix requires updating the generic object processors to check both the base `[obj_type]_id` and the `_random` variant to ensure Dexie primary keys are correctly mapped. + + + + + + + + - **Svelte 5 Untrack:** `untrack()` is critical inside `$effect` when reading reactive state that shouldn't trigger a re-run, but must be used carefully to avoid stale data in complex search logic. + - - **Svelte 5 Effects:** Using `$effect` to synchronize props to local state requires `untrack` to prevent circular reactivity loops. @@ -147,19 +171,47 @@ - - **Standardized Button Typing:** Applied `type="button"` vs `type="submit"` across Journals, Events, and IDAA modules to prevent accidental form submissions. - - **Hardened File Operations:** Successfully migrated the entire platform to the authenticated V3 Action API for all file downloads and deletes. + + - **Event Session Search Restoration:** Stabilized the Session Search module by standardizing on `event_id` for queries and implementing local result protection against broad API responses. - - **Compiler Error Sweep:** Resolved over 40 critical compiler errors, including duplicate attributes in complex components and missing imports. - - **DX Optimization:** Verified the "Safe Workflow Strategy" (Edit -> Lint -> Check) as an effective baseline for large-scale refactoring. + + - **Platform-Wide ID Hardening:** Updated `_process_generic_props` in all event library files (`location.ts`, `presentation.ts`, `presenter.ts`, `session.ts`, etc.) to support the transition away from string-only IDs. + + + + + + + + - **Bulletin Board Verification:** Confirmed email notification logic for Staff and Posters in the IDAA BB module. + + + + + + + + - **Prop Standardization:** Refactored `presenter` and `presentation` components to use standardized `_id_li` properties. + + + + + + + + - **Code Cleanup:** Standardized common helper identifiers to snake_case (`prevent_default`). + + + + @@ -167,7 +219,15 @@ -- **Immediate Next Step:** Resolve remaining type mismatches (~160) or begin modular refactoring of large components (>800 lines). + + + + +- **Immediate Next Step:** Continue ID standardization across remaining modules or proceed with IDAA Archives/Recovery Meetings verification. + + + + --- diff --git a/TODO.md b/TODO.md index 0644fa69..1f1b3ff5 100644 --- a/TODO.md +++ b/TODO.md @@ -84,4 +84,12 @@ This is a list of tasks to be completed before the next event/show/conference. - [x] **Compiler Error Resolution:** Fixed 40+ critical errors including duplicate attributes in multi-line tags and missing `untrack` imports in Svelte 5 effects. - [x] **Hosted Files Modernization:** Standardized all download, link, and upload components across the platform. +## Recent Accomplishments (Feb 4, 2026) + +- [x] **Event Session Search Restoration:** Restored functionality by standardizing on `event_id` for Dexie queries and implementing dual-layer filtering (local + API revalidation guard) to prevent broad results from clobbering filtered views. +- [x] **String-Only ID Standardization (Phase 2):** Updated generic object processors (`_process_generic_props`) across all event-related library modules to support string IDs without the `_random` suffix. +- [x] **Standardized Component Props:** Refactored `ae_comp__event_presenter_obj_li_wrapper.svelte` and `ae_comp__event_presentation_obj_li.svelte` to support standardized `_id_li` properties while maintaining backward compatibility. +- [x] **IDAA Bulletin Board Audit:** Verified that staff and poster email notifications are correctly triggered during post/comment creation. +- [x] **Snake_Case Cleanup:** Standardized common helper identifiers (e.g., `prevent_default`) to match project naming conventions. + --- \ No newline at end of file diff --git a/src/lib/ae_events/ae_events__event_location.ts b/src/lib/ae_events/ae_events__event_location.ts index 9548d875..eade72b8 100644 --- a/src/lib/ae_events/ae_events__event_location.ts +++ b/src/lib/ae_events/ae_events__event_location.ts @@ -290,7 +290,9 @@ async function _process_generic_props>({ obj_li, o } } const randomIdKey = `${obj_type}_id_random`; + const baseIdKey = `${obj_type}_id`; if (processed_obj[randomIdKey]) (processed_obj as any).id = processed_obj[randomIdKey]; + else if (processed_obj[baseIdKey]) (processed_obj as any).id = processed_obj[baseIdKey]; const group = processed_obj.group ?? '0'; const priority = processed_obj.priority ? 1 : 0; const sort = processed_obj.sort ?? '0'; diff --git a/src/lib/ae_events/ae_events__event_presentation.ts b/src/lib/ae_events/ae_events__event_presentation.ts index 6d268346..29639eec 100644 --- a/src/lib/ae_events/ae_events__event_presentation.ts +++ b/src/lib/ae_events/ae_events__event_presentation.ts @@ -313,7 +313,9 @@ async function _process_generic_props>({ obj_li, o } } const randomIdKey = `${obj_type}_id_random`; + const baseIdKey = `${obj_type}_id`; if (processed_obj[randomIdKey]) (processed_obj as any).id = processed_obj[randomIdKey]; + else if (processed_obj[baseIdKey]) (processed_obj as any).id = processed_obj[baseIdKey]; const group = processed_obj.group ?? '0'; const priority = processed_obj.priority ? 1 : 0; const sort = processed_obj.sort ?? '0'; diff --git a/src/lib/ae_events/ae_events__event_presenter.ts b/src/lib/ae_events/ae_events__event_presenter.ts index 4099f07d..6f91b1de 100644 --- a/src/lib/ae_events/ae_events__event_presenter.ts +++ b/src/lib/ae_events/ae_events__event_presenter.ts @@ -382,7 +382,9 @@ async function _process_generic_props>({ obj_li, o } } const randomIdKey = `${obj_type}_id_random`; + const baseIdKey = `${obj_type}_id`; if (processed_obj[randomIdKey]) (processed_obj as any).id = processed_obj[randomIdKey]; + else if (processed_obj[baseIdKey]) (processed_obj as any).id = processed_obj[baseIdKey]; const group = processed_obj.group ?? '0'; const priority = processed_obj.priority ? 1 : 0; const sort = processed_obj.sort ?? '0'; diff --git a/src/lib/ae_events/ae_events__event_session.ts b/src/lib/ae_events/ae_events__event_session.ts index 46f117c7..eea880fd 100644 --- a/src/lib/ae_events/ae_events__event_session.ts +++ b/src/lib/ae_events/ae_events__event_session.ts @@ -294,6 +294,11 @@ export async function search__event_session({ else if (enabled === 'not_enabled') search_query.and.push({ field: 'enable', op: 'eq', value: 0 }); 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 (location_name) { + search_query.and.push({ field: 'event_location_name', op: 'eq', value: location_name }); + } + const result_li = await api.search_ae_obj_v3({ api_cfg, obj_type: 'event_session', search_query, order_by_li, view, limit, offset, log_lvl }); if (result_li) { const processed = await process_ae_obj__event_session_props({ obj_li: result_li, log_lvl }); @@ -330,7 +335,10 @@ async function _process_generic_props>({ obj_li, o } } const randomIdKey = `${obj_type}_id_random`; + const baseIdKey = `${obj_type}_id`; if (processed_obj[randomIdKey]) (processed_obj as any).id = processed_obj[randomIdKey]; + else if (processed_obj[baseIdKey]) (processed_obj as any).id = processed_obj[baseIdKey]; + const group = processed_obj.group ?? '0'; const priority = processed_obj.priority ? 1 : 0; const sort = processed_obj.sort ?? '0'; diff --git a/src/routes/events/[event_id]/(leads)/README.md b/src/routes/events/[event_id]/(leads)/README.md index 5a186dce..39549bda 100644 --- a/src/routes/events/[event_id]/(leads)/README.md +++ b/src/routes/events/[event_id]/(leads)/README.md @@ -16,7 +16,7 @@ This module provides a comprehensive solution for event exhibitors to capture an ## Data Model -The core data structures managed by this module are `Exhibit` and `Exhibit_tracking`. +The core data structures managed by this module are `Exhibit` and `Exhibit_tracking`. The actual DB table name is event_person_tracking. It was originally intended for generalized "tracking" of a person at an event. This would include other things like session attendance tracking, in addition to exhibitor lead retrieval. However, the initial implementation is focused solely on the lead retrieval use case for exhibitors. ### Exhibit diff --git a/src/routes/events/[event_id]/(pres_mgmt)/presenter/ae_comp__event_presenter_obj_li_wrapper.svelte b/src/routes/events/[event_id]/(pres_mgmt)/presenter/ae_comp__event_presenter_obj_li_wrapper.svelte index 86476711..240d5083 100644 --- a/src/routes/events/[event_id]/(pres_mgmt)/presenter/ae_comp__event_presenter_obj_li_wrapper.svelte +++ b/src/routes/events/[event_id]/(pres_mgmt)/presenter/ae_comp__event_presenter_obj_li_wrapper.svelte @@ -2,6 +2,7 @@ interface Props { container_class_li?: string | Array; display_mode?: string; // 'default', 'compact', 'minimal', 'launcher' + event_presenter_id_li?: Array; event_presenter_id_random_li?: Array; link_to_type: string; link_to_id: string; @@ -12,6 +13,7 @@ let { container_class_li = [], display_mode = 'default', + event_presenter_id_li = [], event_presenter_id_random_li = [], link_to_type, link_to_id, @@ -33,8 +35,8 @@ // let ae_tmp: key_val = {}; // let ae_triggers: key_val = {}; - let dq__where_type_id_val: string = `${link_to_type}_id_random`; - let dq__where_eq_id_val: string = link_to_id; + let dq__where_type_id_val = $derived(`${link_to_type}_id`); + let dq__where_eq_id_val = $derived(link_to_id); // *** Functions and Logic let lq__event_presenter_obj_li = $derived( @@ -45,6 +47,10 @@ .equals(dq__where_eq_id_val) .sortBy('full_name'); + return results; + } else if (event_presenter_id_li.length > 0) { + let results = await db_events.presenter.bulkGet(event_presenter_id_li); + return results; } else if (event_presenter_id_random_li.length > 0) { let results = await db_events.presenter.bulkGet(event_presenter_id_random_li); diff --git a/src/routes/events/[event_id]/+page.svelte b/src/routes/events/[event_id]/+page.svelte index ddeea8a0..ca44d7ee 100644 --- a/src/routes/events/[event_id]/+page.svelte +++ b/src/routes/events/[event_id]/+page.svelte @@ -91,7 +91,7 @@ if (event_id && !$events_loc.pres_mgmt.fulltext_search_qry_str && !$events_loc.pres_mgmt.location_name_qry_str) { if (log_lvl) console.log(`Session Page LQ: Fallback search for event: ${event_id}`); return await db_events.session - .where('event_id_random') + .where('event_id') .equals(event_id) .limit(50) .sortBy('name'); @@ -124,9 +124,7 @@ const params = search_params; if (search_debounce_timer) clearTimeout(search_debounce_timer); search_debounce_timer = setTimeout(() => { - untrack(() => { - handle_search_refresh(params); - }); + handle_search_refresh(params); }, 300); return () => { if (search_debounce_timer) clearTimeout(search_debounce_timer); @@ -157,7 +155,7 @@ try { if (event_id) { let local_results = await db_events.session - .where('event_id_random') + .where('event_id') .equals(event_id) .filter(session => { if (location_name && session.event_location_name !== location_name) return false; @@ -180,7 +178,7 @@ .toArray(); local_results.sort((a, b) => (a.name ?? '').localeCompare(b.name ?? '')); - const local_ids = local_results.map(s => s.id || s.event_session_id_random).filter(Boolean); + const local_ids = local_results.map(s => s.id || s.event_session_id).filter(Boolean); if (current_search_id === last_search_id) { if (log_lvl) console.log(`[Session Search #${current_search_id}] Fast Path found ${local_ids.length} items locally.`); @@ -215,8 +213,26 @@ }); if (current_search_id === last_search_id) { - const api_results = results || []; - const api_ids = api_results.map((s: any) => s.id || s.event_session_id_random).filter(Boolean); + let api_results = results || []; + + // Client-side Filter Guard: Ensure API results match local criteria (Backup filter) + api_results = api_results.filter(session => { + if (location_name && session.event_location_name !== location_name) return false; + if (qry_str) { + const name = (session.name ?? '').toLowerCase(); + const code = (session.code ?? '').toLowerCase(); + const description = (session.description ?? '').toLowerCase(); + const qry_string = (session.default_qry_str ?? '').toLowerCase(); + const match = name.includes(qry_str) || + code.includes(qry_str) || + description.includes(qry_str) || + qry_string.includes(qry_str); + if (!match) return false; + } + return true; + }); + + const api_ids = api_results.map((s: any) => s.id || s.event_session_id).filter(Boolean); untrack(() => { $events_slct.event_session_obj_li = api_results; @@ -251,7 +267,7 @@ $events_loc.pres_mgmt.search_version++; } - function preventDefault(fn: (event: T) => void) { + function prevent_default(fn: (event: T) => void) { return function (event: T) { event.preventDefault(); fn(event); @@ -325,7 +341,7 @@ {#if !$events_loc.pres_mgmt.show_content__event_view || $events_loc.pres_mgmt.show_content__event_view == 'default'}
handle_search_trigger())} + onsubmit={prevent_default(() => handle_search_trigger())} autocomplete="off" class="form grow flex flex-row flex-wrap gap-1 justify-center items-center w-full" > @@ -487,9 +503,9 @@
diff --git a/src/routes/events/ae_comp__event_presentation_obj_li.svelte b/src/routes/events/ae_comp__event_presentation_obj_li.svelte index 229a96f3..208e8185 100644 --- a/src/routes/events/ae_comp__event_presentation_obj_li.svelte +++ b/src/routes/events/ae_comp__event_presentation_obj_li.svelte @@ -463,8 +463,8 @@ {#if event_presentation_obj?.event_presentation_id} {/if} diff --git a/test-results/.last-run.json b/test-results/.last-run.json deleted file mode 100644 index 8460acf9..00000000 --- a/test-results/.last-run.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "status": "failed", - "failedTests": [] -}