From af4b8a34764cf9b4b743c848b109be39f5b735ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89mile=20R=C3=A9?= Date: Thu, 28 May 2026 08:49:53 +0200 Subject: [PATCH] Show third parties on banner trackers page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wire the new CookieBanner.linkedThirdParties aggregation and the TrackerPattern.thirdParty / commonThirdParty fields into the trackers configuration UI. Three surfaces change: CookieBannerTrackersPage: a new dedicated "Third party" column sits between Name and Source so the link is visible at a glance, and a fourth Select to the right of the category filter exposes the deduped list returned by linkedThirdParties. The Select sends the chosen GID straight through; the backend dispatches on the entity-type prefix (ThirdParty vs CommonThirdParty), so the frontend stays oblivious to which table the filter ends up hitting. The "%other" branch of the union is filtered out before render to keep TypeScript happy if the backend grows the union later. TrackerPatternRow: extends the row fragment with thirdParty and commonThirdParty, prefers the org-scoped link (mirroring the resolver priority), and renders an Avatar + name. The org-scoped ThirdParty has no logoUrl in the schema today so the Avatar falls back to initials; CommonThirdParty supplies its catalog logo. TrackerPatternPropertiesSection: same priority logic in a PropertyRow under Category, so the detail page surfaces the same information. The third-party Select uses the same set as the underlying patterns, so picking a value never produces an empty list — the linkedThirdParties resolver only walks rows the user can see. Signed-off-by: Émile Ré --- .../trackers/CookieBannerTrackersPage.tsx | 39 ++++++++++++++++++- .../TrackerPatternPropertiesSection.tsx | 31 ++++++++++++++- .../_components/TrackerPatternRow.tsx | 30 ++++++++++++++ 3 files changed, 98 insertions(+), 2 deletions(-) diff --git a/apps/console/src/pages/organizations/cookie-banners/configuration/trackers/CookieBannerTrackersPage.tsx b/apps/console/src/pages/organizations/cookie-banners/configuration/trackers/CookieBannerTrackersPage.tsx index 7d947db39..a1ba65c02 100644 --- a/apps/console/src/pages/organizations/cookie-banners/configuration/trackers/CookieBannerTrackersPage.tsx +++ b/apps/console/src/pages/organizations/cookie-banners/configuration/trackers/CookieBannerTrackersPage.tsx @@ -76,14 +76,27 @@ const trackersFragment = graphql` source: { type: "CookieSource", defaultValue: null } trackerType: { type: "TrackerType", defaultValue: null } cookieCategoryId: { type: "ID", defaultValue: null } + thirdPartyId: { type: "ID", defaultValue: null } ) { + linkedThirdParties { + __typename + ... on ThirdParty { + id + name + } + ... on CommonThirdParty { + id + name + logoUrl + } + } trackerPatterns( first: $first after: $after last: $last before: $before orderBy: $order - filter: { query: $query, source: $source, trackerType: $trackerType, cookieCategoryId: $cookieCategoryId } + filter: { query: $query, source: $source, trackerType: $trackerType, cookieCategoryId: $cookieCategoryId, thirdPartyId: $thirdPartyId } ) @connection( key: "CookieBannerTrackersPage_trackerPatterns" @@ -120,6 +133,7 @@ export default function CookieBannerTrackersPage({ const [sourceFilter, setSourceFilter] = useState(null); const [trackerTypeFilter, setTrackerTypeFilter] = useState(null); const [categoryFilter, setCategoryFilter] = useState(null); + const [thirdPartyFilter, setThirdPartyFilter] = useState(null); const { data: fragmentData, ...pagination } = usePaginationFragment< CookieBannerTrackersPageRefetchQuery, @@ -128,6 +142,11 @@ export default function CookieBannerTrackersPage({ const connectionId = fragmentData.trackerPatterns.__id; const patterns = fragmentData.trackerPatterns.edges.map(edge => edge.node) ?? []; + const linkedThirdParties = (fragmentData.linkedThirdParties ?? []).filter( + (party): party is Extract => + party.__typename === "ThirdParty" + || party.__typename === "CommonThirdParty", + ); const categories = data.node.__typename === "CookieBanner" ? data.node.categories.edges.map(edge => edge.node) @@ -141,6 +160,7 @@ export default function CookieBannerTrackersPage({ source: sourceFilter, trackerType: trackerTypeFilter, cookieCategoryId: categoryFilter, + thirdPartyId: thirdPartyFilter, ...overrides, }, { fetchPolicy: "network-only" }, @@ -170,6 +190,12 @@ export default function CookieBannerTrackersPage({ refetchFilters({ cookieCategoryId: newCategory }); }; + const handleThirdPartyFilterChange = (value: string) => { + const newThirdParty = value === "ALL" ? null : value; + setThirdPartyFilter(newThirdParty); + refetchFilters({ thirdPartyId: newThirdParty }); + }; + const refetchWithFilters: ComponentProps["refetch"] = ({ order }) => { pagination.refetch({ order: { direction: order.direction, field: order.field as TrackerPatternOrderField }, @@ -177,6 +203,7 @@ export default function CookieBannerTrackersPage({ source: sourceFilter, trackerType: trackerTypeFilter, cookieCategoryId: categoryFilter, + thirdPartyId: thirdPartyFilter, }); }; @@ -220,6 +247,15 @@ export default function CookieBannerTrackersPage({ ))} +
@@ -234,6 +270,7 @@ export default function CookieBannerTrackersPage({ {__("Type")} {__("Name")} + {__("Third party")} {__("Source")} {__("Category")} {__("Last Matched")} diff --git a/apps/console/src/pages/organizations/cookie-banners/configuration/trackers/_components/TrackerPatternPropertiesSection.tsx b/apps/console/src/pages/organizations/cookie-banners/configuration/trackers/_components/TrackerPatternPropertiesSection.tsx index 75713189a..ad874647b 100644 --- a/apps/console/src/pages/organizations/cookie-banners/configuration/trackers/_components/TrackerPatternPropertiesSection.tsx +++ b/apps/console/src/pages/organizations/cookie-banners/configuration/trackers/_components/TrackerPatternPropertiesSection.tsx @@ -14,7 +14,7 @@ import { humanizeSeconds } from "@probo/helpers"; import { useTranslate } from "@probo/i18n"; -import { Badge, Card, PropertyRow } from "@probo/ui"; +import { Avatar, Badge, Card, PropertyRow } from "@probo/ui"; import { graphql, useFragment } from "react-relay"; import type { TrackerPatternPropertiesSection_trackerPattern$key } from "#/__generated__/core/TrackerPatternPropertiesSection_trackerPattern.graphql"; @@ -33,6 +33,15 @@ const trackerPatternPropertiesSectionFragment = graphql` cookieCategory { name } + thirdParty { + id + name + } + commonThirdParty { + id + name + logoUrl + } } `; @@ -93,6 +102,26 @@ export function TrackerPatternPropertiesSection({ {pattern.cookieCategory?.name ?? "-"} + + {pattern.thirdParty + ? ( +
+ + {pattern.thirdParty.name} +
+ ) + : pattern.commonThirdParty + ? ( +
+ + {pattern.commonThirdParty.name} +
+ ) + : -} +
{humanizeSeconds(pattern.maxAgeSeconds ?? null)} diff --git a/apps/console/src/pages/organizations/cookie-banners/configuration/trackers/_components/TrackerPatternRow.tsx b/apps/console/src/pages/organizations/cookie-banners/configuration/trackers/_components/TrackerPatternRow.tsx index 3adf35ec2..cdc60264d 100644 --- a/apps/console/src/pages/organizations/cookie-banners/configuration/trackers/_components/TrackerPatternRow.tsx +++ b/apps/console/src/pages/organizations/cookie-banners/configuration/trackers/_components/TrackerPatternRow.tsx @@ -16,6 +16,7 @@ import { EyeIcon, EyeSlashIcon } from "@phosphor-icons/react"; import { formatError, type GraphQLError } from "@probo/helpers"; import { useTranslate } from "@probo/i18n"; import { + Avatar, Badge, Dropdown, IconArrowBoxLeft, @@ -56,6 +57,15 @@ const trackerPatternFragment = graphql` cookieCategory { name } + thirdParty { + id + name + } + commonThirdParty { + id + name + logoUrl + } } `; @@ -314,6 +324,26 @@ export function TrackerPatternRow({ patternKey, connectionId }: TrackerPatternRo )}
+ + {pattern.thirdParty + ? ( +
+ + {pattern.thirdParty.name} +
+ ) + : pattern.commonThirdParty + ? ( +
+ + {pattern.commonThirdParty.name} +
+ ) + : -} + {srcBadge ? {srcBadge.label}