diff --git a/airflow/ui/src/components/DagRunInfo.tsx b/airflow/ui/src/components/DagRunInfo.tsx
index 0d30e9c7667c4..2cd7cac433721 100644
--- a/airflow/ui/src/components/DagRunInfo.tsx
+++ b/airflow/ui/src/components/DagRunInfo.tsx
@@ -21,10 +21,7 @@ import dayjs from "dayjs";
import type { DAGRunResponse } from "openapi/requests/types.gen";
import Time from "src/components/Time";
-import { Tooltip } from "src/components/ui";
-import { stateColor } from "src/utils/stateColor";
-
-import { StateCircle } from "./StateCircle";
+import { Tooltip, Status } from "src/components/ui";
type Props = {
readonly dataIntervalEnd?: string | null;
@@ -82,10 +79,7 @@ const DagRunInfo = ({
{state === undefined ? undefined : (
- <>
-
- {state}
- >
+ {state}
)}
diff --git a/airflow/ui/src/components/RunTypeIcon.tsx b/airflow/ui/src/components/RunTypeIcon.tsx
new file mode 100644
index 0000000000000..f465fbf298f2b
--- /dev/null
+++ b/airflow/ui/src/components/RunTypeIcon.tsx
@@ -0,0 +1,48 @@
+/*!
+ * 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 type { IconBaseProps } from "react-icons";
+import { HiDatabase } from "react-icons/hi";
+import { MdPlayArrow, MdOutlineSchedule } from "react-icons/md";
+import { RiArrowGoBackFill } from "react-icons/ri";
+
+import type { DAGRunResponse } from "openapi/requests/types.gen";
+
+type Props = {
+ readonly runType: DAGRunResponse["run_type"];
+} & IconBaseProps;
+
+const iconStyle = {
+ display: "inline",
+ verticalAlign: "bottom",
+};
+
+export const RunTypeIcon = ({ runType, ...rest }: Props) => {
+ switch (runType) {
+ case "asset_triggered":
+ return ;
+ case "backfill":
+ return ;
+ case "manual":
+ return ;
+ case "scheduled":
+ return ;
+ default:
+ return undefined;
+ }
+};
diff --git a/airflow/ui/src/components/TimeRangeSelector.tsx b/airflow/ui/src/components/TimeRangeSelector.tsx
index 8ce7bef53a871..93f8d7674deb5 100644
--- a/airflow/ui/src/components/TimeRangeSelector.tsx
+++ b/airflow/ui/src/components/TimeRangeSelector.tsx
@@ -39,7 +39,6 @@ type Props = {
const defaultTimeOptions = createListCollection({
items: [
{ label: "Last 1 hour", value: "1" },
- { label: "Last 8 hours", value: "8" },
{ label: "Last 12 hours", value: "12" },
{ label: "Last 24 hours", value: "24" },
{ label: "Last week", value: "168" },
diff --git a/airflow/ui/src/components/TrendCountButton.tsx b/airflow/ui/src/components/TrendCountButton.tsx
new file mode 100644
index 0000000000000..18dcfa7241d3a
--- /dev/null
+++ b/airflow/ui/src/components/TrendCountButton.tsx
@@ -0,0 +1,70 @@
+/*!
+ * 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 { HStack, Badge, Text, Skeleton } from "@chakra-ui/react";
+import { Link, type To } from "react-router-dom";
+
+import { pluralize } from "src/utils";
+
+import { TrendCountChart, type ChartEvent } from "./TrendCountChart";
+
+type Props = {
+ readonly colorPalette: string;
+ readonly count: number;
+ readonly endDate: string;
+ readonly events: Array;
+ readonly isLoading?: boolean;
+ readonly label: string;
+ readonly route: To;
+ readonly startDate: string;
+};
+
+export const TrendCountButton = ({
+ colorPalette,
+ count,
+ endDate,
+ events,
+ isLoading,
+ label,
+ route,
+ startDate,
+}: Props) => {
+ if (count === 0 && !isLoading) {
+ return undefined;
+ }
+
+ return isLoading ? (
+
+ ) : (
+
+
+
+ {count}
+
+
+ {pluralize(label, count, undefined, true)}
+
+
+
+
+ );
+};
diff --git a/airflow/ui/src/pages/DagsList/Dag/Overview/Chart.tsx b/airflow/ui/src/components/TrendCountChart.tsx
similarity index 94%
rename from airflow/ui/src/pages/DagsList/Dag/Overview/Chart.tsx
rename to airflow/ui/src/components/TrendCountChart.tsx
index b67796a7a684f..0e7c011499276 100644
--- a/airflow/ui/src/pages/DagsList/Dag/Overview/Chart.tsx
+++ b/airflow/ui/src/components/TrendCountChart.tsx
@@ -42,10 +42,10 @@ ChartJS.register(
Tooltip,
);
-type Event = { timestamp: string };
+export type ChartEvent = { timestamp: string };
const aggregateEventsIntoIntervals = (
- events: Array,
+ events: Array,
startDate: string,
endDate: string,
) => {
@@ -103,11 +103,11 @@ const options = {
type Props = {
readonly endDate: string;
- readonly events: Array;
+ readonly events: Array;
readonly startDate: string;
};
-export const Chart = ({ endDate, events, startDate }: Props) => {
+export const TrendCountChart = ({ endDate, events, startDate }: Props) => {
const { colorMode } = useColorMode();
const chartRef = useRef>();
diff --git a/airflow/ui/src/components/ui/Status.tsx b/airflow/ui/src/components/ui/Status.tsx
new file mode 100644
index 0000000000000..7c408b931d378
--- /dev/null
+++ b/airflow/ui/src/components/ui/Status.tsx
@@ -0,0 +1,45 @@
+/*!
+ * 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 { Status as ChakraStatus } from "@chakra-ui/react";
+import * as React from "react";
+
+import type {
+ DagRunState,
+ TaskInstanceState,
+} from "openapi/requests/types.gen";
+import { stateColor } from "src/utils/stateColor";
+
+type StatusValue = DagRunState | TaskInstanceState;
+
+export type StatusProps = {
+ state?: StatusValue;
+} & ChakraStatus.RootProps;
+
+export const Status = React.forwardRef(
+ ({ children, state, ...rest }, ref) => {
+ const colorPalette = state === undefined ? "info" : stateColor[state];
+
+ return (
+
+
+ {children}
+
+ );
+ },
+);
diff --git a/airflow/ui/src/components/ui/index.ts b/airflow/ui/src/components/ui/index.ts
index f596effa12a93..b6fa2898e9e72 100644
--- a/airflow/ui/src/components/ui/index.ts
+++ b/airflow/ui/src/components/ui/index.ts
@@ -31,3 +31,4 @@ export * from "./Tooltip";
export * from "./ProgressBar";
export * from "./Menu";
export * from "./Accordion";
+export * from "./Status";
diff --git a/airflow/ui/src/main.tsx b/airflow/ui/src/main.tsx
index 524efdb6695ce..964e6e32b9b5a 100644
--- a/airflow/ui/src/main.tsx
+++ b/airflow/ui/src/main.tsx
@@ -19,6 +19,7 @@
import { ChakraProvider, defaultSystem } from "@chakra-ui/react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import axios, { type AxiosError } from "axios";
+import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { RouterProvider } from "react-router-dom";
@@ -61,13 +62,15 @@ axios.interceptors.response.use(
);
createRoot(document.querySelector("#root") as HTMLDivElement).render(
-
-
-
-
-
-
-
-
- ,
+
+
+
+
+
+
+
+
+
+
+ ,
);
diff --git a/airflow/ui/src/pages/DagsList/Dag/Overview/Overview.tsx b/airflow/ui/src/pages/DagsList/Dag/Overview/Overview.tsx
index be94a01d8edc2..dfe118bd18e10 100644
--- a/airflow/ui/src/pages/DagsList/Dag/Overview/Overview.tsx
+++ b/airflow/ui/src/pages/DagsList/Dag/Overview/Overview.tsx
@@ -16,19 +16,20 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { Box, HStack, Badge, Text, Skeleton } from "@chakra-ui/react";
+import { Box, HStack } from "@chakra-ui/react";
import dayjs from "dayjs";
import { useState } from "react";
-import { Link, useLocation, useParams } from "react-router-dom";
+import { useParams } from "react-router-dom";
-import { useTaskInstanceServiceGetTaskInstances } from "openapi/queries";
+import {
+ useDagRunServiceGetDagRuns,
+ useTaskInstanceServiceGetTaskInstances,
+} from "openapi/queries";
import TimeRangeSelector from "src/components/TimeRangeSelector";
-import { pluralize } from "src/utils";
+import { TrendCountButton } from "src/components/TrendCountButton";
import { stateColor } from "src/utils/stateColor";
-import { Chart } from "./Chart";
-
-const defaultHour = "8";
+const defaultHour = "12";
export const Overview = () => {
const { dagId } = useParams();
@@ -48,7 +49,13 @@ export const Overview = () => {
state: ["failed"],
});
- const location = useLocation();
+ const { data: failedRuns, isLoading: isLoadingRuns } =
+ useDagRunServiceGetDagRuns({
+ dagId: dagId ?? "",
+ logicalDateGte: startDate,
+ logicalDateLte: endDate,
+ state: ["failed"],
+ });
// TODO actually link to task instances list
return (
@@ -62,35 +69,38 @@ export const Overview = () => {
startDate={startDate}
/>
- {failedTasks?.total_entries !== undefined &&
- failedTasks.total_entries > 0 ? (
- // TODO: make sure url params pass correctly
-
-
-
- {failedTasks.total_entries}
-
-
- Failed{" "}
- {pluralize("Task", failedTasks.total_entries, undefined, true)}
-
- ({
- timestamp: ti.start_date ?? ti.logical_date,
- }))}
- startDate={startDate}
- />
-
-
- ) : undefined}
- {isLoading ? (
-
- ) : undefined}
+
+ ({
+ timestamp: ti.start_date ?? ti.logical_date,
+ }))}
+ isLoading={isLoading}
+ label="Failed Task"
+ route={{
+ pathname: "tasks",
+ search: "state=failed",
+ }}
+ startDate={startDate}
+ />
+ ({
+ timestamp: dr.start_date ?? dr.logical_date ?? "",
+ }))}
+ isLoading={isLoadingRuns}
+ label="Failed Run"
+ route={{
+ pathname: "runs",
+ search: "state=failed",
+ }}
+ startDate={startDate}
+ />
+
);
};
diff --git a/airflow/ui/src/pages/DagsList/Dag/Runs/Runs.tsx b/airflow/ui/src/pages/DagsList/Dag/Runs/Runs.tsx
new file mode 100644
index 0000000000000..02c157d90576a
--- /dev/null
+++ b/airflow/ui/src/pages/DagsList/Dag/Runs/Runs.tsx
@@ -0,0 +1,197 @@
+/*!
+ * 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,
+ createListCollection,
+ Flex,
+ HStack,
+ Link,
+ type SelectValueChangeDetails,
+ Text,
+} from "@chakra-ui/react";
+import type { ColumnDef } from "@tanstack/react-table";
+import dayjs from "dayjs";
+import { useCallback } from "react";
+import {
+ useParams,
+ Link as RouterLink,
+ useSearchParams,
+} from "react-router-dom";
+
+import { useDagRunServiceGetDagRuns } from "openapi/queries";
+import type { DAGRunResponse, DagRunState } 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 { RunTypeIcon } from "src/components/RunTypeIcon";
+import Time from "src/components/Time";
+import { Select, Status } from "src/components/ui";
+import { capitalize } from "src/utils";
+
+const columns: Array> = [
+ {
+ accessorKey: "run_id",
+ cell: ({ row: { original } }) => (
+
+
+ {original.dag_run_id}
+
+
+ ),
+ enableSorting: false,
+ header: "Run ID",
+ },
+ {
+ accessorKey: "state",
+ cell: ({
+ row: {
+ original: { state },
+ },
+ }) => {state},
+ header: () => "State",
+ },
+ {
+ accessorKey: "run_type",
+ cell: ({ row: { original } }) => (
+
+
+ {original.run_type}
+
+ ),
+ enableSorting: false,
+ header: "Run Type",
+ },
+ {
+ accessorKey: "start_date",
+ cell: ({ row: { original } }) => ,
+ header: "Start Date",
+ },
+ {
+ accessorKey: "end_date",
+ cell: ({ row: { original } }) => ,
+ header: "End Date",
+ },
+ {
+ cell: ({ row: { original } }) =>
+ `${dayjs.duration(dayjs(original.end_date).diff(original.start_date)).asSeconds().toFixed(2)}s`,
+ header: "Duration",
+ },
+];
+
+const stateOptions = createListCollection({
+ items: [
+ { label: "All States", value: "all" },
+ { label: "Queued", value: "queued" },
+ { label: "Running", value: "running" },
+ { label: "Failed", value: "failed" },
+ { label: "Success", value: "success" },
+ ],
+});
+
+const STATE_PARAM = "state";
+
+export const Runs = () => {
+ const { dagId } = useParams();
+
+ const [searchParams, setSearchParams] = useSearchParams();
+
+ const { setTableURLState, tableURLState } = useTableURLState();
+ const { pagination, sorting } = tableURLState;
+ const [sort] = sorting;
+ const orderBy = sort ? `${sort.desc ? "-" : ""}${sort.id}` : undefined;
+
+ const filteredState = searchParams.get(STATE_PARAM);
+
+ const { data, error, isFetching, isLoading } = useDagRunServiceGetDagRuns({
+ dagId: dagId ?? "~",
+ limit: pagination.pageSize,
+ offset: pagination.pageIndex * pagination.pageSize,
+ orderBy,
+ state: filteredState === null ? undefined : [filteredState],
+ });
+
+ const handleStateChange = useCallback(
+ ({ value }: SelectValueChangeDetails) => {
+ const [val] = value;
+
+ if (val === undefined || val === "all") {
+ searchParams.delete(STATE_PARAM);
+ } else {
+ searchParams.set(STATE_PARAM, val);
+ }
+ setSearchParams(searchParams);
+ setTableURLState({
+ pagination: { ...pagination, pageIndex: 0 },
+ sorting,
+ });
+ },
+ [pagination, searchParams, setSearchParams, setTableURLState, sorting],
+ );
+
+ return (
+
+
+
+
+
+ {() =>
+ filteredState === null ? (
+ "All States"
+ ) : (
+
+ {capitalize(filteredState)}
+
+ )
+ }
+
+
+
+ {stateOptions.items.map((option) => (
+
+ {option.value === "all" ? (
+ option.label
+ ) : (
+
+ {option.label}
+
+ )}
+
+ ))}
+
+
+
+ }
+ initialState={tableURLState}
+ isFetching={isFetching}
+ isLoading={isLoading}
+ modelName="Dag Run"
+ onStateChange={setTableURLState}
+ total={data?.total_entries}
+ />
+
+ );
+};
diff --git a/airflow/ui/src/components/StateCircle.tsx b/airflow/ui/src/pages/DagsList/Dag/Runs/index.ts
similarity index 67%
rename from airflow/ui/src/components/StateCircle.tsx
rename to airflow/ui/src/pages/DagsList/Dag/Runs/index.ts
index 50a2310a0ffc8..04ea8727764fb 100644
--- a/airflow/ui/src/components/StateCircle.tsx
+++ b/airflow/ui/src/pages/DagsList/Dag/Runs/index.ts
@@ -16,23 +16,5 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { Box, type BoxProps } from "@chakra-ui/react";
-import type { TaskInstanceState } from "openapi/requests/types.gen";
-import { stateColor } from "src/utils/stateColor";
-
-export const StateCircle = ({
- state,
- ...rest
-}: {
- readonly state: TaskInstanceState;
-} & BoxProps) => (
-
-);
+export * from "./Runs";
diff --git a/airflow/ui/src/pages/DagsList/Dag/Tabs.tsx b/airflow/ui/src/pages/DagsList/Dag/Tabs.tsx
index c2d0c4d2c40c4..e3447051dbf25 100644
--- a/airflow/ui/src/pages/DagsList/Dag/Tabs.tsx
+++ b/airflow/ui/src/pages/DagsList/Dag/Tabs.tsx
@@ -16,13 +16,8 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { Button, Flex, Tabs } from "@chakra-ui/react";
-import {
- Link as RouterLink,
- useLocation,
- useParams,
- useSearchParams,
-} from "react-router-dom";
+import { Button, Center, Flex } from "@chakra-ui/react";
+import { NavLink, useSearchParams } from "react-router-dom";
import type { DAGResponse } from "openapi/requests/types.gen";
import { DagIcon } from "src/assets/DagIcon";
@@ -30,12 +25,11 @@ import { capitalize } from "src/utils";
import { DagVizModal } from "./DagVizModal";
-const tabs = ["runs", "tasks", "events", "code"];
+const tabs = ["overview", "runs", "tasks", "events", "code"];
const MODAL = "modal";
export const DagTabs = ({ dag }: { readonly dag?: DAGResponse }) => {
- const { dagId } = useParams();
const [searchParams, setSearchParams] = useSearchParams();
const modal = searchParams.get(MODAL);
@@ -51,35 +45,41 @@ export const DagTabs = ({ dag }: { readonly dag?: DAGResponse }) => {
setSearchParams(searchParams);
};
- const { pathname } = useLocation();
-
- const activeTab =
- tabs.find((tab) => pathname.endsWith(`/${tab}`)) ?? "overview";
-
return (
<>
-
-
-
-
- Overview
-
- {tabs.map((tab) => (
-
-
+
+
+ {tabs.map((tab) => (
+
+ {({ isActive }) => (
+
{capitalize(tab)}
-
-
- ))}
-
-
-
-
-
-
+
+ )}
+
+ ))}
+
+
+
+
+
{
onClick={handleStateChange}
value="failed"
>
-
- Failed
+ Failed
-
- Running
+ Running
-
- Success
+ Success
{
+ const { dagId = "", runId = "" } = useParams();
+
+ const {
+ data: dagRun,
+ error,
+ isLoading,
+ } = useDagRunServiceGetDagRun({
+ dagId,
+ dagRunId: runId,
+ });
+
+ return (
+
+
+
+ {dagId}.{runId}
+
+
+
+ {dagRun === undefined ? undefined : (
+ {dagRun.state}
+ )}
+
+ );
+};
diff --git a/airflow/ui/src/pages/DagsList/Run/index.ts b/airflow/ui/src/pages/DagsList/Run/index.ts
new file mode 100644
index 0000000000000..824441d6d7241
--- /dev/null
+++ b/airflow/ui/src/pages/DagsList/Run/index.ts
@@ -0,0 +1,20 @@
+/*!
+ * 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.
+ */
+
+export * from "./Run";
diff --git a/airflow/ui/src/pages/Dashboard/HistoricalMetrics/HistoricalMetrics.tsx b/airflow/ui/src/pages/Dashboard/HistoricalMetrics/HistoricalMetrics.tsx
index 7d1037cbeb0d9..feb6ffeeb0b0d 100644
--- a/airflow/ui/src/pages/Dashboard/HistoricalMetrics/HistoricalMetrics.tsx
+++ b/airflow/ui/src/pages/Dashboard/HistoricalMetrics/HistoricalMetrics.tsx
@@ -28,7 +28,7 @@ import { DagRunMetrics } from "./DagRunMetrics";
import { MetricSectionSkeleton } from "./MetricSectionSkeleton";
import { TaskInstanceMetrics } from "./TaskInstanceMetrics";
-const defaultHour = "8";
+const defaultHour = "12";
export const HistoricalMetrics = () => {
const now = dayjs();
diff --git a/airflow/ui/src/router.tsx b/airflow/ui/src/router.tsx
index ae33b021d211d..6c5081c35e2b4 100644
--- a/airflow/ui/src/router.tsx
+++ b/airflow/ui/src/router.tsx
@@ -18,15 +18,16 @@
*/
import { createBrowserRouter } from "react-router-dom";
+import { BaseLayout } from "src/layouts/BaseLayout";
import { DagsList } from "src/pages/DagsList";
+import { Dag } from "src/pages/DagsList/Dag";
+import { Code } from "src/pages/DagsList/Dag/Code";
+import { Overview } from "src/pages/DagsList/Dag/Overview";
+import { Runs } from "src/pages/DagsList/Dag/Runs";
+import { Run } from "src/pages/DagsList/Run";
import { Dashboard } from "src/pages/Dashboard";
-
-import { BaseLayout } from "./layouts/BaseLayout";
-import { Dag } from "./pages/DagsList/Dag";
-import { Code } from "./pages/DagsList/Dag/Code";
-import { Overview } from "./pages/DagsList/Dag/Overview";
-import { ErrorPage } from "./pages/Error";
-import { Events } from "./pages/Events";
+import { ErrorPage } from "src/pages/Error";
+import { Events } from "src/pages/Events";
export const router = createBrowserRouter(
[
@@ -46,8 +47,8 @@ export const router = createBrowserRouter(
},
{
children: [
- { element: , path: "" },
- { element: Runs
, path: "runs" },
+ { element: , index: true },
+ { element: , path: "runs" },
{ element: Tasks
, path: "tasks" },
{ element: , path: "events" },
{ element:
, path: "code" },
@@ -55,6 +56,7 @@ export const router = createBrowserRouter(
element: ,
path: "dags/:dagId",
},
+ { element: , path: "dags/:dagId/runs/:runId" },
],
element: ,
errorElement: (