From 308ca6686de02d42a055ff94365766581fb8dedf Mon Sep 17 00:00:00 2001 From: Devin Villarosa Date: Sun, 16 Feb 2025 12:40:46 -0800 Subject: [PATCH] [UI v2] feat: Start flow runs data table UX --- ui-v2/src/api/flow-runs/index.ts | 3 + .../data-table/data-table.stories.tsx | 20 ++++ .../flow-runs/data-table/data-table.tsx | 92 +++++++++++++++++++ .../flow-runs/data-table/deployment-cell.tsx | 20 ++++ .../flow-runs/data-table/name-cell.tsx | 42 +++++++++ 5 files changed, 177 insertions(+) create mode 100644 ui-v2/src/components/flow-runs/data-table/data-table.stories.tsx create mode 100644 ui-v2/src/components/flow-runs/data-table/data-table.tsx create mode 100644 ui-v2/src/components/flow-runs/data-table/deployment-cell.tsx create mode 100644 ui-v2/src/components/flow-runs/data-table/name-cell.tsx diff --git a/ui-v2/src/api/flow-runs/index.ts b/ui-v2/src/api/flow-runs/index.ts index 5c153afe09e7..c2e6eef9c8c3 100644 --- a/ui-v2/src/api/flow-runs/index.ts +++ b/ui-v2/src/api/flow-runs/index.ts @@ -9,6 +9,9 @@ import { components } from "../prefect"; import { getQueryService } from "../service"; export type FlowRun = components["schemas"]["FlowRun"]; +export type FlowRunWithFlow = FlowRun & { + flow: Flow; +}; export type FlowRunWithDeploymentAndFlow = FlowRun & { deployment: Deployment; flow: Flow; 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 new file mode 100644 index 000000000000..6eed3514a44a --- /dev/null +++ b/ui-v2/src/components/flow-runs/data-table/data-table.stories.tsx @@ -0,0 +1,20 @@ +import type { Meta, StoryObj } from "@storybook/react"; + +import { createFakeFlowRunWithDeploymentAndFlow } from "@/mocks/create-fake-flow-run"; +import { routerDecorator } from "@/storybook/utils"; +import { FlowRunsDataTable } from "./data-table"; + +const MOCK_DATA = Array.from( + { length: 5 }, + createFakeFlowRunWithDeploymentAndFlow, +); + +const meta: Meta = { + title: "Components/FlowRuns/DataTable/FlowRunsDataTable", + decorators: [routerDecorator], + args: { flowRuns: MOCK_DATA }, + component: FlowRunsDataTable, +}; +export default meta; + +export const story: StoryObj = { name: "FlowRunsDataTable" }; 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 new file mode 100644 index 000000000000..f7e067633ab4 --- /dev/null +++ b/ui-v2/src/components/flow-runs/data-table/data-table.tsx @@ -0,0 +1,92 @@ +import type { Deployment } from "@/api/deployments"; +import { + type FlowRunWithDeploymentAndFlow, + type FlowRunWithFlow, +} from "@/api/flow-runs"; +import { DataTable } from "@/components/ui/data-table"; +import { StateBadge } from "@/components/ui/state-badge"; +import { TagBadgeGroup } from "@/components/ui/tag-badge-group"; +import { + createColumnHelper, + getCoreRowModel, + getPaginationRowModel, + useReactTable, +} from "@tanstack/react-table"; +import { useMemo } from "react"; + +import { DeploymentCell } from "./deployment-cell"; +import { NameCell } from "./name-cell"; + +const columnHelper = createColumnHelper< + FlowRunWithDeploymentAndFlow | FlowRunWithFlow +>(); + +const createColumns = ({ + showDeployment, +}: { + showDeployment: boolean; +}) => { + const ret = [ + columnHelper.display({ + id: "name", + header: "Name", + cell: ({ row }) => , + }), + columnHelper.accessor("state", { + id: "state", + header: "State", + cell: (props) => { + const state = props.getValue(); + if (!state) { + return null; + } + return ; + }, + }), + columnHelper.accessor("tags", { + id: "tags", + header: "Tags", + cell: (props) => ( + + ), + }), + ]; + if (showDeployment) { + ret.push( + columnHelper.accessor("deployment", { + id: "deployment", + header: "Deployment", + cell: (props) => { + const deployment = props.getValue() as Deployment; + return ; + }, + }), + ); + } + return ret; +}; + +type FlowRunsDataTableProps = { + flowRuns: Array; +}; +export const FlowRunsDataTable = ({ flowRuns }: FlowRunsDataTableProps) => { + const showDeployment = useMemo( + () => flowRuns.some((flowRun) => "deployment" in flowRun), + [flowRuns], + ); + + const table = useReactTable({ + data: flowRuns, + columns: createColumns({ + showDeployment, + }), + getCoreRowModel: getCoreRowModel(), + getPaginationRowModel: getPaginationRowModel(), // TODO: use server-side pagination + }); + + return ( +
+ +
+ ); +}; diff --git a/ui-v2/src/components/flow-runs/data-table/deployment-cell.tsx b/ui-v2/src/components/flow-runs/data-table/deployment-cell.tsx new file mode 100644 index 000000000000..ed670b0b53b8 --- /dev/null +++ b/ui-v2/src/components/flow-runs/data-table/deployment-cell.tsx @@ -0,0 +1,20 @@ +import { Deployment } from "@/api/deployments"; + +import { Icon } from "@/components/ui/icons"; +import { Typography } from "@/components/ui/typography"; + +import { Link } from "@tanstack/react-router"; + +type DeploymentCellProps = { deployment: Deployment }; +export const DeploymentCell = ({ deployment }: DeploymentCellProps) => { + return ( + + + {deployment.name} + + ); +}; diff --git a/ui-v2/src/components/flow-runs/data-table/name-cell.tsx b/ui-v2/src/components/flow-runs/data-table/name-cell.tsx new file mode 100644 index 000000000000..a48072da5cb2 --- /dev/null +++ b/ui-v2/src/components/flow-runs/data-table/name-cell.tsx @@ -0,0 +1,42 @@ +import { + type FlowRunWithDeploymentAndFlow, + type FlowRunWithFlow, +} from "@/api/flow-runs"; +import { + Breadcrumb, + BreadcrumbItem, + BreadcrumbLink, + BreadcrumbList, + BreadcrumbSeparator, +} from "@/components/ui/breadcrumb"; + +type NameCellProps = { + flowRun: FlowRunWithDeploymentAndFlow | FlowRunWithFlow; +}; + +export const NameCell = ({ flowRun }: NameCellProps) => { + return ( +
+ + + + + {flowRun.flow.name} + + + + / + + + + {flowRun.name} + + + + +
+ ); +};