Add GCM consent type mapping to console UI

Display Google Consent Mode mappings as badges on each
category and add a checkbox selector in the edit form
so users can configure which GCM signals each category
controls.

Signed-off-by: Émile Ré <emile@getprobo.com>
This commit is contained in:
Émile Ré
2026-04-24 13:51:42 +04:00
parent 4436059483
commit 076e0129f6
2 changed files with 64 additions and 3 deletions

View File

@@ -59,6 +59,7 @@ export const categorySectionFragment = graphql`
slug slug
description description
kind kind
gcmConsentTypes
cookies(first: 100, orderBy: { field: CREATED_AT, direction: ASC }) cookies(first: 100, orderBy: { field: CREATED_AT, direction: ASC })
@connection(key: "CategorySection_cookies", filters: []) @connection(key: "CategorySection_cookies", filters: [])
@required(action: THROW) { @required(action: THROW) {
@@ -97,6 +98,7 @@ const updateCategoryMutation = graphql`
slug slug
description description
rank rank
gcmConsentTypes
updatedAt updatedAt
} }
cookieBanner { cookieBanner {
@@ -237,7 +239,7 @@ export function CategorySection({ categoryKey, onDelete }: CategorySectionProps)
const cookies = category.cookies.edges.map(e => e.node); const cookies = category.cookies.edges.map(e => e.node);
const isMutating = isUpdating || isCreating || isUpdatingCookie; const isMutating = isUpdating || isCreating || isUpdatingCookie;
const handleSaveCategory = (name: string, slug: string, description: string) => { const handleSaveCategory = (name: string, slug: string, description: string, gcmConsentTypes: string[]) => {
updateCategory({ updateCategory({
variables: { variables: {
input: { input: {
@@ -245,6 +247,7 @@ export function CategorySection({ categoryKey, onDelete }: CategorySectionProps)
name, name,
slug, slug,
description, description,
gcmConsentTypes,
}, },
}, },
onCompleted(_response, errors) { onCompleted(_response, errors) {
@@ -481,6 +484,7 @@ export function CategorySection({ categoryKey, onDelete }: CategorySectionProps)
name={category.name} name={category.name}
slug={category.slug} slug={category.slug}
description={category.description} description={category.description}
gcmConsentTypes={[...category.gcmConsentTypes]}
isUpdating={isUpdating} isUpdating={isUpdating}
onSave={handleSaveCategory} onSave={handleSaveCategory}
onCancel={() => setIsEditingCategory(false)} onCancel={() => setIsEditingCategory(false)}
@@ -525,6 +529,18 @@ export function CategorySection({ categoryKey, onDelete }: CategorySectionProps)
&quot; &quot;
</code> </code>
</p> </p>
{category.gcmConsentTypes.length > 0 && (
<div className="mt-2 flex items-center gap-1.5">
<span className="text-xs text-txt-secondary/70">
{__("Google Consent Mode:")}
</span>
{category.gcmConsentTypes.map(type => (
<Badge key={type} variant="neutral">
{type}
</Badge>
))}
</div>
)}
</> </>
)} )}
</div> </div>

View File

@@ -16,12 +16,23 @@ import { useTranslate } from "@probo/i18n";
import { Button, Input, Textarea } from "@probo/ui"; import { Button, Input, Textarea } from "@probo/ui";
import { useState } from "react"; import { useState } from "react";
const GCM_CONSENT_TYPES = [
"analytics_storage",
"ad_storage",
"ad_user_data",
"ad_personalization",
"functionality_storage",
"personalization_storage",
"security_storage",
] as const;
interface EditCategoryFormProps { interface EditCategoryFormProps {
name: string; name: string;
slug: string; slug: string;
description: string; description: string;
gcmConsentTypes: string[];
isUpdating: boolean; isUpdating: boolean;
onSave: (name: string, slug: string, description: string) => void; onSave: (name: string, slug: string, description: string, gcmConsentTypes: string[]) => void;
onCancel: () => void; onCancel: () => void;
} }
@@ -29,6 +40,7 @@ export function EditCategoryForm({
name, name,
slug, slug,
description, description,
gcmConsentTypes,
isUpdating, isUpdating,
onSave, onSave,
onCancel, onCancel,
@@ -37,6 +49,15 @@ export function EditCategoryForm({
const [editName, setEditName] = useState(name); const [editName, setEditName] = useState(name);
const [editSlug, setEditSlug] = useState(slug); const [editSlug, setEditSlug] = useState(slug);
const [editDescription, setEditDescription] = useState(description); const [editDescription, setEditDescription] = useState(description);
const [editGcmTypes, setEditGcmTypes] = useState<string[]>(gcmConsentTypes);
const toggleGcmType = (type: string) => {
setEditGcmTypes(prev =>
prev.includes(type)
? prev.filter(t => t !== type)
: [...prev, type],
);
};
return ( return (
<div className="space-y-3"> <div className="space-y-3">
@@ -57,11 +78,35 @@ export function EditCategoryForm({
placeholder={__("Category description")} placeholder={__("Category description")}
rows={2} rows={2}
/> />
<div>
<label className="text-sm font-medium">
{__("Google Consent Mode")}
</label>
<p className="text-xs text-muted-foreground mb-2">
{__("Select the Google Consent Mode signals this category controls.")}
</p>
<div className="flex flex-wrap gap-2">
{GCM_CONSENT_TYPES.map(type => (
<label
key={type}
className="flex items-center gap-1.5 text-xs cursor-pointer"
>
<input
type="checkbox"
checked={editGcmTypes.includes(type)}
onChange={() => toggleGcmType(type)}
className="rounded"
/>
<code className="font-mono">{type}</code>
</label>
))}
</div>
</div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Button <Button
onClick={() => { onClick={() => {
if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(editSlug)) return; if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(editSlug)) return;
onSave(editName, editSlug, editDescription); onSave(editName, editSlug, editDescription, editGcmTypes);
}} }}
disabled={isUpdating} disabled={isUpdating}
> >