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 e5452b75bbb Add global runs and instances views (#46661)
e5452b75bbb is described below

commit e5452b75bbb4b40ed8f56b33d8f0413c73c3bcfc
Author: Brent Bovenzi <[email protected]>
AuthorDate: Wed Feb 12 12:34:14 2025 -0500

    Add global runs and instances views (#46661)
    
    * Add global runs and instances table views
    
    * Remove old instances component
    
    * Simplify Tabs component
    
    * Rename Instance to Task Instance
    
    * Fix runs url
---
 .../Dag/Runs/index.ts => layouts/DagsLayout.tsx}   |  17 +-
 airflow/ui/src/layouts/Details/DetailsLayout.tsx   |  31 +++-
 airflow/ui/src/layouts/Details/NavTabs.tsx         |  38 +----
 .../src/pages/{Dag/Runs/Runs.tsx => DagRuns.tsx}   |  39 +++--
 airflow/ui/src/pages/DagsList/DagsList.tsx         |   5 +-
 airflow/ui/src/pages/Task/Instances.tsx            | 186 ---------------------
 airflow/ui/src/pages/Task/index.ts                 |   1 -
 airflow/ui/src/pages/{Run => }/TaskInstances.tsx   |  79 ++++++---
 airflow/ui/src/router.tsx                          |  33 +++-
 9 files changed, 169 insertions(+), 260 deletions(-)

diff --git a/airflow/ui/src/pages/Dag/Runs/index.ts 
b/airflow/ui/src/layouts/DagsLayout.tsx
similarity index 66%
rename from airflow/ui/src/pages/Dag/Runs/index.ts
rename to airflow/ui/src/layouts/DagsLayout.tsx
index 04ea8727764..331fd77e68f 100644
--- a/airflow/ui/src/pages/Dag/Runs/index.ts
+++ b/airflow/ui/src/layouts/DagsLayout.tsx
@@ -16,5 +16,20 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+import { Box } from "@chakra-ui/react";
+import type { PropsWithChildren } from "react";
 
-export * from "./Runs";
+import { NavTabs } from "./Details/NavTabs";
+
+const tabs = [
+  { label: "Dags", value: "/dags" },
+  { label: "Runs", value: "/dag_runs" },
+  { label: "Task Instances", value: "/task_instances" },
+];
+
+export const DagsLayout = ({ children }: PropsWithChildren) => (
+  <Box>
+    <NavTabs tabs={tabs} />
+    {children}
+  </Box>
+);
diff --git a/airflow/ui/src/layouts/Details/DetailsLayout.tsx 
b/airflow/ui/src/layouts/Details/DetailsLayout.tsx
index ec6cb3a5e8a..04402f5e35f 100644
--- a/airflow/ui/src/layouts/Details/DetailsLayout.tsx
+++ b/airflow/ui/src/layouts/Details/DetailsLayout.tsx
@@ -18,10 +18,12 @@
  */
 import { Box, Button, HStack } from "@chakra-ui/react";
 import type { PropsWithChildren } from "react";
-import { FiChevronsLeft } from "react-icons/fi";
+import { FaChartGantt } from "react-icons/fa6";
+import { FiChevronsLeft, FiGrid } from "react-icons/fi";
 import { Outlet, Link as RouterLink, useParams, useSearchParams } from 
"react-router-dom";
 
 import type { DAGResponse } from "openapi/requests/types.gen";
+import { DagIcon } from "src/assets/DagIcon";
 import { ErrorAlert } from "src/components/ErrorAlert";
 import { SearchDagsButton } from "src/components/SearchDags";
 import { ProgressBar } from "src/components/ui";
@@ -67,7 +69,32 @@ export const DetailsLayout = ({ children, dag, error, 
isLoading, tabs }: Props)
         {isModalOpen ? undefined : children}
         <ErrorAlert error={error} />
         <ProgressBar size="xs" visibility={isLoading ? "visible" : "hidden"} />
-        <NavTabs tabs={tabs} />
+        <NavTabs
+          keepSearch
+          rightButtons={
+            <>
+              <Button asChild colorPalette="blue" variant="ghost">
+                <RouterLink to={{ search: 
`${searchParams.toString()}&modal=gantt` }}>
+                  <FaChartGantt height={5} width={5} />
+                  Gantt
+                </RouterLink>
+              </Button>
+              <Button asChild colorPalette="blue" variant="ghost">
+                <RouterLink to={{ search: 
`${searchParams.toString()}&modal=grid` }}>
+                  <FiGrid height={5} width={5} />
+                  Grid
+                </RouterLink>
+              </Button>
+              <Button asChild colorPalette="blue" variant="ghost">
+                <RouterLink to={{ search: 
`${searchParams.toString()}&modal=graph` }}>
+                  <DagIcon height={5} width={5} />
+                  Graph
+                </RouterLink>
+              </Button>
+            </>
+          }
+          tabs={tabs}
+        />
         <DagVizModal
           dagDisplayName={dag?.dag_display_name}
           dagId={dag?.dag_id}
diff --git a/airflow/ui/src/layouts/Details/NavTabs.tsx 
b/airflow/ui/src/layouts/Details/NavTabs.tsx
index cb0234a31d3..55d62c72da5 100644
--- a/airflow/ui/src/layouts/Details/NavTabs.tsx
+++ b/airflow/ui/src/layouts/Details/NavTabs.tsx
@@ -16,22 +16,21 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { Button, Center, Flex } from "@chakra-ui/react";
-import { FaChartGantt } from "react-icons/fa6";
-import { FiGrid } from "react-icons/fi";
-import { NavLink, Link as RouterLink, useSearchParams } from 
"react-router-dom";
-
-import { DagIcon } from "src/assets/DagIcon";
+import { Center, Flex } from "@chakra-ui/react";
+import type { ReactNode } from "react";
+import { NavLink, useSearchParams } from "react-router-dom";
 
 type Props = {
+  readonly keepSearch?: boolean;
+  readonly rightButtons?: ReactNode;
   readonly tabs: Array<{ label: string; value: string }>;
 };
 
-export const NavTabs = ({ tabs }: Props) => {
+export const NavTabs = ({ keepSearch, rightButtons, tabs }: Props) => {
   const [searchParams] = useSearchParams();
 
   return (
-    <Flex alignItems="center" borderBottomWidth={1} 
justifyContent="space-between">
+    <Flex alignItems="center" borderBottomWidth={1} 
justifyContent="space-between" mb={2}>
       <Flex>
         {tabs.map(({ label, value }) => (
           <NavLink
@@ -40,7 +39,7 @@ export const NavTabs = ({ tabs }: Props) => {
             to={{
               pathname: value,
               // Preserve search params when navigating
-              search: searchParams.toString(),
+              search: keepSearch ? searchParams.toString() : undefined,
             }}
           >
             {({ isActive }) => (
@@ -61,26 +60,7 @@ export const NavTabs = ({ tabs }: Props) => {
           </NavLink>
         ))}
       </Flex>
-      <Flex alignSelf="flex-end">
-        <Button asChild colorPalette="blue" variant="ghost">
-          <RouterLink to={{ search: `${searchParams.toString()}&modal=gantt` 
}}>
-            <FaChartGantt height={5} width={5} />
-            Gantt
-          </RouterLink>
-        </Button>
-        <Button asChild colorPalette="blue" variant="ghost">
-          <RouterLink to={{ search: `${searchParams.toString()}&modal=grid` }}>
-            <FiGrid height={5} width={5} />
-            Grid
-          </RouterLink>
-        </Button>
-        <Button asChild colorPalette="blue" variant="ghost">
-          <RouterLink to={{ search: `${searchParams.toString()}&modal=graph` 
}}>
-            <DagIcon height={5} width={5} />
-            Graph
-          </RouterLink>
-        </Button>
-      </Flex>
+      <Flex alignSelf="flex-end">{rightButtons}</Flex>
     </Flex>
   );
 };
diff --git a/airflow/ui/src/pages/Dag/Runs/Runs.tsx 
b/airflow/ui/src/pages/DagRuns.tsx
similarity index 85%
rename from airflow/ui/src/pages/Dag/Runs/Runs.tsx
rename to airflow/ui/src/pages/DagRuns.tsx
index 36b6195eeea..752d1de5e47 100644
--- a/airflow/ui/src/pages/Dag/Runs/Runs.tsx
+++ b/airflow/ui/src/pages/DagRuns.tsx
@@ -16,10 +16,10 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { Box, Flex, HStack, Link, type SelectValueChangeDetails, Text } from 
"@chakra-ui/react";
+import { Flex, HStack, Link, type SelectValueChangeDetails, Text } from 
"@chakra-ui/react";
 import type { ColumnDef } from "@tanstack/react-table";
 import { useCallback } from "react";
-import { useParams, Link as RouterLink, useSearchParams } from 
"react-router-dom";
+import { Link as RouterLink, useParams, useSearchParams } from 
"react-router-dom";
 
 import { useDagRunServiceGetDagRuns } from "openapi/queries";
 import type { DAGRunResponse, DagRunState } from "openapi/requests/types.gen";
@@ -32,13 +32,29 @@ import { RunTypeIcon } from "src/components/RunTypeIcon";
 import { StateBadge } from "src/components/StateBadge";
 import Time from "src/components/Time";
 import { Select } from "src/components/ui";
-import { taskInstanceStateOptions as stateOptions } from 
"src/constants/stateOptions";
+import { dagRunStateOptions as stateOptions } from 
"src/constants/stateOptions";
 import { capitalize, getDuration, useAutoRefresh, isStatePending } from 
"src/utils";
 
-const columns: Array<ColumnDef<DAGRunResponse>> = [
+type DagRunRow = { row: { original: DAGRunResponse } };
+
+const runColumns = (dagId?: string): Array<ColumnDef<DAGRunResponse>> => [
+  ...(Boolean(dagId)
+    ? []
+    : [
+        {
+          accessorKey: "dag_id",
+          cell: ({ row: { original } }: DagRunRow) => (
+            <Link asChild color="fg.info" fontWeight="bold">
+              <RouterLink 
to={`/dags/${original.dag_id}`}>{original.dag_id}</RouterLink>
+            </Link>
+          ),
+          enableSorting: false,
+          header: "Dag ID",
+        },
+      ]),
   {
     accessorKey: "run_id",
-    cell: ({ row: { original } }) => (
+    cell: ({ row: { original } }: DagRunRow) => (
       <Link asChild color="fg.info" fontWeight="bold">
         <RouterLink 
to={`/dags/${original.dag_id}/runs/${original.dag_run_id}`}>
           {original.dag_run_id}
@@ -100,9 +116,8 @@ const columns: Array<ColumnDef<DAGRunResponse>> = [
 
 const STATE_PARAM = "state";
 
-export const Runs = () => {
+export const DagRuns = () => {
   const { dagId } = useParams();
-
   const [searchParams, setSearchParams] = useSearchParams();
 
   const { setTableURLState, tableURLState } = useTableURLState();
@@ -112,11 +127,11 @@ export const Runs = () => {
 
   const filteredState = searchParams.get(STATE_PARAM);
 
-  const refetchInterval = useAutoRefresh({ dagId });
+  const refetchInterval = useAutoRefresh({});
 
   const { data, error, isLoading } = useDagRunServiceGetDagRuns(
     {
-      dagId: dagId ?? "~",
+      dagId: "~",
       limit: pagination.pageSize,
       offset: pagination.pageIndex * pagination.pageSize,
       orderBy,
@@ -149,7 +164,7 @@ export const Runs = () => {
   );
 
   return (
-    <Box pt={4}>
+    <>
       <Flex>
         <Select.Root
           collection={stateOptions}
@@ -182,7 +197,7 @@ export const Runs = () => {
         </Select.Root>
       </Flex>
       <DataTable
-        columns={columns}
+        columns={runColumns(dagId)}
         data={data?.dag_runs ?? []}
         errorMessage={<ErrorAlert error={error} />}
         initialState={tableURLState}
@@ -191,6 +206,6 @@ export const Runs = () => {
         onStateChange={setTableURLState}
         total={data?.total_entries}
       />
-    </Box>
+    </>
   );
 };
diff --git a/airflow/ui/src/pages/DagsList/DagsList.tsx 
b/airflow/ui/src/pages/DagsList/DagsList.tsx
index c8c32b74e8e..31b5eb04a46 100644
--- a/airflow/ui/src/pages/DagsList/DagsList.tsx
+++ b/airflow/ui/src/pages/DagsList/DagsList.tsx
@@ -41,6 +41,7 @@ import { SearchBar } from "src/components/SearchBar";
 import { TogglePause } from "src/components/TogglePause";
 import TriggerDAGButton from "src/components/TriggerDag/TriggerDAGButton";
 import { SearchParamsKeys, type SearchParamsKeysType } from 
"src/constants/searchParams";
+import { DagsLayout } from "src/layouts/DagsLayout";
 import { useConfig } from "src/queries/useConfig";
 import { useDags } from "src/queries/useDags";
 import { pluralize } from "src/utils";
@@ -214,7 +215,7 @@ export const DagsList = () => {
   );
 
   return (
-    <>
+    <DagsLayout>
       <VStack alignItems="none">
         <SearchBar
           buttonProps={{ disabled: true }}
@@ -251,6 +252,6 @@ export const DagsList = () => {
           total={data.total_entries}
         />
       </Box>
-    </>
+    </DagsLayout>
   );
 };
diff --git a/airflow/ui/src/pages/Task/Instances.tsx 
b/airflow/ui/src/pages/Task/Instances.tsx
deleted file mode 100644
index 912c588c931..00000000000
--- a/airflow/ui/src/pages/Task/Instances.tsx
+++ /dev/null
@@ -1,186 +0,0 @@
-/*!
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-import { Box, Flex, HStack, Link, type SelectValueChangeDetails } from 
"@chakra-ui/react";
-import type { ColumnDef } from "@tanstack/react-table";
-import { useCallback } from "react";
-import { Link as RouterLink, useParams, useSearchParams } from 
"react-router-dom";
-
-import { useTaskInstanceServiceGetTaskInstances, useTaskServiceGetTask } from 
"openapi/queries";
-import type { TaskInstanceResponse, TaskInstanceState } from 
"openapi/requests/types.gen";
-import { DataTable } from "src/components/DataTable";
-import { useTableURLState } from "src/components/DataTable/useTableUrlState";
-import { ErrorAlert } from "src/components/ErrorAlert";
-import { StateBadge } from "src/components/StateBadge";
-import Time from "src/components/Time";
-import { Select } from "src/components/ui";
-import { taskInstanceStateOptions as stateOptions } from 
"src/constants/stateOptions";
-import { capitalize, getDuration } from "src/utils";
-import { getTaskInstanceLink } from "src/utils/links";
-
-const columns = (isMapped?: boolean): Array<ColumnDef<TaskInstanceResponse>> 
=> [
-  {
-    accessorKey: "dag_run_id",
-    cell: ({ row: { original } }) => (
-      <Link asChild color="fg.info" fontWeight="bold">
-        <RouterLink 
to={getTaskInstanceLink(original)}>{original.dag_run_id}</RouterLink>
-      </Link>
-    ),
-    enableSorting: false,
-    header: "Dag Run ID",
-  },
-  {
-    accessorKey: "state",
-    cell: ({
-      row: {
-        original: { state },
-      },
-    }) => <StateBadge state={state}>{state}</StateBadge>,
-    header: () => "State",
-  },
-  {
-    accessorKey: "start_date",
-    cell: ({ row: { original } }) => <Time datetime={original.start_date} />,
-    header: "Start Date",
-  },
-  {
-    accessorKey: "end_date",
-    cell: ({ row: { original } }) => <Time datetime={original.end_date} />,
-    header: "End Date",
-  },
-  ...(isMapped
-    ? [
-        {
-          accessorFn: (row: TaskInstanceResponse) => row.rendered_map_index ?? 
row.map_index,
-          header: "Map Index",
-        },
-      ]
-    : []),
-  {
-    accessorKey: "try_number",
-    enableSorting: false,
-    header: "Try Number",
-  },
-  {
-    cell: ({ row: { original } }) => `${getDuration(original.start_date, 
original.end_date)}s`,
-    header: "Duration",
-  },
-];
-
-const STATE_PARAM = "state";
-
-export const Instances = () => {
-  const { dagId = "", taskId } = useParams();
-  const [searchParams, setSearchParams] = useSearchParams();
-  const { setTableURLState, tableURLState } = useTableURLState();
-  const { pagination, sorting } = tableURLState;
-  const [sort] = sorting;
-  const orderBy = sort ? `${sort.desc ? "-" : ""}${sort.id}` : "-start_date";
-  const filteredState = searchParams.getAll(STATE_PARAM);
-  const hasFilteredState = filteredState.length > 0;
-
-  const { data: task, error: taskError, isLoading: isTaskLoading } = 
useTaskServiceGetTask({ dagId, taskId });
-
-  const handleStateChange = useCallback(
-    ({ value }: SelectValueChangeDetails<string>) => {
-      const [val, ...rest] = value;
-
-      if ((val === undefined || val === "all") && rest.length === 0) {
-        searchParams.delete(STATE_PARAM);
-      } else {
-        searchParams.delete(STATE_PARAM);
-        value.filter((state) => state !== "all").map((state) => 
searchParams.append(STATE_PARAM, state));
-      }
-      setTableURLState({
-        pagination: { ...pagination, pageIndex: 0 },
-        sorting,
-      });
-      setSearchParams(searchParams);
-    },
-    [pagination, searchParams, setSearchParams, setTableURLState, sorting],
-  );
-
-  const { data, error, isFetching, isLoading } = 
useTaskInstanceServiceGetTaskInstances({
-    dagId,
-    dagRunId: "~",
-    limit: pagination.pageSize,
-    offset: pagination.pageIndex * pagination.pageSize,
-    orderBy,
-    state: hasFilteredState ? filteredState : undefined,
-    taskId,
-  });
-
-  return (
-    <Box pt={4}>
-      <Flex>
-        <Select.Root
-          collection={stateOptions}
-          maxW="250px"
-          multiple
-          onValueChange={handleStateChange}
-          value={hasFilteredState ? filteredState : ["all"]}
-        >
-          <Select.Trigger
-            {...(hasFilteredState ? { clearable: true } : {})}
-            colorPalette="blue"
-            isActive={Boolean(filteredState)}
-          >
-            <Select.ValueText>
-              {() =>
-                hasFilteredState ? (
-                  <HStack gap="10px">
-                    {filteredState.map((state) => (
-                      <StateBadge key={state} state={state as 
TaskInstanceState}>
-                        {state === "none" ? "No Status" : capitalize(state)}
-                      </StateBadge>
-                    ))}
-                  </HStack>
-                ) : (
-                  "All States"
-                )
-              }
-            </Select.ValueText>
-          </Select.Trigger>
-          <Select.Content>
-            {stateOptions.items.map((option) => (
-              <Select.Item item={option} key={option.label}>
-                {option.value === "all" ? (
-                  option.label
-                ) : (
-                  <StateBadge state={option.value as 
TaskInstanceState}>{option.label}</StateBadge>
-                )}
-              </Select.Item>
-            ))}
-          </Select.Content>
-        </Select.Root>
-      </Flex>
-
-      <DataTable
-        columns={columns(Boolean(task?.is_mapped))}
-        data={data?.task_instances ?? []}
-        errorMessage={<ErrorAlert error={error ?? taskError} />}
-        initialState={tableURLState}
-        isFetching={isFetching}
-        isLoading={isLoading || isTaskLoading}
-        modelName="Task Instance"
-        onStateChange={setTableURLState}
-        total={data?.total_entries}
-      />
-    </Box>
-  );
-};
diff --git a/airflow/ui/src/pages/Task/index.ts 
b/airflow/ui/src/pages/Task/index.ts
index 99691b18e52..667b7b1d114 100644
--- a/airflow/ui/src/pages/Task/index.ts
+++ b/airflow/ui/src/pages/Task/index.ts
@@ -18,4 +18,3 @@
  */
 
 export * from "./Task";
-export * from "./Instances";
diff --git a/airflow/ui/src/pages/Run/TaskInstances.tsx 
b/airflow/ui/src/pages/TaskInstances.tsx
similarity index 80%
rename from airflow/ui/src/pages/Run/TaskInstances.tsx
rename to airflow/ui/src/pages/TaskInstances.tsx
index f05a83e8c56..2e67334f730 100644
--- a/airflow/ui/src/pages/Run/TaskInstances.tsx
+++ b/airflow/ui/src/pages/TaskInstances.tsx
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { Box, Flex, Link, HStack, type SelectValueChangeDetails } from 
"@chakra-ui/react";
+import { Flex, Link, HStack, type SelectValueChangeDetails } from 
"@chakra-ui/react";
 import type { ColumnDef } from "@tanstack/react-table";
 import { useCallback, useState } from "react";
 import { Link as RouterLink, useParams, useSearchParams } from 
"react-router-dom";
@@ -37,17 +37,57 @@ import { taskInstanceStateOptions as stateOptions } from 
"src/constants/stateOpt
 import { capitalize, getDuration, useAutoRefresh, isStatePending } from 
"src/utils";
 import { getTaskInstanceLink } from "src/utils/links";
 
-const columns: Array<ColumnDef<TaskInstanceResponse>> = [
-  {
-    accessorKey: "task_display_name",
-    cell: ({ row: { original } }) => (
-      <Link asChild color="fg.info" fontWeight="bold">
-        <RouterLink 
to={getTaskInstanceLink(original)}>{original.task_display_name}</RouterLink>
-      </Link>
-    ),
-    enableSorting: false,
-    header: "Task ID",
-  },
+type TaskInstanceRow = { row: { original: TaskInstanceResponse } };
+
+const taskInstanceColumns = (
+  dagId?: string,
+  runId?: string,
+  taskId?: string,
+): Array<ColumnDef<TaskInstanceResponse>> => [
+  ...(Boolean(dagId)
+    ? []
+    : [
+        {
+          accessorKey: "dag_id",
+          cell: ({ row: { original } }: TaskInstanceRow) => (
+            <Link asChild color="fg.info" fontWeight="bold">
+              <RouterLink 
to={`/dags/${original.dag_id}`}>{original.dag_id}</RouterLink>
+            </Link>
+          ),
+          enableSorting: false,
+          header: "Dag ID",
+        },
+      ]),
+  ...(Boolean(runId)
+    ? []
+    : [
+        {
+          accessorKey: "run_id",
+          cell: ({ row: { original } }: TaskInstanceRow) => (
+            <Link asChild color="fg.info" fontWeight="bold">
+              <RouterLink 
to={`/dags/${original.dag_id}/runs/${original.dag_run_id}`}>
+                {original.dag_run_id}
+              </RouterLink>
+            </Link>
+          ),
+          enableSorting: false,
+          header: "Run ID",
+        },
+      ]),
+  ...(Boolean(taskId)
+    ? []
+    : [
+        {
+          accessorKey: "task_display_name",
+          cell: ({ row: { original } }: TaskInstanceRow) => (
+            <Link asChild color="fg.info" fontWeight="bold">
+              <RouterLink 
to={getTaskInstanceLink(original)}>{original.task_display_name}</RouterLink>
+            </Link>
+          ),
+          enableSorting: false,
+          header: "Task ID",
+        },
+      ]),
   {
     accessorKey: "state",
     cell: ({
@@ -106,7 +146,7 @@ const columns: Array<ColumnDef<TaskInstanceResponse>> = [
 const STATE_PARAM = "state";
 
 export const TaskInstances = () => {
-  const { dagId = "", runId = "" } = useParams();
+  const { dagId, runId, taskId } = useParams();
   const [searchParams, setSearchParams] = useSearchParams();
   const { setTableURLState, tableURLState } = useTableURLState();
   const { pagination, sorting } = tableURLState;
@@ -153,17 +193,18 @@ export const TaskInstances = () => {
     setSearchParams(searchParams);
   };
 
-  const refetchInterval = useAutoRefresh({ dagId });
+  const refetchInterval = useAutoRefresh({});
 
   const { data, error, isLoading } = useTaskInstanceServiceGetTaskInstances(
     {
-      dagId,
-      dagRunId: runId,
+      dagId: dagId ?? "~",
+      dagRunId: runId ?? "~",
       limit: pagination.pageSize,
       offset: pagination.pageIndex * pagination.pageSize,
       orderBy,
       state: hasFilteredState ? filteredState : undefined,
       taskDisplayNamePattern: Boolean(taskDisplayNamePattern) ? 
taskDisplayNamePattern : undefined,
+      taskId: taskId ?? undefined,
     },
     undefined,
     {
@@ -174,7 +215,7 @@ export const TaskInstances = () => {
   );
 
   return (
-    <Box pt={4}>
+    <>
       <HStack>
         <Select.Root
           collection={stateOptions}
@@ -225,7 +266,7 @@ export const TaskInstances = () => {
         />
       </HStack>
       <DataTable
-        columns={columns}
+        columns={taskInstanceColumns(dagId, runId, taskId)}
         data={data?.task_instances ?? []}
         errorMessage={<ErrorAlert error={error} />}
         initialState={tableURLState}
@@ -234,6 +275,6 @@ export const TaskInstances = () => {
         onStateChange={setTableURLState}
         total={data?.total_entries}
       />
-    </Box>
+    </>
   );
 };
diff --git a/airflow/ui/src/router.tsx b/airflow/ui/src/router.tsx
index 0aa4b146c7b..bd18bca140d 100644
--- a/airflow/ui/src/router.tsx
+++ b/airflow/ui/src/router.tsx
@@ -22,27 +22,28 @@ import { createBrowserRouter } from "react-router-dom";
 import { UseConfigServiceGetConfigsKeyFn } from "openapi/queries";
 import { ConfigService } from "openapi/requests/services.gen";
 import { BaseLayout } from "src/layouts/BaseLayout";
+import { DagsLayout } from "src/layouts/DagsLayout";
+import { AssetsList } from "src/pages/AssetsList";
 import { Dag } from "src/pages/Dag";
 import { Code } from "src/pages/Dag/Code";
 import { Overview } from "src/pages/Dag/Overview";
-import { Runs } from "src/pages/Dag/Runs";
 import { Tasks } from "src/pages/Dag/Tasks";
+import { DagRuns } from "src/pages/DagRuns";
 import { DagsList } from "src/pages/DagsList";
 import { Dashboard } from "src/pages/Dashboard";
 import { ErrorPage } from "src/pages/Error";
 import { Events } from "src/pages/Events";
+import { Pools } from "src/pages/Pools";
 import { Providers } from "src/pages/Providers";
 import { Run } from "src/pages/Run";
 import { Details as DagRunDetails } from "src/pages/Run/Details";
-import { TaskInstances } from "src/pages/Run/TaskInstances";
-import { Task, Instances } from "src/pages/Task";
+import { Task } from "src/pages/Task";
 import { TaskInstance, Logs } from "src/pages/TaskInstance";
 import { Details } from "src/pages/TaskInstance/Details";
+import { TaskInstances } from "src/pages/TaskInstances";
+import { Variables } from "src/pages/Variables";
 import { XCom } from "src/pages/XCom";
 
-import { AssetsList } from "./pages/AssetsList";
-import { Pools } from "./pages/Pools";
-import { Variables } from "./pages/Variables";
 import { queryClient } from "./queryClient";
 
 export const routerConfig = [
@@ -56,6 +57,22 @@ export const routerConfig = [
         element: <DagsList />,
         path: "dags",
       },
+      {
+        element: (
+          <DagsLayout>
+            <DagRuns />
+          </DagsLayout>
+        ),
+        path: "dag_runs",
+      },
+      {
+        element: (
+          <DagsLayout>
+            <TaskInstances />
+          </DagsLayout>
+        ),
+        path: "task_instances",
+      },
       {
         element: <AssetsList />,
         path: "assets",
@@ -83,7 +100,7 @@ export const routerConfig = [
       {
         children: [
           { element: <Overview />, index: true },
-          { element: <Runs />, path: "runs" },
+          { element: <DagRuns />, path: "runs" },
           { element: <Tasks />, path: "tasks" },
           { element: <Events />, path: "events" },
           { element: <Code />, path: "code" },
@@ -114,7 +131,7 @@ export const routerConfig = [
       },
       {
         children: [
-          { element: <Instances />, index: true },
+          { element: <TaskInstances />, index: true },
           { element: <Events />, path: "events" },
         ],
         element: <Task />,

Reply via email to