This is an automated email from the ASF dual-hosted git repository.
bbovenzi pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/main by this push:
new e1155edf1e0 Display latest dagrun state per dag in search result.
(#69414)
e1155edf1e0 is described below
commit e1155edf1e01a3096348b86270c43d586b2277a5
Author: Karthikeyan Singaravelan <[email protected]>
AuthorDate: Thu Jul 9 21:44:20 2026 +0530
Display latest dagrun state per dag in search result. (#69414)
* Display latest dagrun state per dag in search result.
* Limit dagrun to 1 for latest run and use option.label to show
dag_display_name if present.
---
.../ui/src/components/SearchDags/SearchDags.tsx | 38 ++++++++++++++++------
.../SearchDags/SearchDagsDropdownIndicator.tsx | 4 +--
airflow-core/src/airflow/ui/src/utils/option.ts | 5 +++
3 files changed, 35 insertions(+), 12 deletions(-)
diff --git
a/airflow-core/src/airflow/ui/src/components/SearchDags/SearchDags.tsx
b/airflow-core/src/airflow/ui/src/components/SearchDags/SearchDags.tsx
index 76e42dde6f6..56f7a1a8550 100644
--- a/airflow-core/src/airflow/ui/src/components/SearchDags/SearchDags.tsx
+++ b/airflow-core/src/airflow/ui/src/components/SearchDags/SearchDags.tsx
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { Field } from "@chakra-ui/react";
+import { Field, Flex, Text } from "@chakra-ui/react";
import { useQueryClient } from "@tanstack/react-query";
import { AsyncSelect } from "chakra-react-select";
import type { OptionsOrGroups, GroupBase, SingleValue } from
"chakra-react-select";
@@ -25,10 +25,14 @@ import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom";
import { useDebouncedCallback } from "use-debounce";
-import { UseDagServiceGetDagsKeyFn } from "openapi/queries";
+import { UseDagServiceGetDagsUiKeyFn } from "openapi/queries";
import { DagService } from "openapi/requests/services.gen";
-import type { DAGCollectionResponse, DAGResponse } from
"openapi/requests/types.gen";
-import type { Option } from "src/utils/option";
+import type {
+ DAGWithLatestDagRunsCollectionResponse,
+ DAGWithLatestDagRunsResponse,
+} from "openapi/requests/types.gen";
+import { StateBadge } from "src/components/StateBadge";
+import type { DagSearchOption } from "src/utils/option";
import { DropdownIndicator } from "./SearchDagsDropdownIndicator";
@@ -42,7 +46,7 @@ export const SearchDags = ({
const navigate = useNavigate();
const SEARCH_LIMIT = 10;
- const onSelect = (selected: SingleValue<Option>) => {
+ const onSelect = (selected: SingleValue<DagSearchOption>) => {
if (selected) {
setIsOpen(false);
void Promise.resolve(navigate(`/dags/${selected.value}`));
@@ -50,15 +54,20 @@ export const SearchDags = ({
};
const searchDagDebounced = useDebouncedCallback(
- (inputValue: string, callback: (options: OptionsOrGroups<Option,
GroupBase<Option>>) => void) => {
+ (
+ inputValue: string,
+ callback: (options: OptionsOrGroups<DagSearchOption,
GroupBase<DagSearchOption>>) => void,
+ ) => {
void queryClient.fetchQuery({
queryFn: () =>
- DagService.getDags({
+ DagService.getDagsUi({
dagDisplayNamePrefixPattern: inputValue,
+ dagRunsLimit: 1,
limit: SEARCH_LIMIT,
- }).then((data: DAGCollectionResponse) => {
- const options = data.dags.map((dag: DAGResponse) => ({
+ }).then((data: DAGWithLatestDagRunsCollectionResponse) => {
+ const options = data.dags.map((dag: DAGWithLatestDagRunsResponse)
=> ({
label: dag.dag_display_name || dag.dag_id,
+ state: dag.latest_dag_runs[0]?.state ?? null,
value: dag.dag_id,
}));
@@ -66,8 +75,9 @@ export const SearchDags = ({
return options;
}),
- queryKey: UseDagServiceGetDagsKeyFn({
+ queryKey: UseDagServiceGetDagsUiKeyFn({
dagDisplayNamePrefixPattern: inputValue,
+ dagRunsLimit: 1,
}),
staleTime: 0,
});
@@ -75,6 +85,13 @@ export const SearchDags = ({
300,
);
+ const formatOptionLabel = (option: DagSearchOption) => (
+ <Flex alignItems="center" gap={2}>
+ <StateBadge state={option.state} />
+ <Text>{option.label}</Text>
+ </Flex>
+ );
+
return (
<Field.Root>
<AsyncSelect
@@ -82,6 +99,7 @@ export const SearchDags = ({
components={{ DropdownIndicator }}
defaultOptions
filterOption={undefined}
+ formatOptionLabel={formatOptionLabel}
loadOptions={searchDagDebounced}
menuIsOpen
onChange={onSelect}
diff --git
a/airflow-core/src/airflow/ui/src/components/SearchDags/SearchDagsDropdownIndicator.tsx
b/airflow-core/src/airflow/ui/src/components/SearchDags/SearchDagsDropdownIndicator.tsx
index 4bfbd83984c..cffc8aaf2ac 100644
---
a/airflow-core/src/airflow/ui/src/components/SearchDags/SearchDagsDropdownIndicator.tsx
+++
b/airflow-core/src/airflow/ui/src/components/SearchDags/SearchDagsDropdownIndicator.tsx
@@ -20,9 +20,9 @@ import { chakraComponents } from "chakra-react-select";
import type { DropdownIndicatorProps } from "chakra-react-select";
import { FiSearch } from "react-icons/fi";
-import type { Option } from "src/utils/option";
+import type { DagSearchOption } from "src/utils/option";
-export const DropdownIndicator: React.FC<DropdownIndicatorProps<Option,
false>> = (props) => (
+export const DropdownIndicator:
React.FC<DropdownIndicatorProps<DagSearchOption, false>> = (props) => (
<chakraComponents.DropdownIndicator {...props}>
<FiSearch />
</chakraComponents.DropdownIndicator>
diff --git a/airflow-core/src/airflow/ui/src/utils/option.ts
b/airflow-core/src/airflow/ui/src/utils/option.ts
index 0d6b799ab3c..7251892a7e4 100644
--- a/airflow-core/src/airflow/ui/src/utils/option.ts
+++ b/airflow-core/src/airflow/ui/src/utils/option.ts
@@ -16,9 +16,14 @@
* specific language governing permissions and limitations
* under the License.
*/
+import type { DagRunState } from "openapi/requests/types.gen";
export type Option = {
readonly disabled?: boolean;
readonly label: string;
readonly value: string;
};
+
+export type DagSearchOption = {
+ readonly state: DagRunState | null;
+} & Option;