diff --git a/src/routes/idaa/(idaa)/recovery_meetings/+page.svelte b/src/routes/idaa/(idaa)/recovery_meetings/+page.svelte index bfe3543b..53be306e 100644 --- a/src/routes/idaa/(idaa)/recovery_meetings/+page.svelte +++ b/src/routes/idaa/(idaa)/recovery_meetings/+page.svelte @@ -10,86 +10,63 @@ // *** Import Svelte specific import { page } from '$app/state'; import { browser } from '$app/environment'; - // import { goto, invalidate, pushState, replaceState } from '$app/navigation'; + import { untrack } from 'svelte'; // *** Import other supporting libraries - import { Modal } from 'flowbite-svelte'; - import { liveQuery } from 'dexie'; - - // *** Import Aether specific variables and functions - // import type { key_val } from '$lib/ae_stores'; - import { ae_util } from '$lib/ae_utils/ae_utils'; - import { core_func } from '$lib/ae_core/ae_core_functions'; + import { db_events } from '$lib/ae_events/db_events'; import { idaa_loc, idaa_sess, idaa_slct, - idaa_trig, - idaa_prom + idaa_trig } from '$lib/stores/ae_idaa_stores'; - - import { db_events } from '$lib/ae_events/db_events'; import { - ae_snip, ae_loc, - ae_sess, - ae_api, - ae_trig, - slct, - slct_trigger + 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'; - // import Comp__event_obj_id_edit from './ae_idaa_comp__event_obj_id_edit.svelte'; - // import Comp__event_obj_id_view from './ae_idaa_comp__event_obj_id_view.svelte'; - - // let event_id = page.url.searchParams.get('event_id') ?? null; - // if (!event_id) { - // $idaa_slct.event_id = null; - // } else { - // console.log(`ae Recovery Meetings - [event_id] +page.ts: event_id = `, event_id); - // $idaa_slct.event_id = event_id; - // $idaa_trig.event_id = event_id; - // } if (browser) { - $idaa_slct.event_id = null; // Just in case - let message = { event_id: null }; - window.parent.postMessage(message, '*'); - - $idaa_trig.event_li_qry = true; - // $idaa_trig = 'load__event_obj_li'; + $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 = $state([]); - - // *** Functions and Logic - - // Debounced Search Logic (Refactored 2026-01-27) let search_debounce_timer: any = null; let last_search_id = 0; + // Standardized Reactive Search Pattern (Aether UI V3) $effect(() => { - // Reactive dependencies - const qry_str = $idaa_loc.recovery_meetings.qry__fulltext_str; - 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; - const qry_enabled = $idaa_loc.recovery_meetings.qry__enabled; - const qry_hidden = $idaa_loc.recovery_meetings.qry__hidden; - const qry_limit = $idaa_loc.recovery_meetings.qry__limit; - const qry_order_by_li = $idaa_loc.recovery_meetings.qry__order_by_li; - const account_id = $ae_loc.account_id; - const s_trigger = $idaa_trig.event_li_qry; + // 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, + account: $ae_loc.account_id + }; - // Trigger search on parameter change (debounced) + // 2. Debounce Logic if (search_debounce_timer) clearTimeout(search_debounce_timer); search_debounce_timer = setTimeout(() => { - handle_search_refresh(); + // 3. Execution (Untracked to prevent loops) + untrack(() => { + handle_search_refresh(); + }); }, 350); return () => { @@ -99,135 +76,124 @@ async function handle_search_refresh() { const current_search_id = ++last_search_id; + const account_id = $ae_loc.account_id; - if (log_lvl) console.log(`[Search #${current_search_id}] Refreshing recovery meetings...`); + if (log_lvl) console.log(`[Search #${current_search_id}] Refreshing recovery meetings for account: ${account_id}...`); $idaa_sess.recovery_meetings.qry__status = 'loading'; - // Clearing logic for hidden/disabled filter changes - if ($idaa_loc.recovery_meetings.qry__enabled !== 'all' || $idaa_loc.recovery_meetings.qry__hidden !== 'all') { - await db_events.event.clear(); + // 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; + const remote_first = $idaa_loc.recovery_meetings.qry__remote_first; + + let local_ids: string[] = []; + + try { + if (account_id) { + // DEBUG: Inspect the first few items in the DB to check account_id format + if (log_lvl > 1) { + const sample = await db_events.event.limit(5).toArray(); + console.log(`[Search #${current_search_id}] DB Sample:`, sample.map(s => ({id: s.id, acct: s.account_id, acct_r: s.account_id_random}))); + } + + 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.event_id_random || e.id).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; + } + } + } 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: $ae_loc.account_id, + for_obj_id: account_id, qry_conference: false, - qry_physical: $idaa_loc.recovery_meetings.qry__physical, - qry_virtual: $idaa_loc.recovery_meetings.qry__virtual, - qry_type: $idaa_loc.recovery_meetings.qry__type, - qry_str: $idaa_loc.recovery_meetings.qry__fulltext_str?.trim() ?? null, + 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 // Keep noise low during typing + log_lvl: 0 }); - // Race condition check: only update if this is still the latest request if (current_search_id === last_search_id) { - $idaa_slct.event_obj_li = results; - event_id_random_li = results.map((e: any) => e.event_id_random); + const api_results = results || []; + const api_ids = api_results.map((e: any) => 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}] Done. Found ${results.length} results.`); + 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('Search failed:', error); + 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 detected.'); - - let message = { event_id: $idaa_slct?.event_id ?? null }; - window.parent.postMessage(message, '*'); + console.log('Browser environment ready.'); + window.parent.postMessage({ event_id: $idaa_slct?.event_id ?? null }, '*'); } - - - function add_activity_log({ - action = 'idaa_meetings_page', - action_with = 'none' - }: { - action?: string; - action_with?: string; - }) { - let last_cache_refresh_iso = new Date($ae_loc?.last_cache_refresh); - - let activity_description = ` - ${$idaa_loc.novi_full_name ?? 'none'} ${$idaa_loc.novi_email ?? 'no-email'} - allow=${$ae_loc?.allow_access} - last_cache_refresh=${last_cache_refresh_iso.toLocaleString()} - data_route=${data?.route.toString() ?? 'unknown'} - `; - - let data_kv = { - external_client_id: data?.route.id, - name: `IDAA: ${$idaa_loc.novi_full_name ?? 'none'} ${$idaa_loc.novi_email ?? ''}`, - description: activity_description ?? null, - object_type: 'event', // archive, post, event - // object_id_random: data?.params?.event_id ?? null, - // object_id_random: ae_acct.slct.archive_id, // data?.params?.archive_id ?? null, - url_root: data?.url.origin, - // url_full_path: data?.url.href, - url_full_path: data?.url.pathname, - url_params: data?.url.searchParams.toString(), - action: action, - action_with: action_with ?? 'none', - meta_json: { - allow_access: $ae_loc?.allow_access, - last_cache_refresh: $ae_loc?.last_cache_refresh, - last_cache_refresh_iso: last_cache_refresh_iso.toISOString(), - last_cache_refresh_locale: last_cache_refresh_iso.toLocaleString(), - access_level: $ae_loc?.access_level, - iframe: $ae_loc?.iframe - // site_access_key: $ae_loc?.site_access_key, - // site_domain_access_key: $ae_loc?.site_domain_access_key, - // site_domain: $ae_loc?.site_domain, - // extra_data: extra_data ?? '', - // log_lvl: log_lvl, - } - }; - - core_func.create_ae_obj__activity_log({ - api_cfg: $ae_api, - account_id: $ae_loc.account_id, - data_kv: data_kv, - log_lvl: log_lvl - }); - } - - // let hide_scroll_btn = $state(true); // This is to hide the scroll to top button initially - // let scroll_y = $state(75); - - // function go_top() { - // document.body.scrollIntoView(); - // } - - // function scroll_container() { - // console.log('Element', document.getElementById('ae_idaa__recovery_meetings')) - // return document.getElementById('ae_idaa__recovery_meetings') || document.documentElement || document.body; - // // return document.documentElement || document.body; - // } - - // function handle_on_scroll() { - // if (!scroll_container()) { - // return; - // } - // console.log(`Scroll position: ${scroll_container().scrollTop}`); - - // let show_on_px = 500; // The scroll position at which the button will be shown - - // if (scroll_container().scrollTop > show_on_px) { - // hide_scroll_btn = false; - // } else { - // hide_scroll_btn = true; - // } - // } @@ -238,21 +204,13 @@ - - - {#if Array.isArray(event_id_random_li) && event_id_random_li.length} - Loading... + Searching... {:else}
- No recovery meetings available to show. The search may need to be changed. - {$idaa_sess.recovery_meetings.qry__status ?? 'Unknown Query Status'} + No recovery meetings found matching your criteria.
{/if} diff --git a/src/routes/idaa/(idaa)/recovery_meetings/ae_idaa_comp__event_obj_li.svelte b/src/routes/idaa/(idaa)/recovery_meetings/ae_idaa_comp__event_obj_li.svelte index cd4f2828..7450cce6 100644 --- a/src/routes/idaa/(idaa)/recovery_meetings/ae_idaa_comp__event_obj_li.svelte +++ b/src/routes/idaa/(idaa)/recovery_meetings/ae_idaa_comp__event_obj_li.svelte @@ -41,17 +41,31 @@ // Derived list of visible items (Refactored 2026-01-27) // Ensures count matches exactly what is rendered to the user let visible_event_obj_li = $derived((() => { - if (!$lq__event_obj_li) return []; - return $lq__event_obj_li - .filter((item: any) => { - if (!item) return false; - // If not trusted, exclude hidden or disabled items - if (!$ae_loc.trusted_access) { - return !item.hide && item.enable; - } - return true; - }) - .slice(0, $idaa_loc.recovery_meetings.qry__limit); + // Subscribe to the LiveQuery observable using $ prefix + const list = $lq__event_obj_li; + + if (!list || !Array.isArray(list)) { + if (log_lvl > 1) console.log('visible_event_obj_li: Waiting for data stream...'); + return []; + } + + const filtered = list.filter((item: any) => { + if (!item) return false; + + // ADMIN/TRUSTED: See everything + if ($ae_loc.trusted_access) return true; + + // PUBLIC: Filter hidden/disabled + // Safely handle null/undefined fields by assuming visible/enabled (permissive default) + const is_hidden = item.hide === true || item.hide === 1; + const is_disabled = item.enable === false || item.enable === 0; // Only block if explicitly false/0 + + return !is_hidden && !is_disabled; + }); + + if (log_lvl) console.log(`visible_event_obj_li: Input=${list.length}, Output=${filtered.length} (trusted=${$ae_loc.trusted_access})`); + + return filtered.slice(0, $idaa_loc.recovery_meetings.qry__limit || 150); })()); if (browser) { diff --git a/src/routes/idaa/(idaa)/recovery_meetings/ae_idaa_comp__event_obj_li_wrapper.svelte b/src/routes/idaa/(idaa)/recovery_meetings/ae_idaa_comp__event_obj_li_wrapper.svelte index de3cd428..e7d7e9f0 100644 --- a/src/routes/idaa/(idaa)/recovery_meetings/ae_idaa_comp__event_obj_li_wrapper.svelte +++ b/src/routes/idaa/(idaa)/recovery_meetings/ae_idaa_comp__event_obj_li_wrapper.svelte @@ -54,51 +54,41 @@ // *** Functions and Logic let lq__event_obj_li = $derived( liveQuery(async () => { - // Check if event_id_random_li is an array and has items - // if (Array.isArray(event_id_random_li)) { // && event_id_random_li.length > 0) { - if (event_id_random_li && event_id_random_li.length) { - if (log_lvl) { - (console.log(`Trying IDB bulkGet: ${event_id_random_li.length}`), - event_id_random_li); + // SCENARIO 1: Specific IDs provided (from Search) + // If the search results in an empty array [], we MUST return an empty array to show 0 results. + if (Array.isArray(event_id_random_li)) { + if (event_id_random_li.length > 0) { + if (log_lvl) { + console.log(`Wrapper: Loading ${event_id_random_li.length} specific IDs via bulkGet`); + } + const results = await db_events.event.bulkGet(event_id_random_li); + // Filter out undefined items (holes in bulkGet) + return results.filter(item => item !== undefined); + } else { + // Empty array explicitly means 0 results found + if (log_lvl) console.log('Wrapper: Search returned 0 specific IDs. Displaying empty list.'); + return []; } - // if (!event_id_random_li.length) { - // return []; - // } - const results = await db_events.event.bulkGet(event_id_random_li); - - // Filter out undefined items (holes in bulkGet) - return results.filter(item => item !== undefined); - } else if (link_to_type && link_to_id) { + } + + // SCENARIO 2: No specific IDs provided (Broad Search fallback) + else if (link_to_type && link_to_id) { if (log_lvl) { - console.log( - `Trying where: ${link_to_type}; equals: ${link_to_id}; event_id_random_li: ${event_id_random_li}`, - event_id_random_li - ); + console.log(`Wrapper: No specific IDs. Performing broad search for ${link_to_type}: ${link_to_id}`); } let results: any = null; + const base_query = db_events.event + .where(dq__where_type_id_val) + .equals(dq__where_eq_id_val); + if (order_by == 'name') { - results = await db_events.event - .where(dq__where_type_id_val) - .equals(dq__where_eq_id_val) - .and((event) => { - return event.hide == false; - }) - .and((event) => { - return event.enable == true; - }) + results = await base_query + .and(ev => !ev.hide && !!ev.enable) .limit(limit > 0 ? limit : 500) .sortBy('name'); - // This should be sorted by a custom sort field } else { - results = await db_events.event - .where(dq__where_type_id_val) - .equals(dq__where_eq_id_val) - .and((event) => { - return event.hide == false; - }) - .and((event) => { - return event.enable == true; - }) + results = await base_query + .and(ev => !ev.hide && !!ev.enable) .limit(limit > 0 ? limit : 500) .sortBy('updated_on'); } diff --git a/src/routes/idaa/(idaa)/recovery_meetings/ae_idaa_comp__event_obj_qry.svelte b/src/routes/idaa/(idaa)/recovery_meetings/ae_idaa_comp__event_obj_qry.svelte index bb9b4df0..7c35b05a 100644 --- a/src/routes/idaa/(idaa)/recovery_meetings/ae_idaa_comp__event_obj_qry.svelte +++ b/src/routes/idaa/(idaa)/recovery_meetings/ae_idaa_comp__event_obj_qry.svelte @@ -54,6 +54,13 @@ } // *** Functions and Logic + function handle_search_trigger() { + if ($idaa_loc.recovery_meetings.search_version === undefined) { + $idaa_loc.recovery_meetings.search_version = 0; + } + $idaa_loc.recovery_meetings.search_version++; + } + function preventDefault(fn: (event: T) => void) { return function (event: T) { event.preventDefault(); @@ -92,7 +99,7 @@
{ - $idaa_trig.event_li_qry = true; + handle_search_trigger(); })} autocomplete="off" class=" @@ -116,7 +123,7 @@ onclick={() => { $idaa_loc.recovery_meetings.qry__fulltext_str = ''; $idaa_sess.recovery_meetings.qry__fulltext_str = ''; - $idaa_trig.event_li_qry = true; + handle_search_trigger(); }} title="Clear search text" > @@ -260,6 +267,21 @@ title="Open to spouses, parents, and children of medical professionals who have substance use disorder." /> + + + +