Skip to content

Commit

Permalink
[UI v2] feat: Adds data and UX for task run concurrency limit (#16287)
Browse files Browse the repository at this point in the history
  • Loading branch information
devinvillarosa authored Dec 10, 2024
1 parent 6aaf035 commit 404090b
Show file tree
Hide file tree
Showing 22 changed files with 1,070 additions and 35 deletions.
12 changes: 5 additions & 7 deletions ui-v2/src/components/concurrency/concurrency-tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,16 @@ export const ConcurrencyTabs = ({
const navigate = routeApi.useNavigate();

return (
<Tabs defaultValue="Global" value={tab}>
<Tabs className="flex flex-col gap-4" defaultValue="Global" value={tab}>
<TabsList className="grid w-full grid-cols-2">
<TabsTrigger
value={TAB_OPTIONS.global.tabSearchValue}
onClick={() => {
void navigate({
to: "/concurrency-limits",
search: (prev) => ({
...prev,
search: {
tab: TAB_OPTIONS.global.tabSearchValue,
}),
},
});
}}
>
Expand All @@ -60,10 +59,9 @@ export const ConcurrencyTabs = ({
onClick={() => {
void navigate({
to: "/concurrency-limits",
search: (prev) => ({
...prev,
search: {
tab: TAB_OPTIONS["task-run"].tabSearchValue,
}),
},
});
}}
>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
import { DataTable } from "@/components/ui/data-table";
import { type GlobalConcurrencyLimit } from "@/hooks/global-concurrency-limits";
import { getRouteApi } from "@tanstack/react-router";
import {
createColumnHelper,
getCoreRowModel,
getPaginationRowModel,
useReactTable,
} from "@tanstack/react-table";

import { SearchInput } from "@/components/ui/input";
import { useDeferredValue, useMemo } from "react";
import { ActionsCell } from "./actions-cell";
import { ActiveCell } from "./active-cell";

const routeApi = getRouteApi("/concurrency-limits");

const columnHelper = createColumnHelper<GlobalConcurrencyLimit>();

const createColumns = ({
Expand Down Expand Up @@ -53,11 +59,36 @@ export const GlobalConcurrencyDataTable = ({
onEditRow,
onDeleteRow,
}: Props) => {
const navigate = routeApi.useNavigate();
const { search } = routeApi.useSearch();
const deferredSearch = useDeferredValue(search ?? "");

const filteredData = useMemo(() => {
return data.filter((row) =>
row.name.toLowerCase().includes(deferredSearch.toLowerCase()),
);
}, [data, deferredSearch]);

const table = useReactTable({
data,
data: filteredData,
columns: createColumns({ onEditRow, onDeleteRow }),
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(), //load client-side pagination code
});

return <DataTable table={table} />;
return (
<div className="flex flex-col gap-4">
<SearchInput
placeholder="Search global concurrency limit"
value={search}
onChange={(e) =>
void navigate({
to: ".",
search: (prev) => ({ ...prev, search: e.target.value }),
})
}
/>
<DataTable table={table} />
</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const DeleteLimitDialog = ({ limit, onOpenChange, onDelete }: Props) => {

const handleOnClick = (id: string | undefined) => {
if (!id) {
throw new Error("'id' field expected in GlobalConcurrencyLimit");
throw new Error("'id' field expected in TaskRunConcurrencyLimit");
}
deleteGlobalConcurrencyLimit(id, {
onSuccess: () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,12 @@ export const GlobalConcurrencyLimitsHeader = ({ onAdd }: Props) => {
return (
<div className="flex gap-2 items-center">
<Typography variant="h4">Global Concurrency Limits</Typography>
<Button onClick={onAdd} size="icon">
<Icon id="Plus" />
<Button
onClick={onAdd}
size="icon"
aria-label="add global concurrency limit"
>
<Icon id="Plus" className="h-4 w-4" />
</Button>
</div>
);
Expand Down
18 changes: 8 additions & 10 deletions ui-v2/src/components/concurrency/global-concurrency-view/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,24 +37,22 @@ export const GlobalConcurrencyView = () => {
};

return (
<>
<div className="flex flex-col gap-4">
<GlobalConcurrencyLimitsHeader onAdd={handleAddRow} />
{data.length === 0 ? (
<GlobalConcurrencyLimitEmptyState onAdd={handleAddRow} />
) : (
<div className="flex flex-col gap-4">
<GlobalConcurrencyLimitsHeader onAdd={handleAddRow} />
<GlobalConcurrencyDataTable
data={data}
onEditRow={handleEditRow}
onDeleteRow={handleDeleteRow}
/>
</div>
<GlobalConcurrencyDataTable
data={data}
onEditRow={handleEditRow}
onDeleteRow={handleDeleteRow}
/>
)}
<DialogView
openDialog={openDialog}
onCloseDialog={handleCloseDialog}
onOpenChange={handleOpenChange}
/>
</>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { Button } from "@/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Icon } from "@/components/ui/icons";
import { type TaskRunConcurrencyLimit } from "@/hooks/task-run-concurrency-limits";
import { useToast } from "@/hooks/use-toast";
import { CellContext } from "@tanstack/react-table";

type Props = CellContext<TaskRunConcurrencyLimit, unknown> & {
onDeleteRow: (row: TaskRunConcurrencyLimit) => void;
onResetRow: (row: TaskRunConcurrencyLimit) => void;
};

export const ActionsCell = ({ onDeleteRow, onResetRow, ...props }: Props) => {
const { toast } = useToast();

const handleCopyId = (id: string | undefined) => {
if (!id) {
throw new Error("'id' field expected in GlobalConcurrencyLimit");
}
void navigator.clipboard.writeText(id);
toast({ title: "Name copied" });
};

const row = props.row.original;

return (
<div className="flex flex-row justify-end">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" className="h-8 w-8 p-0">
<span className="sr-only">Open menu</span>
<Icon id="MoreVertical" className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>Actions</DropdownMenuLabel>
<DropdownMenuItem onClick={() => handleCopyId(row.id)}>
Copy ID
</DropdownMenuItem>
<DropdownMenuItem onClick={() => onDeleteRow(row)}>
Delete
</DropdownMenuItem>
<DropdownMenuItem onClick={() => onResetRow(row)}>
Reset
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { DataTable } from "@/components/ui/data-table";
import { type TaskRunConcurrencyLimit } from "@/hooks/task-run-concurrency-limits";
import { getRouteApi } from "@tanstack/react-router";
import {
createColumnHelper,
getCoreRowModel,
getPaginationRowModel,
useReactTable,
} from "@tanstack/react-table";

import { SearchInput } from "@/components/ui/input";
import { useDeferredValue, useMemo } from "react";
import { ActionsCell } from "./actions-cell";

const routeApi = getRouteApi("/concurrency-limits");

const columnHelper = createColumnHelper<TaskRunConcurrencyLimit>();

const createColumns = ({
onDeleteRow,
onResetRow,
}: {
onDeleteRow: (row: TaskRunConcurrencyLimit) => void;
onResetRow: (row: TaskRunConcurrencyLimit) => void;
}) => [
columnHelper.accessor("tag", {
header: "Tag", // TODO: Make this a link when starting the tak run concurrency page
}),
columnHelper.accessor("concurrency_limit", {
header: "Slots",
}),
columnHelper.accessor("active_slots", {
header: "Active Task Runs", // TODO: Give this styling once knowing what it looks like
}),
columnHelper.display({
id: "actions",
cell: (props) => (
<ActionsCell
{...props}
onDeleteRow={onDeleteRow}
onResetRow={onResetRow}
/>
),
}),
];

type Props = {
data: Array<TaskRunConcurrencyLimit>;
onDeleteRow: (row: TaskRunConcurrencyLimit) => void;
onResetRow: (row: TaskRunConcurrencyLimit) => void;
};

export const TaskRunConcurrencyDataTable = ({
data,
onDeleteRow,
onResetRow,
}: Props) => {
const navigate = routeApi.useNavigate();
const { search } = routeApi.useSearch();
const deferredSearch = useDeferredValue(search ?? "");

const filteredData = useMemo(() => {
return data.filter((row) =>
row.tag.toLowerCase().includes(deferredSearch.toLowerCase()),
);
}, [data, deferredSearch]);

const table = useReactTable({
data: filteredData,
columns: createColumns({ onDeleteRow, onResetRow }),
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(), //load client-side pagination code
});

return (
<div className="flex flex-col gap-4">
<SearchInput
placeholder="Search active task limit"
value={search}
onChange={(e) =>
void navigate({
to: ".",
search: (prev) => ({ ...prev, search: e.target.value }),
})
}
/>
<DataTable table={table} />
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { TaskRunConcurrencyDataTable } from "./data-table";
Loading

0 comments on commit 404090b

Please sign in to comment.