Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

feat: add remove task button into the tasks list#20036

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
BrunoQuaresma merged 4 commits intomainfrombq/add-remove-task-on-table
Oct 1, 2025
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
import { MockTasks, MockWorkspace } from "testHelpers/entities";
import { withGlobalSnackbar } from "testHelpers/storybook";
import type { Meta, StoryObj } from "@storybook/react-vite";
import { API } from "api/api";
import { expect, spyOn, userEvent, waitFor, within } from "storybook/test";
import { TaskDeleteDialog } from "./TaskDeleteDialog";

const meta: Meta<typeof TaskDeleteDialog> = {
title: "modules/tasks/TaskDeleteDialog",
component: TaskDeleteDialog,
decorators: [withGlobalSnackbar],
};

export default meta;
type Story = StoryObj<typeof TaskDeleteDialog>;

export const DeleteTaskSuccess: Story = {
decorators: [withGlobalSnackbar],
args: {
open: true,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Do we also need to pass intask andonClose since those are non-optional args?

BrunoQuaresma reacted with thumbs up emoji
Copy link
CollaboratorAuthor

@BrunoQuaresmaBrunoQuaresmaOct 1, 2025
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

It’s not necessary, and Storybook won’t complain about it, or it is what I would expect but I see some storybook errors related toonClose.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Ah yeah I noticed there is no actual enforcement of the types in Storybook tests, but really seems like there should be. 🤔 Seems weird to discard type safety for tests.

task: { prompt: "My Task", workspace: MockWorkspace },
onClose: () => {},
},
parameters: {
chromatic: {
disableSnapshot: false,
},
},
beforeEach: () => {
spyOn(API.experimental, "deleteTask").mockResolvedValue();
},
play: async ({ canvasElement, step }) => {
const body = within(canvasElement.ownerDocument.body);

await step("Confirm delete", async () => {
const confirmButton = await body.findByRole("button", {
name: /delete/i,
});
await userEvent.click(confirmButton);
await step("Confirm delete", async () => {
await waitFor(() => {
expect(API.experimental.deleteTask).toHaveBeenCalledWith(
MockTasks[0].workspace.owner_name,
MockTasks[0].workspace.id,
);
});
});
});
},
};
58 changes: 58 additions & 0 deletionssite/src/modules/tasks/TaskDeleteDialog/TaskDeleteDialog.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
import { API } from "api/api";
import { getErrorDetail, getErrorMessage } from "api/errors";
import { ConfirmDialog } from "components/Dialogs/ConfirmDialog/ConfirmDialog";
import { displayError, displaySuccess } from "components/GlobalSnackbar/utils";
import type { FC } from "react";
import { QueryClient, useMutation } from "react-query";
import type { Task } from "../tasks";

type TaskDeleteDialogProps = {
open: boolean;
task: Task;
onClose: () => void;
onSuccess?: () => void;
};

export const TaskDeleteDialog: FC<TaskDeleteDialogProps> = ({
task,
onSuccess,
...props
}) => {
const queryClient = new QueryClient();
const deleteTaskMutation = useMutation({
mutationFn: () =>
API.experimental.deleteTask(task.workspace.owner_name, task.workspace.id),
onSuccess: async () => {
await queryClient.invalidateQueries({ queryKey: ["tasks"] });
},
});

return (
<ConfirmDialog
{...props}
type="delete"
confirmLoading={deleteTaskMutation.isPending}
title="Delete task"
onConfirm={async () => {
try {
await deleteTaskMutation.mutateAsync();
displaySuccess("Task deleted successfully");
onSuccess?.();
} catch (error) {
displayError(
getErrorMessage(error, "Failed to delete task"),
getErrorDetail(error),
);
} finally {
props.onClose();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Just curious if there is a reasononSuccess is being destructured above but notonClose.

Copy link
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

onSuccess isn’t part of theConfirmDialog props—that’s the only reason.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I might be misunderstanding, butonClose is not a part ofConfirmDialog props either? BothonClose andonSuccess are called in thisonConfirm callback, so it looks inconsistent.

No big deal though, just wondered if there was a reason for it or was just random.

Copy link
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

You're right, my bad.

}
}}
description={
<p>
This action is irreversible and removes all workspace resources and
data.
</p>
}
/>
);
};
52 changes: 3 additions & 49 deletionssite/src/modules/tasks/TasksSidebar/TasksSidebar.stories.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
import { MockTasks, MockUserOwner, mockApiError } from "testHelpers/entities";
import { withAuthProvider, withGlobalSnackbar } from "testHelpers/storybook";
import { withAuthProvider } from "testHelpers/storybook";
import type { Meta, StoryObj } from "@storybook/react-vite";
import { API } from "api/api";
import { MockUsers } from "pages/UsersPage/storybookData/users";
import {expect,spyOn, userEvent, waitFor, within } from "storybook/test";
import { spyOn, userEvent, within } from "storybook/test";
import { reactRouterParameters } from "storybook-addon-remix-react-router";
import { TasksSidebar } from "./TasksSidebar";

Expand DownExpand Up@@ -93,7 +93,7 @@ export const OpenOptionsMenu: Story = {
},
};

