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.
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -30,4 +30,6 @@ backups/
|
||||
# Temporary files
|
||||
tmp/
|
||||
temp/
|
||||
*.kate-swp
|
||||
*.kate-swp
|
||||
test_results
|
||||
test-results
|
||||
78
GEMINI.md
78
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.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
8
TODO.md
8
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.
|
||||
|
||||
---
|
||||
@@ -290,7 +290,9 @@ async function _process_generic_props<T extends Record<string, any>>({ 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';
|
||||
|
||||
@@ -313,7 +313,9 @@ async function _process_generic_props<T extends Record<string, any>>({ 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';
|
||||
|
||||
@@ -382,7 +382,9 @@ async function _process_generic_props<T extends Record<string, any>>({ 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';
|
||||
|
||||
@@ -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<T extends Record<string, any>>({ 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';
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
interface Props {
|
||||
container_class_li?: string | Array<string>;
|
||||
display_mode?: string; // 'default', 'compact', 'minimal', 'launcher'
|
||||
event_presenter_id_li?: Array<string>;
|
||||
event_presenter_id_random_li?: Array<string>;
|
||||
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);
|
||||
|
||||
@@ -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<T extends Event>(fn: (event: T) => void) {
|
||||
function prevent_default<T extends Event>(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'}
|
||||
<div class="ae_container_actions">
|
||||
<form
|
||||
onsubmit={preventDefault(() => 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 @@
|
||||
<div class="overflow-x-auto w-max max-w-full">
|
||||
<Element_manage_event_file_li_wrap
|
||||
link_to_type={'event'}
|
||||
link_to_id={$lq__event_obj?.event_id_random}
|
||||
allow_basic={$events_loc.auth__kv.session[$lq__event_obj?.event_id_random]}
|
||||
allow_moderator={$events_loc.auth__kv.session[$lq__event_obj?.event_id_random]}
|
||||
link_to_id={$lq__event_obj?.event_id}
|
||||
allow_basic={$events_loc.auth__kv.session[$lq__event_obj?.event_id]}
|
||||
allow_moderator={$events_loc.auth__kv.session[$lq__event_obj?.event_id]}
|
||||
container_class_li={''}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -463,8 +463,8 @@
|
||||
{#if event_presentation_obj?.event_presentation_id}
|
||||
<Comp_event_presenter_obj_li
|
||||
link_to_type={'event_presentation'}
|
||||
link_to_id={event_presentation_obj?.event_presentation_id}
|
||||
event_presenter_id_random_li={[]}
|
||||
link_to_id={event_presentation_obj.event_presentation_id}
|
||||
event_presenter_id_li={[]}
|
||||
log_lvl={2}
|
||||
></Comp_event_presenter_obj_li>
|
||||
{/if}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"status": "failed",
|
||||
"failedTests": []
|
||||
}
|
||||
Reference in New Issue
Block a user