- Stabilized the 'liveQuery' observable in 'ae_idaa_comp__event_obj_li_wrapper.svelte' by re-wrapping it in '', ensuring smooth UI updates when switching between local and API results. - Reduced search debounce time to 250ms in '+page.svelte' for a more instantaneous user experience. - Hardened 'Fast Path' local filtering to be more permissive and added debug logging to help diagnose IndexedDB sync issues. - Restricted the visibility of the 'Remote First' toggle to Edit Mode (.edit_mode) for a cleaner standard user interface.
244 lines
9.9 KiB
Svelte
244 lines
9.9 KiB
Svelte
<script lang="ts">
|
|
interface Props {
|
|
/** @type {import('./$types').PageData} */
|
|
data: any;
|
|
}
|
|
let { data }: Props = $props();
|
|
|
|
let log_lvl: number = $state(1);
|
|
|
|
// *** Import Svelte specific
|
|
import { page } from '$app/state';
|
|
import { browser } from '$app/environment';
|
|
import { untrack } from 'svelte';
|
|
|
|
// *** Import other supporting libraries
|
|
import { db_events } from '$lib/ae_events/db_events';
|
|
import {
|
|
idaa_loc,
|
|
idaa_sess,
|
|
idaa_slct,
|
|
idaa_trig
|
|
} from '$lib/stores/ae_idaa_stores';
|
|
import {
|
|
ae_loc,
|
|
ae_api
|
|
} from '$lib/stores/ae_stores';
|
|
import { events_func } from '$lib/ae_events_functions';
|
|
|
|
import Element_data_store from '$lib/elements/element_data_store_v2.svelte';
|
|
import Comp__event_obj_qry from './ae_idaa_comp__event_obj_qry.svelte';
|
|
import Comp__event_obj_li_wrapper from './ae_idaa_comp__event_obj_li_wrapper.svelte';
|
|
|
|
if (browser) {
|
|
$idaa_slct.event_id = null;
|
|
window.parent.postMessage({ event_id: null }, '*');
|
|
|
|
// Use versioning instead of boolean to avoid loops
|
|
if ($idaa_loc.recovery_meetings.search_version === undefined) {
|
|
$idaa_loc.recovery_meetings.search_version = 0;
|
|
}
|
|
$idaa_loc.recovery_meetings.search_version++;
|
|
}
|
|
|
|
let event_id_random_li: Array<string> = $state([]);
|
|
let search_debounce_timer: any = null;
|
|
let last_search_id = 0;
|
|
|
|
// Standardized Reactive Search Pattern (Aether UI V3)
|
|
$effect(() => {
|
|
// 1. Reactive Dependencies
|
|
// Track filters and the search version (trigger)
|
|
const qry_params = {
|
|
v: $idaa_loc.recovery_meetings.search_version,
|
|
str: $idaa_loc.recovery_meetings.qry__fulltext_str,
|
|
phys: $idaa_loc.recovery_meetings.qry__physical,
|
|
virt: $idaa_loc.recovery_meetings.qry__virtual,
|
|
type: $idaa_loc.recovery_meetings.qry__type,
|
|
limit: $idaa_loc.recovery_meetings.qry__limit,
|
|
order: $idaa_loc.recovery_meetings.qry__order_by,
|
|
remote: $idaa_loc.recovery_meetings.qry__remote_first,
|
|
account: $ae_loc.account_id
|
|
};
|
|
|
|
// 2. Debounce Logic
|
|
if (search_debounce_timer) clearTimeout(search_debounce_timer);
|
|
search_debounce_timer = setTimeout(() => {
|
|
// 3. Execution (Untracked to prevent loops)
|
|
untrack(() => {
|
|
handle_search_refresh();
|
|
});
|
|
}, 250);
|
|
|
|
return () => {
|
|
if (search_debounce_timer) clearTimeout(search_debounce_timer);
|
|
};
|
|
});
|
|
|
|
async function handle_search_refresh() {
|
|
const current_search_id = ++last_search_id;
|
|
const account_id = $ae_loc.account_id;
|
|
const remote_first = $idaa_loc.recovery_meetings.qry__remote_first;
|
|
|
|
if (log_lvl) console.log(`[Search #${current_search_id}] Refreshing recovery meetings (remote_first=${remote_first}) for account: ${account_id}...`);
|
|
|
|
$idaa_sess.recovery_meetings.qry__status = 'loading';
|
|
|
|
// If remote first, clear immediately to show fresh state
|
|
if (remote_first) {
|
|
event_id_random_li = [];
|
|
}
|
|
|
|
// Snapshot current params to ensure Fast Path matches revalidation
|
|
const qry_str = ($idaa_loc.recovery_meetings.qry__fulltext_str ?? '').toLowerCase().trim();
|
|
const qry_physical = $idaa_loc.recovery_meetings.qry__physical;
|
|
const qry_virtual = $idaa_loc.recovery_meetings.qry__virtual;
|
|
const qry_type = $idaa_loc.recovery_meetings.qry__type;
|
|
|
|
let local_ids: string[] = [];
|
|
|
|
// 1. FAST PATH: Local IDB Search (SWR Pattern) - Skip if Remote First
|
|
if (!remote_first) {
|
|
try {
|
|
if (account_id) {
|
|
let local_results = await db_events.event
|
|
.filter(ev => {
|
|
// Resilient account check: match either account_id or account_id_random
|
|
const acct_match = ev.account_id === account_id || ev.account_id_random === account_id;
|
|
if (!acct_match) return false;
|
|
|
|
if (qry_type && ev.type !== qry_type) return false;
|
|
if (qry_physical || qry_virtual) {
|
|
let match = false;
|
|
if (qry_physical && ev.physical) match = true;
|
|
if (qry_virtual && ev.virtual) match = true;
|
|
if (!match) return false;
|
|
}
|
|
if (qry_str) {
|
|
const name = (ev.name ?? '').toLowerCase();
|
|
const desc = (ev.description ?? '').toLowerCase();
|
|
const loc = (ev.location_text ?? '').toLowerCase();
|
|
return name.includes(qry_str) || desc.includes(qry_str) || loc.includes(qry_str);
|
|
}
|
|
return true;
|
|
})
|
|
.toArray();
|
|
|
|
// Sort local results
|
|
if ($idaa_loc.recovery_meetings.qry__order_by === 'name') {
|
|
local_results.sort((a, b) => (a.name ?? '').localeCompare(b.name ?? ''));
|
|
} else {
|
|
local_results.sort((a, b) => {
|
|
const dateA = a.updated_on ? new Date(a.updated_on).getTime() : 0;
|
|
const dateB = b.updated_on ? new Date(b.updated_on).getTime() : 0;
|
|
return dateB - dateA;
|
|
});
|
|
}
|
|
|
|
local_ids = local_results.map(e => e.id || e.event_id_random).filter(Boolean);
|
|
|
|
// Update UI immediately with local results
|
|
if (current_search_id === last_search_id) {
|
|
if (log_lvl) console.log(`[Search #${current_search_id}] Fast Path complete. Found ${local_ids.length} items locally.`);
|
|
event_id_random_li = local_ids;
|
|
// If we found results locally, we can mark as done to stop spinning,
|
|
// revalidation will still finish in background
|
|
if (local_ids.length > 0) {
|
|
$idaa_sess.recovery_meetings.qry__status = 'done';
|
|
}
|
|
}
|
|
}
|
|
} catch (e) {
|
|
if (log_lvl) console.warn('Fast Path failed, waiting for API...', e);
|
|
}
|
|
}
|
|
|
|
// 2. REVALIDATE: Slow API Request
|
|
try {
|
|
const results = await events_func.search__event({
|
|
api_cfg: $ae_api,
|
|
for_obj_type: 'account',
|
|
for_obj_id: account_id,
|
|
qry_conference: false,
|
|
qry_physical: qry_physical,
|
|
qry_virtual: qry_virtual,
|
|
qry_type: qry_type,
|
|
qry_str: qry_str || null,
|
|
enabled: $idaa_loc.recovery_meetings.qry__enabled,
|
|
hidden: $idaa_loc.recovery_meetings.qry__hidden,
|
|
limit: $idaa_loc.recovery_meetings.qry__limit,
|
|
order_by_li: $idaa_loc.recovery_meetings.qry__order_by_li,
|
|
log_lvl: 0
|
|
});
|
|
|
|
if (current_search_id === last_search_id) {
|
|
const api_results = results || [];
|
|
const api_ids = api_results.map((e: any) => e.id || e.event_id_random).filter(Boolean);
|
|
|
|
// If API returns 0 but local search found broad results, protect the UI
|
|
if (api_ids.length === 0 && local_ids.length > 0 && !remote_first && !qry_str) {
|
|
if (log_lvl) console.warn(`[Search #${current_search_id}] Revalidation returned 0. Preserving cache.`);
|
|
$idaa_sess.recovery_meetings.qry__status = 'done';
|
|
return;
|
|
}
|
|
|
|
$idaa_slct.event_obj_li = api_results;
|
|
event_id_random_li = api_ids;
|
|
$idaa_sess.recovery_meetings.qry__status = 'done';
|
|
if (log_lvl) console.log(`[Search #${current_search_id}] Revalidation Complete. Found ${api_ids.length} items.`);
|
|
}
|
|
} catch (error) {
|
|
if (current_search_id === last_search_id) {
|
|
console.error('Revalidation failed:', error);
|
|
$idaa_sess.recovery_meetings.qry__status = 'error';
|
|
if (event_id_random_li.length === 0 && local_ids.length > 0) {
|
|
event_id_random_li = local_ids;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (browser) {
|
|
console.log('Browser environment ready.');
|
|
window.parent.postMessage({ event_id: $idaa_slct?.event_id ?? null }, '*');
|
|
}
|
|
</script>
|
|
|
|
<svelte:head>
|
|
<title>
|
|
IDAA Recovery Meetings - Novi - {$ae_loc?.title}
|
|
</title>
|
|
</svelte:head>
|
|
|
|
<Comp__event_obj_qry />
|
|
|
|
<Element_data_store
|
|
ds_code="recovery_meetings_info"
|
|
ds_type="html"
|
|
class_li="rounded-lg preset-outlined-surface-200-800 m-auto p-2 space-y-2 w-full max-w-xl"
|
|
show_edit_btn={true}
|
|
/>
|
|
|
|
{#if Array.isArray(event_id_random_li) && event_id_random_li.length}
|
|
<Comp__event_obj_li_wrapper
|
|
{event_id_random_li}
|
|
link_to_type={'account'}
|
|
link_to_id={$ae_loc.account_id}
|
|
limit={$idaa_loc.recovery_meetings.qry__limit}
|
|
{log_lvl}
|
|
/>
|
|
{:else}
|
|
<div class="space-y-2">
|
|
{#if $idaa_sess.recovery_meetings.qry__status === 'loading'}
|
|
<div class="ae_highlight ae_padding_md ae_row ae_flex_justify_center">
|
|
<span class="fas fa-spinner fa-spin m-1"></span>
|
|
Searching...
|
|
</div>
|
|
{:else}
|
|
<div class="ae_highlight ae_padding_md ae_row ae_flex_justify_center">
|
|
No recovery meetings found matching your criteria.
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
{/if}
|