diff --git a/apps/console/src/pages/organizations/cookie-banners/configuration/cookies/_components/AddCookieRow.tsx b/apps/console/src/pages/organizations/cookie-banners/configuration/cookies/_components/AddCookieRow.tsx index 315f7ded7..ddd943864 100644 --- a/apps/console/src/pages/organizations/cookie-banners/configuration/cookies/_components/AddCookieRow.tsx +++ b/apps/console/src/pages/organizations/cookie-banners/configuration/cookies/_components/AddCookieRow.tsx @@ -12,12 +12,12 @@ // OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. +import { toMaxAgeSeconds } from "@probo/helpers"; import { useTranslate } from "@probo/i18n"; -import { Button, Input, Td, Tr } from "@probo/ui"; +import { Button, DurationInput, Input, Td, Tr } from "@probo/ui"; import { useState } from "react"; import type { CookieEntry } from "./CategorySection"; -import { DurationInput, toMaxAgeSeconds } from "./DurationInput"; interface AddCookieRowProps { isUpdating: boolean; diff --git a/apps/console/src/pages/organizations/cookie-banners/configuration/cookies/_components/EditCookieRow.tsx b/apps/console/src/pages/organizations/cookie-banners/configuration/cookies/_components/EditCookieRow.tsx index f74aad121..357b760aa 100644 --- a/apps/console/src/pages/organizations/cookie-banners/configuration/cookies/_components/EditCookieRow.tsx +++ b/apps/console/src/pages/organizations/cookie-banners/configuration/cookies/_components/EditCookieRow.tsx @@ -12,8 +12,9 @@ // OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. +import { fromMaxAgeSeconds, toMaxAgeSeconds } from "@probo/helpers"; import { useTranslate } from "@probo/i18n"; -import { Button, Input, Td, Tr } from "@probo/ui"; +import { Button, DurationInput, Input, Td, Tr } from "@probo/ui"; import { useState } from "react"; import { useFragment } from "react-relay"; import { graphql } from "relay-runtime"; @@ -21,7 +22,6 @@ import { graphql } from "relay-runtime"; import type { EditCookieRowFragment$key } from "#/__generated__/core/EditCookieRowFragment.graphql"; import type { CookieEntry } from "./CategorySection"; -import { DurationInput, fromMaxAgeSeconds, toMaxAgeSeconds } from "./DurationInput"; export const editCookieRowFragment = graphql` fragment EditCookieRowFragment on CookiePattern { diff --git a/packages/helpers/src/duration.ts b/packages/helpers/src/duration.ts index 6fdf18a36..73db1a32f 100644 --- a/packages/helpers/src/duration.ts +++ b/packages/helpers/src/duration.ts @@ -21,6 +21,16 @@ const UNITS: [number, string, string][] = [ [60, "minute", "minutes"], ]; +export const DURATION_UNITS: { value: string; label: string; seconds: number }[] = [ + { value: "seconds", label: "seconds", seconds: 1 }, + { value: "minutes", label: "minutes", seconds: 60 }, + { value: "hours", label: "hours", seconds: 3600 }, + { value: "days", label: "days", seconds: 86400 }, + { value: "weeks", label: "weeks", seconds: 604800 }, + { value: "months", label: "months", seconds: 2592000 }, + { value: "years", label: "years", seconds: 31536000 }, +]; + export function humanizeSeconds(seconds: number | null): string { if (seconds === null || seconds <= 0) return "session"; for (const [unit, singular, plural] of UNITS) { @@ -31,3 +41,23 @@ export function humanizeSeconds(seconds: number | null): string { } return `${seconds} ${seconds === 1 ? "second" : "seconds"}`; } + +export function toMaxAgeSeconds(value: string, unit: string): number | null { + const num = parseFloat(value); + if (isNaN(num) || num <= 0) return null; + const u = DURATION_UNITS.find(u => u.value === unit); + if (!u) return null; + const rounded = Math.round(num * u.seconds); + if (rounded <= 0) return null; + return rounded; +} + +export function fromMaxAgeSeconds(seconds: number | null): { value: string; unit: string } { + if (seconds === null || seconds <= 0) return { value: "", unit: "days" }; + for (const u of [...DURATION_UNITS].reverse()) { + if (seconds >= u.seconds && seconds % u.seconds === 0) { + return { value: String(seconds / u.seconds), unit: u.value }; + } + } + return { value: String(seconds), unit: "seconds" }; +} diff --git a/packages/helpers/src/index.ts b/packages/helpers/src/index.ts index 4dcb6e939..538625fc1 100644 --- a/packages/helpers/src/index.ts +++ b/packages/helpers/src/index.ts @@ -109,7 +109,12 @@ export { formatDuration, parseDate, } from "./date"; -export { humanizeSeconds } from "./duration"; +export { + humanizeSeconds, + DURATION_UNITS, + toMaxAgeSeconds, + fromMaxAgeSeconds, +} from "./duration"; export { getTrustCenterUrl } from "./trustCenter"; export { detectSocialName } from "./socialUrl"; export { formatError, type GraphQLError } from "./error"; diff --git a/apps/console/src/pages/organizations/cookie-banners/configuration/cookies/_components/DurationInput.tsx b/packages/ui/src/Atoms/Input/DurationInput.tsx similarity index 56% rename from apps/console/src/pages/organizations/cookie-banners/configuration/cookies/_components/DurationInput.tsx rename to packages/ui/src/Atoms/Input/DurationInput.tsx index fe0a43a95..f7774d63d 100644 --- a/apps/console/src/pages/organizations/cookie-banners/configuration/cookies/_components/DurationInput.tsx +++ b/packages/ui/src/Atoms/Input/DurationInput.tsx @@ -12,35 +12,9 @@ // OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. -import { Input } from "@probo/ui"; +import { DURATION_UNITS } from "@probo/helpers"; -const UNITS: { value: string; label: string; seconds: number }[] = [ - { value: "seconds", label: "seconds", seconds: 1 }, - { value: "minutes", label: "minutes", seconds: 60 }, - { value: "hours", label: "hours", seconds: 3600 }, - { value: "days", label: "days", seconds: 86400 }, - { value: "weeks", label: "weeks", seconds: 604800 }, - { value: "months", label: "months", seconds: 2592000 }, - { value: "years", label: "years", seconds: 31536000 }, -]; - -export function toMaxAgeSeconds(value: string, unit: string): number | null { - const num = parseFloat(value); - if (isNaN(num) || num <= 0) return null; - const u = UNITS.find(u => u.value === unit); - if (!u) return null; - return Math.round(num * u.seconds); -} - -export function fromMaxAgeSeconds(seconds: number | null): { value: string; unit: string } { - if (seconds === null || seconds <= 0) return { value: "", unit: "days" }; - for (const u of [...UNITS].reverse()) { - if (seconds >= u.seconds && seconds % u.seconds === 0) { - return { value: String(seconds / u.seconds), unit: u.value }; - } - } - return { value: String(seconds), unit: "seconds" }; -} +import { Input } from "./Input"; interface DurationInputProps { value: string; @@ -65,7 +39,7 @@ export function DurationInput({ value, unit, onValueChange, onUnitChange }: Dura onChange={e => onUnitChange(e.target.value)} className="rounded border border-border bg-background px-2 py-1 text-sm" > - {UNITS.map(u => ( + {DURATION_UNITS.map(u => ( ))} diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts index 8ee4b4205..87b24fdb5 100644 --- a/packages/ui/src/index.ts +++ b/packages/ui/src/index.ts @@ -39,6 +39,7 @@ export { export { Avatar } from "./Atoms/Avatar/Avatar"; export { Field } from "./Molecules/Field/Field"; export { Input } from "./Atoms/Input/Input"; +export { DurationInput } from "./Atoms/Input/DurationInput"; export { Textarea } from "./Atoms/Textarea/Textarea"; export { Option, Select, SelectGroup, SelectLabel } from "./Atoms/Select/Select"; export { Label } from "./Atoms/Label/Label"; diff --git a/pkg/cookiebanner/worker.go b/pkg/cookiebanner/worker.go index 710b534eb..ae6b0deaa 100644 --- a/pkg/cookiebanner/worker.go +++ b/pkg/cookiebanner/worker.go @@ -120,6 +120,10 @@ func (h *patternAnalysisHandler) Process(ctx context.Context, task coredata.Cook if err := prefixPattern.LoadByBannerIDAndPattern(ctx, tx, scope, task.BannerID, key.prefix); err != nil { return fmt.Errorf("cannot load existing prefix pattern %q: %w", key.prefix, err) } + + if prefixPattern.CookieCategoryID != key.categoryID || prefixPattern.MatchType != coredata.CookiePatternMatchTypePrefix { + continue + } } for _, exactPattern := range group { diff --git a/pkg/cookiebanner/worker_test.go b/pkg/cookiebanner/worker_test.go index cd3c078e1..22a1fc214 100644 --- a/pkg/cookiebanner/worker_test.go +++ b/pkg/cookiebanner/worker_test.go @@ -20,6 +20,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.probo.inc/probo/pkg/coredata" + "go.probo.inc/probo/pkg/gid" ) func TestSeparatorPrefixes(t *testing.T) { @@ -108,7 +109,7 @@ func TestFindMergeGroups(t *testing.T) { groups := findMergeGroups(patterns, 3) require.Len(t, groups, 1) - group, ok := groups["ph_phc_"] + group, ok := groups[mergeGroupKey{categoryID: gid.Nil, prefix: "ph_phc_"}] require.True(t, ok) assert.Len(t, group, 3) }, @@ -128,7 +129,7 @@ func TestFindMergeGroups(t *testing.T) { groups := findMergeGroups(patterns, 3) require.Len(t, groups, 1) - group, ok := groups["_ga_"] + group, ok := groups[mergeGroupKey{categoryID: gid.Nil, prefix: "_ga_"}] require.True(t, ok) assert.Len(t, group, 3) }, @@ -148,7 +149,7 @@ func TestFindMergeGroups(t *testing.T) { groups := findMergeGroups(patterns, 3) require.Len(t, groups, 1) - group, ok := groups["auth0_session_"] + group, ok := groups[mergeGroupKey{categoryID: gid.Nil, prefix: "auth0_session_"}] require.True(t, ok) assert.Len(t, group, 3) }, @@ -186,11 +187,11 @@ func TestFindMergeGroups(t *testing.T) { groups := findMergeGroups(patterns, 3) require.Len(t, groups, 2) - barGroup, ok := groups["foo_bar_"] + barGroup, ok := groups[mergeGroupKey{categoryID: gid.Nil, prefix: "foo_bar_"}] require.True(t, ok) assert.Len(t, barGroup, 3) - bazGroup, ok := groups["foo_baz_"] + bazGroup, ok := groups[mergeGroupKey{categoryID: gid.Nil, prefix: "foo_baz_"}] require.True(t, ok) assert.Len(t, bazGroup, 3) }, @@ -211,7 +212,7 @@ func TestFindMergeGroups(t *testing.T) { groups := findMergeGroups(patterns, 3) require.Len(t, groups, 1) - group, ok := groups["ph_phc_"] + group, ok := groups[mergeGroupKey{categoryID: gid.Nil, prefix: "ph_phc_"}] require.True(t, ok) assert.Len(t, group, 3) }, @@ -235,7 +236,7 @@ func TestFindMergeGroups(t *testing.T) { groups := findMergeGroups(patterns, 3) require.Len(t, groups, 1) - group := groups["ph_phc_"] + group := groups[mergeGroupKey{categoryID: gid.Nil, prefix: "ph_phc_"}] assert.Len(t, group, 3) }, ) @@ -257,11 +258,11 @@ func TestFindMergeGroups(t *testing.T) { groups := findMergeGroups(patterns, 3) require.Len(t, groups, 2) - phcGroup, ok := groups["ph_phc_"] + phcGroup, ok := groups[mergeGroupKey{categoryID: gid.Nil, prefix: "ph_phc_"}] require.True(t, ok) assert.Len(t, phcGroup, 3) - sessionGroup, ok := groups["ph_session_"] + sessionGroup, ok := groups[mergeGroupKey{categoryID: gid.Nil, prefix: "ph_session_"}] require.True(t, ok) assert.Len(t, sessionGroup, 3) },