diff --git a/src/agents/agents.service.ts b/src/agents/agents.service.ts index a99729d..c523204 100644 --- a/src/agents/agents.service.ts +++ b/src/agents/agents.service.ts @@ -457,6 +457,43 @@ export class AgentsService { where.agentTypeId = agentTypeId; } + // Pre-compute RADIO option expansion map for filter matching. + // For ordinal RADIO fields (e.g. years_in_business), selecting "2+" should + // also match "5+" and "10+" — all options at or above the selected index. + let radioExpansionMap: Map | null = null; + if (filters) { + try { + const pf: Record = JSON.parse(filters); + const slugs = Object.keys(pf).filter(k => pf[k]?.length > 0); + if (slugs.length > 0) { + const radioFields = await this.prisma.profileField.findMany({ + where: { slug: { in: slugs }, fieldType: 'RADIO' }, + select: { slug: true, options: true }, + }); + radioExpansionMap = new Map(); + for (const rf of radioFields) { + if (!Array.isArray(rf.options)) continue; + const optValues = (rf.options as Array<{ value: string }>).map(o => String(o.value)); + radioExpansionMap.set(rf.slug, optValues); + } + } + } catch { /* invalid JSON — ignore */ } + } + + const expandRadioValuesFromMap = (slug: string, selected: string[]): string[] => { + if (!radioExpansionMap) return selected; + const optValues = radioExpansionMap.get(slug); + if (!optValues) return selected; + const expanded = new Set(selected); + for (const sv of selected) { + const idx = optValues.indexOf(sv); + if (idx >= 0) { + for (let i = idx; i < optValues.length; i++) expanded.add(optValues[i]); + } + } + return [...expanded]; + }; + // Process dynamic profile field filters if (filters) { try { @@ -464,12 +501,14 @@ export class AgentsService { const filterSlugs = Object.keys(parsedFilters); if (filterSlugs.length > 0) { - // Get field IDs for the filter slugs + // Get field IDs + type + options for the filter slugs const fields = await this.prisma.profileField.findMany({ where: { slug: { in: filterSlugs } }, select: { id: true, slug: true, + fieldType: true, + options: true, }, }); @@ -485,9 +524,10 @@ export class AgentsService { // Use AND logic between different filter slugs, OR logic within same slug const filterConditions: string[][] = []; - for (const [slug, selectedValues] of Object.entries(parsedFilters)) { + for (const [slug, rawSelectedValues] of Object.entries(parsedFilters)) { const slugFields = slugToFields.get(slug); - if (!slugFields || slugFields.length === 0 || !selectedValues || selectedValues.length === 0) continue; + if (!slugFields || slugFields.length === 0 || !rawSelectedValues || rawSelectedValues.length === 0) continue; + const selectedValues = expandRadioValuesFromMap(slug, rawSelectedValues); // Search across ALL fields with this slug (e.g. loan_type in both Professional and Lender sections) const allMatchingAgentIds: string[] = []; @@ -673,7 +713,9 @@ export class AgentsService { if (filterKeys.length > 0) { let matchedFilters = 0; for (const filterSlug of filterKeys) { - const selectedValues = parsedFilters[filterSlug]; + // Expand RADIO filters so "2+" also matches "5+", "10+", etc. + const rawSelected = parsedFilters[filterSlug]; + const selectedValues = expandRadioValuesFromMap(filterSlug, rawSelected); const agentFieldValues = (agent.fieldValues || []) as any[]; const fieldMatch = agentFieldValues.some((fv: any) => { if (fv.field?.slug !== filterSlug) return false;