diff --git a/apps/console/src/pages/organizations/cookie-banners/configuration/resources/CookieBannerResourcesPage.tsx b/apps/console/src/pages/organizations/cookie-banners/configuration/resources/CookieBannerResourcesPage.tsx index 3fdfb9e49..ace5afc6d 100644 --- a/apps/console/src/pages/organizations/cookie-banners/configuration/resources/CookieBannerResourcesPage.tsx +++ b/apps/console/src/pages/organizations/cookie-banners/configuration/resources/CookieBannerResourcesPage.tsx @@ -187,8 +187,9 @@ export default function CookieBannerResourcesPage({ {__("Type")} {__("Origin")} {__("Path")} + {__("Category")} {__("Last Detected")} - + diff --git a/apps/console/src/pages/organizations/cookie-banners/configuration/resources/_components/TrackerResourceRow.tsx b/apps/console/src/pages/organizations/cookie-banners/configuration/resources/_components/TrackerResourceRow.tsx index f1d8afcac..35b1aa79a 100644 --- a/apps/console/src/pages/organizations/cookie-banners/configuration/resources/_components/TrackerResourceRow.tsx +++ b/apps/console/src/pages/organizations/cookie-banners/configuration/resources/_components/TrackerResourceRow.tsx @@ -16,9 +16,9 @@ import { EyeIcon, EyeSlashIcon } from "@phosphor-icons/react"; import { formatError, type GraphQLError } 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 { TrackerResourceRowDeleteMutation } from "#/__generated__/core/TrackerResourceRowDeleteMutation.graphql"; import type { TrackerResourceRowFragment$key } from "#/__generated__/core/TrackerResourceRowFragment.graphql"; import type { TrackerResourceRowMoveMutation } from "#/__generated__/core/TrackerResourceRowMoveMutation.graphql"; import type { TrackerResourceRowUpdateMutation } from "#/__generated__/core/TrackerResourceRowUpdateMutation.graphql"; -import { - MoveToCategoryDropdown, - moveToCategoryDropdownQuery, -} from "../../trackers/_components/MoveToCategoryDropdown"; +import { MoveToCategorySelect } from "../../trackers/_components/MoveToCategorySelect"; import { TrackerResourceRowEdit } from "./TrackerResourceRowEdit"; @@ -54,6 +49,10 @@ const trackerResourceFragment = graphql` description excluded lastDetectedAt + cookieCategory { + id + name + } } `; @@ -147,22 +146,10 @@ export function TrackerResourceRow({ resourceKey, connectionId }: TrackerResourc const { __ } = useTranslate(); const { toast } = useToast(); const confirm = useConfirm(); - const { cookieBannerId } = useParams<{ cookieBannerId: string }>(); const resource = useFragment(trackerResourceFragment, resourceKey); const typeBadge = resourceTypeBadge(resource.type, __); const [isEditing, setIsEditing] = useState(false); - const [categoryQueryRef, loadCategoryQuery] - = useQueryLoader(moveToCategoryDropdownQuery); - - const handleCategoryDropdownOpen = useCallback( - (open: boolean) => { - if (open && cookieBannerId) { - loadCategoryQuery({ cookieBannerId }); - } - }, - [loadCategoryQuery, cookieBannerId], - ); const [deleteResource] = useMutation(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 })} + /> + )} + /> +
+ + +