Skip to content

Commit

Permalink
[UI v2] feat: Adds Runs list to deployment runs tab (#17246)
Browse files Browse the repository at this point in the history
  • Loading branch information
devinvillarosa authored Feb 25, 2025
1 parent 4f96bde commit 764b79f
Show file tree
Hide file tree
Showing 11 changed files with 420 additions and 47 deletions.
236 changes: 233 additions & 3 deletions ui-v2/src/components/deployments/deployment-details-runs-tab.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,24 @@
import { Deployment } from "@/api/deployments";
import { useFilterFlowRunswithFlows } from "@/api/flow-runs/use-filter-flow-runs-with-flows";
import { usePaginateFlowRunswithFlows } from "@/api/flow-runs/use-paginate-flow-runs-with-flows";
import { FlowRunCard } from "@/components/flow-runs/flow-run-card";
import {
FlowRunState,
FlowRunsFilters,
FlowRunsList,
FlowRunsPagination,
FlowRunsRowCount,
PaginationState,
SortFilters,
useFlowRunsSelectedRows,
} from "@/components/flow-runs/flow-runs-list";
import { FLOW_RUN_STATES_NO_SCHEDULED } from "@/components/flow-runs/flow-runs-list";
import { Typography } from "@/components/ui/typography";
import { useMemo } from "react";
import useDebounce from "@/hooks/use-debounce";
import { getRouteApi } from "@tanstack/react-router";
import { useCallback, useMemo } from "react";

const routeApi = getRouteApi("/deployments/deployment/$id");

type DeploymentDetailsRunsTabProps = {
deployment: Deployment;
Expand All @@ -11,16 +27,94 @@ type DeploymentDetailsRunsTabProps = {
export const DeploymentDetailsRunsTab = ({
deployment,
}: DeploymentDetailsRunsTabProps) => {
const [selectedRows, setSelectedRows, { clearSet, onSelectRow }] =
useFlowRunsSelectedRows();
const [pagination, onChangePagination] = usePagination();
const [search, setSearch] = useSearch();
const [sort, setSort] = useSort();
const [filter, setFilter] = useFilter();
const resetFilters = useResetFilters();

const debouncedSearch = useDebounce(search, 400);

const { data } = usePaginateFlowRunswithFlows({
deployments: {
operator: "and_",
id: { any_: [deployment.id] },
},
flow_runs: {
name: { like_: debouncedSearch || undefined },
state: {
name: { any_: filter.length === 0 ? undefined : filter },
operator: "or_",
},
operator: "and_",
},
limit: pagination.limit,
page: pagination.page,
sort,
});

const dataWithDeployment = useMemo(() => {
if (!data) {
return undefined;
}
return {
...data,
results: data.results.map((flowRun) => ({ ...flowRun, deployment })),
};
}, [data, deployment]);

const handleResetFilters = !resetFilters
? undefined
: () => {
resetFilters();
clearSet();
};

const nextRun = useGetNextRun(deployment);

return (
<div className="flex flex-col">
<div className="flex flex-col gap-2">
{nextRun && (
<div className="flex flex-col gap-2 border-b py-2">
<Typography variant="bodyLarge">Next Run</Typography>
<FlowRunCard flowRun={nextRun} />
</div>
)}
<div className="flex flex-col gap-2">
<div className="flex items-center justify-between">
<FlowRunsRowCount
count={dataWithDeployment?.count}
results={dataWithDeployment?.results}
selectedRows={selectedRows}
setSelectedRows={setSelectedRows}
/>
<FlowRunsFilters
search={{ value: search, onChange: setSearch }}
sort={{ value: sort, onSelect: setSort }}
stateFilter={{
value: new Set(filter),
onSelect: setFilter,
}}
/>
</div>
<FlowRunsList
flowRuns={dataWithDeployment?.results}
selectedRows={selectedRows}
onSelect={onSelectRow}
onClearFilters={handleResetFilters}
/>

{dataWithDeployment && (
<FlowRunsPagination
count={dataWithDeployment.count}
pagination={pagination}
onChangePagination={onChangePagination}
pages={dataWithDeployment.pages}
/>
)}
</div>
</div>
);
};
Expand All @@ -32,7 +126,7 @@ function useGetNextRun(deployment: Deployment) {
state: { name: { any_: ["Scheduled"] }, operator: "and_" },
operator: "and_",
},
sort: "NAME_ASC",
sort: "EXPECTED_START_TIME_ASC",
limit: 1,
offset: 0,
});
Expand All @@ -47,3 +141,139 @@ function useGetNextRun(deployment: Deployment) {
};
}, [data, deployment]);
}

function useResetFilters() {
const { runs } = routeApi.useSearch();
const navigate = routeApi.useNavigate();
const resetFilters = useCallback(() => {
void navigate({
to: ".",
search: (prev) => ({
...prev,
runs: undefined,
}),
replace: true,
});
}, [navigate]);
const hasFiltersApplied = useMemo(() => Boolean(runs), [runs]);

return hasFiltersApplied ? resetFilters : undefined;
}

function usePagination() {
const { runs } = routeApi.useSearch();
const navigate = routeApi.useNavigate();

const onChangePagination = useCallback(
(pagination?: PaginationState) => {
void navigate({
to: ".",
search: (prev) => ({
...prev,
runs: {
...runs,
...pagination,
},
}),
replace: true,
});
},
[navigate, runs],
);

const pagination = useMemo(() => {
return {
page: runs?.page ?? 1,
limit: runs?.limit ?? 10,
};
}, [runs?.limit, runs?.page]);

return [pagination, onChangePagination] as const;
}

