From 2548731089e4949ab79d7955840071dd6b1a6337 Mon Sep 17 00:00:00 2001 From: Brent Bovenzi Date: Sat, 25 Jan 2025 10:39:21 -0500 Subject: [PATCH] Add icons to task states (#46028) * Add icons to task states * Move stateColor to theme * Fix graph --- airflow/ui/src/components/DagRunInfo.tsx | 2 +- airflow/ui/src/components/MetricsBadge.tsx | 9 +- airflow/ui/src/components/StateIcon.tsx | 70 +++++++ .../ui/src/components/TaskInstanceTooltip.tsx | 1 + airflow/ui/src/components/ui/Status.tsx | 26 ++- .../ui/src/layouts/Details/Graph/Graph.tsx | 3 +- .../ui/src/layouts/Details/Graph/TaskNode.tsx | 9 +- airflow/ui/src/main.tsx | 5 +- .../ui/src/pages/Dag/Overview/Overview.tsx | 5 +- airflow/ui/src/pages/Dag/Tasks/TaskCard.tsx | 4 +- .../ui/src/pages/Dag/Tasks/TaskRecentRuns.tsx | 3 +- airflow/ui/src/pages/DagsList/RecentRuns.tsx | 3 +- .../src/pages/Dashboard/Health/HealthTag.tsx | 12 +- .../HistoricalMetrics/AssetEvents.tsx | 2 +- .../HistoricalMetrics/DagRunMetrics.tsx | 2 +- .../HistoricalMetrics/MetricSection.tsx | 5 +- .../HistoricalMetrics/TaskInstanceMetrics.tsx | 2 +- .../pages/Dashboard/Stats/DAGImportErrors.tsx | 3 +- .../pages/Dashboard/Stats/DagFilterButton.tsx | 9 +- .../ui/src/pages/Dashboard/Stats/Stats.tsx | 14 +- airflow/ui/src/pages/Pools/PoolBar.tsx | 23 ++- airflow/ui/src/theme.ts | 176 ++++++++++++++++++ airflow/ui/src/utils/stateColor.ts | 37 ---- 23 files changed, 319 insertions(+), 106 deletions(-) create mode 100644 airflow/ui/src/components/StateIcon.tsx create mode 100644 airflow/ui/src/theme.ts delete mode 100644 airflow/ui/src/utils/stateColor.ts diff --git a/airflow/ui/src/components/DagRunInfo.tsx b/airflow/ui/src/components/DagRunInfo.tsx index dd5569dd45a2a..dca0c3e2f3fbe 100644 --- a/airflow/ui/src/components/DagRunInfo.tsx +++ b/airflow/ui/src/components/DagRunInfo.tsx @@ -74,7 +74,7 @@ const DagRunInfo = ({ > ) : undefined; diff --git a/airflow/ui/src/components/MetricsBadge.tsx b/airflow/ui/src/components/MetricsBadge.tsx index d744d2f6eb2e7..f763a9510b2a8 100644 --- a/airflow/ui/src/components/MetricsBadge.tsx +++ b/airflow/ui/src/components/MetricsBadge.tsx @@ -17,15 +17,16 @@ * under the License. */ import { Badge, type BadgeProps } from "@chakra-ui/react"; +import type { ReactNode } from "react"; type MetricBadgeProps = { - readonly backgroundColor: string; - readonly color?: string; + readonly icon?: ReactNode; readonly runs?: number; } & BadgeProps; -export const MetricsBadge = ({ backgroundColor, color = "fg.inverted", runs }: MetricBadgeProps) => ( - +export const MetricsBadge = ({ icon, runs, ...rest }: MetricBadgeProps) => ( + + {icon} {runs} ); diff --git a/airflow/ui/src/components/StateIcon.tsx b/airflow/ui/src/components/StateIcon.tsx new file mode 100644 index 0000000000000..4102cae75df21 --- /dev/null +++ b/airflow/ui/src/components/StateIcon.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 type { IconBaseProps } from "react-icons"; +import { + FiActivity, + FiAlertCircle, + FiAlertOctagon, + FiCalendar, + FiCheckCircle, + FiCircle, + FiRepeat, + FiSkipForward, + FiSlash, + FiWatch, +} from "react-icons/fi"; +import { LuCalendarSync, LuRedo2 } from "react-icons/lu"; +import { PiQueue } from "react-icons/pi"; + +import type { TaskInstanceState } from "openapi/requests/types.gen"; + +type Props = { + readonly state?: TaskInstanceState | null; +} & IconBaseProps; + +export const StateIcon = ({ state, ...rest }: Props) => { + switch (state) { + case "deferred": + return ; + case "failed": + return ; + case "queued": + return ; + case "removed": + return ; + case "restarting": + return ; + case "running": + return ; + case "scheduled": + return ; + case "skipped": + return ; + case "success": + return ; + case "up_for_reschedule": + return ; + case "up_for_retry": + return ; + case "upstream_failed": + return ; + default: + return ; + } +}; diff --git a/airflow/ui/src/components/TaskInstanceTooltip.tsx b/airflow/ui/src/components/TaskInstanceTooltip.tsx index bc44a23856ffd..ed09f9a9f93d3 100644 --- a/airflow/ui/src/components/TaskInstanceTooltip.tsx +++ b/airflow/ui/src/components/TaskInstanceTooltip.tsx @@ -38,6 +38,7 @@ const TaskInstanceTooltip = ({ children, positioning, taskInstance, ...rest }: P {...rest} content={ + State: {taskInstance.state} {"dag_run_id" in taskInstance ? Run ID: {taskInstance.dag_run_id} : undefined} Start Date: ({ @@ -79,7 +78,7 @@ export const Overview = () => { startDate={startDate} /> ({ diff --git a/airflow/ui/src/pages/Dag/Tasks/TaskCard.tsx b/airflow/ui/src/pages/Dag/Tasks/TaskCard.tsx index 2d517dd4e5798..db8c01c1d77c4 100644 --- a/airflow/ui/src/pages/Dag/Tasks/TaskCard.tsx +++ b/airflow/ui/src/pages/Dag/Tasks/TaskCard.tsx @@ -63,9 +63,7 @@ export const TaskCard = ({ dagId, task, taskInstances }: Props) => ( diff --git a/airflow/ui/src/pages/Dag/Tasks/TaskRecentRuns.tsx b/airflow/ui/src/pages/Dag/Tasks/TaskRecentRuns.tsx index fee2b943582d0..3a7fc13d44949 100644 --- a/airflow/ui/src/pages/Dag/Tasks/TaskRecentRuns.tsx +++ b/airflow/ui/src/pages/Dag/Tasks/TaskRecentRuns.tsx @@ -24,7 +24,6 @@ import { Link } from "react-router-dom"; import type { TaskInstanceResponse } from "openapi/requests/types.gen"; import TaskInstanceTooltip from "src/components/TaskInstanceTooltip"; import { getTaskInstanceLink } from "src/utils/links"; -import { stateColor } from "src/utils/stateColor"; dayjs.extend(duration); @@ -58,7 +57,7 @@ export const TaskRecentRuns = ({ ; } + const state = status === "healthy" ? "success" : "failed"; + return ( - - {title} + + + + {title} + ); diff --git a/airflow/ui/src/pages/Dashboard/HistoricalMetrics/AssetEvents.tsx b/airflow/ui/src/pages/Dashboard/HistoricalMetrics/AssetEvents.tsx index 21c425f0c5a76..2021a6add8fc7 100644 --- a/airflow/ui/src/pages/Dashboard/HistoricalMetrics/AssetEvents.tsx +++ b/airflow/ui/src/pages/Dashboard/HistoricalMetrics/AssetEvents.tsx @@ -51,7 +51,7 @@ export const AssetEvents = ({ assetSortBy, endDate, setAssetSortBy, startDate }: - + Asset Events diff --git a/airflow/ui/src/pages/Dashboard/HistoricalMetrics/DagRunMetrics.tsx b/airflow/ui/src/pages/Dashboard/HistoricalMetrics/DagRunMetrics.tsx index 75b18c85fe64a..c298ee92aa2fe 100644 --- a/airflow/ui/src/pages/Dashboard/HistoricalMetrics/DagRunMetrics.tsx +++ b/airflow/ui/src/pages/Dashboard/HistoricalMetrics/DagRunMetrics.tsx @@ -33,7 +33,7 @@ const DAGRUN_STATES: Array = ["queued", "running", "success" export const DagRunMetrics = ({ dagRunStates, total }: DagRunMetricsProps) => ( - + Dag Runs {DAGRUN_STATES.map((state) => ( diff --git a/airflow/ui/src/pages/Dashboard/HistoricalMetrics/MetricSection.tsx b/airflow/ui/src/pages/Dashboard/HistoricalMetrics/MetricSection.tsx index c2455a8c96161..736943afd6771 100644 --- a/airflow/ui/src/pages/Dashboard/HistoricalMetrics/MetricSection.tsx +++ b/airflow/ui/src/pages/Dashboard/HistoricalMetrics/MetricSection.tsx @@ -20,7 +20,6 @@ import { Box, Flex, HStack, VStack, Text } from "@chakra-ui/react"; import { MetricsBadge } from "src/components/MetricsBadge"; import { capitalize } from "src/utils"; -import { stateColor } from "src/utils/stateColor"; const BAR_WIDTH = 100; const BAR_HEIGHT = 5; @@ -42,14 +41,14 @@ export const MetricSection = ({ runs, state, total }: MetricSectionProps) => { - + {capitalize(state)} {statePercent}% = [ export const TaskInstanceMetrics = ({ taskInstanceStates, total }: TaskInstanceMetricsProps) => ( - + Task Instances diff --git a/airflow/ui/src/pages/Dashboard/Stats/DAGImportErrors.tsx b/airflow/ui/src/pages/Dashboard/Stats/DAGImportErrors.tsx index e28efa0f9e05a..9b6723ae849e5 100644 --- a/airflow/ui/src/pages/Dashboard/Stats/DAGImportErrors.tsx +++ b/airflow/ui/src/pages/Dashboard/Stats/DAGImportErrors.tsx @@ -22,7 +22,6 @@ import { FiChevronRight } from "react-icons/fi"; import { useImportErrorServiceGetImportErrors } from "openapi/queries"; import { ErrorAlert } from "src/components/ErrorAlert"; import { MetricsBadge } from "src/components/MetricsBadge"; -import { stateColor } from "src/utils/stateColor"; import { DAGImportErrorsModal } from "./DAGImportErrorsModal"; @@ -49,7 +48,7 @@ export const DAGImportErrors = () => { onClick={onOpen} variant="outline" > - + Dag Import Errors diff --git a/airflow/ui/src/pages/Dashboard/Stats/DagFilterButton.tsx b/airflow/ui/src/pages/Dashboard/Stats/DagFilterButton.tsx index b9498566fe85a..f5cdaf49f6014 100644 --- a/airflow/ui/src/pages/Dashboard/Stats/DagFilterButton.tsx +++ b/airflow/ui/src/pages/Dashboard/Stats/DagFilterButton.tsx @@ -17,6 +17,7 @@ * under the License. */ import { Box, Text, Button } from "@chakra-ui/react"; +import type { ReactNode } from "react"; import { FiChevronRight } from "react-icons/fi"; import { Link as RouterLink } from "react-router-dom"; @@ -26,18 +27,20 @@ import { capitalize } from "src/utils"; // TODO: Add badge count once API is available export const DagFilterButton = ({ - badgeColor, + colorPalette, filter, + icon, link, }: { - readonly badgeColor: string; + readonly colorPalette: string; readonly filter: string; + readonly icon?: ReactNode; readonly link: string; }) => (