Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,28 +18,49 @@
*/
import { ReactFlowProvider } from "@xyflow/react";
import { useTranslation } from "react-i18next";
import { FiUser } from "react-icons/fi";
import { MdOutlineTask } from "react-icons/md";
import { useParams } from "react-router-dom";

import { useHumanInTheLoopServiceGetHitlDetails } from "openapi/queries";
import { DetailsLayout } from "src/layouts/Details/DetailsLayout";
import { useGridTiSummaries } from "src/queries/useGridTISummaries.ts";
import { isStatePending, useAutoRefresh } from "src/utils";

import { Header } from "./Header";

export const GroupTaskInstance = () => {
const { dagId = "", runId = "", taskId = "" } = useParams();
const { dagId = "", groupId = "", runId = "" } = useParams();
const { t: translate } = useTranslation("dag");
const { data: gridTISummaries } = useGridTiSummaries({ dagId, runId });
const taskInstance = gridTISummaries?.task_instances.find((ti) => ti.task_id === taskId);
const taskInstance = gridTISummaries?.task_instances.find((ti) => ti.task_id === groupId);

const refetchInterval = useAutoRefresh({ dagId });

const tabs = [{ icon: <MdOutlineTask />, label: translate("tabs.taskInstances"), value: "" }];
const { data: hitlData } = useHumanInTheLoopServiceGetHitlDetails(
{
dagId,
dagRunId: runId,
taskIdPattern: groupId,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using taskIdPattern to retrieve 'group related' HILT will not work in every case. While this is true that task from a same group tend to share the same prefix, it also mean that any task outside the group that also maches the pattern will get returned wrongly in here and displayed in the group actions.

For instance, based on your example, I added a TI, outside all groups, and since the name matches the 'validation' prefix, this will end up like this in the UI

    validation_single_task = HITLBranchOperator(
        task_id="validation_single_task",
        subject="This is the validation for the single task",
        options=["1", "2", "3"],
    )
Screenshot 2025-08-12 at 17 16 02

We need to add a backend filter on group_id I think. To be sure to only retrieve 'group' related TIs.

Copy link
Member Author

@guan404ming guan404ming Aug 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for clear example that makes sense to me. Let me mark this as draft to wait the backend update.

},
undefined,
{
enabled: Boolean(dagId && groupId),
},
);

const hasHitlForTask = (hitlData?.total_entries ?? 0) > 0;

const tabs = [
{ icon: <MdOutlineTask />, label: translate("tabs.taskInstances"), value: "" },
{ icon: <FiUser />, label: translate("tabs.requiredActions"), value: "required_actions" },
];
Comment on lines +54 to +57
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need to create the full list to then filter it again.

Example:

    ...(Boolean(taskId)
    ? []
    : [
        {
          accessorKey: "task_id",
          enableSorting: true,
          header: translate("common:taskId"),
          meta: {
            skeletonWidth: 10,
          },
        },
      ]),

Copy link
Member Author

@guan404ming guan404ming Aug 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it does make the code more clear and more efficient. I would update it.


const displayTabs = tabs.filter((tab) => tab.value !== "required_actions" || hasHitlForTask);

return (
<ReactFlowProvider>
<DetailsLayout tabs={tabs}>
<DetailsLayout tabs={displayTabs}>
{taskInstance === undefined ? undefined : (
<Header
isRefreshing={Boolean(isStatePending(taskInstance.state) && Boolean(refetchInterval))}
Expand Down
5 changes: 4 additions & 1 deletion airflow-core/src/airflow/ui/src/router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,10 @@ export const routerConfig = [
path: "dags/:dagId/runs/:runId/tasks/:taskId/mapped",
},
{
children: [{ element: <TaskInstances />, index: true }],
children: [
{ element: <TaskInstances />, index: true },
{ element: <HITLTaskInstances />, path: "required_actions" },
],
element: <GroupTaskInstance />,
path: "dags/:dagId/runs/:runId/tasks/group/:groupId",
},
Expand Down
Loading