From ffe09e0b88118708b08b4d888cef4bcc560e1cb6 Mon Sep 17 00:00:00 2001 From: rich7420 Date: Sun, 5 Oct 2025 11:22:45 +0800 Subject: [PATCH 1/6] Add auto-refresh functionality to Required Actions page --- .../ui/src/pages/HITLTaskInstances/HITLTaskInstances.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLTaskInstances.tsx b/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLTaskInstances.tsx index 82e43b6aa4f5f..360eea623fc97 100644 --- a/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLTaskInstances.tsx +++ b/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLTaskInstances.tsx @@ -34,6 +34,7 @@ import { TruncatedText } from "src/components/TruncatedText"; import { SearchParamsKeys, type SearchParamsKeysType } from "src/constants/searchParams"; import { getHITLState } from "src/utils/hitl"; import { getTaskInstanceLink } from "src/utils/links"; +import { useAutoRefresh } from "src/utils"; import { HITLFilters } from "./HITLFilters"; @@ -127,6 +128,9 @@ export const HITLTaskInstances = () => { const [sort] = sorting; const responseReceived = searchParams.get(RESPONSE_RECEIVED_PARAM); + // Add auto-refresh functionality + const refetchInterval = useAutoRefresh({}); + const dagIdPattern = searchParams.get(DAG_DISPLAY_NAME_PATTERN) ?? undefined; const taskIdPattern = searchParams.get(TASK_ID_PATTERN) ?? undefined; const filterResponseReceived = searchParams.get(RESPONSE_RECEIVED_PARAM) ?? undefined; @@ -148,6 +152,8 @@ export const HITLTaskInstances = () => { state: effectiveResponseReceived === "false" ? ["deferred"] : undefined, taskId, taskIdPattern, + }, undefined, { + refetchInterval, }); const handleResponseChange = useCallback(() => { From 9f2fb6a5f84bb816b459945a5f02cd12584eea9d Mon Sep 17 00:00:00 2001 From: rich7420 Date: Thu, 9 Oct 2025 17:04:56 +0800 Subject: [PATCH 2/6] switch refresh interval --- .../pages/HITLTaskInstances/HITLTaskInstances.tsx | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLTaskInstances.tsx b/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLTaskInstances.tsx index 360eea623fc97..7729fba3961da 100644 --- a/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLTaskInstances.tsx +++ b/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLTaskInstances.tsx @@ -129,7 +129,7 @@ export const HITLTaskInstances = () => { const responseReceived = searchParams.get(RESPONSE_RECEIVED_PARAM); // Add auto-refresh functionality - const refetchInterval = useAutoRefresh({}); + const baseRefetchInterval = useAutoRefresh({}); const dagIdPattern = searchParams.get(DAG_DISPLAY_NAME_PATTERN) ?? undefined; const taskIdPattern = searchParams.get(TASK_ID_PATTERN) ?? undefined; @@ -153,7 +153,18 @@ export const HITLTaskInstances = () => { taskId, taskIdPattern, }, undefined, { - refetchInterval, + // Only continue auto-refetching when filtering for unreceived responses + // and at least one TaskInstance is still deferred without a response. + refetchInterval: (query) => { + const isFilteringUnreceived = effectiveResponseReceived === "false"; + if (!isFilteringUnreceived) return false; + const hasDeferredWithoutResponse = Boolean( + query.state.data?.hitl_details?.some( + (detail: HITLDetail) => !detail.responded_at && detail.task_instance.state === "deferred", + ), + ); + return hasDeferredWithoutResponse ? baseRefetchInterval : false; + }, }); const handleResponseChange = useCallback(() => { From 537760cc8a32559fac828cb2481dab58e586475f Mon Sep 17 00:00:00 2001 From: rich7420 Date: Mon, 13 Oct 2025 19:22:05 +0800 Subject: [PATCH 3/6] remove dependance of effectiveResponseReceived --- .../ui/src/pages/HITLTaskInstances/HITLTaskInstances.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLTaskInstances.tsx b/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLTaskInstances.tsx index 7729fba3961da..ab4935f51c5b3 100644 --- a/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLTaskInstances.tsx +++ b/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLTaskInstances.tsx @@ -156,8 +156,6 @@ export const HITLTaskInstances = () => { // Only continue auto-refetching when filtering for unreceived responses // and at least one TaskInstance is still deferred without a response. refetchInterval: (query) => { - const isFilteringUnreceived = effectiveResponseReceived === "false"; - if (!isFilteringUnreceived) return false; const hasDeferredWithoutResponse = Boolean( query.state.data?.hitl_details?.some( (detail: HITLDetail) => !detail.responded_at && detail.task_instance.state === "deferred", From f81602bf3f68141460130e47875d77cd7b63ecdd Mon Sep 17 00:00:00 2001 From: rich7420 Date: Sat, 18 Oct 2025 14:43:23 +0800 Subject: [PATCH 4/6] remove typescript error and comment --- .../ui/src/pages/HITLTaskInstances/HITLTaskInstances.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLTaskInstances.tsx b/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLTaskInstances.tsx index ab4935f51c5b3..72bb1bb644edd 100644 --- a/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLTaskInstances.tsx +++ b/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLTaskInstances.tsx @@ -157,10 +157,11 @@ export const HITLTaskInstances = () => { // and at least one TaskInstance is still deferred without a response. refetchInterval: (query) => { const hasDeferredWithoutResponse = Boolean( - query.state.data?.hitl_details?.some( - (detail: HITLDetail) => !detail.responded_at && detail.task_instance.state === "deferred", + query.state.data?.hitl_details.some( + (detail: HITLDetail) => detail.responded_at == undefined && detail.task_instance.state === "deferred", ), ); + return hasDeferredWithoutResponse ? baseRefetchInterval : false; }, }); From 6a5cf9526c1886227adbcd7757ff8f62805342de Mon Sep 17 00:00:00 2001 From: rich7420 Date: Wed, 22 Oct 2025 13:14:48 +0800 Subject: [PATCH 5/6] fix static check error --- .../ui/src/pages/HITLTaskInstances/HITLTaskInstances.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLTaskInstances.tsx b/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLTaskInstances.tsx index 41179fdc2c7c4..09ff0c94f11f0 100644 --- a/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLTaskInstances.tsx +++ b/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLTaskInstances.tsx @@ -177,7 +177,6 @@ export const HITLTaskInstances = () => { const [sort] = sorting; const responseReceived = searchParams.get(RESPONSE_RECEIVED_PARAM); - // Add auto-refresh functionality const baseRefetchInterval = useAutoRefresh({}); const bodySearch = searchParams.get(BODY_SEARCH) ?? undefined; @@ -219,7 +218,7 @@ export const HITLTaskInstances = () => { refetchInterval: (query) => { const hasDeferredWithoutResponse = Boolean( query.state.data?.hitl_details.some( - (detail: HITLDetail) => detail.responded_at == undefined && detail.task_instance.state === "deferred", + (detail: HITLDetail) => detail.responded_at === undefined && detail.task_instance.state === "deferred", ), ); From 94aedcebc1ac2014e009ea16181b2db6bf2be558 Mon Sep 17 00:00:00 2001 From: rich7420 Date: Fri, 24 Oct 2025 00:33:51 +0800 Subject: [PATCH 6/6] fix ts-compile-lint-ui error --- .../HITLTaskInstances/HITLTaskInstances.tsx | 69 ++++++++++--------- 1 file changed, 37 insertions(+), 32 deletions(-) diff --git a/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLTaskInstances.tsx b/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLTaskInstances.tsx index 09ff0c94f11f0..8b391dd0828c0 100644 --- a/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLTaskInstances.tsx +++ b/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLTaskInstances.tsx @@ -32,9 +32,9 @@ import { StateBadge } from "src/components/StateBadge"; import Time from "src/components/Time"; import { TruncatedText } from "src/components/TruncatedText"; import { SearchParamsKeys, type SearchParamsKeysType } from "src/constants/searchParams"; +import { useAutoRefresh } from "src/utils"; import { getHITLState } from "src/utils/hitl"; import { getTaskInstanceLink } from "src/utils/links"; -import { useAutoRefresh } from "src/utils"; import { HITLFilters } from "./HITLFilters"; @@ -192,39 +192,44 @@ export const HITLTaskInstances = () => { // Use the filter value if available, otherwise fall back to the old responseReceived param const effectiveResponseReceived = filterResponseReceived ?? responseReceived; - const { data, error, isLoading } = useTaskInstanceServiceGetHitlDetails({ - bodySearch, - createdAtGte, - createdAtLte, - dagId: dagId ?? "~", - dagIdPattern, - dagRunId: runId ?? "~", - limit: pagination.pageSize, - mapIndex: parseInt(mapIndex, 10), - offset: pagination.pageIndex * pagination.pageSize, - orderBy: sort ? [`${sort.desc ? "-" : ""}${sort.id}`] : [], - respondedByUserName: respondedByUserName === undefined ? undefined : [respondedByUserName], - responseReceived: - Boolean(effectiveResponseReceived) && effectiveResponseReceived !== "all" - ? effectiveResponseReceived === "true" - : undefined, - state: effectiveResponseReceived === "false" ? ["deferred"] : undefined, - subjectSearch, - taskId, - taskIdPattern, - }, undefined, { - // Only continue auto-refetching when filtering for unreceived responses - // and at least one TaskInstance is still deferred without a response. - refetchInterval: (query) => { - const hasDeferredWithoutResponse = Boolean( - query.state.data?.hitl_details.some( - (detail: HITLDetail) => detail.responded_at === undefined && detail.task_instance.state === "deferred", - ), - ); + const { data, error, isLoading } = useTaskInstanceServiceGetHitlDetails( + { + bodySearch, + createdAtGte, + createdAtLte, + dagId: dagId ?? "~", + dagIdPattern, + dagRunId: runId ?? "~", + limit: pagination.pageSize, + mapIndex: parseInt(mapIndex, 10), + offset: pagination.pageIndex * pagination.pageSize, + orderBy: sort ? [`${sort.desc ? "-" : ""}${sort.id}`] : [], + respondedByUserName: respondedByUserName === undefined ? undefined : [respondedByUserName], + responseReceived: + Boolean(effectiveResponseReceived) && effectiveResponseReceived !== "all" + ? effectiveResponseReceived === "true" + : undefined, + state: effectiveResponseReceived === "false" ? ["deferred"] : undefined, + subjectSearch, + taskId, + taskIdPattern, + }, + undefined, + { + // Only continue auto-refetching when filtering for unreceived responses + // and at least one TaskInstance is still deferred without a response. + refetchInterval: (query) => { + const hasDeferredWithoutResponse = Boolean( + query.state.data?.hitl_details.some( + (detail: HITLDetail) => + detail.responded_at === undefined && detail.task_instance.state === "deferred", + ), + ); - return hasDeferredWithoutResponse ? baseRefetchInterval : false; + return hasDeferredWithoutResponse ? baseRefetchInterval : false; + }, }, - }); + ); const handleResponseChange = useCallback(() => { setTableURLState({