diff --git a/api/src/controllers/iframe.ts b/api/src/controllers/iframe.ts index 123162b6..ad7428f0 100644 --- a/api/src/controllers/iframe.ts +++ b/api/src/controllers/iframe.ts @@ -236,9 +236,9 @@ const buildMissionFilters = ( } } - const organizationNames = normalizeToArray(query.organization); - if (organizationNames?.length) { - filters.organizationName = organizationNames; + const organizationClientIds = normalizeToArray(query.organization); + if (organizationClientIds?.length) { + filters.organizationClientId = organizationClientIds; } const actions = normalizeToArray(query.action); diff --git a/api/src/services/widget-mission.ts b/api/src/services/widget-mission.ts index 26bed48e..2f503653 100644 --- a/api/src/services/widget-mission.ts +++ b/api/src/services/widget-mission.ts @@ -7,7 +7,7 @@ import type { MissionRecord, MissionSearchFilters, MissionSelect } from "@/types import { buildWhere, missionService } from "./mission"; import publisherOrganizationService from "./publisher-organization"; -type Bucket = { key: string; doc_count: number }; +type Bucket = { key: string; doc_count: number; label?: string }; type PublisherOrganizationTuple = { publisherId: string; @@ -40,7 +40,7 @@ const buildWidgetWhere = (widget: WidgetRecord, filters: MissionSearchFilters): const isPlainObject = (value: unknown): value is Record => typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Date); /** - * Builds a mission condition from `(publisherId, organizationClientId)` tuples. + * Builds a mission condition from `(publisherId, clientId)` tuples. * Returns either one condition or an `OR` of conditions; empty input matches nothing. */ const buildMissionConditionFromPublisherOrganizationTuples = (tuples: PublisherOrganizationTuple[]): Prisma.MissionWhereInput => { @@ -58,7 +58,7 @@ const buildMissionConditionFromPublisherOrganizationTuples = (tuples: PublisherO const conditions = Array.from(byPublisher.entries()).map(([publisherId, clientIds]) => ({ publisherId, - organizationClientId: { in: Array.from(clientIds) }, + publisherOrganization: { clientId: { in: Array.from(clientIds) } }, })); return conditions.length === 1 ? conditions[0] : { OR: conditions }; @@ -221,9 +221,14 @@ const aggregateWidgetAggs = async ( const formatOrganization = async () => { const orgRows = await aggregateMissionField("publisherOrganizationId"); const orgIds = orgRows.map((row) => row.key); - const orgs = orgIds.length ? await publisherOrganizationService.findMany({ ids: orgIds }, { select: { id: true, name: true } }) : []; - const orgById = new Map(orgs.map((org) => [org.id, org.name ?? ""])); - return orgRows.map((row) => ({ key: orgById.get(row.key) ?? "", doc_count: row.doc_count })).filter((row) => row.key); + const orgs = orgIds.length ? await publisherOrganizationService.findMany({ ids: orgIds }, { select: { id: true, name: true, clientId: true } }) : []; + const orgById = new Map(orgs.map((org) => [org.id, { name: org.name ?? "", clientId: org.clientId ?? "" }])); + return orgRows + .map((row) => { + const org = orgById.get(row.key); + return { key: org?.clientId ?? "", doc_count: row.doc_count, label: org?.name ?? "" }; + }) + .filter((row) => row.key); }; const result: any = {}; diff --git a/api/tests/integration/api/iframe/search.test.ts b/api/tests/integration/api/iframe/search.test.ts index 1360130a..98d74559 100644 --- a/api/tests/integration/api/iframe/search.test.ts +++ b/api/tests/integration/api/iframe/search.test.ts @@ -201,17 +201,17 @@ describe("GET /iframe/:id/search", () => { expect(response.body.data[0].title).toContain("Environnement"); }); - it("should filter by organization name", async () => { - const response = await request(app).get(`/iframe/${widget.id}/search`).query({ organization: "Green Org" }).expect(200); + it("should filter by organization client ID", async () => { + const response = await request(app).get(`/iframe/${widget.id}/search`).query({ organization: "green-org-1" }).expect(200); expect(response.body.ok).toBe(true); - expect(response.body.total).toBeGreaterThanOrEqual(0); + expect(response.body.total).toBe(1); }); - it("should support multiple organization names", async () => { + it("should support multiple organization client IDs", async () => { const response = await request(app) .get(`/iframe/${widget.id}/search`) - .query({ organization: ["Green Org", "Edu Org"] }) + .query({ organization: ["green-org-1", "edu-org-1"] }) .expect(200); expect(response.body.total).toBe(2); diff --git a/widget/components/ComboxFilter.tsx b/widget/components/ComboxFilter.tsx index 583e81cd..b2333b72 100644 --- a/widget/components/ComboxFilter.tsx +++ b/widget/components/ComboxFilter.tsx @@ -2,8 +2,8 @@ import { usePlausible } from "next-plausible"; import { useEffect, useRef, useState } from "react"; import { RiArrowDownSLine, RiCheckFill, RiSearchLine } from "react-icons/ri"; -import useStore from "@/utils/store"; import { FilterOption } from "@/types"; +import useStore from "@/utils/store"; interface ComboboxFilterProps { options: FilterOption[]; @@ -120,7 +120,7 @@ const ComboboxFilter = ({ }; return ( -
+
@@ -131,7 +131,7 @@ const ComboboxFilter = ({ id={id} aria-label={placeholder} aria-expanded={show} - className={`select relative truncate text-left ${!values || values.length === 0 ? "!text-[#666666]" : ""}`} + className={`select relative truncate text-left ${!values || values.length === 0 ? "text-[#666666]!" : ""}`} onClick={() => setShow(!show)} onKeyDown={handleButtonKeyDown} > diff --git a/widget/components/FiltersBenevolat.tsx b/widget/components/FiltersBenevolat.tsx index 35ad7861..d6290a50 100644 --- a/widget/components/FiltersBenevolat.tsx +++ b/widget/components/FiltersBenevolat.tsx @@ -50,7 +50,7 @@ const FiltersBenevolat = ({ widget, apiUrl, values, total, onChange, show, onSho const remote = data.remote.filter((b) => b.key === "full" || b.key === "possible"); const presentiel = data.remote.filter((b) => b.key === "no"); const newOptions: FilterOptions = { - organizations: data.organization.map((b) => ({ value: b.key, count: b.doc_count, label: b.key })), + organizations: data.organization.map((b) => ({ value: b.key, count: b.doc_count, label: b.label || b.key })), domains: data.domain.map((b) => ({ value: b.key, count: b.doc_count, label: DOMAINS[b.key] ? DOMAINS[b.key].label : b.key })), departments: data.department.map((b) => ({ value: b.key === "" ? "none" : b.key, diff --git a/widget/next-env.d.ts b/widget/next-env.d.ts index 19709046..7996d352 100644 --- a/widget/next-env.d.ts +++ b/widget/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -import "./.next/types/routes.d.ts"; +import "./.next/dev/types/routes.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/pages/api-reference/config/typescript for more information. diff --git a/widget/types/index.ts b/widget/types/index.ts index 760254c6..bf7619bd 100644 --- a/widget/types/index.ts +++ b/widget/types/index.ts @@ -124,6 +124,7 @@ export interface ApiResponse { export interface AggregationBucket { key: string; doc_count: number; + label?: string; } export interface AggregationData {