From f053691d4aabec8771197baa36c9ba66d787edf8 Mon Sep 17 00:00:00 2001 From: pradeepkumar Date: Wed, 15 Apr 2026 12:43:05 +0530 Subject: [PATCH] fix --- src/agents/agents.service.ts | 30 ++++++--- src/profile-fields/profile-fields.service.ts | 65 +++++++++++++++++++- 2 files changed, 86 insertions(+), 9 deletions(-) diff --git a/src/agents/agents.service.ts b/src/agents/agents.service.ts index 720e565..62b3ef8 100644 --- a/src/agents/agents.service.ts +++ b/src/agents/agents.service.ts @@ -503,9 +503,17 @@ export class AgentsService { const matchingIds = matchingValues .filter((fv) => { - const jsonVal = fv.jsonValue as string[] | null; - if (!jsonVal || !Array.isArray(jsonVal)) return false; - return selectedValues.some((sv) => jsonVal.includes(sv)); + const jsonVal = fv.jsonValue as unknown; + if (!Array.isArray(jsonVal)) return false; + // Direct match (normalized option values stored in jsonVal) + if (selectedValues.some((sv) => jsonVal.includes(sv))) return true; + // TAG_INPUT fallback: strip " - N yrs" suffix and normalize + const normalized = (jsonVal as unknown[]) + .map((item) => typeof item === 'string' ? item : '') + .map((s) => s.replace(/\s*[-–]\s*\d+\s*(yrs?|years?)\s*$/i, '').trim()) + .map((s) => s.toLowerCase().replace(/[^a-z0-9]+/g, '_').replace(/^_+|_+$/g, '')) + .filter(Boolean); + return selectedValues.some((sv) => normalized.includes(sv)); }) .map((fv) => fv.agentProfileId); @@ -664,11 +672,17 @@ export class AgentsService { const agentFieldValues = (agent.fieldValues || []) as any[]; const fieldMatch = agentFieldValues.some((fv: any) => { if (fv.field?.slug !== filterSlug) return false; - const jsonVal = fv.jsonValue as string[] | null; - if (jsonVal && Array.isArray(jsonVal)) { - return selectedValues.some(sv => jsonVal.includes(sv)); - } - return false; + const jsonVal = fv.jsonValue as unknown; + if (!Array.isArray(jsonVal)) return false; + // Direct match (normalized option values already stored in jsonVal) + if (selectedValues.some(sv => jsonVal.includes(sv))) return true; + // TAG_INPUT fallback: strip " - N yrs" suffix and normalize to match aggregated expertise values + const normalized = (jsonVal as unknown[]) + .map((item) => typeof item === 'string' ? item : '') + .map((s) => s.replace(/\s*[-–]\s*\d+\s*(yrs?|years?)\s*$/i, '').trim()) + .map((s) => s.toLowerCase().replace(/[^a-z0-9]+/g, '_').replace(/^_+|_+$/g, '')) + .filter(Boolean); + return selectedValues.some(sv => normalized.includes(sv)); }); if (fieldMatch) matchedFilters++; } diff --git a/src/profile-fields/profile-fields.service.ts b/src/profile-fields/profile-fields.service.ts index 1c4eb52..f81e7fa 100644 --- a/src/profile-fields/profile-fields.service.ts +++ b/src/profile-fields/profile-fields.service.ts @@ -298,8 +298,16 @@ export class ProfileFieldsService { // Map normalized names to their primary slug for de-duplication const nameToSlugMap = new Map(); + // Options to exclude from search filters (still available on profile forms via admin) + const EXCLUDED_OPTION_VALUES = new Set(['prefer_not_to_say', 'prefer-not-to-say', 'preferNotToSay']); + const isExcludedOption = (opt: { label: string; value: string }) => { + if (EXCLUDED_OPTION_VALUES.has(opt.value)) return true; + return opt.label.toLowerCase().replace(/[^a-z0-9]+/g, '_').replace(/^_+|_+$/g, '') === 'prefer_not_to_say'; + }; + for (const field of fields) { - const fieldOptions = (field.options as { label: string; value: string }[]) || []; + const rawOptions = (field.options as { label: string; value: string }[]) || []; + const fieldOptions = rawOptions.filter((opt) => !isExcludedOption(opt)); const normalizedName = field.name.toLowerCase().trim(); // Check if we already have a field with this name (different slug, same name) @@ -341,6 +349,61 @@ export class ProfileFieldsService { } } + // Append TAG_INPUT expertise_areas as a dynamic filter — options aggregated from agent-entered values + const expertiseField = await this.prisma.profileField.findFirst({ + where: { + slug: 'expertise_areas', + isActive: true, + fieldType: FieldType.TAG_INPUT, + section: { + isActive: true, + OR: [ + { isGlobal: true }, + { agentTypeSections: { some: {} } }, + ], + }, + }, + select: { id: true, name: true, slug: true, fieldType: true }, + }); + + if (expertiseField && !mergedFields.has(expertiseField.slug)) { + const fieldValues = await this.prisma.profileFieldValue.findMany({ + where: { + fieldId: expertiseField.id, + jsonValue: { not: Prisma.JsonNull }, + }, + select: { jsonValue: true }, + }); + + // value → label (preserve first-seen label casing) + const areaMap = new Map(); + for (const fv of fieldValues) { + const arr = fv.jsonValue as unknown; + if (!Array.isArray(arr)) continue; + for (const item of arr) { + if (typeof item !== 'string') continue; + // Strip " - N yrs" / " – N years" suffix used in the profile UI + const areaName = item.replace(/\s*[-–]\s*\d+\s*(yrs?|years?)\s*$/i, '').trim(); + if (!areaName) continue; + const value = areaName.toLowerCase().replace(/[^a-z0-9]+/g, '_').replace(/^_+|_+$/g, ''); + if (!value) continue; + if (!areaMap.has(value)) areaMap.set(value, areaName); + } + } + + if (areaMap.size > 0) { + const options = Array.from(areaMap.entries()) + .map(([value, label]) => ({ label, value })) + .sort((a, b) => a.label.localeCompare(b.label)); + mergedFields.set(expertiseField.slug, { + slug: expertiseField.slug, + name: expertiseField.name, + fieldType: expertiseField.fieldType, + options, + }); + } + } + return Array.from(mergedFields.values()); } }