From 172bb6b9a03210891bdabd627e8b021c9a2a9672 Mon Sep 17 00:00:00 2001 From: Brent Bovenzi Date: Thu, 29 May 2025 15:50:33 -0400 Subject: [PATCH 1/3] Translate Task Instance and Dag Run tables --- .../ActionAccordion/ActionAccordion.tsx | 23 ++-- .../components/ActionAccordion/columns.tsx | 52 +++------ .../components/Clear/Run/ClearRunButton.tsx | 13 ++- .../components/Clear/Run/ClearRunDialog.tsx | 51 ++++---- .../TaskInstance/ClearTaskInstanceButton.tsx | 13 ++- .../TaskInstance/ClearTaskInstanceDialog.tsx | 37 ++++-- .../components/DagActions/DeleteDagButton.tsx | 8 +- .../components/MarkAs/Run/MarkRunAsButton.tsx | 15 +-- .../components/MarkAs/Run/MarkRunAsDialog.tsx | 7 +- .../TaskInstance/MarkTaskInstanceAsButton.tsx | 15 +-- .../TaskInstance/MarkTaskInstanceAsDialog.tsx | 33 ++++-- .../airflow/ui/src/components/SearchBar.tsx | 4 +- .../TriggerDag/TriggerDAGButton.tsx | 4 +- .../airflow/ui/src/constants/stateOptions.ts | 48 ++++---- .../ui/src/i18n/locales/en/common.json | 7 ++ .../airflow/ui/src/i18n/locales/en/dags.json | 109 ++++++++++++++++-- .../src/airflow/ui/src/pages/DagRuns.tsx | 47 ++++---- .../airflow/ui/src/pages/DeleteRunButton.tsx | 15 ++- .../DeleteTaskInstanceButton.tsx | 15 ++- .../src/pages/TaskInstances/TaskInstances.tsx | 54 +++++---- .../TaskInstances/TaskInstancesFilter.tsx | 19 +-- .../airflow/ui/src/queries/useDeleteDagRun.ts | 12 +- .../ui/src/queries/useDeleteTaskInstance.ts | 22 ++-- 23 files changed, 405 insertions(+), 218 deletions(-) diff --git a/airflow-core/src/airflow/ui/src/components/ActionAccordion/ActionAccordion.tsx b/airflow-core/src/airflow/ui/src/components/ActionAccordion/ActionAccordion.tsx index 3995b7893080b..af251a402e358 100644 --- a/airflow-core/src/airflow/ui/src/components/ActionAccordion/ActionAccordion.tsx +++ b/airflow-core/src/airflow/ui/src/components/ActionAccordion/ActionAccordion.tsx @@ -18,13 +18,14 @@ */ import { Box, Editable, Text, VStack } from "@chakra-ui/react"; import type { ChangeEvent } from "react"; +import { useTranslation } from "react-i18next"; import type { DAGRunResponse, TaskInstanceCollectionResponse } from "openapi/requests/types.gen"; import ReactMarkdown from "src/components/ReactMarkdown"; import { Accordion } from "src/components/ui"; import { DataTable } from "../DataTable"; -import { columns } from "./columns"; +import { getColumns } from "./columns"; type Props = { readonly affectedTasks?: TaskInstanceCollectionResponse; @@ -36,6 +37,7 @@ type Props = { // TODO: Make a front-end only unconnected table component with client side ordering and pagination const ActionAccordion = ({ affectedTasks, note, setNote }: Props) => { const showTaskSection = affectedTasks !== undefined; + const { t: translate } = useTranslation(); return ( { {showTaskSection ? ( - Affected Tasks: {affectedTasks.total_entries} + + {translate("dags:runAndTaskActions.clear.dialog.affectedTasks.title", { + count: affectedTasks.total_entries, + })} + @@ -64,7 +71,7 @@ const ActionAccordion = ({ affectedTasks, note, setNote }: Props) => { ) : undefined} - Note + {translate("dags:runAndTaskActions.clear.dialog.note.title")} { {Boolean(note) ? ( {note} ) : ( - Add a note... + + {translate("dags:runAndTaskActions.clear.dialog.note.placeholder")} + )} diff --git a/airflow-core/src/airflow/ui/src/components/ActionAccordion/columns.tsx b/airflow-core/src/airflow/ui/src/components/ActionAccordion/columns.tsx index dd008cbafb584..8f3a200ea2f97 100644 --- a/airflow-core/src/airflow/ui/src/components/ActionAccordion/columns.tsx +++ b/airflow-core/src/airflow/ui/src/components/ActionAccordion/columns.tsx @@ -16,50 +16,36 @@ * specific language governing permissions and limitations * under the License. */ -import { Link } from "@chakra-ui/react"; -import type { ColumnDef } from "@tanstack/react-table"; -import { Link as RouterLink } from "react-router-dom"; +import type { TFunction } from "i18next"; import type { TaskInstanceResponse } from "openapi/requests/types.gen"; +import type { MetaColumn } from "src/components/DataTable/types"; import { StateBadge } from "src/components/StateBadge"; -import { Tooltip } from "src/components/ui"; -import { getTaskInstanceLink } from "src/utils/links"; -import { trimText } from "src/utils/trimTextFn"; -export const columns: Array> = [ +export const getColumns = (translate: TFunction): Array> => [ { - accessorKey: "task_display_name", - cell: ({ row: { original } }) => ( - - - - {trimText(original.task_display_name, 25).trimmedText} - - - - ), - enableSorting: false, - header: "Task ID", + accessorKey: "task_id", + header: translate("dags:runAndTaskActions.clear.dialog.affectedTasks.columns.taskId"), + size: 200, }, { accessorKey: "state", - cell: ({ - row: { - original: { state }, - }, - }) => {state}, - enableSorting: false, - header: () => "State", + cell: ({ getValue }) => { + const state = getValue(); + + return ; + }, + header: translate("dags:runAndTaskActions.clear.dialog.affectedTasks.columns.state"), + size: 100, }, { - accessorKey: "rendered_map_index", - enableSorting: false, - header: "Map Index", + accessorKey: "map_index", + header: translate("dags:runAndTaskActions.clear.dialog.affectedTasks.columns.mapIndex"), + size: 100, }, - { - accessorKey: "dag_run_id", - enableSorting: false, - header: "Run Id", + accessorKey: "run_id", + header: translate("dags:runAndTaskActions.clear.dialog.affectedTasks.columns.runId"), + size: 200, }, ]; diff --git a/airflow-core/src/airflow/ui/src/components/Clear/Run/ClearRunButton.tsx b/airflow-core/src/airflow/ui/src/components/Clear/Run/ClearRunButton.tsx index ba9019ba12b02..0124598fd20c1 100644 --- a/airflow-core/src/airflow/ui/src/components/Clear/Run/ClearRunButton.tsx +++ b/airflow-core/src/airflow/ui/src/components/Clear/Run/ClearRunButton.tsx @@ -18,6 +18,7 @@ */ import { Box, useDisclosure } from "@chakra-ui/react"; import { useHotkeys } from "react-hotkeys-hook"; +import { useTranslation } from "react-i18next"; import { CgRedo } from "react-icons/cg"; import type { DAGRunResponse } from "openapi/requests/types.gen"; @@ -34,6 +35,7 @@ type Props = { const ClearRunButton = ({ dagRun, isHotkeyEnabled = false, withText = true }: Props) => { const { onClose, onOpen, open } = useDisclosure(); + const { t: translate } = useTranslation(); useHotkeys( "shift+c", @@ -44,13 +46,18 @@ const ClearRunButton = ({ dagRun, isHotkeyEnabled = false, withText = true }: Pr ); return ( - + } onClick={onOpen} - text="Clear Run" + text={translate("dags:runAndTaskActions.clear.button", { type: "Run" })} withText={withText} /> diff --git a/airflow-core/src/airflow/ui/src/components/Clear/Run/ClearRunDialog.tsx b/airflow-core/src/airflow/ui/src/components/Clear/Run/ClearRunDialog.tsx index 6323fa1541c1c..d1f2ec552b558 100644 --- a/airflow-core/src/airflow/ui/src/components/Clear/Run/ClearRunDialog.tsx +++ b/airflow-core/src/airflow/ui/src/components/Clear/Run/ClearRunDialog.tsx @@ -18,6 +18,7 @@ */ import { Flex, Heading, VStack } from "@chakra-ui/react"; import { useState } from "react"; +import { useTranslation } from "react-i18next"; import { CgRedo } from "react-icons/cg"; import type { DAGRunResponse } from "openapi/requests/types.gen"; @@ -35,10 +36,19 @@ type Props = { }; const ClearRunDialog = ({ dagRun, onClose, open }: Props) => { - const [selectedOptions, setSelectedOptions] = useState>([]); - const dagId = dagRun.dag_id; const dagRunId = dagRun.dag_run_id; + const { t: translate } = useTranslation(); + + const [note, setNote] = useState(dagRun.note); + const [selectedOptions, setSelectedOptions] = useState>(["existingTasks"]); + const onlyFailed = selectedOptions.includes("onlyFailed"); + + const { data: affectedTasks = { task_instances: [], total_entries: 0 } } = useClearDagRunDryRun({ + dagId, + dagRunId, + requestBody: { only_failed: onlyFailed }, + }); const { isPending, mutate } = useClearDagRun({ dagId, @@ -46,35 +56,20 @@ const ClearRunDialog = ({ dagRun, onClose, open }: Props) => { onSuccessConfirm: onClose, }); - const onlyFailed = selectedOptions.includes("onlyFailed"); - - const [note, setNote] = useState(dagRun.note); - const { isPending: isPendingPatchDagRun, mutate: mutatePatchDagRun } = usePatchDagRun({ dagId, dagRunId }); - - const { data } = useClearDagRunDryRun({ + const { isPending: isPendingPatchDagRun, mutate: mutatePatchDagRun } = usePatchDagRun({ dagId, dagRunId, - options: { - enabled: open, - refetchOnMount: "always", - }, - requestBody: { - only_failed: onlyFailed, - }, + onSuccess: onClose, }); - const affectedTasks = data ?? { - task_instances: [], - total_entries: 0, - }; - return ( - Clear DagRun: {dagRunId} + {translate("dags:runAndTaskActions.clear.dialog.title", { type: "Run" })}: {" "} + {dagRunId} @@ -87,11 +82,17 @@ const ClearRunDialog = ({ dagRun, onClose, open }: Props) => { defaultValues={["existingTasks"]} onChange={setSelectedOptions} options={[ - { label: "Clear existing tasks", value: "existingTasks" }, - { label: "Clear only failed tasks", value: "onlyFailed" }, + { + label: translate("dags:runAndTaskActions.clear.dialog.options.existingTasks"), + value: "existingTasks", + }, + { + label: translate("dags:runAndTaskActions.clear.dialog.options.onlyFailed"), + value: "onlyFailed", + }, { disabled: true, - label: "Queue up new tasks", + label: translate("dags:runAndTaskActions.clear.dialog.options.queueNew"), value: "new_tasks", }, ]} @@ -118,7 +119,7 @@ const ClearRunDialog = ({ dagRun, onClose, open }: Props) => { } }} > - Confirm + {translate("dags:runAndTaskActions.clear.dialog.confirm")} diff --git a/airflow-core/src/airflow/ui/src/components/Clear/TaskInstance/ClearTaskInstanceButton.tsx b/airflow-core/src/airflow/ui/src/components/Clear/TaskInstance/ClearTaskInstanceButton.tsx index 0badf52acba6d..369c35655838c 100644 --- a/airflow-core/src/airflow/ui/src/components/Clear/TaskInstance/ClearTaskInstanceButton.tsx +++ b/airflow-core/src/airflow/ui/src/components/Clear/TaskInstance/ClearTaskInstanceButton.tsx @@ -18,6 +18,7 @@ */ import { Box, useDisclosure } from "@chakra-ui/react"; import { useHotkeys } from "react-hotkeys-hook"; +import { useTranslation } from "react-i18next"; import { CgRedo } from "react-icons/cg"; import type { TaskInstanceResponse } from "openapi/requests/types.gen"; @@ -34,6 +35,7 @@ type Props = { const ClearTaskInstanceButton = ({ isHotkeyEnabled = false, taskInstance, withText = true }: Props) => { const { onClose, onOpen, open } = useDisclosure(); + const { t: translate } = useTranslation(); useHotkeys( "shift+c", @@ -44,13 +46,18 @@ const ClearTaskInstanceButton = ({ isHotkeyEnabled = false, taskInstance, withTe ); return ( - + } onClick={onOpen} - text="Clear Task Instance" + text={translate("dags:runAndTaskActions.clear.button", { type: "Task Instance" })} withText={withText} /> diff --git a/airflow-core/src/airflow/ui/src/components/Clear/TaskInstance/ClearTaskInstanceDialog.tsx b/airflow-core/src/airflow/ui/src/components/Clear/TaskInstance/ClearTaskInstanceDialog.tsx index 3d251138ff15b..c64f3edfc9656 100644 --- a/airflow-core/src/airflow/ui/src/components/Clear/TaskInstance/ClearTaskInstanceDialog.tsx +++ b/airflow-core/src/airflow/ui/src/components/Clear/TaskInstance/ClearTaskInstanceDialog.tsx @@ -18,6 +18,7 @@ */ import { Flex, Heading, VStack } from "@chakra-ui/react"; import { useState } from "react"; +import { useTranslation } from "react-i18next"; import { CgRedo } from "react-icons/cg"; import type { TaskInstanceResponse } from "openapi/requests/types.gen"; @@ -38,6 +39,7 @@ type Props = { const ClearTaskInstanceDialog = ({ onClose, open, taskInstance }: Props) => { const taskId = taskInstance.task_id; const mapIndex = taskInstance.map_index; + const { t: translate } = useTranslation(); const dagId = taskInstance.dag_id; const dagRunId = taskInstance.dag_run_id; @@ -92,8 +94,10 @@ const ClearTaskInstanceDialog = ({ onClose, open, taskInstance }: Props) => { - Clear Task Instance: {taskInstance.task_display_name}{" "} - @@ -106,11 +110,28 @@ const ClearTaskInstanceDialog = ({ onClose, open, taskInstance }: Props) => { multiple onChange={setSelectedOptions} options={[ - { disabled: taskInstance.logical_date === null, label: "Past", value: "past" }, - { disabled: taskInstance.logical_date === null, label: "Future", value: "future" }, - { label: "Upstream", value: "upstream" }, - { label: "Downstream", value: "downstream" }, - { label: "Only Failed", value: "onlyFailed" }, + { + disabled: taskInstance.logical_date === null, + label: translate("dags:runAndTaskActions.clear.dialog.options.past"), + value: "past", + }, + { + disabled: taskInstance.logical_date === null, + label: translate("dags:runAndTaskActions.clear.dialog.options.future"), + value: "future", + }, + { + label: translate("dags:runAndTaskActions.clear.dialog.options.upstream"), + value: "upstream", + }, + { + label: translate("dags:runAndTaskActions.clear.dialog.options.downstream"), + value: "downstream", + }, + { + label: translate("dags:runAndTaskActions.clear.dialog.options.onlyFailed"), + value: "onlyFailed", + }, ]} /> @@ -145,7 +166,7 @@ const ClearTaskInstanceDialog = ({ onClose, open, taskInstance }: Props) => { } }} > - Confirm + {translate("dags:runAndTaskActions.clear.dialog.confirm")} diff --git a/airflow-core/src/airflow/ui/src/components/DagActions/DeleteDagButton.tsx b/airflow-core/src/airflow/ui/src/components/DagActions/DeleteDagButton.tsx index afddb68436ffb..e65bb1932dbd0 100644 --- a/airflow-core/src/airflow/ui/src/components/DagActions/DeleteDagButton.tsx +++ b/airflow-core/src/airflow/ui/src/components/DagActions/DeleteDagButton.tsx @@ -46,11 +46,11 @@ const DeleteDagButton = ({ dagDisplayName, dagId, withText = true }: DeleteDagBu return ( <> } onClick={onOpen} - text={translate("actions.delete")} + text={translate("dagActions.delete.button")} variant="solid" withText={withText} /> @@ -61,8 +61,8 @@ const DeleteDagButton = ({ dagDisplayName, dagId, withText = true }: DeleteDagBu onDelete={() => deleteDag({ dagId })} open={open} resourceName={dagDisplayName} - title={translate("actions.delete")} - warningText={translate("actions.deleteDagWarning")} + title={translate("dagActions.delete.button")} + warningText={translate("dagActions.delete.warning")} /> ); diff --git a/airflow-core/src/airflow/ui/src/components/MarkAs/Run/MarkRunAsButton.tsx b/airflow-core/src/airflow/ui/src/components/MarkAs/Run/MarkRunAsButton.tsx index ad6fa2ef53795..72e2c9a434aa4 100644 --- a/airflow-core/src/airflow/ui/src/components/MarkAs/Run/MarkRunAsButton.tsx +++ b/airflow-core/src/airflow/ui/src/components/MarkAs/Run/MarkRunAsButton.tsx @@ -19,6 +19,7 @@ import { Box, useDisclosure } from "@chakra-ui/react"; import { useState } from "react"; import { useHotkeys } from "react-hotkeys-hook"; +import { useTranslation } from "react-i18next"; import { MdArrowDropDown } from "react-icons/md"; import type { DAGRunPatchStates, DAGRunResponse } from "openapi/requests/types.gen"; @@ -38,6 +39,7 @@ type Props = { const MarkRunAsButton = ({ dagRun, isHotkeyEnabled = false, withText = true }: Props) => { const { onClose, onOpen, open } = useDisclosure(); const [state, setState] = useState("success"); + const { t: translate } = useTranslation(); useHotkeys( "shift+f", @@ -62,19 +64,18 @@ const MarkRunAsButton = ({ dagRun, isHotkeyEnabled = false, withText = true }: P } - text="Mark Run as..." + text={translate("dags:runAndTaskActions.markAs.button", { type: "Run" })} withText={withText} /> {allowedStates.map((menuState) => { - const content = - menuState === "success" - ? "Press shift+s to mark as success" - : "Press shift+f to mark as failed"; + const content = translate( + `dags:runAndTaskActions.markAs.buttonTooltip.${menuState === "success" ? "success" : "failed"}`, + ); return ( - {menuState} + {translate(`common:states.${menuState}`)} diff --git a/airflow-core/src/airflow/ui/src/components/MarkAs/Run/MarkRunAsDialog.tsx b/airflow-core/src/airflow/ui/src/components/MarkAs/Run/MarkRunAsDialog.tsx index 5cdce40ebe3d6..3abd8174a5b32 100644 --- a/airflow-core/src/airflow/ui/src/components/MarkAs/Run/MarkRunAsDialog.tsx +++ b/airflow-core/src/airflow/ui/src/components/MarkAs/Run/MarkRunAsDialog.tsx @@ -18,6 +18,7 @@ */ import { Flex, Heading, VStack } from "@chakra-ui/react"; import { useState } from "react"; +import { useTranslation } from "react-i18next"; import type { DAGRunPatchStates, DAGRunResponse } from "openapi/requests/types.gen"; import { ActionAccordion } from "src/components/ActionAccordion"; @@ -35,6 +36,7 @@ type Props = { const MarkRunAsDialog = ({ dagRun, onClose, open, state }: Props) => { const dagId = dagRun.dag_id; const dagRunId = dagRun.dag_run_id; + const { t: translate } = useTranslation(); const [note, setNote] = useState(dagRun.note); const { isPending, mutate } = usePatchDagRun({ dagId, dagRunId, onSuccess: onClose }); @@ -45,7 +47,8 @@ const MarkRunAsDialog = ({ dagRun, onClose, open, state }: Props) => { - Mark DagRun as {state}: {dagRunId} + {translate("dags:runAndTaskActions.markAs.dialog.title", { state, type: "Run" })}: {dagRunId}{" "} + @@ -66,7 +69,7 @@ const MarkRunAsDialog = ({ dagRun, onClose, open, state }: Props) => { }); }} > - Confirm + {translate("dags:runAndTaskActions.markAs.dialog.confirm")} diff --git a/airflow-core/src/airflow/ui/src/components/MarkAs/TaskInstance/MarkTaskInstanceAsButton.tsx b/airflow-core/src/airflow/ui/src/components/MarkAs/TaskInstance/MarkTaskInstanceAsButton.tsx index e8ffcddd49ae7..f75607d5a3fba 100644 --- a/airflow-core/src/airflow/ui/src/components/MarkAs/TaskInstance/MarkTaskInstanceAsButton.tsx +++ b/airflow-core/src/airflow/ui/src/components/MarkAs/TaskInstance/MarkTaskInstanceAsButton.tsx @@ -19,6 +19,7 @@ import { Box, useDisclosure } from "@chakra-ui/react"; import { useState } from "react"; import { useHotkeys } from "react-hotkeys-hook"; +import { useTranslation } from "react-i18next"; import { MdArrowDropDown } from "react-icons/md"; import type { TaskInstanceResponse, TaskInstanceState } from "openapi/requests/types.gen"; @@ -37,6 +38,7 @@ type Props = { const MarkTaskInstanceAsButton = ({ isHotkeyEnabled = false, taskInstance, withText = true }: Props) => { const { onClose, onOpen, open } = useDisclosure(); + const { t: translate } = useTranslation(); const [state, setState] = useState("success"); @@ -63,19 +65,18 @@ const MarkTaskInstanceAsButton = ({ isHotkeyEnabled = false, taskInstance, withT } - text="Mark Task Instance as..." + text={translate("dags:runAndTaskActions.markAs.button", { type: "Task Instance" })} withText={withText} /> {allowedStates.map((menuState) => { - const content = - menuState === "success" - ? "Press shift+s to mark as success" - : "Press shift+f to mark as failed"; + const content = translate( + `dags:runAndTaskActions.markAs.buttonTooltip.${menuState === "success" ? "success" : "failed"}`, + ); return ( - {menuState} + {translate(`common:states.${menuState}`)} diff --git a/airflow-core/src/airflow/ui/src/components/MarkAs/TaskInstance/MarkTaskInstanceAsDialog.tsx b/airflow-core/src/airflow/ui/src/components/MarkAs/TaskInstance/MarkTaskInstanceAsDialog.tsx index b22992d39f973..8f079331e17f0 100644 --- a/airflow-core/src/airflow/ui/src/components/MarkAs/TaskInstance/MarkTaskInstanceAsDialog.tsx +++ b/airflow-core/src/airflow/ui/src/components/MarkAs/TaskInstance/MarkTaskInstanceAsDialog.tsx @@ -18,6 +18,7 @@ */ import { Flex, Heading, VStack } from "@chakra-ui/react"; import { useState } from "react"; +import { useTranslation } from "react-i18next"; import type { TaskInstanceResponse, TaskInstanceState } from "openapi/requests/types.gen"; import { ActionAccordion } from "src/components/ActionAccordion"; @@ -40,6 +41,7 @@ const MarkTaskInstanceAsDialog = ({ onClose, open, state, taskInstance }: Props) const dagRunId = taskInstance.dag_run_id; const taskId = taskInstance.task_id; const mapIndex = taskInstance.map_index; + const { t: translate } = useTranslation(); const [selectedOptions, setSelectedOptions] = useState>([]); @@ -87,8 +89,11 @@ const MarkTaskInstanceAsDialog = ({ onClose, open, state, taskInstance }: Props) - Mark Task Instance as {state}: {taskInstance.task_display_name}{" "} - @@ -101,10 +106,24 @@ const MarkTaskInstanceAsDialog = ({ onClose, open, state, taskInstance }: Props) multiple onChange={setSelectedOptions} options={[ - { disabled: taskInstance.logical_date === null, label: "Past", value: "past" }, - { disabled: taskInstance.logical_date === null, label: "Future", value: "future" }, - { label: "Upstream", value: "upstream" }, - { label: "Downstream", value: "downstream" }, + { + disabled: taskInstance.logical_date === null, + label: translate("dags:runAndTaskActions.markAs.dialog.options.past"), + value: "past", + }, + { + disabled: taskInstance.logical_date === null, + label: translate("dags:runAndTaskActions.markAs.dialog.options.future"), + value: "future", + }, + { + label: translate("dags:runAndTaskActions.markAs.dialog.options.upstream"), + value: "upstream", + }, + { + label: translate("dags:runAndTaskActions.markAs.dialog.options.downstream"), + value: "downstream", + }, ]} /> @@ -130,7 +149,7 @@ const MarkTaskInstanceAsDialog = ({ onClose, open, state, taskInstance }: Props) }); }} > - Confirm + {translate("dags:runAndTaskActions.markAs.dialog.confirm")} diff --git a/airflow-core/src/airflow/ui/src/components/SearchBar.tsx b/airflow-core/src/airflow/ui/src/components/SearchBar.tsx index b70a5655b7127..5ab1ab3144e30 100644 --- a/airflow-core/src/airflow/ui/src/components/SearchBar.tsx +++ b/airflow-core/src/airflow/ui/src/components/SearchBar.tsx @@ -74,7 +74,7 @@ export const SearchBar = ({ <> {Boolean(value) ? ( { @@ -86,7 +86,7 @@ export const SearchBar = ({ ) : undefined} {Boolean(hideAdvanced) ? undefined : ( )} {!hotkeyDisabled && {metaKey}+K} diff --git a/airflow-core/src/airflow/ui/src/components/TriggerDag/TriggerDAGButton.tsx b/airflow-core/src/airflow/ui/src/components/TriggerDag/TriggerDAGButton.tsx index 07f87d11cb747..11b7db1c00ff2 100644 --- a/airflow-core/src/airflow/ui/src/components/TriggerDag/TriggerDAGButton.tsx +++ b/airflow-core/src/airflow/ui/src/components/TriggerDag/TriggerDAGButton.tsx @@ -38,11 +38,11 @@ const TriggerDAGButton: React.FC = ({ dag, withText = true }) => { return ( } onClick={onOpen} - text={translate("actions.trigger")} + text={translate("dagActions.trigger.button")} variant="solid" withText={withText} /> diff --git a/airflow-core/src/airflow/ui/src/constants/stateOptions.ts b/airflow-core/src/airflow/ui/src/constants/stateOptions.ts index 16aafbd7be257..d66418bcfd1d6 100644 --- a/airflow-core/src/airflow/ui/src/constants/stateOptions.ts +++ b/airflow-core/src/airflow/ui/src/constants/stateOptions.ts @@ -25,39 +25,39 @@ export const taskInstanceStateOptions = createListCollection<{ value: TaskInstanceState | "all" | "none"; }>({ items: [ - { label: "All States", value: "all" }, - { label: "Scheduled", value: "scheduled" }, - { label: "Queued", value: "queued" }, - { label: "Running", value: "running" }, - { label: "Success", value: "success" }, - { label: "Restarting", value: "restarting" }, - { label: "Failed", value: "failed" }, - { label: "Up For Retry", value: "up_for_retry" }, - { label: "Up For Reschedule", value: "up_for_reschedule" }, - { label: "Upstream failed", value: "upstream_failed" }, - { label: "Skipped", value: "skipped" }, - { label: "Deferred", value: "deferred" }, - { label: "Removed", value: "removed" }, - { label: "No Status", value: "none" }, + { label: "dags:taskInstances.allStates", value: "all" }, + { label: "common:states.scheduled", value: "scheduled" }, + { label: "common:states.queued", value: "queued" }, + { label: "common:states.running", value: "running" }, + { label: "common:states.success", value: "success" }, + { label: "common:states.restarting", value: "restarting" }, + { label: "common:states.failed", value: "failed" }, + { label: "common:states.up_for_retry", value: "up_for_retry" }, + { label: "common:states.up_for_reschedule", value: "up_for_reschedule" }, + { label: "common:states.upstream_failed", value: "upstream_failed" }, + { label: "common:states.skipped", value: "skipped" }, + { label: "common:states.deferred", value: "deferred" }, + { label: "common:states.removed", value: "removed" }, + { label: "common:states.none", value: "none" }, ], }); export const dagRunStateOptions = createListCollection({ items: [ - { label: "All States", value: "all" }, - { label: "Queued", value: "queued" }, - { label: "Running", value: "running" }, - { label: "Failed", value: "failed" }, - { label: "Success", value: "success" }, + { label: "dags:runs.allStates", value: "all" }, + { label: "common:states.queued", value: "queued" }, + { label: "common:states.running", value: "running" }, + { label: "common:states.failed", value: "failed" }, + { label: "common:states.success", value: "success" }, ], }); export const dagRunTypeOptions = createListCollection({ items: [ - { label: "All Run Types", value: "all" }, - { label: "Backfill", value: "backfill" }, - { label: "Manual", value: "manual" }, - { label: "Scheduled", value: "scheduled" }, - { label: "Asset Triggered", value: "asset_triggered" }, + { label: "dags:runs.allRunTypes", value: "all" }, + { label: "common:runTypes.backfill", value: "backfill" }, + { label: "common:runTypes.manual", value: "manual" }, + { label: "common:runTypes.scheduled", value: "scheduled" }, + { label: "common:runTypes.asset_triggered", value: "asset_triggered" }, ], }); diff --git a/airflow-core/src/airflow/ui/src/i18n/locales/en/common.json b/airflow-core/src/airflow/ui/src/i18n/locales/en/common.json index 59e26eae483ff..fb5230c50faec 100644 --- a/airflow-core/src/airflow/ui/src/i18n/locales/en/common.json +++ b/airflow-core/src/airflow/ui/src/i18n/locales/en/common.json @@ -53,6 +53,12 @@ "running": "Running", "scheduled": "Scheduled" }, + "runTypes": { + "asset_triggered": "Asset Triggered", + "backfill": "Backfill", + "manual": "Manual", + "scheduled": "Scheduled" + }, "security": { "actions": "Actions", "permissions": "Permissions", @@ -65,6 +71,7 @@ "deferred": "Deferred", "failed": "Failed", "no_status": "No Status", + "none": "No Status", "queued": "Queued", "removed": "Removed", "restarting": "Restarting", diff --git a/airflow-core/src/airflow/ui/src/i18n/locales/en/dags.json b/airflow-core/src/airflow/ui/src/i18n/locales/en/dags.json index cb6db5cc3cec7..13491dac885a7 100644 --- a/airflow-core/src/airflow/ui/src/i18n/locales/en/dags.json +++ b/airflow-core/src/airflow/ui/src/i18n/locales/en/dags.json @@ -1,11 +1,15 @@ { - "actions": { - "delete": "Delete Dag", - "deleteDagWarning": "This will remove all metadata related to the DAG, including DAG Runs and Tasks.", - "trigger": "Trigger", - "triggerDag": "Trigger Dag" - }, "assetSchedule": "{{count}} of {{total}} assets updated", + "dagActions": { + "delete": { + "button": "Delete Dag", + "warning": "This will remove all metadata related to the Dag, including Runs and Tasks." + }, + "trigger": { + "button": "Trigger", + "triggerDag": "Trigger Dag" + } + }, "filters": { "paused": { "active": "Active", @@ -24,14 +28,83 @@ "tags": "Tags" }, "ownerLink": "Owner link for {{owner}}", - "runs": { + "searchPlaceholder": "Search Dags" + }, + "runAndTaskActions": { + "clear": { + "button": "Clear {{type}}", + "buttonTooltip": "Press shift+c to clear", + "dialog": { + "affectedTasks": { + "columns": { + "mapIndex": "Map Index", + "runId": "Run ID", + "state": "State", + "taskId": "Task ID" + }, + "noItemsFound": "No tasks found.", + "title": "Affected Tasks: {{count}}" + }, + "confirm": "Confirm", + "note": { + "placeholder": "Add a note...", + "title": "Note" + }, + "options": { + "downstream": "Downstream", + "existingTasks": "Clear existing tasks", + "future": "Future", + "onlyFailed": "Clear only failed tasks", + "past": "Past", + "queueNew": "Queue up new tasks", + "upstream": "Upstream" + }, + "title": "Clear {{type}}" + } + }, + "delete": { + "button": "Delete {{type}}", + "dialog": { + "resourceName": "{{type}} {{id}}", + "title": "Delete {{type}}", + "warning": "This will remove all metadata related to the {{type}}." + }, + "success": { + "description": "The {{type}} deletion request was successful.", + "title": "{{type}} Deleted Successfully" + } + }, + "markAs": { + "button": "Mark {{type}} as...", + "buttonTooltip": { + "failed": "Press shift+f to mark as failed", + "success": "Press shift+s to mark as success" + }, + "dialog": { + "confirm": "Confirm", + "options": { + "downstream": "Downstream", + "future": "Future", + "past": "Past", + "upstream": "Upstream" + }, + "title": "Mark {{type}} as {{state}}" + } + } + }, + "runs": { + "allRunTypes": "All Run Types", + "allStates": "All States", + "columns": { + "dagId": "Dag ID", + "dagVersions": "Dag Versions", "duration": "Duration", "endDate": "End Date", "runAfter": "Run After", + "runType": "Run Type", "startDate": "Start Date", "state": "State" - }, - "searchPlaceholder": "Search Dags" + } }, "sort": { "displayName": { @@ -51,5 +124,23 @@ "desc": "Sort by Next Dag Run (Latest-Earliest)" }, "placeholder": "Sort by" + }, + "taskInstances": { + "allStates": "All States", + "columns": { + "dagId": "Dag ID", + "dagRun": "Dag Run", + "dagVersion": "Dag Version", + "duration": "Duration", + "endDate": "End Date", + "mapIndex": "Map Index", + "operator": "Operator", + "pool": "Pool", + "startDate": "Start Date", + "state": "State", + "taskId": "Task ID", + "tryNumber": "Try Number" + }, + "searchPlaceholder": "Search Tasks" } } diff --git a/airflow-core/src/airflow/ui/src/pages/DagRuns.tsx b/airflow-core/src/airflow/ui/src/pages/DagRuns.tsx index 062857cc53972..5873879a0dd9e 100644 --- a/airflow-core/src/airflow/ui/src/pages/DagRuns.tsx +++ b/airflow-core/src/airflow/ui/src/pages/DagRuns.tsx @@ -20,7 +20,9 @@ */ import { Flex, HStack, Link, type SelectValueChangeDetails, Text } from "@chakra-ui/react"; import type { ColumnDef } from "@tanstack/react-table"; +import type { TFunction } from "i18next"; import { useCallback } from "react"; +import { useTranslation } from "react-i18next"; import { Link as RouterLink, useParams, useSearchParams } from "react-router-dom"; import { useDagRunServiceGetDagRuns } from "openapi/queries"; @@ -39,7 +41,7 @@ import { Select } from "src/components/ui"; import { SearchParamsKeys, type SearchParamsKeysType } from "src/constants/searchParams"; import { dagRunTypeOptions, dagRunStateOptions as stateOptions } from "src/constants/stateOptions"; import DeleteRunButton from "src/pages/DeleteRunButton"; -import { capitalize, getDuration, useAutoRefresh, isStatePending } from "src/utils"; +import { getDuration, useAutoRefresh, isStatePending } from "src/utils"; type DagRunRow = { row: { original: DAGRunResponse } }; const { @@ -49,14 +51,14 @@ const { STATE: STATE_PARAM, }: SearchParamsKeysType = SearchParamsKeys; -const runColumns = (dagId?: string): Array> => [ +const runColumns = (translate: TFunction, dagId?: string): Array> => [ ...(Boolean(dagId) ? [] : [ { accessorKey: "dag_display_name", enableSorting: false, - header: "Dag ID", + header: translate("dags:runs.columns.dagId"), }, ]), { @@ -68,7 +70,7 @@ const runColumns = (dagId?: string): Array> => [ ), - header: "Run After", + header: translate("dags:runs.columns.runAfter"), }, { accessorKey: "state", @@ -77,7 +79,7 @@ const runColumns = (dagId?: string): Array> => [ original: { state }, }, }) => {state}, - header: () => "State", + header: () => translate("dags:runs.columns.state"), }, { accessorKey: "run_type", @@ -88,21 +90,21 @@ const runColumns = (dagId?: string): Array> => [ ), enableSorting: false, - header: "Run Type", + header: translate("dags:runs.columns.runType"), }, { accessorKey: "start_date", cell: ({ row: { original } }) =>