(deleteResourceMutation);
@@ -309,6 +296,13 @@ export function TrackerResourceRow({ resourceKey, connectionId }: TrackerResourc
|
{resource.path}
|
+
+
+ |
{resource.lastDetectedAt
? (
@@ -318,7 +312,7 @@ export function TrackerResourceRow({ resourceKey, connectionId }: TrackerResourc
)
: -}
|
-
+ |
-
-
-
- )}
- >
- {categoryQueryRef && (
-
-
-
- )}
-
-
-
+
+
+ {resource.excluded ? __("Include") : __("Exclude")}
+
+
+ {__("Delete")}
+
+
|
diff --git a/apps/console/src/pages/organizations/cookie-banners/configuration/resources/_components/TrackerResourceRowEdit.tsx b/apps/console/src/pages/organizations/cookie-banners/configuration/resources/_components/TrackerResourceRowEdit.tsx
index ad5c4bb8d..84a8bb39c 100644
--- a/apps/console/src/pages/organizations/cookie-banners/configuration/resources/_components/TrackerResourceRowEdit.tsx
+++ b/apps/console/src/pages/organizations/cookie-banners/configuration/resources/_components/TrackerResourceRowEdit.tsx
@@ -61,7 +61,7 @@ export function TrackerResourceRowEdit({
placeholder={__("Display name")}
/>
-
+ |
{__("Third party")}
{__("Source")}
{__("Category")} |
+ {__("Max Age")} |
{__("Last Matched")}
- |
+ |
diff --git a/apps/console/src/pages/organizations/cookie-banners/configuration/trackers/_components/MoveToCategorySelect.tsx b/apps/console/src/pages/organizations/cookie-banners/configuration/trackers/_components/MoveToCategorySelect.tsx
new file mode 100644
index 000000000..1e8adc150
--- /dev/null
+++ b/apps/console/src/pages/organizations/cookie-banners/configuration/trackers/_components/MoveToCategorySelect.tsx
@@ -0,0 +1,105 @@
+// Copyright (c) 2026 Probo Inc .
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+import { useTranslate } from "@probo/i18n";
+import { Option, Select } from "@probo/ui";
+import { Suspense, useCallback } from "react";
+import { type PreloadedQuery, usePreloadedQuery, useQueryLoader } from "react-relay";
+import { useParams } from "react-router";
+
+import type { MoveToCategoryDropdownQuery } from "#/__generated__/core/MoveToCategoryDropdownQuery.graphql";
+
+import { moveToCategoryDropdownQuery } from "./MoveToCategoryDropdown";
+
+interface MoveToCategorySelectProps {
+ currentCategoryId?: string;
+ currentCategoryName?: string;
+ onSelect: (categoryId: string) => void;
+}
+
+export function MoveToCategorySelect({
+ currentCategoryId,
+ currentCategoryName,
+ onSelect,
+}: MoveToCategorySelectProps) {
+ const { cookieBannerId } = useParams<{ cookieBannerId: string }>();
+ const [categoryQueryRef, loadCategoryQuery]
+ = useQueryLoader(moveToCategoryDropdownQuery);
+
+ const handleOpenChange = useCallback(
+ (open: boolean) => {
+ if (open && cookieBannerId) {
+ loadCategoryQuery({ cookieBannerId });
+ }
+ },
+ [loadCategoryQuery, cookieBannerId],
+ );
+
+ const handleValueChange = useCallback(
+ (categoryId: string) => {
+ if (categoryId !== currentCategoryId) {
+ onSelect(categoryId);
+ }
+ },
+ [currentCategoryId, onSelect],
+ );
+
+ return (
+
+ );
+}
+
+interface MoveToCategoryOptionsProps {
+ queryRef: PreloadedQuery;
+}
+
+function MoveToCategoryOptions({ queryRef }: MoveToCategoryOptionsProps) {
+ const { __ } = useTranslate();
+ const data = usePreloadedQuery(moveToCategoryDropdownQuery, queryRef);
+
+ if (data.node.__typename !== "CookieBanner") {
+ return null;
+ }
+
+ const categories = data.node.categories.edges.map(e => e.node);
+
+ if (categories.length === 0) {
+ return (
+
+ );
+ }
+
+ return (
+ <>
+ {categories.map(cat => (
+
+ ))}
+ >
+ );
+}
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 379d2fc92..fa01a0c0c 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
@@ -13,12 +13,12 @@
// PERFORMANCE OF THIS SOFTWARE.
import { EyeIcon, EyeSlashIcon } from "@phosphor-icons/react";
-import { formatError, getTrackerSourceBadge, getTrackerTypeBadge, type GraphQLError } from "@probo/helpers";
+import { formatError, getTrackerSourceBadge, getTrackerTypeBadge, type GraphQLError, humanizeSeconds } from "@probo/helpers";
import { useTranslate } from "@probo/i18n";
import {
+ ActionDropdown,
Badge,
- Dropdown,
- IconArrowBoxLeft,
+ DropdownItem,
IconPencil,
IconTrashCan,
Td,
@@ -26,21 +26,16 @@ import {
useConfirm,
useToast,
} from "@probo/ui";
-import { Suspense, useCallback, useState } from "react";
-import { graphql, useFragment, useMutation, useQueryLoader } from "react-relay";
-import { useParams } from "react-router";
+import { useState } from "react";
+import { graphql, useFragment, useMutation } from "react-relay";
import { ConnectionHandler } from "relay-runtime";
-import type { MoveToCategoryDropdownQuery } from "#/__generated__/core/MoveToCategoryDropdownQuery.graphql";
import type { TrackerPatternRowDeleteMutation } from "#/__generated__/core/TrackerPatternRowDeleteMutation.graphql";
import type { TrackerPatternRowFragment$key } from "#/__generated__/core/TrackerPatternRowFragment.graphql";
import type { TrackerPatternRowMoveMutation } from "#/__generated__/core/TrackerPatternRowMoveMutation.graphql";
import type { TrackerPatternRowUpdateMutation } from "#/__generated__/core/TrackerPatternRowUpdateMutation.graphql";
-import {
- MoveToCategoryDropdown,
- moveToCategoryDropdownQuery,
-} from "./MoveToCategoryDropdown";
+import { MoveToCategorySelect } from "./MoveToCategorySelect";
import { TrackerPatternRowEdit } from "./TrackerPatternRowEdit";
const trackerPatternFragment = graphql`
@@ -54,6 +49,7 @@ const trackerPatternFragment = graphql`
excluded
lastMatchedAt
cookieCategory {
+ id
name
}
thirdParty {
@@ -143,21 +139,9 @@ export function TrackerPatternRow({ patternKey, connectionId }: TrackerPatternRo
const { __ } = useTranslate();
const { toast } = useToast();
const confirm = useConfirm();
- const { cookieBannerId } = useParams<{ cookieBannerId: string }>();
const pattern = useFragment(trackerPatternFragment, patternKey);
const [isEditing, setIsEditing] = useState(false);
- const [categoryQueryRef, loadCategoryQuery]
- = useQueryLoader(moveToCategoryDropdownQuery);
-
- const handleCategoryDropdownOpen = useCallback(
- (open: boolean) => {
- if (open && cookieBannerId) {
- loadCategoryQuery({ cookieBannerId });
- }
- },
- [loadCategoryQuery, cookieBannerId],
- );
const [deletePattern]
= useMutation(deletePatternMutation);
@@ -229,6 +213,22 @@ export function TrackerPatternRow({ patternKey, connectionId }: TrackerPatternRo
});
};
+ const handleMoveWithConfirm = (targetCategoryId: string) => {
+ if (targetCategoryId === pattern.cookieCategory?.id) {
+ return;
+ }
+ confirm(
+ () => {
+ handleMove(targetCategoryId);
+ },
+ {
+ message: __("Moving this tracker to a category will create a third party for it (or link an existing one) if it doesn't have one yet. Continue?"),
+ variant: "primary",
+ label: __("Move"),
+ },
+ );
+ };
+
const handleToggleExcluded = () => {
updatePattern({
variables: {
@@ -323,10 +323,15 @@ export function TrackerPatternRow({ patternKey, connectionId }: TrackerPatternRo
? {srcBadge.label}
: -}
+
+
+ |
- {pattern.cookieCategory
- ? {pattern.cookieCategory.name}
- : -}
+ {humanizeSeconds(pattern.maxAgeSeconds ?? null)}
|
{pattern.lastMatchedAt
@@ -337,7 +342,7 @@ export function TrackerPatternRow({ patternKey, connectionId }: TrackerPatternRo
)
: -}
|
-
+ |
-
-
-
- )}
- >
- {categoryQueryRef && (
-
-
-
- )}
-
-
-
+
+
+ {pattern.excluded ? __("Include") : __("Exclude")}
+
+
+ {__("Delete")}
+
+
|
diff --git a/apps/console/src/pages/organizations/cookie-banners/configuration/trackers/_components/TrackerPatternRowEdit.tsx b/apps/console/src/pages/organizations/cookie-banners/configuration/trackers/_components/TrackerPatternRowEdit.tsx
index cf7a49c58..20c2a4472 100644
--- a/apps/console/src/pages/organizations/cookie-banners/configuration/trackers/_components/TrackerPatternRowEdit.tsx
+++ b/apps/console/src/pages/organizations/cookie-banners/configuration/trackers/_components/TrackerPatternRowEdit.tsx
@@ -58,44 +58,45 @@ export function TrackerPatternRowEdit({
return (
- |
- {pattern}
- |
- |
- |
-
- (
- field.onChange({ ...field.value, value: v })}
- onUnitChange={u => field.onChange({ ...field.value, unit: u })}
- />
- )}
- />
- |
-
-
-
-
-
+
+
+ {pattern}
+
+
+
+
+
+
+
+ (
+ field.onChange({ ...field.value, value: v })}
+ onUnitChange={u => field.onChange({ ...field.value, unit: u })}
+ />
+ )}
+ />
+
+
+
+
|
|
|