diff --git a/src/agents/agents.service.ts b/src/agents/agents.service.ts index 9020024..7c4b53a 100644 --- a/src/agents/agents.service.ts +++ b/src/agents/agents.service.ts @@ -236,41 +236,67 @@ export class AgentsService { const filterSlugs = Object.keys(parsedFilters); if (filterSlugs.length > 0) { - // Get field IDs for the filter slugs + // Get field IDs for the filter slugs, including section info for assignment validation const fields = await this.prisma.profileField.findMany({ where: { slug: { in: filterSlugs } }, - select: { id: true, slug: true }, + select: { + id: true, + slug: true, + sectionId: true, + section: { + select: { + isGlobal: true, + agentTypeSections: { + select: { agentTypeId: true }, + }, + }, + }, + }, }); - const slugToFieldId = new Map(fields.map((f) => [f.slug, f.id])); + const slugToField = new Map(fields.map((f) => [f.slug, f])); // For each filter, find agents that have ANY of the selected values // Use AND logic between different fields const filterConditions: string[][] = []; for (const [slug, selectedValues] of Object.entries(parsedFilters)) { - const fieldId = slugToFieldId.get(slug); - if (!fieldId || !selectedValues || selectedValues.length === 0) continue; + const field = slugToField.get(slug); + if (!field || !selectedValues || selectedValues.length === 0) continue; + + // Build set of agent type IDs that have access to this field's section + const isGlobal = field.section.isGlobal; + const allowedAgentTypeIds = new Set( + field.section.agentTypeSections.map((ats) => ats.agentTypeId), + ); // Find agentProfileIds where jsonValue contains ANY of the selected values const matchingValues = await this.prisma.agentProfileFieldValue.findMany({ - where: { - fieldId, - // jsonValue contains any of the selected values (for MULTI_SELECT, CHECKBOX_GROUP) - // We need to check if the jsonValue array contains any of the selected values - }, + where: { fieldId: field.id }, select: { agentProfileId: true, jsonValue: true, + agentProfile: { + select: { agentTypeId: true }, + }, }, }); - // Filter in memory - check if jsonValue array contains any selected value + // Filter in memory: + // 1. Check if jsonValue array contains any selected value + // 2. Check if the agent's type has access to this field's section const matchingAgentIds = matchingValues .filter((fv) => { const jsonVal = fv.jsonValue as string[] | null; if (!jsonVal || !Array.isArray(jsonVal)) return false; - return selectedValues.some((sv) => jsonVal.includes(sv)); + if (!selectedValues.some((sv) => jsonVal.includes(sv))) return false; + + // Validate that this agent's type has access to the field's section + const agentTypeId = fv.agentProfile.agentTypeId; + if (!isGlobal && (!agentTypeId || !allowedAgentTypeIds.has(agentTypeId))) { + return false; + } + return true; }) .map((fv) => fv.agentProfileId); diff --git a/src/profile-fields/profile-fields.service.ts b/src/profile-fields/profile-fields.service.ts index ca1bc70..12f6d24 100644 --- a/src/profile-fields/profile-fields.service.ts +++ b/src/profile-fields/profile-fields.service.ts @@ -235,6 +235,7 @@ export class ProfileFieldsService { /** * Get all filterable fields (public endpoint for search filters) * Returns fields with CHECKBOX_GROUP, SELECT, MULTI_SELECT types that have options + * Only includes fields from sections that are global or assigned to at least one agent type * De-duplicates by both slug AND name to handle duplicate fields with different slugs */ async getFilterableFields() { @@ -245,6 +246,14 @@ export class ProfileFieldsService { isActive: true, fieldType: { in: filterableTypes }, options: { not: Prisma.JsonNull }, + // Only include fields from sections that are global or assigned to at least one agent type + section: { + isActive: true, + OR: [ + { isGlobal: true }, + { agentTypeSections: { some: {} } }, + ], + }, }, select: { id: true,