feat(leads): implement reactive search for exhibitors and lead tracking

- Implemented V3-style reactive search (Local Cache -> Remote Revalidation) for exhibitors.
- Standardized search fields to 'name' for Exhibits and 'event_badge_full_name' for Lead Tracking.
- Refactored Leads UI with standardized search components and grid layout.
- Updated event routing to exclusively use string-based IDs (Triple-ID pattern).
- Hardened 'ae_EventSession' type definitions to handle null values from V3 API/Dexie.
This commit is contained in:
Scott Idem
2026-01-28 12:05:42 -05:00
parent f145b4dcac
commit cc3a6b0f59
11 changed files with 937 additions and 42 deletions

View File

@@ -104,7 +104,7 @@ async function _process_generic_props<T extends Record<string, any>>({
}
const randomIdKey = `${obj_type}_id_random`;
if (processed_obj[randomIdKey]) {
(processed_obj as any).id = processed_obj[randomIdKey];
(processed_obj as any).id = String(processed_obj[randomIdKey]);
}
const group = processed_obj.group ?? '0';
@@ -514,4 +514,179 @@ export async function download_export__event_exhibit_tracking({
auto_download: true,
log_lvl
});
}
// Updated 2026-01-28 to V3
export async function search__exhibit_tracking({
api_cfg,
event_exhibit_id,
fulltext_search_qry_str = null,
enabled = 'enabled',
hidden = 'all',
order_by_li = { created_on: 'DESC' },
limit = 100,
offset = 0,
log_lvl = 0
}: {
api_cfg: any;
event_exhibit_id: string;
fulltext_search_qry_str?: string | null;
enabled?: 'enabled' | 'all' | 'not_enabled';
hidden?: 'hidden' | 'all' | 'not_hidden';
order_by_li?: any;
limit?: number;
offset?: number;
log_lvl?: number;
}): Promise<ae_EventExhibitTracking[]> {
if (log_lvl) {
console.log(`*** search__exhibit_tracking() *** exhibit_id=${event_exhibit_id} ft=${fulltext_search_qry_str}`);
}
const search_query: any = {
q: '',
and: []
};
const params: key_val = {};
if (fulltext_search_qry_str && fulltext_search_qry_str.trim().length > 0) {
const qry = fulltext_search_qry_str.trim();
// Search across badge name and notes
search_query.and.push({ field: 'event_badge_full_name', op: 'like', value: `%${qry}%` });
params['lk_qry'] = { event_badge_full_name: qry };
}
if (enabled === 'enabled') search_query.and.push({ field: 'enable', op: 'eq', value: 1 });
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 });
try {
const result_get = await api.search_ae_obj_v3({
api_cfg,
obj_type: 'event_exhibit_tracking',
for_obj_type: 'event_exhibit',
for_obj_id: event_exhibit_id,
search_query,
params,
order_by_li,
limit,
offset,
log_lvl
});
let result_li: ae_EventExhibitTracking[] = [];
if (Array.isArray(result_get)) {
result_li = result_get;
} else if (result_get?.data && Array.isArray(result_get.data)) {
result_li = result_get.data;
}
if (result_li.length > 0) {
const processed_obj_li = await process_ae_obj__exhibit_tracking_props({
obj_li: result_li,
log_lvl
});
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
table_name: 'exhibit_tracking',
obj_li: processed_obj_li,
properties_to_save: properties_to_save_exhibit_tracking,
log_lvl
});
return processed_obj_li;
}
} catch (error: any) {
console.error('search__exhibit_tracking V3 Request failed.', error);
}
return [];
}
// Updated 2026-01-28 to V3
export async function search__exhibit({
api_cfg,
event_id,
fulltext_search_qry_str = null,
enabled = 'enabled',
hidden = 'not_hidden',
order_by_li = { name: 'ASC' },
limit = 100,
offset = 0,
log_lvl = 0
}: {
api_cfg: any;
event_id: string;
fulltext_search_qry_str?: string | null;
enabled?: 'enabled' | 'all' | 'not_enabled';
hidden?: 'hidden' | 'all' | 'not_hidden';
order_by_li?: any;
limit?: number;
offset?: number;
log_lvl?: number;
}): Promise<ae_EventExhibit[]> {
if (log_lvl) {
console.log(`*** search__exhibit() *** event_id=${event_id} ft=${fulltext_search_qry_str}`);
}
const search_query: any = {
q: '',
and: []
};
const params: key_val = {};
if (fulltext_search_qry_str && fulltext_search_qry_str.trim().length > 0) {
const qry = fulltext_search_qry_str.trim();
search_query.and.push({ field: 'name', op: 'like', value: `%${qry}%` });
params['lk_qry'] = { name: qry };
}
if (enabled === 'enabled') search_query.and.push({ field: 'enable', op: 'eq', value: 1 });
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 });
try {
const result_get = await api.search_ae_obj_v3({
api_cfg,
obj_type: 'event_exhibit',
for_obj_type: 'event',
for_obj_id: event_id,
search_query,
params,
order_by_li,
limit,
offset,
log_lvl
});
let result_li: ae_EventExhibit[] = [];
if (Array.isArray(result_get)) {
result_li = result_get;
} else if (result_get?.data && Array.isArray(result_get.data)) {
result_li = result_get.data;
}
if (result_li.length > 0) {
const processed_obj_li = await process_ae_obj__exhibit_props({
obj_li: result_li,
log_lvl
});
await db_save_ae_obj_li__ae_obj({
db_instance: db_events,
table_name: 'exhibit',
obj_li: processed_obj_li,
properties_to_save,
log_lvl
});
return processed_obj_li;
}
} catch (error: any) {
console.error('search__exhibit V3 Request failed.', error);
}
return [];
}