function useSearch() {
const { runs } = routeApi.useSearch();
const navigate = routeApi.useNavigate();

const onSearch = useCallback(
(value?: string) => {
void navigate({
to: ".",
search: (prev) => ({
...prev,
runs: {
...runs,
flowRuns: {
...runs?.flowRuns,
name: value,
},
},
}),
replace: true,
});
},
[navigate, runs],
);
const search = useMemo(
() => runs?.flowRuns?.name ?? "",
[runs?.flowRuns?.name],
);
return [search, onSearch] as const;
}

function useSort() {
const { runs } = routeApi.useSearch();
const navigate = routeApi.useNavigate();

const onSort = useCallback(
(value?: SortFilters) => {
void navigate({
to: ".",
search: (prev) => ({
...prev,
runs: {
...runs,
sort: value,
},
}),
replace: true,
});
},
[navigate, runs],
);
const sort = useMemo(() => runs?.sort ?? "START_TIME_DESC", [runs?.sort]);
return [sort, onSort] as const;
}

function useFilter() {
const { runs } = routeApi.useSearch();
const navigate = routeApi.useNavigate();

const onFilter = useCallback(
(value?: Set<FlowRunState>) => {
void navigate({
to: ".",
search: (prev) => ({
...prev,
runs: {
...runs,
flowRuns: {
...runs?.flowRuns,
state: value ? Array.from(value) : undefined,
},
},
}),
replace: true,
});
},
[navigate, runs],
);

const filter = useMemo(
() =>
runs?.flowRuns?.state ??
(FLOW_RUN_STATES_NO_SCHEDULED satisfies Array<FlowRunState>),
[runs?.flowRuns?.state],
);
return [filter, onFilter] as const;
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ import {
FlowRunsRowCount,
type PaginationState,
SortFilters,
useFlowRunsSelectedRows,
} from "@/components/flow-runs/flow-runs-list";
import { useCallback, useMemo, useState } from "react";
import useDebounce from "@/hooks/use-debounce";
import { useCallback, useMemo } from "react";

const routeApi = getRouteApi("/deployments/deployment/$id");

Expand All @@ -22,20 +24,23 @@ type DeploymentDetailsUpcomingTabProps = {
export const DeploymentDetailsUpcomingTab = ({
deployment,
}: DeploymentDetailsUpcomingTabProps) => {
const [selectedRows, setSelectedRows] = useState<Set<string>>(new Set());
const [selectedRows, setSelectedRows, { clearSet, onSelectRow }] =
useFlowRunsSelectedRows();
const [pagination, onChangePagination] = usePagination();
const [search, setSearch] = useSearch();
const [sort, setSort] = useSort();
const [filter, setFilter] = useFilter();
const resetFilters = useResetFilters();

const debouncedSearch = useDebounce(search, 400);

const { data } = usePaginateFlowRunswithFlows({
deployments: {
operator: "and_",
id: { any_: [deployment.id] },
},
flow_runs: {
name: { like_: search || undefined },
name: { like_: debouncedSearch || undefined },
state: {
name: { any_: filter.length === 0 ? undefined : filter },
operator: "or_",
Expand All @@ -57,28 +62,11 @@ export const DeploymentDetailsUpcomingTab = ({
};
}, [data, deployment]);

const addRow = (id: string) =>
setSelectedRows((curr) => new Set(curr).add(id));
const removeRow = (id: string) =>
setSelectedRows((curr) => {
const newValue = new Set(curr);
newValue.delete(id);
return newValue;
});

const handleSelectRow = (id: string, checked: boolean) => {
if (checked) {
addRow(id);
} else {
removeRow(id);
}
};

const handleResetFilters = !resetFilters
? undefined
: () => {
resetFilters();
setSelectedRows(new Set());
clearSet();
};

return (
Expand All @@ -103,7 +91,7 @@ export const DeploymentDetailsUpcomingTab = ({
<FlowRunsList
flowRuns={dataWithDeployment?.results}
selectedRows={selectedRows}
onSelect={handleSelectRow}
onSelect={onSelectRow}
onClearFilters={handleResetFilters}
/>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,14 @@ export const DeploymentScheduleItem = ({
return (
<Card className="p-3 flex items-center justify-between">
<Typography>{getScheduleTitle(deploymentSchedule)}</Typography>
<div className="flex items-center gap-2">
<ScheduleToggleSwitch
deploymentSchedule={deploymentSchedule}
disabled={disabled}
/>
<div className="flex items-baseline gap-2">
<div>
<ScheduleToggleSwitch
deploymentSchedule={deploymentSchedule}
disabled={disabled}
/>
</div>
<div></div>
<ScheduleActionMenu
deploymentSchedule={deploymentSchedule}
onEditSchedule={onEditSchedule}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,15 @@ export const DeploymentScheduleToggle = ({
return (
<TooltipProvider>
<Tooltip>
<TooltipTrigger>
<Switch
aria-label="Pause or resume all schedules"
disabled={isDeploymentDeprecated}
onCheckedChange={handleChckedChange}
checked={!deployment.paused}
/>
<TooltipTrigger asChild>
<div>
<Switch
aria-label="Pause or resume all schedules"
disabled={isDeploymentDeprecated}
onCheckedChange={handleChckedChange}
checked={!deployment.paused}
/>
</div>
</TooltipTrigger>
<TooltipContent>Pause or resume all schedules</TooltipContent>
</Tooltip>
Expand Down
Loading

0 comments on commit 764b79f

Please sign in to comment.