From 5e6735da2078a5c50fd74fc8c617280a4a4f0462 Mon Sep 17 00:00:00 2001 From: Raquel Smith Date: Fri, 19 Jun 2026 16:59:54 -0700 Subject: [PATCH 1/2] feat(canvas): right-click a canvas in the nav to delete it Add a context menu to canvas rows in the channel navigation tree with a Delete action. Wraps DashboardRow in the same @posthog/quill ContextMenu primitives TaskRow already uses; left-click still navigates to the canvas. Delete calls the existing useDashboardMutations().deleteDashboard, which invalidates the list cache so the row disappears, disables the item while the delete is in flight, and toasts on failure. If you delete the canvas you're currently viewing, it falls back to the channel index. Generated-By: PostHog Code Task-Id: d25cc24c-41de-4106-85f3-13c90eaa3309 --- .../canvas/components/ChannelsList.tsx | 71 +++++++++++++++---- 1 file changed, 57 insertions(+), 14 deletions(-) diff --git a/packages/ui/src/features/canvas/components/ChannelsList.tsx b/packages/ui/src/features/canvas/components/ChannelsList.tsx index 8839bbe36..6da5bcf72 100644 --- a/packages/ui/src/features/canvas/components/ChannelsList.tsx +++ b/packages/ui/src/features/canvas/components/ChannelsList.tsx @@ -56,7 +56,10 @@ import { useChannelTaskMutations, useChannelTasks, } from "@posthog/ui/features/canvas/hooks/useChannelTasks"; -import { useDashboards } from "@posthog/ui/features/canvas/hooks/useDashboards"; +import { + useDashboardMutations, + useDashboards, +} from "@posthog/ui/features/canvas/hooks/useDashboards"; import { TaskIcon } from "@posthog/ui/features/sidebar/components/items/TaskIcon"; import { useTaskPrStatus } from "@posthog/ui/features/sidebar/useTaskPrStatus"; import { useTasks } from "@posthog/ui/features/tasks/useTasks"; @@ -248,7 +251,8 @@ function ChildRow({ ); } -// A single saved canvas under a channel — navigates to its detail view. +// A single saved canvas under a channel — navigates to its detail view, with a +// right-click menu to delete it. function DashboardRow({ channelId, dashboard, @@ -259,19 +263,58 @@ function DashboardRow({ active: boolean; }) { const navigate = useNavigate(); - return ( - - navigate({ - to: "/website/$channelId/dashboards/$dashboardId", - params: { channelId, dashboardId: dashboard.id }, - }) + const pathname = useRouterState({ select: (s) => s.location.pathname }); + const { deleteDashboard, isDeleting } = useDashboardMutations(); + + const onDelete = async () => { + try { + await deleteDashboard(dashboard.id); + if (pathname === `/website/${channelId}/dashboards/${dashboard.id}`) { + void navigate({ + to: "/website/$channelId", + params: { channelId }, + }); } - /> + } catch (error) { + toast.error("Couldn't delete canvas", { + description: error instanceof Error ? error.message : String(error), + }); + } + }; + + return ( + + + + + navigate({ + to: "/website/$channelId/dashboards/$dashboardId", + params: { channelId, dashboardId: dashboard.id }, + }) + } + /> + + } + /> + + + void onDelete()} + > + + Delete + + + ); } From c435c7fe1ff1a862bb050429c68255d07c7cd5aa Mon Sep 17 00:00:00 2001 From: Raquel Smith Date: Fri, 19 Jun 2026 17:13:58 -0700 Subject: [PATCH 2/2] fix(canvas): confirm before deleting a canvas, broaden redirect guard Address review feedback on the right-click delete menu: - Add an AlertDialog confirmation before the permanent delete instead of firing immediately on click, following the ReplaceSkillDialog pattern. - Use pathname.startsWith for the post-delete redirect so deleting while on a child route of the canvas still navigates away (mirrors ChannelMenu.onDelete). Generated-By: PostHog Code Task-Id: d25cc24c-41de-4106-85f3-13c90eaa3309 --- .../canvas/components/ChannelsList.tsx | 102 ++++++++++++------ 1 file changed, 68 insertions(+), 34 deletions(-) diff --git a/packages/ui/src/features/canvas/components/ChannelsList.tsx b/packages/ui/src/features/canvas/components/ChannelsList.tsx index 6da5bcf72..19dde93f1 100644 --- a/packages/ui/src/features/canvas/components/ChannelsList.tsx +++ b/packages/ui/src/features/canvas/components/ChannelsList.tsx @@ -65,7 +65,7 @@ import { useTaskPrStatus } from "@posthog/ui/features/sidebar/useTaskPrStatus"; import { useTasks } from "@posthog/ui/features/tasks/useTasks"; import { useWorkspace } from "@posthog/ui/features/workspace/useWorkspace"; import { toast } from "@posthog/ui/primitives/toast"; -import { Box, Flex, Text, Tooltip } from "@radix-ui/themes"; +import { AlertDialog, Box, Flex, Text, Tooltip } from "@radix-ui/themes"; import { useNavigate, useRouterState } from "@tanstack/react-router"; import { type ReactNode, useEffect, useState } from "react"; import { hostClient } from "../hostClient"; @@ -265,11 +265,16 @@ function DashboardRow({ const navigate = useNavigate(); const pathname = useRouterState({ select: (s) => s.location.pathname }); const { deleteDashboard, isDeleting } = useDashboardMutations(); + const [confirmOpen, setConfirmOpen] = useState(false); const onDelete = async () => { try { await deleteDashboard(dashboard.id); - if (pathname === `/website/${channelId}/dashboards/${dashboard.id}`) { + // Deleting destroys the canvas, including any child routes under it, so + // match the whole subtree (mirrors ChannelMenu.onDelete). + if ( + pathname.startsWith(`/website/${channelId}/dashboards/${dashboard.id}`) + ) { void navigate({ to: "/website/$channelId", params: { channelId }, @@ -283,38 +288,67 @@ function DashboardRow({ }; return ( - - - - - navigate({ - to: "/website/$channelId/dashboards/$dashboardId", - params: { channelId, dashboardId: dashboard.id }, - }) - } - /> - - } - /> - - - void onDelete()} - > - - Delete - - - + <> + + + + + navigate({ + to: "/website/$channelId/dashboards/$dashboardId", + params: { channelId, dashboardId: dashboard.id }, + }) + } + /> + + } + /> + + + setConfirmOpen(true)} + > + + Delete + + + + + + + Delete canvas + + "{dashboard.name}" will be permanently deleted. This can't be + undone. + + + + + + + + + + + + ); }