From 5abd6707074c86213ac71fcf368c4d4ed64b99c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89mile=20R=C3=A9?= Date: Wed, 20 May 2026 11:58:40 +0400 Subject: [PATCH] Update console tracker page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Émile Ré --- .../display/CookieBannerDisplayPage.tsx | 8 ++-- .../display/_components/CategorySection.tsx | 6 +-- .../trackers/CookieBannerTrackersPage.tsx | 47 ++++++++++++++++--- .../_components/MoveToCategoryDropdown.tsx | 4 +- .../_components/TrackerPatternRow.tsx | 8 ++++ .../_components/TrackerPatternRowEdit.tsx | 1 + .../CookieBannerTranslationsPage.tsx | 6 +-- e2e/console/cookie_banner_test.go | 10 ++-- e2e/console/cookie_category_test.go | 22 ++++----- pkg/coredata/tracker_pattern.go | 30 +++--------- .../console/v1/graphql/cookie_banner.graphql | 13 ++++- .../api/console/v1/types/tracker_pattern.go | 7 +-- 12 files changed, 99 insertions(+), 63 deletions(-) diff --git a/apps/console/src/pages/organizations/cookie-banners/configuration/display/CookieBannerDisplayPage.tsx b/apps/console/src/pages/organizations/cookie-banners/configuration/display/CookieBannerDisplayPage.tsx index 7ed58f51b..feeaa242f 100644 --- a/apps/console/src/pages/organizations/cookie-banners/configuration/display/CookieBannerDisplayPage.tsx +++ b/apps/console/src/pages/organizations/cookie-banners/configuration/display/CookieBannerDisplayPage.tsx @@ -30,8 +30,8 @@ export const cookieBannerDisplayPageQuery = graphql` __typename ... on CookieBanner { id - consentCategories(first: 50, orderBy: { field: RANK, direction: ASC }) - @connection(key: "CookieBannerDisplayPage_consentCategories") + categories(first: 50, orderBy: { field: RANK, direction: ASC }, filter: { excludeKind: UNCATEGORISED }) + @connection(key: "CookieBannerDisplayPage_categories") @required(action: THROW) { __id edges { @@ -63,8 +63,8 @@ export default function CookieBannerDisplayPage({ } const banner = data.node; - const connectionId = banner.consentCategories.__id; - const categories = banner.consentCategories.edges.map(e => e.node); + const connectionId = banner.categories.__id; + const categories = banner.categories.edges.map(e => e.node); const [showCreateDialog, setShowCreateDialog] = useState(false); diff --git a/apps/console/src/pages/organizations/cookie-banners/configuration/display/_components/CategorySection.tsx b/apps/console/src/pages/organizations/cookie-banners/configuration/display/_components/CategorySection.tsx index fff3d96d3..3e4bb9a51 100644 --- a/apps/console/src/pages/organizations/cookie-banners/configuration/display/_components/CategorySection.tsx +++ b/apps/console/src/pages/organizations/cookie-banners/configuration/display/_components/CategorySection.tsx @@ -85,7 +85,7 @@ export const categorySectionFragment = graphql` } } cookieBanner @required(action: THROW) { - consentCategories(first: 50, orderBy: { field: RANK, direction: ASC }) @required(action: THROW) { + categories(first: 50, orderBy: { field: RANK, direction: ASC }, filter: { excludeKind: UNCATEGORISED }) @required(action: THROW) { edges { node { id @@ -252,7 +252,7 @@ const reorderCategoryMutation = graphql` reorderCookieCategory(input: $input) { cookieBanner { id - consentCategories(first: 50, orderBy: { field: RANK, direction: ASC }) { + categories(first: 50, orderBy: { field: RANK, direction: ASC }, filter: { excludeKind: UNCATEGORISED }) { edges { node { id @@ -517,7 +517,7 @@ export function CategorySection({ categoryKey, connectionId }: CategorySectionPr ); }; - const allCategories = category.cookieBanner.consentCategories.edges.map(e => e.node) ?? []; + const allCategories = category.cookieBanner.categories.edges.map(e => e.node) ?? []; const siblingCategories = allCategories.filter(c => c.id !== category.id); const selfIndex = allCategories.findIndex(c => c.id === category.id); const isFirst = selfIndex === 0; 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 5c31c01c6..c2eb96608 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 @@ -49,6 +49,15 @@ export const cookieBannerTrackersPageQuery = graphql` __typename ... on CookieBanner { ...CookieBannerTrackersPageFragment + categories(first: 50, orderBy: { field: RANK, direction: ASC }) + @required(action: THROW) { + edges { + node { + id + name + } + } + } } } } @@ -66,17 +75,18 @@ const trackersFragment = graphql` query: { type: "String", defaultValue: null } source: { type: "CookieSource", defaultValue: null } trackerType: { type: "TrackerType", defaultValue: null } + cookieCategoryId: { type: "ID", defaultValue: null } ) { - uncategorisedTrackerPatterns( + trackerPatterns( first: $first after: $after last: $last before: $before orderBy: $order - filter: { query: $query, source: $source, trackerType: $trackerType } + filter: { query: $query, source: $source, trackerType: $trackerType, cookieCategoryId: $cookieCategoryId } ) @connection( - key: "CookieBannerTrackersPage_uncategorisedTrackerPatterns" + key: "CookieBannerTrackersPage_trackerPatterns" filters: ["filter", "orderBy"] ) @required(action: THROW) { @@ -109,14 +119,19 @@ export default function CookieBannerTrackersPage({ const [queryFilter, setQueryFilter] = useState(""); const [sourceFilter, setSourceFilter] = useState(null); const [trackerTypeFilter, setTrackerTypeFilter] = useState(null); + const [categoryFilter, setCategoryFilter] = useState(null); const { data: fragmentData, ...pagination } = usePaginationFragment< CookieBannerTrackersPageRefetchQuery, CookieBannerTrackersPageFragment$key >(trackersFragment, data.node); - const connectionId = fragmentData.uncategorisedTrackerPatterns.__id; - const patterns = fragmentData.uncategorisedTrackerPatterns.edges.map(edge => edge.node) ?? []; + const connectionId = fragmentData.trackerPatterns.__id; + const patterns = fragmentData.trackerPatterns.edges.map(edge => edge.node) ?? []; + + const categories = data.node.__typename === "CookieBanner" + ? data.node.categories.edges.map(edge => edge.node) + : []; const refetchFilters = (overrides: Record = {}) => { startTransition(() => { @@ -125,6 +140,7 @@ export default function CookieBannerTrackersPage({ query: queryFilter || null, source: sourceFilter, trackerType: trackerTypeFilter, + cookieCategoryId: categoryFilter, ...overrides, }, { fetchPolicy: "network-only" }, @@ -148,12 +164,19 @@ export default function CookieBannerTrackersPage({ refetchFilters({ trackerType: newType }); }; + const handleCategoryFilterChange = (value: string) => { + const newCategory = value === "ALL" ? null : value; + setCategoryFilter(newCategory); + refetchFilters({ cookieCategoryId: newCategory }); + }; + const refetchWithFilters: ComponentProps["refetch"] = ({ order }) => { pagination.refetch({ order: { direction: order.direction, field: order.field as TrackerPatternOrderField }, query: queryFilter || null, source: sourceFilter, trackerType: trackerTypeFilter, + cookieCategoryId: categoryFilter, }); }; @@ -187,6 +210,15 @@ export default function CookieBannerTrackersPage({ +
@@ -202,6 +234,7 @@ export default function CookieBannerTrackersPage({ {__("Name")} {__("Type")} {__("Source")} + {__("Category")} {__("Last Matched")} @@ -221,10 +254,10 @@ export default function CookieBannerTrackersPage({

- {__("No uncategorised patterns")} + {__("No tracker patterns")}

- {__("All detected cookie patterns have been categorised. New patterns will appear here when detected.")} + {__("No cookie patterns have been detected yet. Patterns will appear here when detected.")}

diff --git a/apps/console/src/pages/organizations/cookie-banners/configuration/trackers/_components/MoveToCategoryDropdown.tsx b/apps/console/src/pages/organizations/cookie-banners/configuration/trackers/_components/MoveToCategoryDropdown.tsx index a13fdc9fb..b52649ae3 100644 --- a/apps/console/src/pages/organizations/cookie-banners/configuration/trackers/_components/MoveToCategoryDropdown.tsx +++ b/apps/console/src/pages/organizations/cookie-banners/configuration/trackers/_components/MoveToCategoryDropdown.tsx @@ -23,7 +23,7 @@ export const moveToCategoryDropdownQuery = graphql` node(id: $cookieBannerId) @required(action: THROW) { __typename ... on CookieBanner { - consentCategories(first: 50, orderBy: { field: RANK, direction: ASC }) + categories(first: 50, orderBy: { field: RANK, direction: ASC }) @required(action: THROW) { edges { node { @@ -53,7 +53,7 @@ export function MoveToCategoryDropdown({ return null; } - const categories = data.node.consentCategories.edges.map(e => e.node); + const categories = data.node.categories.edges.map(e => e.node); if (categories.length === 0) { return ( 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 23684eb0a..a10374b3a 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 @@ -53,6 +53,9 @@ const trackerPatternFragment = graphql` maxAgeSeconds excluded lastMatchedAt + cookieCategory { + name + } } `; @@ -316,6 +319,11 @@ export function TrackerPatternRow({ patternKey, connectionId }: TrackerPatternRo ? {srcBadge.label} : -} + + {pattern.cookieCategory + ? {pattern.cookieCategory.name} + : -} + {pattern.lastMatchedAt ? ( diff --git a/apps/console/src/pages/organizations/cookie-banners/configuration/trackers/_components/TrackerPatternRowEdit.tsx b/apps/console/src/pages/organizations/cookie-banners/configuration/trackers/_components/TrackerPatternRowEdit.tsx index 79957ff4f..cf7a49c58 100644 --- a/apps/console/src/pages/organizations/cookie-banners/configuration/trackers/_components/TrackerPatternRowEdit.tsx +++ b/apps/console/src/pages/organizations/cookie-banners/configuration/trackers/_components/TrackerPatternRowEdit.tsx @@ -62,6 +62,7 @@ export function TrackerPatternRowEdit({ {pattern} + - banner.consentCategories.edges.map(e => ({ + banner.categories.edges.map(e => ({ id: e.node.id, name: e.node.name, slug: e.node.slug, description: e.node.description, kind: e.node.kind, })), - [banner.consentCategories], + [banner.categories], ); const necessaryCategoryName = useMemo( diff --git a/e2e/console/cookie_banner_test.go b/e2e/console/cookie_banner_test.go index 88e4ab274..c14eddf2a 100644 --- a/e2e/console/cookie_banner_test.go +++ b/e2e/console/cookie_banner_test.go @@ -153,7 +153,7 @@ func TestCookieBanner_Create(t *testing.T) { query($id: ID!) { node(id: $id) { ... on CookieBanner { - consentCategories(first: 10) { + categories(first: 10) { totalCount edges { node { @@ -170,7 +170,7 @@ func TestCookieBanner_Create(t *testing.T) { var result struct { Node struct { - ConsentCategories struct { + Categories struct { TotalCount int `json:"totalCount"` Edges []struct { Node struct { @@ -179,16 +179,16 @@ func TestCookieBanner_Create(t *testing.T) { Kind string `json:"kind"` } `json:"node"` } `json:"edges"` - } `json:"consentCategories"` + } `json:"categories"` } `json:"node"` } err := owner.Execute(query, map[string]any{"id": bannerID}, &result) require.NoError(t, err) - assert.Greater(t, result.Node.ConsentCategories.TotalCount, 0) + assert.Greater(t, result.Node.Categories.TotalCount, 0) kinds := make(map[string]bool) - for _, e := range result.Node.ConsentCategories.Edges { + for _, e := range result.Node.Categories.Edges { kinds[e.Node.Kind] = true } diff --git a/e2e/console/cookie_category_test.go b/e2e/console/cookie_category_test.go index 1d2f06d3e..b750d0f58 100644 --- a/e2e/console/cookie_category_test.go +++ b/e2e/console/cookie_category_test.go @@ -314,7 +314,7 @@ func TestCookieCategory_Delete(t *testing.T) { query($id: ID!) { node(id: $id) { ... on CookieBanner { - consentCategories(first: 20) { + categories(first: 20) { edges { node { id @@ -329,14 +329,14 @@ func TestCookieCategory_Delete(t *testing.T) { var listResult struct { Node struct { - ConsentCategories struct { + Categories struct { Edges []struct { Node struct { ID string `json:"id"` Kind string `json:"kind"` } `json:"node"` } `json:"edges"` - } `json:"consentCategories"` + } `json:"categories"` } `json:"node"` } @@ -345,7 +345,7 @@ func TestCookieCategory_Delete(t *testing.T) { var necessaryCategoryID string - for _, e := range listResult.Node.ConsentCategories.Edges { + for _, e := range listResult.Node.Categories.Edges { if e.Node.Kind == "NECESSARY" { necessaryCategoryID = e.Node.ID break @@ -423,7 +423,7 @@ func TestCookieCategory_List(t *testing.T) { query($id: ID!) { node(id: $id) { ... on CookieBanner { - consentCategories(first: 20, orderBy: {field: RANK, direction: ASC}) { + categories(first: 20, orderBy: {field: RANK, direction: ASC}) { totalCount edges { node { @@ -443,7 +443,7 @@ func TestCookieCategory_List(t *testing.T) { var result struct { Node struct { - ConsentCategories struct { + Categories struct { TotalCount int `json:"totalCount"` Edges []struct { Node struct { @@ -455,7 +455,7 @@ func TestCookieCategory_List(t *testing.T) { HasNextPage bool `json:"hasNextPage"` HasPreviousPage bool `json:"hasPreviousPage"` } `json:"pageInfo"` - } `json:"consentCategories"` + } `json:"categories"` } `json:"node"` } @@ -463,13 +463,13 @@ func TestCookieCategory_List(t *testing.T) { require.NoError(t, err) // Default categories + 2 custom ones - assert.GreaterOrEqual(t, result.Node.ConsentCategories.TotalCount, 4) + assert.GreaterOrEqual(t, result.Node.Categories.TotalCount, 4) // Verify ordering (ranks should be ascending) - for i := 1; i < len(result.Node.ConsentCategories.Edges); i++ { + for i := 1; i < len(result.Node.Categories.Edges); i++ { assert.GreaterOrEqual(t, - result.Node.ConsentCategories.Edges[i].Node.Rank, - result.Node.ConsentCategories.Edges[i-1].Node.Rank, + result.Node.Categories.Edges[i].Node.Rank, + result.Node.Categories.Edges[i-1].Node.Rank, ) } }) diff --git a/pkg/coredata/tracker_pattern.go b/pkg/coredata/tracker_pattern.go index c1f2c7857..6d05068a8 100644 --- a/pkg/coredata/tracker_pattern.go +++ b/pkg/coredata/tracker_pattern.go @@ -647,7 +647,7 @@ WHERE return nil } -func (tps *TrackerPatterns) LoadUncategorisedByCookieBannerID( +func (tps *TrackerPatterns) LoadByCookieBannerID( ctx context.Context, conn pg.Querier, scope Scoper, @@ -680,22 +680,14 @@ FROM WHERE %s AND cookie_banner_id = @cookie_banner_id - AND cookie_category_id = ( - SELECT id FROM cookie_categories - WHERE cookie_banner_id = @cookie_banner_id - AND kind = @category_kind - AND %s - LIMIT 1 - ) AND %s AND %s ` - q = fmt.Sprintf(q, scope.SQLFragment(), scope.SQLFragment(), filter.SQLFragment(), cursor.SQLFragment()) + q = fmt.Sprintf(q, scope.SQLFragment(), filter.SQLFragment(), cursor.SQLFragment()) args := pgx.StrictNamedArgs{ "cookie_banner_id": cookieBannerID, - "category_kind": CookieCategoryKindUncategorised, } maps.Copy(args, scope.SQLArguments()) maps.Copy(args, filter.SQLArguments()) @@ -703,12 +695,12 @@ WHERE rows, err := conn.Query(ctx, q, args) if err != nil { - return fmt.Errorf("cannot query uncategorised tracker patterns: %w", err) + return fmt.Errorf("cannot query tracker patterns: %w", err) } patterns, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[TrackerPattern]) if err != nil { - return fmt.Errorf("cannot collect uncategorised tracker patterns: %w", err) + return fmt.Errorf("cannot collect tracker patterns: %w", err) } *tps = patterns @@ -716,7 +708,7 @@ WHERE return nil } -func (tps *TrackerPatterns) CountUncategorisedByCookieBannerID( +func (tps *TrackerPatterns) CountByCookieBannerID( ctx context.Context, conn pg.Querier, scope Scoper, @@ -731,21 +723,13 @@ FROM WHERE %s AND cookie_banner_id = @cookie_banner_id - AND cookie_category_id = ( - SELECT id FROM cookie_categories - WHERE cookie_banner_id = @cookie_banner_id - AND kind = @category_kind - AND %s - LIMIT 1 - ) AND %s ` - q = fmt.Sprintf(q, scope.SQLFragment(), scope.SQLFragment(), filter.SQLFragment()) + q = fmt.Sprintf(q, scope.SQLFragment(), filter.SQLFragment()) args := pgx.StrictNamedArgs{ "cookie_banner_id": cookieBannerID, - "category_kind": CookieCategoryKindUncategorised, } maps.Copy(args, scope.SQLArguments()) maps.Copy(args, filter.SQLArguments()) @@ -754,7 +738,7 @@ WHERE var count int if err := row.Scan(&count); err != nil { - return 0, fmt.Errorf("cannot scan count: %w", err) + return 0, fmt.Errorf("cannot count tracker patterns: %w", err) } return count, nil diff --git a/pkg/server/api/console/v1/graphql/cookie_banner.graphql b/pkg/server/api/console/v1/graphql/cookie_banner.graphql index 99292d091..ad9d376c1 100644 --- a/pkg/server/api/console/v1/graphql/cookie_banner.graphql +++ b/pkg/server/api/console/v1/graphql/cookie_banner.graphql @@ -112,6 +112,13 @@ input CookieCategoryOrder field: CookieCategoryOrderField! } +input CookieCategoryFilter + @goModel( + model: "go.probo.inc/probo/pkg/server/api/console/v1/types.CookieCategoryFilter" + ) { + excludeKind: CookieCategoryKind +} + type CookieBanner implements Node { id: ID! name: String! @@ -125,12 +132,13 @@ type CookieBanner implements Node { organization: Organization @goField(forceResolver: true) - consentCategories( + categories( first: Int after: CursorKey last: Int before: CursorKey orderBy: CookieCategoryOrder + filter: CookieCategoryFilter ): CookieCategoryConnection @goField(forceResolver: true) translations: [CookieBannerTranslation!]! @goField(forceResolver: true) @@ -146,7 +154,7 @@ type CookieBanner implements Node { filter: CookieConsentRecordFilter ): CookieConsentRecordConnection @goField(forceResolver: true) - uncategorisedTrackerPatterns( + trackerPatterns( first: Int after: CursorKey last: Int @@ -299,6 +307,7 @@ input TrackerPatternFilter query: String source: CookieSource trackerType: TrackerType + cookieCategoryId: ID } enum TrackerResourceType diff --git a/pkg/server/api/console/v1/types/tracker_pattern.go b/pkg/server/api/console/v1/types/tracker_pattern.go index 26f3bb5cd..0c8e4baf0 100644 --- a/pkg/server/api/console/v1/types/tracker_pattern.go +++ b/pkg/server/api/console/v1/types/tracker_pattern.go @@ -34,9 +34,10 @@ type ( } TrackerPatternFilter struct { - Query *string - Source *coredata.CookieSource - TrackerType *coredata.TrackerType + Query *string + Source *coredata.CookieSource + TrackerType *coredata.TrackerType + CookieCategoryID *gid.GID } )