diff --git a/src/routes/idaa/(idaa)/recovery_meetings/+page.svelte b/src/routes/idaa/(idaa)/recovery_meetings/+page.svelte index 9f727171..e07365c5 100644 --- a/src/routes/idaa/(idaa)/recovery_meetings/+page.svelte +++ b/src/routes/idaa/(idaa)/recovery_meetings/+page.svelte @@ -301,8 +301,16 @@ async function handle_search_refresh(qry_key: string) { if (current_search_id === last_search_id) { let api_results = results || []; - // SECONDARY FILTER: Ensure API results respect exact UI filters (handles backend broadness) - api_results = api_results.filter((ev: any) => { + // SECONDARY FILTER: Re-apply structured filters the API may handle loosely. + // WHY type/physical/virtual: the backend uses AND logic for body filters, so + // physical+virtual together would incorrectly exclude either-only meetings — + // we pass only one at a time and handle OR logic here. + // WHY NOT qry_str: the API already applied exact LIKE search on default_qry_str + // (a backend-generated combined index that includes contact name/email). Re-running + // text filtering client-side against the response fields silently drops meetings + // that matched only via default_qry_str (e.g., by contact name) because that + // field may not be present in the response body or may not duplicate the match. + api_results = api_results.filter((ev) => { if (qry_type && ev.type !== qry_type) return false; if (qry_physical || qry_virtual) { let match = false; @@ -310,19 +318,6 @@ async function handle_search_refresh(qry_key: string) { if (qry_virtual && ev.virtual == true) match = true; if (!match) return false; } - if (qry_str && qry_str.length >= 3) { - const name = (ev.name ?? '').toLowerCase(); - const desc = (ev.description ?? '').toLowerCase(); - const loc = (ev.location_text ?? '').toLowerCase(); - const dqs = (ev.default_qry_str ?? '').toLowerCase(); - if ( - !name.includes(qry_str) && - !desc.includes(qry_str) && - !loc.includes(qry_str) && - !dqs.includes(qry_str) - ) - return false; - } return true; });