Skip to content

Commit 73f474b

Browse files
committed
Refactor taskinstances filter to a separate component.
1 parent 222bef5 commit 73f474b

File tree

4 files changed

+166
-95
lines changed

4 files changed

+166
-95
lines changed

airflow/ui/src/pages/Dashboard/HistoricalMetrics/MetricSection.tsx

+4-3
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
* specific language governing permissions and limitations
1717
* under the License.
1818
*/
19-
import { Box, Flex, HStack, VStack, Text, Link } from "@chakra-ui/react";
19+
import { Box, Flex, HStack, VStack, Text } from "@chakra-ui/react";
20+
import { Link as RouterLink } from "react-router-dom";
2021

2122
import type { TaskInstanceState } from "openapi/requests/types.gen";
2223
import { StateBadge } from "src/components/StateBadge";
@@ -45,11 +46,11 @@ export const MetricSection = ({ endDate, kind, runs, startDate, state, total }:
4546
<VStack align="left" gap={1} mb={4} ml={0} pl={0}>
4647
<Flex justify="space-between">
4748
<HStack>
48-
<Link href={`/webapp/${kind}?state=${state}&start_date=${startDate}&end_date=${endDate}`}>
49+
<RouterLink to={`/${kind}?state=${state}&start_date=${startDate}&end_date=${endDate}`}>
4950
<StateBadge fontSize="md" state={state}>
5051
{runs}
5152
</StateBadge>
52-
</Link>
53+
</RouterLink>
5354
<Text>
5455
{state
5556
.split("_")

airflow/ui/src/pages/TaskInstances.tsx airflow/ui/src/pages/TaskInstances/TaskInstances.tsx

+11-92
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
/* eslint-disable max-lines */
2-
31
/*!
42
* Licensed to the Apache Software Foundation (ASF) under one
53
* or more contributor license agreements. See the NOTICE file
@@ -18,27 +16,26 @@
1816
* specific language governing permissions and limitations
1917
* under the License.
2018
*/
21-
import { Flex, Link, HStack, type SelectValueChangeDetails } from "@chakra-ui/react";
19+
import { Flex, Link } from "@chakra-ui/react";
2220
import type { ColumnDef } from "@tanstack/react-table";
23-
import { useCallback, useState } from "react";
21+
import { useState } from "react";
2422
import { Link as RouterLink, useParams, useSearchParams } from "react-router-dom";
2523

2624
import { useTaskInstanceServiceGetTaskInstances } from "openapi/queries";
27-
import type { TaskInstanceResponse, TaskInstanceState } from "openapi/requests/types.gen";
25+
import type { TaskInstanceResponse } from "openapi/requests/types.gen";
2826
import { ClearTaskInstanceButton } from "src/components/Clear";
2927
import { DataTable } from "src/components/DataTable";
3028
import { useTableURLState } from "src/components/DataTable/useTableUrlState";
3129
import { ErrorAlert } from "src/components/ErrorAlert";
3230
import { MarkTaskInstanceAsButton } from "src/components/MarkAs";
33-
import { SearchBar } from "src/components/SearchBar";
3431
import { StateBadge } from "src/components/StateBadge";
3532
import Time from "src/components/Time";
36-
import { Select } from "src/components/ui";
3733
import { SearchParamsKeys, type SearchParamsKeysType } from "src/constants/searchParams";
38-
import { taskInstanceStateOptions as stateOptions } from "src/constants/stateOptions";
39-
import { capitalize, getDuration, useAutoRefresh, isStatePending } from "src/utils";
34+
import { getDuration, useAutoRefresh, isStatePending } from "src/utils";
4035
import { getTaskInstanceLink } from "src/utils/links";
4136

37+
import { TaskInstancesFilter } from "./TaskInstancesFilter";
38+
4239
type TaskInstanceRow = { row: { original: TaskInstanceResponse } };
4340
const {
4441
END_DATE: END_DATE_PARAM,
@@ -151,7 +148,7 @@ const taskInstanceColumns = (
151148

152149
export const TaskInstances = () => {
153150
const { dagId, runId, taskId } = useParams();
154-
const [searchParams, setSearchParams] = useSearchParams();
151+
const [searchParams] = useSearchParams();
155152
const { setTableURLState, tableURLState } = useTableURLState();
156153
const { pagination, sorting } = tableURLState;
157154
const [sort] = sorting;
@@ -166,39 +163,6 @@ export const TaskInstances = () => {
166163
searchParams.get(NAME_PATTERN_PARAM) ?? undefined,
167164
);
168165

169-
const handleStateChange = useCallback(
170-
({ value }: SelectValueChangeDetails<string>) => {
171-
const [val, ...rest] = value;
172-
173-
if ((val === undefined || val === "all") && rest.length === 0) {
174-
searchParams.delete(STATE_PARAM);
175-
} else {
176-
searchParams.delete(STATE_PARAM);
177-
value.filter((state) => state !== "all").map((state) => searchParams.append(STATE_PARAM, state));
178-
}
179-
setTableURLState({
180-
pagination: { ...pagination, pageIndex: 0 },
181-
sorting,
182-
});
183-
setSearchParams(searchParams);
184-
},
185-
[pagination, searchParams, setSearchParams, setTableURLState, sorting],
186-
);
187-
188-
const handleSearchChange = (value: string) => {
189-
if (value) {
190-
searchParams.set(NAME_PATTERN_PARAM, value);
191-
} else {
192-
searchParams.delete(NAME_PATTERN_PARAM);
193-
}
194-
setTableURLState({
195-
pagination: { ...pagination, pageIndex: 0 },
196-
sorting,
197-
});
198-
setTaskDisplayNamePattern(value);
199-
setSearchParams(searchParams);
200-
};
201-
202166
const refetchInterval = useAutoRefresh({});
203167

204168
const { data, error, isLoading } = useTaskInstanceServiceGetTaskInstances(
@@ -224,55 +188,10 @@ export const TaskInstances = () => {
224188

225189
return (
226190
<>
227-
<HStack>
228-
<Select.Root
229-
collection={stateOptions}
230-
maxW="250px"
231-
multiple
232-
onValueChange={handleStateChange}
233-
value={hasFilteredState ? filteredState : ["all"]}
234-
>
235-
<Select.Trigger
236-
{...(hasFilteredState ? { clearable: true } : {})}
237-
colorPalette="blue"
238-
isActive={Boolean(filteredState)}
239-
>
240-
<Select.ValueText>
241-
{() =>
242-
hasFilteredState ? (
243-
<HStack gap="10px">
244-
{filteredState.map((state) => (
245-
<StateBadge key={state} state={state as TaskInstanceState}>
246-
{state === "none" ? "No Status" : capitalize(state)}
247-
</StateBadge>
248-
))}
249-
</HStack>
250-
) : (
251-
"All States"
252-
)
253-
}
254-
</Select.ValueText>
255-
</Select.Trigger>
256-
<Select.Content>
257-
{stateOptions.items.map((option) => (
258-
<Select.Item item={option} key={option.label}>
259-
{option.value === "all" ? (
260-
option.label
261-
) : (
262-
<StateBadge state={option.value as TaskInstanceState}>{option.label}</StateBadge>
263-
)}
264-
</Select.Item>
265-
))}
266-
</Select.Content>
267-
</Select.Root>
268-
<SearchBar
269-
buttonProps={{ disabled: true }}
270-
defaultValue={taskDisplayNamePattern ?? ""}
271-
hideAdvanced
272-
onChange={handleSearchChange}
273-
placeHolder="Search Tasks"
274-
/>
275-
</HStack>
191+
<TaskInstancesFilter
192+
setTaskDisplayNamePattern={setTaskDisplayNamePattern}
193+
taskDisplayNamePattern={taskDisplayNamePattern}
194+
/>
276195
<DataTable
277196
columns={taskInstanceColumns(dagId, runId, taskId)}
278197
data={data?.task_instances ?? []}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
/*!
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
import { HStack, type SelectValueChangeDetails } from "@chakra-ui/react";
20+
import { useCallback } from "react";
21+
import { useSearchParams } from "react-router-dom";
22+
23+
import type { TaskInstanceState } from "openapi/requests/types.gen";
24+
import { useTableURLState } from "src/components/DataTable/useTableUrlState";
25+
import { SearchBar } from "src/components/SearchBar";
26+
import { StateBadge } from "src/components/StateBadge";
27+
import { Select } from "src/components/ui";
28+
import { SearchParamsKeys, type SearchParamsKeysType } from "src/constants/searchParams";
29+
import { taskInstanceStateOptions as stateOptions } from "src/constants/stateOptions";
30+
import { capitalize } from "src/utils";
31+
32+
const { NAME_PATTERN: NAME_PATTERN_PARAM, STATE: STATE_PARAM }: SearchParamsKeysType = SearchParamsKeys;
33+
34+
export const TaskInstancesFilter = ({
35+
setTaskDisplayNamePattern,
36+
taskDisplayNamePattern,
37+
}: {
38+
readonly setTaskDisplayNamePattern: React.Dispatch<React.SetStateAction<string | undefined>>;
39+
readonly taskDisplayNamePattern: string | undefined;
40+
}) => {
41+
const [searchParams, setSearchParams] = useSearchParams();
42+
const { setTableURLState, tableURLState } = useTableURLState();
43+
const { pagination, sorting } = tableURLState;
44+
45+
const filteredState = searchParams.getAll(STATE_PARAM);
46+
const hasFilteredState = filteredState.length > 0;
47+
48+
const handleStateChange = useCallback(
49+
({ value }: SelectValueChangeDetails<string>) => {
50+
const [val, ...rest] = value;
51+
52+
if ((val === undefined || val === "all") && rest.length === 0) {
53+
searchParams.delete(STATE_PARAM);
54+
} else {
55+
searchParams.delete(STATE_PARAM);
56+
value.filter((state) => state !== "all").map((state) => searchParams.append(STATE_PARAM, state));
57+
}
58+
setTableURLState({
59+
pagination: { ...pagination, pageIndex: 0 },
60+
sorting,
61+
});
62+
setSearchParams(searchParams);
63+
},
64+
[pagination, searchParams, setSearchParams, setTableURLState, sorting],
65+
);
66+
67+
const handleSearchChange = (value: string) => {
68+
if (value) {
69+
searchParams.set(NAME_PATTERN_PARAM, value);
70+
} else {
71+
searchParams.delete(NAME_PATTERN_PARAM);
72+
}
73+
setTableURLState({
74+
pagination: { ...pagination, pageIndex: 0 },
75+
sorting,
76+
});
77+
setTaskDisplayNamePattern(value);
78+
setSearchParams(searchParams);
79+
};
80+
81+
return (
82+
<HStack>
83+
<Select.Root
84+
collection={stateOptions}
85+
maxW="250px"
86+
multiple
87+
onValueChange={handleStateChange}
88+
value={hasFilteredState ? filteredState : ["all"]}
89+
>
90+
<Select.Trigger
91+
{...(hasFilteredState ? { clearable: true } : {})}
92+
colorPalette="blue"
93+
isActive={Boolean(filteredState)}
94+
>
95+
<Select.ValueText>
96+
{() =>
97+
hasFilteredState ? (
98+
<HStack gap="10px">
99+
{filteredState.map((state) => (
100+
<StateBadge key={state} state={state as TaskInstanceState}>
101+
{state === "none" ? "No Status" : capitalize(state)}
102+
</StateBadge>
103+
))}
104+
</HStack>
105+
) : (
106+
"All States"
107+
)
108+
}
109+
</Select.ValueText>
110+
</Select.Trigger>
111+
<Select.Content>
112+
{stateOptions.items.map((option) => (
113+
<Select.Item item={option} key={option.label}>
114+
{option.value === "all" ? (
115+
option.label
116+
) : (
117+
<StateBadge state={option.value as TaskInstanceState}>{option.label}</StateBadge>
118+
)}
119+
</Select.Item>
120+
))}
121+
</Select.Content>
122+
</Select.Root>
123+
<SearchBar
124+
buttonProps={{ disabled: true }}
125+
defaultValue={taskDisplayNamePattern ?? ""}
126+
hideAdvanced
127+
onChange={handleSearchChange}
128+
placeHolder="Search Tasks"
129+
/>
130+
</HStack>
131+
);
132+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/*!
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
export { TaskInstances } from "./TaskInstances";

0 commit comments

Comments
 (0)