diff --git a/ui-v2/src/components/deployments/data-table/cells.tsx b/ui-v2/src/components/deployments/data-table/cells.tsx index fff9d2f02f78..5d6ff90f0341 100644 --- a/ui-v2/src/components/deployments/data-table/cells.tsx +++ b/ui-v2/src/components/deployments/data-table/cells.tsx @@ -2,6 +2,7 @@ import { DropdownMenuItem } from "@/components/ui/dropdown-menu"; import type { DeploymentWithFlow } from "@/api/deployments"; import { buildFilterFlowRunsQuery } from "@/api/flow-runs"; +import { useQuickRun } from "@/components/deployments/use-quick-run"; import { Button } from "@/components/ui/button"; import { DropdownMenu, @@ -14,29 +15,21 @@ import { Icon } from "@/components/ui/icons"; import useDebounce from "@/hooks/use-debounce"; import { useToast } from "@/hooks/use-toast"; import { useQuery } from "@tanstack/react-query"; +import { Link } from "@tanstack/react-router"; import type { CellContext } from "@tanstack/react-table"; import { subSeconds } from "date-fns"; import { secondsInWeek } from "date-fns/constants"; import { useCallback, useState } from "react"; type ActionsCellProps = CellContext & { - onQuickRun: (deployment: DeploymentWithFlow) => void; - onCustomRun: (deployment: DeploymentWithFlow) => void; - onEdit: (deployment: DeploymentWithFlow) => void; onDelete: (deployment: DeploymentWithFlow) => void; - onDuplicate: (deployment: DeploymentWithFlow) => void; }; -export const ActionsCell = ({ - row, - onQuickRun, - onCustomRun, - onEdit, - onDelete, - onDuplicate, -}: ActionsCellProps) => { +export const ActionsCell = ({ row, onDelete }: ActionsCellProps) => { const id = row.original.id; const { toast } = useToast(); + const { onQuickRun, isPending } = useQuickRun(); + if (!id) return null; return ( @@ -50,30 +43,35 @@ export const ActionsCell = ({ Actions - onQuickRun(row.original)}> + onQuickRun(id)}> Quick Run - onCustomRun(row.original)}> - Custom Run + + + Custom Run + + { void navigator.clipboard.writeText(id); - toast({ - title: "ID copied", - }); + toast({ title: "ID copied" }); }} > Copy ID - onEdit(row.original)}> - Edit + + + Edit + onDelete(row.original)}> Delete - onDuplicate(row.original)}> - Duplicate + + + Duplicate + diff --git a/ui-v2/src/components/deployments/data-table/data-table.stories.tsx b/ui-v2/src/components/deployments/data-table/data-table.stories.tsx index 456f8a16faba..c8aa8be130c2 100644 --- a/ui-v2/src/components/deployments/data-table/data-table.stories.tsx +++ b/ui-v2/src/components/deployments/data-table/data-table.stories.tsx @@ -56,10 +56,6 @@ export const Default: StoryObj = { args: { numberOfDeployments: 10, onPaginationChange: fn(), - onQuickRun: fn(), - onCustomRun: fn(), - onEdit: fn(), - onDuplicate: fn(), }, render: ( args: Omit< diff --git a/ui-v2/src/components/deployments/data-table/data-table.test.tsx b/ui-v2/src/components/deployments/data-table/data-table.test.tsx index 6daf7aedba96..f75d28076ddc 100644 --- a/ui-v2/src/components/deployments/data-table/data-table.test.tsx +++ b/ui-v2/src/components/deployments/data-table/data-table.test.tsx @@ -1,6 +1,9 @@ import type { DeploymentWithFlow } from "@/api/deployments"; import { Toaster } from "@/components/ui/toaster"; -import { createFakeFlowRunWithDeploymentAndFlow } from "@/mocks/create-fake-flow-run"; +import { + createFakeFlowRun, + createFakeFlowRunWithDeploymentAndFlow, +} from "@/mocks/create-fake-flow-run"; import { QueryClient } from "@tanstack/react-query"; import { RouterProvider, @@ -60,16 +63,17 @@ describe("DeploymentsDataTable", () => { onPaginationChange: vi.fn(), onSortChange: vi.fn(), onColumnFiltersChange: vi.fn(), - onQuickRun: vi.fn(), - onCustomRun: vi.fn(), - onEdit: vi.fn(), - onDuplicate: vi.fn(), }; // Wraps component in test with a Tanstack router provider const DeploymentsDataTableRouter = (props: DeploymentsDataTableProps) => { const rootRoute = createRootRoute({ - component: () => , + component: () => ( + <> + + + + ), }); const router = createRouter({ @@ -152,59 +156,48 @@ describe("DeploymentsDataTable", () => { }); it("calls onQuickRun when quick run action is clicked", async () => { - const onQuickRun = vi.fn(); - render( - , - { wrapper: createWrapper() }, + server.use( + http.post(buildApiUrl("/deployments/:id/create_flow_run"), () => { + return HttpResponse.json(createFakeFlowRun({ name: "new-flow-run" })); + }), ); + render(, { + wrapper: createWrapper(), + }); + await userEvent.click(screen.getByRole("button", { name: "Open menu" })); const quickRunButton = screen.getByRole("menuitem", { name: "Quick Run" }); await userEvent.click(quickRunButton); - expect(onQuickRun).toHaveBeenCalledWith(mockDeployment); + expect(screen.getByText("new-flow-run")).toBeVisible(); + expect(screen.getByRole("button", { name: "View run" })).toBeVisible(); }); - it("calls onCustomRun when custom run action is clicked", async () => { - const onCustomRun = vi.fn(); - render( - , - { wrapper: createWrapper() }, - ); - - await userEvent.click(screen.getByRole("button", { name: "Open menu" })); - const customRunButton = screen.getByRole("menuitem", { - name: "Custom Run", + it("has an action menu item that links to create a custom run", async () => { + render(, { + wrapper: createWrapper(), }); - await userEvent.click(customRunButton); - expect(onCustomRun).toHaveBeenCalledWith(mockDeployment); + await userEvent.click(screen.getByRole("button", { name: "Open menu" })); + expect(screen.getByRole("menuitem", { name: "Custom Run" })).toBeVisible(); + expect(screen.getByRole("link", { name: /custom run/i })).toBeVisible(); }); - it("calls onEdit when edit action is clicked", async () => { - const onEdit = vi.fn(); - render(, { + it("has an action menu item that links to edit deployment", async () => { + render(, { wrapper: createWrapper(), }); await userEvent.click(screen.getByRole("button", { name: "Open menu" })); - const editButton = screen.getByRole("menuitem", { name: "Edit" }); - await userEvent.click(editButton); - - expect(onEdit).toHaveBeenCalledWith(mockDeployment); + expect(screen.getByRole("menuitem", { name: "Edit" })).toBeVisible(); + expect(screen.getByRole("link", { name: /edit/i })).toBeVisible(); }); it("handles deletion", async () => { - render( - <> - - - , - { wrapper: createWrapper() }, - ); + render(, { + wrapper: createWrapper(), + }); await userEvent.click(screen.getByRole("button", { name: "Open menu" })); const deleteButton = screen.getByRole("menuitem", { name: "Delete" }); @@ -218,21 +211,14 @@ describe("DeploymentsDataTable", () => { expect(screen.getByText("Deployment deleted")).toBeInTheDocument(); }); - it("calls onDuplicate when duplicate action is clicked", async () => { - const onDuplicate = vi.fn(); - render( - , - { wrapper: createWrapper() }, - ); + it("has an action menu item that links to duplicate deployment", async () => { + render(, { + wrapper: createWrapper(), + }); await userEvent.click(screen.getByRole("button", { name: "Open menu" })); - const duplicateButton = screen.getByRole("menuitem", { name: "Duplicate" }); - await userEvent.click(duplicateButton); - - expect(onDuplicate).toHaveBeenCalledWith(mockDeployment); + expect(screen.getByRole("menuitem", { name: "Duplicate" })).toBeVisible(); + expect(screen.getByRole("link", { name: /duplicate/i })).toBeVisible(); }); it("calls onPaginationChange when pagination buttons are clicked", async () => { diff --git a/ui-v2/src/components/deployments/data-table/index.tsx b/ui-v2/src/components/deployments/data-table/index.tsx index 276bb0339369..b6342657d02e 100644 --- a/ui-v2/src/components/deployments/data-table/index.tsx +++ b/ui-v2/src/components/deployments/data-table/index.tsx @@ -42,26 +42,14 @@ export type DeploymentsDataTableProps = { onPaginationChange: (pagination: PaginationState) => void; onSortChange: (sort: components["schemas"]["DeploymentSort"]) => void; onColumnFiltersChange: (columnFilters: ColumnFiltersState) => void; - onQuickRun: (deployment: DeploymentWithFlow) => void; - onCustomRun: (deployment: DeploymentWithFlow) => void; - onEdit: (deployment: DeploymentWithFlow) => void; - onDuplicate: (deployment: DeploymentWithFlow) => void; }; const columnHelper = createColumnHelper(); const createColumns = ({ - onQuickRun, - onCustomRun, - onEdit, onDelete, - onDuplicate, }: { - onQuickRun: (deployment: DeploymentWithFlow) => void; - onCustomRun: (deployment: DeploymentWithFlow) => void; - onEdit: (deployment: DeploymentWithFlow) => void; onDelete: (deployment: DeploymentWithFlow) => void; - onDuplicate: (deployment: DeploymentWithFlow) => void; }) => [ columnHelper.display({ id: "name", @@ -132,16 +120,7 @@ const createColumns = ({ }), columnHelper.display({ id: "actions", - cell: (props) => ( - - ), + cell: (props) => , }), ]; @@ -155,10 +134,6 @@ export const DeploymentsDataTable = ({ onPaginationChange, onSortChange, onColumnFiltersChange, - onQuickRun, - onCustomRun, - onEdit, - onDuplicate, }: DeploymentsDataTableProps) => { const [deleteConfirmationDialogState, confirmDelete] = useDeleteDeploymentConfirmationDialog(); @@ -209,16 +184,12 @@ export const DeploymentsDataTable = ({ const table = useReactTable({ data: deployments, columns: createColumns({ - onQuickRun, - onCustomRun, - onEdit, onDelete: (deployment) => { const name = deployment.flow?.name ? `${deployment.flow?.name}/${deployment.name}` : deployment.name; confirmDelete({ ...deployment, name }); }, - onDuplicate, }), getCoreRowModel: getCoreRowModel(), pageCount, diff --git a/ui-v2/src/components/deployments/run-flow-button/run-flow-button.tsx b/ui-v2/src/components/deployments/run-flow-button/run-flow-button.tsx index 7fd611fe06ff..f4d7e81896e0 100644 --- a/ui-v2/src/components/deployments/run-flow-button/run-flow-button.tsx +++ b/ui-v2/src/components/deployments/run-flow-button/run-flow-button.tsx @@ -1,5 +1,5 @@ import { Deployment } from "@/api/deployments"; -import { useDeploymentCreateFlowRun } from "@/api/flow-runs"; +import { useQuickRun } from "@/components/deployments/use-quick-run"; import { Button } from "@/components/ui/button"; import { DropdownMenu, @@ -9,59 +9,14 @@ import { DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { Icon } from "@/components/ui/icons"; -import { useToast } from "@/hooks/use-toast"; import { Link } from "@tanstack/react-router"; -const DEPLOYMENT_QUICK_RUN_PAYLOAD = { - state: { - type: "SCHEDULED", - message: "Run from the Prefect UI with defaults", - state_details: { - deferred: false, - untrackable_result: false, - pause_reschedule: false, - }, - }, -} as const; - export type RunFlowButtonProps = { deployment: Deployment; }; export const RunFlowButton = ({ deployment }: RunFlowButtonProps) => { - const { toast } = useToast(); - const { createDeploymentFlowRun, isPending } = useDeploymentCreateFlowRun(); - - const handleClickQuickRun = (id: string) => { - createDeploymentFlowRun( - { - id, - ...DEPLOYMENT_QUICK_RUN_PAYLOAD, - }, - { - onSuccess: (res) => { - toast({ - action: ( - - - - ), - description: ( -

- {res.name} scheduled to start{" "} - now -

- ), - }); - }, - onError: (error) => { - const message = - error.message || "Unknown error while creating flow run."; - console.error(message); - }, - }, - ); - }; + const { onQuickRun, isPending } = useQuickRun(); return ( @@ -72,7 +27,7 @@ export const RunFlowButton = ({ deployment }: RunFlowButtonProps) => { - handleClickQuickRun(deployment.id)}> + onQuickRun(deployment.id)}> Quick run { + const { createDeploymentFlowRun, isPending } = useDeploymentCreateFlowRun(); + const onQuickRun = (id: string) => { + createDeploymentFlowRun( + { + id, + ...DEPLOYMENT_QUICK_RUN_PAYLOAD, + }, + { + onSuccess: (res) => { + toast({ + action: ( + + + + ), + description: ( +

+ {res.name} scheduled to start{" "} + now +

+ ), + }); + }, + onError: (error) => { + const message = + error.message || "Unknown error while creating flow run."; + console.error(message); + }, + }, + ); + }; + + return { onQuickRun, isPending }; +}; diff --git a/ui-v2/src/routes/deployments/index.tsx b/ui-v2/src/routes/deployments/index.tsx index e5a9052f6609..a4cecda051cd 100644 --- a/ui-v2/src/routes/deployments/index.tsx +++ b/ui-v2/src/routes/deployments/index.tsx @@ -260,11 +260,6 @@ function RouteComponent() { onPaginationChange={onPaginationChange} onSortChange={onSortChange} onColumnFiltersChange={onColumnFiltersChange} - // TODO: Replace console.log with actual handlers for deployment actions - onQuickRun={(deployment) => console.log(deployment)} - onCustomRun={(deployment) => console.log(deployment)} - onEdit={(deployment) => console.log(deployment)} - onDuplicate={(deployment) => console.log(deployment)} /> )}