Add uncategorised cookie category

Replace the `required` boolean column on cookie_categories with a `kind`
enum (NORMAL, NECESSARY, UNCATEGORISED). The Necessary category remains
undeletable and always-on for consent; the new Uncategorised category is
also undeletable but users can opt out of it.

When a category is deleted, its cookies are merged into the Uncategorised
category (lazy-created for legacy banners that don't have one yet).

Signed-off-by: Émile Ré <emile@getprobo.com>
This commit is contained in:
Émile Ré
2026-04-20 19:00:00 +04:00
parent 2147cded9f
commit 7cd8c516b9
18 changed files with 207 additions and 70 deletions

View File

@@ -28,7 +28,7 @@ export interface CookieItem {
export interface Category {
name: string;
description: string;
required: boolean;
kind: string;
cookies: CookieItem[];
}
@@ -164,7 +164,7 @@ export class CookieBannerClient {
const consentData: Record<string, boolean> = {};
for (const cat of cfg.categories) {
consentData[cat.name] = cat.required;
consentData[cat.name] = cat.kind === "NECESSARY";
}
return this.recordConsent("REJECT_ALL", consentData);
@@ -177,7 +177,7 @@ export class CookieBannerClient {
const consentData: Record<string, boolean> = {};
for (const cat of cfg.categories) {
consentData[cat.name] = cat.required || !!categories[cat.name];
consentData[cat.name] = cat.kind === "NECESSARY" || !!categories[cat.name];
}
return this.recordConsent("CUSTOMIZE", consentData);

View File

@@ -57,9 +57,7 @@ export class ProboCategoryList extends ProboElement {
for (const cat of categories) {
const wrapper = document.createElement("probo-category");
wrapper.setAttribute("name", cat.name);
if (cat.required) {
wrapper.setAttribute("required", "");
}
wrapper.setAttribute("kind", cat.kind);
wrapper.setAttribute("description", cat.description);
wrapper.setAttribute("cookies", JSON.stringify(cat.cookies));

View File

@@ -49,7 +49,7 @@ export class ProboCategoryToggle extends ProboElement {
if (!this.category || !this.root) return;
const name = this.category.categoryName;
const isRequired = this.category.required;
const isRequired = this.category.kind === "NECESSARY";
if (isRequired) {
this.checkbox.checked = true;

View File

@@ -21,8 +21,8 @@ export class ProboCategory extends ProboElement {
return this.getAttribute("name") ?? "Other";
}
get required(): boolean {
return this.hasAttribute("required");
get kind(): string {
return this.getAttribute("kind") ?? "NORMAL";
}
get cookies(): CookieItem[] {

View File

@@ -86,7 +86,7 @@ export class ProboCookieBannerRoot extends ProboElement implements ProboRootElem
const existing = this._client?.visitorConsent?.consent_data;
for (const cat of config.categories) {
if (cat.required) {
if (cat.kind === "NECESSARY") {
draft[cat.name] = true;
} else if (existing && cat.name in existing) {
draft[cat.name] = existing[cat.name];