diff --git a/airflow/ui/src/pages/DagsList/Dag/Header.tsx b/airflow/ui/src/pages/DagsList/Dag/Header.tsx index bc67142bd0317..3e118e97fde48 100644 --- a/airflow/ui/src/pages/DagsList/Dag/Header.tsx +++ b/airflow/ui/src/pages/DagsList/Dag/Header.tsx @@ -38,7 +38,7 @@ export const Header = ({ readonly dagId?: string; readonly latestRun?: DAGRunResponse; }) => ( - + diff --git a/airflow/ui/src/pages/DagsList/Run/Header.tsx b/airflow/ui/src/pages/DagsList/Run/Header.tsx index 6d5a5368e17f6..7b1c8798c4960 100644 --- a/airflow/ui/src/pages/DagsList/Run/Header.tsx +++ b/airflow/ui/src/pages/DagsList/Run/Header.tsx @@ -28,7 +28,7 @@ import Time from "src/components/Time"; import { Status } from "src/components/ui"; export const Header = ({ dagRun }: { readonly dagRun: DAGRunResponse }) => ( - + diff --git a/airflow/ui/src/pages/DagsList/TaskInstance/Header.tsx b/airflow/ui/src/pages/DagsList/TaskInstance/Header.tsx new file mode 100644 index 0000000000000..2d578f19c9c88 --- /dev/null +++ b/airflow/ui/src/pages/DagsList/TaskInstance/Header.tsx @@ -0,0 +1,86 @@ +/*! + * 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, Heading, HStack, SimpleGrid, Text } from "@chakra-ui/react"; +import dayjs from "dayjs"; +import { MdOutlineModeComment, MdOutlineTask } from "react-icons/md"; + +import type { TaskInstanceResponse } from "openapi/requests/types.gen"; +import { Stat } from "src/components/Stat"; +import Time from "src/components/Time"; +import { Status } from "src/components/ui"; + +export const Header = ({ + taskInstance, +}: { + readonly taskInstance: TaskInstanceResponse; +}) => ( + + + + + + + Task Instance: + {taskInstance.task_display_name}{" "} + + + {taskInstance.state} + + +
+ + + + {taskInstance.note === null || + taskInstance.note.length === 0 ? undefined : ( + + + + {taskInstance.note} + + + )} + + {taskInstance.operator} + {taskInstance.map_index > -1 ? ( + {taskInstance.map_index} + ) : undefined} + {taskInstance.try_number > 1 ? ( + {taskInstance.try_number} + ) : undefined} + + + + + + {dayjs + .duration( + dayjs(taskInstance.end_date).diff(taskInstance.start_date), + ) + .asSeconds() + .toFixed(2)} + s + + + + +); diff --git a/airflow/ui/src/pages/DagsList/TaskInstance/Tabs.tsx b/airflow/ui/src/pages/DagsList/TaskInstance/Tabs.tsx new file mode 100644 index 0000000000000..226214d722a60 --- /dev/null +++ b/airflow/ui/src/pages/DagsList/TaskInstance/Tabs.tsx @@ -0,0 +1,72 @@ +/*! + * 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 { Button } from "@chakra-ui/react"; +import { useSearchParams } from "react-router-dom"; + +import type { DAGResponse } from "openapi/requests/types.gen"; +import { DagIcon } from "src/assets/DagIcon"; +import { DagVizModal } from "src/components/DagVizModal"; +import { NavTabs } from "src/components/NavTabs"; + +const tabs = [ + { label: "Logs", value: "" }, + { label: "Events", value: "events" }, + { label: "XCom", value: "xcom" }, + { label: "Code", value: "code" }, + { label: "Details", value: "details" }, +]; + +const MODAL = "modal"; + +export const TaskInstanceTabs = ({ dag }: { readonly dag?: DAGResponse }) => { + const [searchParams, setSearchParams] = useSearchParams(); + + const modal = searchParams.get(MODAL); + + const isGraphOpen = modal === "graph"; + const onClose = () => { + searchParams.delete(MODAL); + setSearchParams(searchParams); + }; + + const onOpen = () => { + searchParams.set(MODAL, "graph"); + setSearchParams(searchParams); + }; + + return ( + <> + + + Graph + + } + tabs={tabs} + /> + + + ); +}; diff --git a/airflow/ui/src/pages/DagsList/TaskInstance/TaskInstance.tsx b/airflow/ui/src/pages/DagsList/TaskInstance/TaskInstance.tsx index eb45f9c48c0b9..f105d969c075e 100644 --- a/airflow/ui/src/pages/DagsList/TaskInstance/TaskInstance.tsx +++ b/airflow/ui/src/pages/DagsList/TaskInstance/TaskInstance.tsx @@ -16,26 +16,54 @@ * specific language governing permissions and limitations * under the License. */ +import { Box } from "@chakra-ui/react"; import { LiaSlashSolid } from "react-icons/lia"; -import { useParams, Link as RouterLink } from "react-router-dom"; +import { useParams, Link as RouterLink, Outlet } from "react-router-dom"; +import { + useDagServiceGetDagDetails, + useTaskInstanceServiceGetTaskInstance, +} from "openapi/queries"; import { Breadcrumb } from "src/components/ui"; +import { OpenGroupsProvider } from "src/context/openGroups"; + +import { Header } from "./Header"; +import { TaskInstanceTabs } from "./Tabs"; export const TaskInstance = () => { - const { dagId, runId, taskId } = useParams(); + const { dagId = "", runId = "", taskId = "" } = useParams(); + + const { data: dag } = useDagServiceGetDagDetails({ + dagId, + }); + + const { data: taskInstance } = useTaskInstanceServiceGetTaskInstance({ + dagId, + dagRunId: runId, + taskId, + }); return ( - }> - - Dags - - - {dagId} - - - {runId} - - {taskId} - + + }> + + Dags + + + {dagId} + + + {runId} + + {taskId} + + {taskInstance === undefined ? undefined : ( +
+ )} + + + + + ); }; diff --git a/airflow/ui/src/pages/Events/Events.tsx b/airflow/ui/src/pages/Events/Events.tsx index 050ab9c7cd6bb..60663aefec7d1 100644 --- a/airflow/ui/src/pages/Events/Events.tsx +++ b/airflow/ui/src/pages/Events/Events.tsx @@ -28,8 +28,9 @@ import { ErrorAlert } from "src/components/ErrorAlert"; import Time from "src/components/Time"; const eventsColumn = ( - dagId: string | undefined, - runId: string | undefined, + dagId?: string, + runId?: string, + taskId?: string, ): Array> => [ { accessorKey: "when", @@ -64,14 +65,18 @@ const eventsColumn = ( }, }, ]), - { - accessorKey: "task_id", - enableSorting: true, - header: "Task ID", - meta: { - skeletonWidth: 10, - }, - }, + ...(Boolean(taskId) + ? [] + : [ + { + accessorKey: "task_id", + enableSorting: true, + header: "Task ID", + meta: { + skeletonWidth: 10, + }, + }, + ]), { accessorKey: "map_index", enableSorting: false, @@ -107,7 +112,7 @@ const eventsColumn = ( ]; export const Events = () => { - const { dagId, runId } = useParams(); + const { dagId, runId, taskId } = useParams(); const { setTableURLState, tableURLState } = useTableURLState({ sorting: [{ desc: true, id: "when" }], }); @@ -133,7 +138,7 @@ export const Events = () => { Logs
, index: true }, + { element: , path: "events" }, + { element:
Xcom
, path: "xcom" }, + { element: , path: "code" }, + { element:
Details
, path: "details" }, + ], element: , path: "dags/:dagId/runs/:runId/tasks/:taskId", },