diff --git a/apps/console/src/pages/organizations/cookie-banners/configuration/cookies/CookieBannerCookiesPage.tsx b/apps/console/src/pages/organizations/cookie-banners/configuration/cookies/CookieBannerCookiesPage.tsx index b87d42946..59fd024e8 100644 --- a/apps/console/src/pages/organizations/cookie-banners/configuration/cookies/CookieBannerCookiesPage.tsx +++ b/apps/console/src/pages/organizations/cookie-banners/configuration/cookies/CookieBannerCookiesPage.tsx @@ -31,8 +31,8 @@ export const cookieBannerCookiesPageQuery = graphql` __typename ... on CookieBanner { id - categories(first: 50, orderBy: { field: RANK, direction: ASC }) - @connection(key: "CookieBannerCookiesPage_categories") + consentCategories(first: 50, orderBy: { field: RANK, direction: ASC }) + @connection(key: "CookieBannerCookiesPage_consentCategories") @required(action: THROW) { __id edges { @@ -86,8 +86,8 @@ export default function CookieBannerCookiesPage({ } const banner = data.node; - const connectionId = banner.categories.__id; - const categories = banner.categories.edges.map(e => e.node); + const connectionId = banner.consentCategories.__id; + const categories = banner.consentCategories.edges.map(e => e.node); const sorted = [...categories].sort((a, b) => a.rank - b.rank); const [deleteCategory] diff --git a/apps/console/src/pages/organizations/cookie-banners/configuration/cookies/_components/CategorySection.tsx b/apps/console/src/pages/organizations/cookie-banners/configuration/cookies/_components/CategorySection.tsx index b7426f9aa..859101d97 100644 --- a/apps/console/src/pages/organizations/cookie-banners/configuration/cookies/_components/CategorySection.tsx +++ b/apps/console/src/pages/organizations/cookie-banners/configuration/cookies/_components/CategorySection.tsx @@ -80,7 +80,7 @@ export const categorySectionFragment = graphql` } } cookieBanner @required(action: THROW) { - categories(first: 50, orderBy: { field: RANK, direction: ASC }) @required(action: THROW) { + consentCategories(first: 50, orderBy: { field: RANK, direction: ASC }) @required(action: THROW) { edges { node { id @@ -451,7 +451,7 @@ export function CategorySection({ categoryKey, onDelete }: CategorySectionProps) }); }; - const allCategories = category.cookieBanner.categories.edges.map(e => e.node) ?? []; + const allCategories = category.cookieBanner.consentCategories.edges.map(e => e.node) ?? []; const siblingCategories = allCategories.filter(c => c.id !== category.id); const handleMoveCookie = (patternId: string, targetCategoryId: string) => { diff --git a/apps/console/src/pages/organizations/cookie-banners/configuration/display/_components/CategoryList.tsx b/apps/console/src/pages/organizations/cookie-banners/configuration/display/_components/CategoryList.tsx index 03908d735..ecdf7b2fa 100644 --- a/apps/console/src/pages/organizations/cookie-banners/configuration/display/_components/CategoryList.tsx +++ b/apps/console/src/pages/organizations/cookie-banners/configuration/display/_components/CategoryList.tsx @@ -25,8 +25,8 @@ import type { CategoryListReorderMutation } from "#/__generated__/core/CategoryL const categoryListFragment = graphql` fragment CategoryList_cookieBanner on CookieBanner { id - categories(first: 50, orderBy: { field: RANK, direction: ASC }) - @connection(key: "CategoryList_categories") + consentCategories(first: 50, orderBy: { field: RANK, direction: ASC }) + @connection(key: "CategoryList_consentCategories") @required(action: THROW) { __id edges { @@ -87,8 +87,8 @@ export function CategoryList({ cookieBannerKey }: CategoryListProps) { const confirm = useConfirm(); const banner = useFragment(categoryListFragment, cookieBannerKey); - const connectionId = banner.categories.__id; - const categories = banner.categories.edges.map(e => e.node); + const connectionId = banner.consentCategories.__id; + const categories = banner.consentCategories.edges.map(e => e.node); const [deleteCategory] = useMutation(deleteCategoryMutation); const [reorderCategory] = useMutation(reorderCategoryMutation); diff --git a/apps/console/src/pages/organizations/cookie-banners/configuration/translations/CookieBannerTranslationsPage.tsx b/apps/console/src/pages/organizations/cookie-banners/configuration/translations/CookieBannerTranslationsPage.tsx index 3370e5b17..f99a78d84 100644 --- a/apps/console/src/pages/organizations/cookie-banners/configuration/translations/CookieBannerTranslationsPage.tsx +++ b/apps/console/src/pages/organizations/cookie-banners/configuration/translations/CookieBannerTranslationsPage.tsx @@ -36,7 +36,7 @@ export const cookieBannerTranslationsPageQuery = graphql` language translations } - categories(first: 50, orderBy: { field: RANK, direction: ASC }) @required(action: THROW) { + consentCategories(first: 50, orderBy: { field: RANK, direction: ASC }) @required(action: THROW) { edges { node { id @@ -101,14 +101,14 @@ export default function CookieBannerTranslationsPage({ const categories = useMemo( () => - banner.categories.edges.map(e => ({ + banner.consentCategories.edges.map(e => ({ id: e.node.id, name: e.node.name, slug: e.node.slug, description: e.node.description, kind: e.node.kind, })), - [banner.categories], + [banner.consentCategories], ); const necessaryCategoryName = useMemo( diff --git a/apps/console/src/pages/organizations/cookie-banners/configuration/translations/_components/PanelTranslationSection.tsx b/apps/console/src/pages/organizations/cookie-banners/configuration/translations/_components/PanelTranslationSection.tsx index 12622c2b3..71a91c24d 100644 --- a/apps/console/src/pages/organizations/cookie-banners/configuration/translations/_components/PanelTranslationSection.tsx +++ b/apps/console/src/pages/organizations/cookie-banners/configuration/translations/_components/PanelTranslationSection.tsx @@ -42,8 +42,6 @@ export function PanelTranslationSection({ const buttonSave = useWatch({ control, name: "button_save" }); const categoryTranslations = useWatch({ control, name: "categories" }); - const visibleCategories = categories.filter(c => c.kind !== "UNCATEGORISED"); - const translatedNecessaryName = (() => { const necessaryCat = categories.find(c => c.kind === "NECESSARY"); if (!necessaryCat) return necessaryCategoryName; @@ -157,13 +155,13 @@ export function PanelTranslationSection({ - {visibleCategories.length > 0 && ( + {categories.length > 0 && (

{__("Category names")}

- {visibleCategories.map(cat => ( + {categories.map(cat => (
{cat.name} diff --git a/apps/console/src/pages/organizations/cookie-banners/configuration/translations/_components/TranslationEditor.tsx b/apps/console/src/pages/organizations/cookie-banners/configuration/translations/_components/TranslationEditor.tsx index c2fbf8eb0..96b91ae63 100644 --- a/apps/console/src/pages/organizations/cookie-banners/configuration/translations/_components/TranslationEditor.tsx +++ b/apps/console/src/pages/organizations/cookie-banners/configuration/translations/_components/TranslationEditor.tsx @@ -99,7 +99,6 @@ export function TranslationEditor({ const catDefaults: CategoryTranslations = {}; for (const cat of categories) { - if (cat.kind === "UNCATEGORISED") continue; const existing = existingCategoryTranslations?.[cat.id]; catDefaults[cat.id] = { name: existing?.name ?? "", diff --git a/pkg/cookiebanner/service.go b/pkg/cookiebanner/service.go index 3b17041ec..17484a382 100644 --- a/pkg/cookiebanner/service.go +++ b/pkg/cookiebanner/service.go @@ -428,7 +428,7 @@ func (s *Service) ensureDraftVersionForBanner( } var categories coredata.CookieCategories - if err := categories.LoadConsentCategoriesByCookieBannerID(ctx, tx, scope, bannerID); err != nil { + if err := categories.LoadAllConsentCategoriesByCookieBannerID(ctx, tx, scope, bannerID); err != nil { return nil, fmt.Errorf("cannot load cookie categories: %w", err) } @@ -1078,7 +1078,7 @@ func (s *Service) ListCookieCategoriesForBanner( err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { - if err := categories.LoadByCookieBannerID(ctx, conn, scope, bannerID, cursor); err != nil { + if err := categories.LoadConsentCategoriesByCookieBannerID(ctx, conn, scope, bannerID, cursor); err != nil { return fmt.Errorf("cannot list cookie categories: %w", err) } @@ -1105,7 +1105,7 @@ func (s *Service) CountCookieCategoriesForBanner( var categories coredata.CookieCategories var err error - count, err = categories.CountByCookieBannerID(ctx, conn, scope, bannerID) + count, err = categories.CountConsentCategoriesByCookieBannerID(ctx, conn, scope, bannerID) if err != nil { return fmt.Errorf("cannot count cookie categories: %w", err) } @@ -1943,7 +1943,7 @@ func (s *Service) GetActiveBannerConfig( } var categories coredata.CookieCategories - if err := categories.LoadConsentCategoriesByCookieBannerID(ctx, conn, scope, banner.ID); err != nil { + if err := categories.LoadAllConsentCategoriesByCookieBannerID(ctx, conn, scope, banner.ID); err != nil { return fmt.Errorf("cannot load cookie categories: %w", err) } diff --git a/pkg/coredata/cookie_category.go b/pkg/coredata/cookie_category.go index 1f6cb063c..a39f079d0 100644 --- a/pkg/coredata/cookie_category.go +++ b/pkg/coredata/cookie_category.go @@ -274,6 +274,95 @@ WHERE return count, nil } +func (c *CookieCategories) LoadConsentCategoriesByCookieBannerID( + ctx context.Context, + conn pg.Querier, + scope Scoper, + cookieBannerID gid.GID, + cursor *page.Cursor[CookieCategoryOrderField], +) error { + q := ` +SELECT + id, + organization_id, + cookie_banner_id, + name, + slug, + description, + kind, + rank, + gcm_consent_types, + posthog_consent, + created_at, + updated_at +FROM + cookie_categories +WHERE + %s + AND cookie_banner_id = @cookie_banner_id + AND kind != @excluded_kind + AND %s +` + + q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment()) + + args := pgx.StrictNamedArgs{ + "cookie_banner_id": cookieBannerID, + "excluded_kind": CookieCategoryKindUncategorised, + } + maps.Copy(args, scope.SQLArguments()) + maps.Copy(args, cursor.SQLArguments()) + + rows, err := conn.Query(ctx, q, args) + if err != nil { + return fmt.Errorf("cannot query consent cookie categories: %w", err) + } + + categories, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[CookieCategory]) + if err != nil { + return fmt.Errorf("cannot collect consent cookie categories: %w", err) + } + + *c = categories + + return nil +} + +func (c *CookieCategories) CountConsentCategoriesByCookieBannerID( + ctx context.Context, + conn pg.Querier, + scope Scoper, + cookieBannerID gid.GID, +) (int, error) { + q := ` +SELECT + COUNT(id) +FROM + cookie_categories +WHERE + %s + AND cookie_banner_id = @cookie_banner_id + AND kind != @excluded_kind +` + + q = fmt.Sprintf(q, scope.SQLFragment()) + + args := pgx.StrictNamedArgs{ + "cookie_banner_id": cookieBannerID, + "excluded_kind": CookieCategoryKindUncategorised, + } + maps.Copy(args, scope.SQLArguments()) + + row := conn.QueryRow(ctx, q, args) + + var count int + if err := row.Scan(&count); err != nil { + return 0, fmt.Errorf("cannot scan count: %w", err) + } + + return count, nil +} + func (c *CookieCategories) LoadAllByCookieBannerID( ctx context.Context, conn pg.Querier, @@ -323,9 +412,9 @@ ORDER BY return nil } -// LoadConsentCategoriesByCookieBannerID loads all categories except +// LoadAllConsentCategoriesByCookieBannerID loads all categories except // UNCATEGORISED, which is an admin-side inbox never shown to visitors. -func (c *CookieCategories) LoadConsentCategoriesByCookieBannerID( +func (c *CookieCategories) LoadAllConsentCategoriesByCookieBannerID( ctx context.Context, conn pg.Querier, scope Scoper, diff --git a/pkg/server/api/console/v1/cookie_banner_resolvers.go b/pkg/server/api/console/v1/cookie_banner_resolvers.go index 4dc209015..5cda839fb 100644 --- a/pkg/server/api/console/v1/cookie_banner_resolvers.go +++ b/pkg/server/api/console/v1/cookie_banner_resolvers.go @@ -43,8 +43,8 @@ func (r *cookieBannerResolver) Organization(ctx context.Context, obj *types.Cook return types.NewOrganization(organization), nil } -// Categories is the resolver for the categories field. -func (r *cookieBannerResolver) Categories(ctx context.Context, obj *types.CookieBanner, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.CookieCategoryOrderBy) (*types.CookieCategoryConnection, error) { +// ConsentCategories is the resolver for the consentCategories field. +func (r *cookieBannerResolver) ConsentCategories(ctx context.Context, obj *types.CookieBanner, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.CookieCategoryOrderBy) (*types.CookieCategoryConnection, error) { if err := r.authorize(ctx, obj.ID, probo.ActionCookieCategoryList); err != nil { return nil, err } diff --git a/pkg/server/api/console/v1/graphql/cookie_banner.graphql b/pkg/server/api/console/v1/graphql/cookie_banner.graphql index ed86ae7cf..9f986a777 100644 --- a/pkg/server/api/console/v1/graphql/cookie_banner.graphql +++ b/pkg/server/api/console/v1/graphql/cookie_banner.graphql @@ -102,7 +102,7 @@ type CookieBanner implements Node { organization: Organization @goField(forceResolver: true) - categories( + consentCategories( first: Int after: CursorKey last: Int