export constDeleteTaskDialog: Story = {
export constOpenDeleteDialog: Story = {
beforeEach: () => {
spyOn(API.experimental, "getTasks").mockResolvedValue(MockTasks);
},
Expand All@@ -114,49 +114,3 @@ export const DeleteTaskDialog: Story = {
});
},
};

export const DeleteTaskSuccess: Story = {
decorators: [withGlobalSnackbar],
parameters: {
chromatic: {
disableSnapshot: false,
},
},
beforeEach: () => {
spyOn(API.experimental, "getTasks").mockResolvedValue(MockTasks);
spyOn(API.experimental, "deleteTask").mockResolvedValue();
},
play: async ({ canvasElement, step }) => {
const body = within(canvasElement.ownerDocument.body);
const canvas = within(canvasElement);

await step("Open menu", async () => {
const optionButtons = await canvas.findAllByRole("button", {
name: /task options/i,
});
await userEvent.click(optionButtons[0]);
});

await step("Open delete dialog", async () => {
const deleteButton = await body.findByRole("menuitem", {
name: /delete/i,
});
await userEvent.click(deleteButton);
});

await step("Confirm delete", async () => {
const confirmButton = await body.findByRole("button", {
name: /delete/i,
});
await userEvent.click(confirmButton);
await step("Confirm delete", async () => {
await waitFor(() => {
expect(API.experimental.deleteTask).toHaveBeenCalledWith(
MockTasks[0].workspace.owner_name,
MockTasks[0].workspace.id,
);
});
});
});
},
};
46 changes: 9 additions & 37 deletionssite/src/modules/tasks/TasksSidebar/TasksSidebar.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
import { API } from "api/api";
import {getErrorDetail,getErrorMessage } from "api/errors";
import { getErrorMessage } from "api/errors";
import { cva } from "class-variance-authority";
import { Button } from "components/Button/Button";
import { ConfirmDialog } from "components/Dialogs/ConfirmDialog/ConfirmDialog";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuTrigger,
} from "components/DropdownMenu/DropdownMenu";
import { displayError, displaySuccess } from "components/GlobalSnackbar/utils";
import { CoderIcon } from "components/Icons/CoderIcon";
import { ScrollArea } from "components/ScrollArea/ScrollArea";
import { Skeleton } from "components/Skeleton/Skeleton";
Expand All@@ -25,9 +23,10 @@ import { useSearchParamsKey } from "hooks/useSearchParamsKey";
import { EditIcon, EllipsisIcon, PanelLeftIcon, TrashIcon } from "lucide-react";
import type { Task } from "modules/tasks/tasks";
import { type FC, useState } from "react";
import {QueryClient, useMutation,useQuery } from "react-query";
import { useQuery } from "react-query";
import { Link as RouterLink, useNavigate, useParams } from "react-router";
import { cn } from "utils/cn";
import { TaskDeleteDialog } from "../TaskDeleteDialog/TaskDeleteDialog";
import { UserCombobox } from "./UserCombobox";

export const TasksSidebar: FC = () => {
Expand DownExpand Up@@ -180,14 +179,6 @@ const TaskSidebarMenuItem: FC<TaskSidebarMenuItemProps> = ({ task }) => {
const isActive = task.workspace.name === workspace;
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);
const navigate = useNavigate();
const queryClient = new QueryClient();
const deleteTaskMutation = useMutation({
mutationFn: () =>
API.experimental.deleteTask(task.workspace.owner_name, task.workspace.id),
onSuccess: async () => {
await queryClient.invalidateQueries({ queryKey: ["tasks"] });
},
});

return (
<>
Expand DownExpand Up@@ -249,36 +240,17 @@ const TaskSidebarMenuItem: FC<TaskSidebarMenuItemProps> = ({ task }) => {
</RouterLink>
</Button>

<ConfirmDialog
type="delete"
confirmLoading={deleteTaskMutation.isPending}
<TaskDeleteDialog
open={isDeleteDialogOpen}
title="Deletetask"
onClose={(): void => {
task={task}
onClose={() => {
setIsDeleteDialogOpen(false);
}}
onConfirm={async () => {
try {
await deleteTaskMutation.mutateAsync();
displaySuccess("Task deleted successfully");
if (isActive) {
navigate("/tasks");
}
} catch (error) {
displayError(
getErrorMessage(error, "Failed to delete task"),
getErrorDetail(error),
);
} finally {
setIsDeleteDialogOpen(false);
onSuccess={() => {
if (isActive) {
navigate("/tasks");
}
}}
description={
<p>
This action is irreversible and removes all workspace resources and
data.
</p>
}
/>
</>
);
Expand Down
14 changes: 14 additions & 0 deletionssite/src/pages/TasksPage/TasksPage.stories.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -424,3 +424,17 @@ export const NonAdmin: Story = {
});
},
};

export const OpenDeleteDialog: Story = {
beforeEach: () => {
spyOn(API, "getTemplates").mockResolvedValue([MockTemplate]);
spyOn(API.experimental, "getTasks").mockResolvedValue(MockTasks);
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const deleteButtons = await canvas.findAllByRole("button", {
name: /delete task/i,
});
await userEvent.click(deleteButtons[0]);
},
};
Loading
Loading

[8]ページ先頭

©2009-2025 Movatter.jp