diff --git a/airflow-core/src/airflow/ui/src/layouts/Details/Grid/GridTI.tsx b/airflow-core/src/airflow/ui/src/layouts/Details/Grid/GridTI.tsx index 9e25de4c29efe..0fabe1cf89a90 100644 --- a/airflow-core/src/airflow/ui/src/layouts/Details/Grid/GridTI.tsx +++ b/airflow-core/src/airflow/ui/src/layouts/Details/Grid/GridTI.tsx @@ -16,22 +16,24 @@ * specific language governing permissions and limitations * under the License. */ -import { Badge, Flex } from "@chakra-ui/react"; +import { Badge, chakra, Flex } from "@chakra-ui/react"; import type { MouseEvent } from "react"; -import React from "react"; +import React, { useRef } from "react"; +import { useTranslation } from "react-i18next"; import { Link, useParams } from "react-router-dom"; -import type { TaskInstanceState } from "openapi/requests/types.gen"; +import type { LightGridTaskInstanceSummary } from "openapi/requests/types.gen"; import { StateIcon } from "src/components/StateIcon"; +import Time from "src/components/Time"; type Props = { readonly dagId: string; + readonly instance: LightGridTaskInstanceSummary; readonly isGroup?: boolean; readonly isMapped?: boolean | null; readonly label: string; readonly runId: string; readonly search: string; - readonly state?: TaskInstanceState | null; readonly taskId: string; }; @@ -51,8 +53,44 @@ const onMouseLeave = (event: MouseEvent) => { }); }; -const Instance = ({ dagId, isGroup, isMapped, runId, search, state, taskId }: Props) => { +const Instance = ({ dagId, instance, isGroup, isMapped, runId, search, taskId }: Props) => { const { groupId: selectedGroupId, taskId: selectedTaskId } = useParams(); + const { t: translate } = useTranslation(); + const debounceTimeoutRef = useRef(undefined); + const tooltipRef = useRef(undefined); + + const onBadgeMouseEnter = (event: MouseEvent) => { + // Clear any existing timeout + if (debounceTimeoutRef.current) { + clearTimeout(debounceTimeoutRef.current); + } + + // Store reference to the tooltip element + const tooltip = event.currentTarget.querySelector("#tooltip") as HTMLElement; + + tooltipRef.current = tooltip; + + // Set a new timeout to show the tooltip after 200ms + debounceTimeoutRef.current = setTimeout(() => { + if (tooltipRef.current) { + tooltipRef.current.style.visibility = "visible"; + } + }, 200); + }; + + const onBadgeMouseLeave = () => { + // Clear any existing timeout + if (debounceTimeoutRef.current) { + clearTimeout(debounceTimeoutRef.current); + debounceTimeoutRef.current = undefined; + } + + // Hide the tooltip immediately + if (tooltipRef.current) { + tooltipRef.current.style.visibility = "hidden"; + tooltipRef.current = undefined; + } + }; return ( - {state === undefined ? undefined : ( - - )} + + + {translate("taskId")}: {taskId} +
+ {translate("state")}: {instance.state} + {instance.min_start_date !== null && ( + <> +
+ {translate("startDate")}:
diff --git a/airflow-core/src/airflow/ui/src/layouts/Details/Grid/TaskInstancesColumn.tsx b/airflow-core/src/airflow/ui/src/layouts/Details/Grid/TaskInstancesColumn.tsx index ad864fe1f2acd..e4efa0128c1ac 100644 --- a/airflow-core/src/airflow/ui/src/layouts/Details/Grid/TaskInstancesColumn.tsx +++ b/airflow-core/src/airflow/ui/src/layouts/Details/Grid/TaskInstancesColumn.tsx @@ -47,13 +47,13 @@ export const TaskInstancesColumn = ({ nodes, runId, taskInstances }: Props) => { return ( );