Add cookie banner deletion from overview page
Wire the existing deleteCookieBanner GraphQL mutation to the cookie banners overview list. Inactive banners with delete permission show an action dropdown with a confirmed delete flow. Signed-off-by: Émile Ré <emile@getprobo.com>
This commit is contained in:
@@ -12,13 +12,25 @@
|
|||||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
// PERFORMANCE OF THIS SOFTWARE.
|
// PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
import { formatError, type GraphQLError, sprintf } from "@probo/helpers";
|
||||||
import { usePageTitle } from "@probo/hooks";
|
import { usePageTitle } from "@probo/hooks";
|
||||||
import { useTranslate } from "@probo/i18n";
|
import { useTranslate } from "@probo/i18n";
|
||||||
import { Badge, Button, Card, PageHeader } from "@probo/ui";
|
import {
|
||||||
import { type PreloadedQuery, usePreloadedQuery } from "react-relay";
|
ActionDropdown,
|
||||||
|
Badge,
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
DropdownItem,
|
||||||
|
IconTrashCan,
|
||||||
|
PageHeader,
|
||||||
|
useConfirm,
|
||||||
|
useToast,
|
||||||
|
} from "@probo/ui";
|
||||||
|
import { type PreloadedQuery, useMutation, usePreloadedQuery } from "react-relay";
|
||||||
import { Link } from "react-router";
|
import { Link } from "react-router";
|
||||||
import { graphql } from "relay-runtime";
|
import { graphql } from "relay-runtime";
|
||||||
|
|
||||||
|
import type { CookieBannersOverviewPageDeleteMutation } from "#/__generated__/core/CookieBannersOverviewPageDeleteMutation.graphql";
|
||||||
import type { CookieBannersOverviewPageQuery } from "#/__generated__/core/CookieBannersOverviewPageQuery.graphql";
|
import type { CookieBannersOverviewPageQuery } from "#/__generated__/core/CookieBannersOverviewPageQuery.graphql";
|
||||||
import { useOrganizationId } from "#/hooks/useOrganizationId";
|
import { useOrganizationId } from "#/hooks/useOrganizationId";
|
||||||
|
|
||||||
@@ -29,7 +41,10 @@ export const cookieBannersOverviewPageQuery = graphql`
|
|||||||
organization: node(id: $organizationId) {
|
organization: node(id: $organizationId) {
|
||||||
__typename
|
__typename
|
||||||
... on Organization {
|
... on Organization {
|
||||||
cookieBanners(first: 50, orderBy: { field: CREATED_AT, direction: DESC }) @required(action: THROW) {
|
cookieBanners(first: 50, orderBy: { field: CREATED_AT, direction: DESC })
|
||||||
|
@connection(key: "CookieBannersOverviewPage_cookieBanners", filters: [])
|
||||||
|
@required(action: THROW) {
|
||||||
|
__id
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
id
|
id
|
||||||
@@ -37,6 +52,7 @@ export const cookieBannersOverviewPageQuery = graphql`
|
|||||||
origin
|
origin
|
||||||
state
|
state
|
||||||
createdAt
|
createdAt
|
||||||
|
canDelete: permission(action: "core:cookie-banner:delete")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -45,6 +61,17 @@ export const cookieBannersOverviewPageQuery = graphql`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const deleteCookieBannerMutation = graphql`
|
||||||
|
mutation CookieBannersOverviewPageDeleteMutation(
|
||||||
|
$input: DeleteCookieBannerInput!
|
||||||
|
$connections: [ID!]!
|
||||||
|
) {
|
||||||
|
deleteCookieBanner(input: $input) {
|
||||||
|
deletedCookieBannerId @deleteEdge(connections: $connections)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
interface CookieBannersOverviewPageProps {
|
interface CookieBannersOverviewPageProps {
|
||||||
queryRef: PreloadedQuery<CookieBannersOverviewPageQuery>;
|
queryRef: PreloadedQuery<CookieBannersOverviewPageQuery>;
|
||||||
}
|
}
|
||||||
@@ -52,6 +79,8 @@ interface CookieBannersOverviewPageProps {
|
|||||||
export function CookieBannersOverviewPage({ queryRef }: CookieBannersOverviewPageProps) {
|
export function CookieBannersOverviewPage({ queryRef }: CookieBannersOverviewPageProps) {
|
||||||
const { __ } = useTranslate();
|
const { __ } = useTranslate();
|
||||||
const organizationId = useOrganizationId();
|
const organizationId = useOrganizationId();
|
||||||
|
const { toast } = useToast();
|
||||||
|
const confirm = useConfirm();
|
||||||
|
|
||||||
usePageTitle(__("Cookie Banners"));
|
usePageTitle(__("Cookie Banners"));
|
||||||
|
|
||||||
@@ -60,9 +89,61 @@ export function CookieBannersOverviewPage({ queryRef }: CookieBannersOverviewPag
|
|||||||
throw new Error("invalid type for node");
|
throw new Error("invalid type for node");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const connectionId = organization.cookieBanners.__id;
|
||||||
const banners = organization.cookieBanners.edges.map(e => e.node);
|
const banners = organization.cookieBanners.edges.map(e => e.node);
|
||||||
const newBannerHref = `/organizations/${organizationId}/cookie-banners/new`;
|
const newBannerHref = `/organizations/${organizationId}/cookie-banners/new`;
|
||||||
|
|
||||||
|
const [deleteCookieBanner] = useMutation<CookieBannersOverviewPageDeleteMutation>(deleteCookieBannerMutation);
|
||||||
|
|
||||||
|
const handleDelete = (bannerId: string, bannerName: string) => {
|
||||||
|
confirm(
|
||||||
|
() =>
|
||||||
|
new Promise<void>((resolve) => {
|
||||||
|
deleteCookieBanner({
|
||||||
|
variables: {
|
||||||
|
input: { cookieBannerId: bannerId },
|
||||||
|
connections: [connectionId],
|
||||||
|
},
|
||||||
|
onCompleted(_, errors) {
|
||||||
|
if (errors?.length) {
|
||||||
|
toast({
|
||||||
|
title: __("Error"),
|
||||||
|
description: errors[0].message,
|
||||||
|
variant: "error",
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
toast({
|
||||||
|
title: __("Success"),
|
||||||
|
description: __("Cookie banner deleted successfully"),
|
||||||
|
variant: "success",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
|
},
|
||||||
|
onError(error) {
|
||||||
|
toast({
|
||||||
|
title: __("Error"),
|
||||||
|
description: formatError(
|
||||||
|
__("Failed to delete cookie banner"),
|
||||||
|
error as GraphQLError,
|
||||||
|
),
|
||||||
|
variant: "error",
|
||||||
|
});
|
||||||
|
resolve();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
message: sprintf(
|
||||||
|
__("This will permanently delete the cookie banner \"%s\". This action cannot be undone."),
|
||||||
|
bannerName,
|
||||||
|
),
|
||||||
|
variant: "danger",
|
||||||
|
label: __("Delete"),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
if (banners.length === 0) {
|
if (banners.length === 0) {
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
@@ -111,6 +192,19 @@ export function CookieBannersOverviewPage({ queryRef }: CookieBannersOverviewPag
|
|||||||
<span className="text-xs text-muted-foreground">
|
<span className="text-xs text-muted-foreground">
|
||||||
{new Date(banner.createdAt).toLocaleDateString()}
|
{new Date(banner.createdAt).toLocaleDateString()}
|
||||||
</span>
|
</span>
|
||||||
|
{banner.canDelete && banner.state !== "ACTIVE" && (
|
||||||
|
<div onClick={e => e.preventDefault()}>
|
||||||
|
<ActionDropdown>
|
||||||
|
<DropdownItem
|
||||||
|
onClick={() => handleDelete(banner.id, banner.name)}
|
||||||
|
variant="danger"
|
||||||
|
icon={IconTrashCan}
|
||||||
|
>
|
||||||
|
{__("Delete")}
|
||||||
|
</DropdownItem>
|
||||||
|
</ActionDropdown>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
|
|||||||
Reference in New Issue
Block a user