diff --git a/apps/console/src/pages/organizations/cookie-banners/configuration/consent-records/CookieBannerConsentRecordPage.tsx b/apps/console/src/pages/organizations/cookie-banners/configuration/consent-records/CookieBannerConsentRecordPage.tsx
index abd6033db..c0e6036b0 100644
--- a/apps/console/src/pages/organizations/cookie-banners/configuration/consent-records/CookieBannerConsentRecordPage.tsx
+++ b/apps/console/src/pages/organizations/cookie-banners/configuration/consent-records/CookieBannerConsentRecordPage.tsx
@@ -49,6 +49,7 @@ export const cookieBannerConsentRecordPageQuery = graphql`
kind
cookies {
name
+ trackerType
maxAgeSeconds
description
}
@@ -235,7 +236,7 @@ export default function CookieBannerConsentRecordPage({
{cookie.name}
- {humanizeSeconds(cookie.maxAgeSeconds ?? null)}
+ {humanizeSeconds(cookie.maxAgeSeconds ?? null, cookie.trackerType)}
|
{cookie.description}
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 dd8911da9..506a16fd1 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
@@ -795,7 +795,7 @@ export function CategorySection({ categoryKey, connectionId }: CategorySectionPr
})()}
|
- {humanizeSeconds(pattern.maxAgeSeconds ?? null)}
+ {humanizeSeconds(pattern.maxAgeSeconds ?? null, pattern.trackerType)}
|
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 7a71c4a56..f342cbf03 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
@@ -161,7 +161,7 @@ export function TrackerPatternPropertiesSection({
- {humanizeSeconds(pattern.maxAgeSeconds ?? null)}
+ {humanizeSeconds(pattern.maxAgeSeconds ?? null, pattern.trackerType)}
{pattern.description && (
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 54ed0c506..f6539abe3 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
@@ -28,7 +28,6 @@ import {
} from "@probo/ui";
import { useState } from "react";
import { graphql, useFragment, useMutation } from "react-relay";
-import { ConnectionHandler } from "relay-runtime";
import type { TrackerPatternRowDeleteMutation } from "#/__generated__/core/TrackerPatternRowDeleteMutation.graphql";
import type { TrackerPatternRowFragment$key } from "#/__generated__/core/TrackerPatternRowFragment.graphql";
@@ -94,6 +93,8 @@ const movePatternMutation = graphql`
id
cookieCategory {
id
+ name
+ kind
}
}
cookieBanner {
@@ -214,17 +215,6 @@ export function TrackerPatternRow({ patternKey, connectionId }: TrackerPatternRo
targetCookieCategoryId: targetCategoryId,
},
},
- updater(store) {
- const payload = store.getRootField("moveTrackerPatternToCategory");
- if (!payload?.getLinkedRecord("trackerPattern")) {
- return;
- }
-
- const conn = store.get(connectionId);
- if (conn) {
- ConnectionHandler.deleteNode(conn, pattern.id);
- }
- },
onCompleted(_, errors) {
if (errors?.length) {
toast({ title: __("Error"), description: errors[0].message, variant: "error" });
@@ -392,7 +382,7 @@ export function TrackerPatternRow({ patternKey, connectionId }: TrackerPatternRo
|
- {humanizeSeconds(pattern.maxAgeSeconds ?? null)}
+ {humanizeSeconds(pattern.maxAgeSeconds ?? null, pattern.trackerType)}
|
{pattern.lastMatchedAt
diff --git a/packages/cookie-banner/src/components/cookie-list.ts b/packages/cookie-banner/src/components/cookie-list.ts
index ed318a04c..fe833458a 100644
--- a/packages/cookie-banner/src/components/cookie-list.ts
+++ b/packages/cookie-banner/src/components/cookie-list.ts
@@ -52,8 +52,8 @@ export class ProboCookieList extends ProboElement {
if (!this.template) return;
const duration = cookie.max_age_seconds != null
- ? humanizeDuration(cookie.max_age_seconds, lang)
- : humanizeDuration(0, lang);
+ ? humanizeDuration(cookie.max_age_seconds, lang, cookie.tracker_type)
+ : humanizeDuration(0, lang, cookie.tracker_type);
const type = getTrackerTypeLabel(cookie.tracker_type);
diff --git a/packages/cookie-banner/src/cookie-utils.ts b/packages/cookie-banner/src/cookie-utils.ts
index de94a09eb..31c479d2d 100644
--- a/packages/cookie-banner/src/cookie-utils.ts
+++ b/packages/cookie-banner/src/cookie-utils.ts
@@ -13,6 +13,16 @@
// PERFORMANCE OF THIS SOFTWARE.
import { interpolate } from "./i18n";
+import type { TrackerType } from "./types";
+
+// Tracker types whose data persists until explicitly cleared. When such a
+// tracker has no max-age, its lifetime is "persistent" rather than "session"
+// (cookies and session storage are cleared when the session/tab ends).
+const PERSISTENT_TRACKER_TYPES: ReadonlySet = new Set([
+ "LOCAL_STORAGE",
+ "INDEXED_DB",
+ "CACHE_STORAGE",
+]);
interface DurationTexts {
[key: string]: string;
@@ -35,6 +45,7 @@ const durationTextsByLanguage: Record = {
duration_second_one: "{{count}} second",
duration_second_other: "{{count}} seconds",
duration_session: "session",
+ duration_persistent: "persistent",
},
fr: {
duration_year_one: "{{count}} an",
@@ -52,6 +63,7 @@ const durationTextsByLanguage: Record = {
duration_second_one: "{{count}} seconde",
duration_second_other: "{{count}} secondes",
duration_session: "session",
+ duration_persistent: "persistant",
},
de: {
duration_year_one: "{{count}} Jahr",
@@ -69,6 +81,7 @@ const durationTextsByLanguage: Record = {
duration_second_one: "{{count}} Sekunde",
duration_second_other: "{{count}} Sekunden",
duration_session: "Sitzung",
+ duration_persistent: "dauerhaft",
},
es: {
duration_year_one: "{{count}} año",
@@ -86,6 +99,7 @@ const durationTextsByLanguage: Record = {
duration_second_one: "{{count}} segundo",
duration_second_other: "{{count}} segundos",
duration_session: "sesión",
+ duration_persistent: "persistente",
},
};
@@ -107,9 +121,17 @@ const DURATION_UNITS: [number, string, number][] = [
[1, "duration_second", 0],
];
-export function humanizeDuration(seconds: number, lang?: string): string {
+export function humanizeDuration(
+ seconds: number,
+ lang?: string,
+ trackerType?: TrackerType,
+): string {
const texts = getDurationTexts(lang);
- if (seconds <= 0) return texts.duration_session;
+ if (seconds <= 0) {
+ return trackerType && PERSISTENT_TRACKER_TYPES.has(trackerType)
+ ? texts.duration_persistent
+ : texts.duration_session;
+ }
let remaining = seconds;
const parts: string[] = [];
diff --git a/packages/helpers/src/duration.ts b/packages/helpers/src/duration.ts
index f8d5cf229..2f1c2cf77 100644
--- a/packages/helpers/src/duration.ts
+++ b/packages/helpers/src/duration.ts
@@ -22,8 +22,24 @@ export const DURATION_UNITS: { value: string; label: string; singular: string; s
{ value: "years", label: "years", singular: "year", seconds: 31536000, snap: 21 * 24 * 3600 },
] as const;
-export function humanizeSeconds(seconds: number | null): string {
- if (seconds === null || seconds <= 0) return "session";
+// Tracker types whose data persists until explicitly cleared. When such a
+// tracker has no max-age, its lifetime is "persistent" rather than "session"
+// (cookies and session storage are cleared when the session/tab ends).
+const PERSISTENT_TRACKER_TYPES = new Set([
+ "LOCAL_STORAGE",
+ "INDEXED_DB",
+ "CACHE_STORAGE",
+]);
+
+export function humanizeSeconds(
+ seconds: number | null,
+ trackerType?: string | null,
+): string {
+ if (seconds === null || seconds <= 0) {
+ return trackerType && PERSISTENT_TRACKER_TYPES.has(trackerType)
+ ? "persistent"
+ : "session";
+ }
let remaining = seconds;
const parts: string[] = [];
|