From e80c37cc46578674b47d7be858e7b612dd0f8cd7 Mon Sep 17 00:00:00 2001 From: Devin Villarosa Date: Tue, 18 Feb 2025 15:57:13 -0800 Subject: [PATCH] [UI v2] feat: Adds task run columns to flow run data table --- .../data-table/data-table.stories.tsx | 34 ++++++++++++++-- .../flow-runs/data-table/data-table.tsx | 25 ++++++++++-- .../flow-runs/data-table/tasks-cell.tsx | 39 +++++++++++++++++++ ui-v2/src/components/ui/icons/constants.ts | 2 + ui-v2/src/mocks/create-fake-flow-run.ts | 19 ++++----- 5 files changed, 103 insertions(+), 16 deletions(-) create mode 100644 ui-v2/src/components/flow-runs/data-table/tasks-cell.tsx diff --git a/ui-v2/src/components/flow-runs/data-table/data-table.stories.tsx b/ui-v2/src/components/flow-runs/data-table/data-table.stories.tsx index 19b21f53c3dd..08e5d58fd833 100644 --- a/ui-v2/src/components/flow-runs/data-table/data-table.stories.tsx +++ b/ui-v2/src/components/flow-runs/data-table/data-table.stories.tsx @@ -6,18 +6,44 @@ import { routerDecorator, toastDecorator, } from "@/storybook/utils"; +import { faker } from "@faker-js/faker"; +import { buildApiUrl } from "@tests/utils/handlers"; +import { http, HttpResponse } from "msw"; import { FlowRunsDataTable } from "./data-table"; -const MOCK_DATA = Array.from( - { length: 5 }, - createFakeFlowRunWithDeploymentAndFlow, -); +const MOCK_DATA = [ + createFakeFlowRunWithDeploymentAndFlow({ + id: "0", + state: { type: "SCHEDULED", id: "0" }, + }), + createFakeFlowRunWithDeploymentAndFlow({ id: "1" }), + createFakeFlowRunWithDeploymentAndFlow({ id: "2" }), + createFakeFlowRunWithDeploymentAndFlow({ id: "3" }), + createFakeFlowRunWithDeploymentAndFlow({ id: "4" }), +]; + +const MOCK_FLOW_RUNS_TASK_COUNT = { + "0": faker.number.int({ min: 0, max: 5 }), + "1": faker.number.int({ min: 0, max: 5 }), + "2": faker.number.int({ min: 0, max: 5 }), + "3": faker.number.int({ min: 0, max: 5 }), + "4": faker.number.int({ min: 0, max: 5 }), +}; const meta: Meta = { title: "Components/FlowRuns/DataTable/FlowRunsDataTable", decorators: [routerDecorator, reactQueryDecorator, toastDecorator], args: { flowRuns: MOCK_DATA, flowRunsCount: MOCK_DATA.length }, component: FlowRunsDataTable, + parameters: { + msw: { + handlers: [ + http.post(buildApiUrl("/ui/flow_runs/count-task-runs"), () => { + return HttpResponse.json(MOCK_FLOW_RUNS_TASK_COUNT); + }), + ], + }, + }, }; export default meta; diff --git a/ui-v2/src/components/flow-runs/data-table/data-table.tsx b/ui-v2/src/components/flow-runs/data-table/data-table.tsx index 78e0c7f2c829..6525134cc116 100644 --- a/ui-v2/src/components/flow-runs/data-table/data-table.tsx +++ b/ui-v2/src/components/flow-runs/data-table/data-table.tsx @@ -16,12 +16,13 @@ import { getPaginationRowModel, useReactTable, } from "@tanstack/react-table"; -import { useMemo, useState } from "react"; +import { Suspense, useMemo, useState } from "react"; import { Flow } from "@/api/flows"; import { Checkbox } from "@/components/ui/checkbox"; import { DeleteConfirmationDialog } from "@/components/ui/delete-confirmation-dialog"; import { Icon } from "@/components/ui/icons"; +import { Skeleton } from "@/components/ui/skeleton"; import { Typography } from "@/components/ui/typography"; import { pluralize } from "@/utils"; import { CheckedState } from "@radix-ui/react-checkbox"; @@ -33,10 +34,12 @@ import { RunNameSearch } from "./run-name-search"; import { SortFilter } from "./sort-filter"; import { StartTimeCell } from "./start-time-cell"; import { StateFilter } from "./state-filter"; +import { TasksCell } from "./tasks-cell"; import { useDeleteFlowRunsDialog } from "./use-delete-flow-runs-dialog"; export type FlowRunsDataTableRow = FlowRun & { flow: Flow; + numTaskRuns?: number; deployment?: Deployment; }; @@ -79,7 +82,7 @@ const createColumns = ({ enableHiding: false, }), columnHelper.display({ - size: 200, + size: 320, id: "name", header: "Name", cell: ({ row }) => , @@ -116,7 +119,23 @@ const createColumns = ({ return ; }, }), - + columnHelper.display({ + size: 200, + id: "taskRuns", + header: "Task Runs", + cell: ({ row }) => { + const flowRun = row.original; + if (flowRun.state?.type === "SCHEDULED") { + return null; + } + // nb: Defer data loading since this column is not guaranteed + return ( + }> + + + ); + }, + }), columnHelper.accessor("tags", { id: "tags", header: "Tags", diff --git a/ui-v2/src/components/flow-runs/data-table/tasks-cell.tsx b/ui-v2/src/components/flow-runs/data-table/tasks-cell.tsx new file mode 100644 index 000000000000..90d6cc670a4d --- /dev/null +++ b/ui-v2/src/components/flow-runs/data-table/tasks-cell.tsx @@ -0,0 +1,39 @@ +import { + type FlowRunWithDeploymentAndFlow, + type FlowRunWithFlow, +} from "@/api/flow-runs"; +import { buildGetFlowRusTaskRunsCountQuery } from "@/api/task-runs"; +import { Icon } from "@/components/ui/icons"; + +import { Typography } from "@/components/ui/typography"; +import { pluralize } from "@/utils"; +import { useSuspenseQuery } from "@tanstack/react-query"; +import clsx from "clsx"; + +type TasksCellsProp = { + flowRun: FlowRunWithFlow | FlowRunWithDeploymentAndFlow; +}; + +export const TasksCell = ({ flowRun }: TasksCellsProp) => { + const { data } = useSuspenseQuery( + buildGetFlowRusTaskRunsCountQuery([flowRun.id]), + ); + + const taskRunsCount = data[flowRun.id]; + + if (taskRunsCount === undefined) { + return "Tasks not found"; + } + + return ( +
+ + + {taskRunsCount} {pluralize(taskRunsCount, "Task run")} + +
+ ); +}; diff --git a/ui-v2/src/components/ui/icons/constants.ts b/ui-v2/src/components/ui/icons/constants.ts index 398a3f6901f7..de961542fdba 100644 --- a/ui-v2/src/components/ui/icons/constants.ts +++ b/ui-v2/src/components/ui/icons/constants.ts @@ -34,6 +34,7 @@ import { Search, ServerCrash, SlidersVertical, + Spline, Sun, Trash2, Variable, @@ -77,6 +78,7 @@ export const ICONS = { Search, ServerCrash, SlidersVertical, + Spline, Sun, Trash2, Variable, diff --git a/ui-v2/src/mocks/create-fake-flow-run.ts b/ui-v2/src/mocks/create-fake-flow-run.ts index 18029e4e99f1..a48752cab09b 100644 --- a/ui-v2/src/mocks/create-fake-flow-run.ts +++ b/ui-v2/src/mocks/create-fake-flow-run.ts @@ -82,13 +82,14 @@ export const createFakeFlowRun = ( }; }; -export const createFakeFlowRunWithDeploymentAndFlow = - (): FlowRunWithDeploymentAndFlow => { - const flowRun = createFakeFlowRun(); - - return { - ...flowRun, - deployment: createFakeDeployment(), - flow: createFakeFlow(), - }; +export const createFakeFlowRunWithDeploymentAndFlow = ( + overrides?: Partial, +): FlowRunWithDeploymentAndFlow => { + const flowRun = createFakeFlowRun(); + return { + ...flowRun, + deployment: createFakeDeployment(), + flow: createFakeFlow(), + ...overrides, }; +};