fix(web): allow delete/archive of kanban cards in All Workflows view (#886)
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
import { test, expect } from "../../fixtures/test-base";
|
||||
import { KanbanPage } from "../../pages/kanban-page";
|
||||
|
||||
const TASK_VISIBLE_TIMEOUT = 10_000;
|
||||
|
||||
// Regression: clicking Delete / Archive must show the confirm dialog, not navigate to the task.
|
||||
test.describe("Kanban card actions menu — delete/archive does not navigate", () => {
|
||||
test("clicking Delete in card dropdown shows confirm dialog and does not navigate", async ({
|
||||
@@ -81,3 +83,76 @@ test.describe("Kanban card actions menu — delete/archive does not navigate", (
|
||||
await expect(testPage.getByTestId("task-preview-panel")).not.toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
// Regression: in "All Workflows" swimlane view, state.kanban.workflowId is null.
|
||||
// useTaskCRUD's handleDelete/handleArchive used to early-return on that, so the
|
||||
// dialog closed but no API call ran and the task stayed on the board.
|
||||
test.describe("Kanban card actions menu — delete/archive in All Workflows view", () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- testPage is listed to force its fixture (user-settings reset) to run before this hook's API seeding; without it the reset wipes our workflow_filter_id on first use
|
||||
test.beforeEach(async ({ apiClient, seedData, testPage }) => {
|
||||
// Need a second workflow so resolveDesiredWorkflowId does not auto-select
|
||||
// the only visible workflow when filter is null.
|
||||
await apiClient.createWorkflow(seedData.workspaceId, "Secondary Workflow", "simple");
|
||||
await apiClient.saveUserSettings({
|
||||
workspace_id: seedData.workspaceId,
|
||||
workflow_filter_id: "",
|
||||
repository_ids: [],
|
||||
});
|
||||
});
|
||||
|
||||
test("confirming Delete removes the task from the board in All Workflows view", async ({
|
||||
testPage,
|
||||
apiClient,
|
||||
seedData,
|
||||
}) => {
|
||||
const task = await apiClient.createTask(seedData.workspaceId, "All-Wf Delete Task", {
|
||||
workflow_id: seedData.workflowId,
|
||||
workflow_step_id: seedData.startStepId,
|
||||
});
|
||||
const kanban = new KanbanPage(testPage);
|
||||
await kanban.goto();
|
||||
|
||||
await expect(kanban.taskCardByTitle("All-Wf Delete Task")).toBeVisible({
|
||||
timeout: TASK_VISIBLE_TIMEOUT,
|
||||
});
|
||||
|
||||
await kanban.openTaskActionsMenu(task.id);
|
||||
await testPage.getByRole("menuitem", { name: "Delete" }).click();
|
||||
|
||||
const dialog = testPage.getByRole("alertdialog");
|
||||
await expect(dialog).toBeVisible();
|
||||
await dialog.getByRole("button", { name: "Delete" }).click();
|
||||
|
||||
await expect(kanban.taskCardByTitle("All-Wf Delete Task")).not.toBeVisible({
|
||||
timeout: TASK_VISIBLE_TIMEOUT,
|
||||
});
|
||||
});
|
||||
|
||||
test("confirming Archive removes the task from the board in All Workflows view", async ({
|
||||
testPage,
|
||||
apiClient,
|
||||
seedData,
|
||||
}) => {
|
||||
const task = await apiClient.createTask(seedData.workspaceId, "All-Wf Archive Task", {
|
||||
workflow_id: seedData.workflowId,
|
||||
workflow_step_id: seedData.startStepId,
|
||||
});
|
||||
const kanban = new KanbanPage(testPage);
|
||||
await kanban.goto();
|
||||
|
||||
await expect(kanban.taskCardByTitle("All-Wf Archive Task")).toBeVisible({
|
||||
timeout: TASK_VISIBLE_TIMEOUT,
|
||||
});
|
||||
|
||||
await kanban.openTaskActionsMenu(task.id);
|
||||
await testPage.getByRole("menuitem", { name: "Archive" }).click();
|
||||
|
||||
const dialog = testPage.getByRole("alertdialog");
|
||||
await expect(dialog).toBeVisible();
|
||||
await dialog.getByRole("button", { name: "Archive" }).click();
|
||||
|
||||
await expect(kanban.taskCardByTitle("All-Wf Archive Task")).not.toBeVisible({
|
||||
timeout: TASK_VISIBLE_TIMEOUT,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback, useState } from "react";
|
||||
import { useAppStore, useAppStoreApi } from "@/components/state-provider";
|
||||
import { useAppStoreApi } from "@/components/state-provider";
|
||||
import { useTaskActions } from "@/hooks/use-task-actions";
|
||||
import type { Task } from "@/components/kanban-card";
|
||||
import type { KanbanState } from "@/lib/state/slices";
|
||||
@@ -18,7 +18,6 @@ export function useTaskCRUD() {
|
||||
const [deletingTaskId, setDeletingTaskId] = useState<string | null>(null);
|
||||
const [archivingTaskId, setArchivingTaskId] = useState<string | null>(null);
|
||||
const { deleteTaskById, archiveTaskById } = useTaskActions();
|
||||
const kanban = useAppStore((state) => state.kanban);
|
||||
const store = useAppStoreApi();
|
||||
|
||||
const handleCreate = useCallback(() => {
|
||||
@@ -33,8 +32,6 @@ export function useTaskCRUD() {
|
||||
|
||||
const handleDelete = useCallback(
|
||||
async (task: Task) => {
|
||||
if (!kanban.workflowId) return;
|
||||
|
||||
setDeletingTaskId(task.id);
|
||||
try {
|
||||
await deleteTaskById(task.id);
|
||||
@@ -52,13 +49,11 @@ export function useTaskCRUD() {
|
||||
setDeletingTaskId(null);
|
||||
}
|
||||
},
|
||||
[deleteTaskById, kanban.workflowId, store],
|
||||
[deleteTaskById, store],
|
||||
);
|
||||
|
||||
const handleArchive = useCallback(
|
||||
async (task: Task) => {
|
||||
if (!kanban.workflowId) return;
|
||||
|
||||
setArchivingTaskId(task.id);
|
||||
try {
|
||||
await archiveTaskById(task.id);
|
||||
@@ -76,7 +71,7 @@ export function useTaskCRUD() {
|
||||
setArchivingTaskId(null);
|
||||
}
|
||||
},
|
||||
[archiveTaskById, kanban.workflowId, store],
|
||||
[archiveTaskById, store],
|
||||
);
|
||||
|
||||
const handleDialogOpenChange = useCallback((open: boolean) => {
|
||||
|
||||
Reference in New Issue
Block a user