bbovenzi commented on code in PR #56351:
URL: https://github.com/apache/airflow/pull/56351#discussion_r2449307791


##########
airflow-core/src/airflow/ui/src/queries/useClearTaskInstances.ts:
##########
@@ -46,14 +49,49 @@ export const useClearTaskInstances = ({
   const queryClient = useQueryClient();
   const { t: translate } = useTranslation("dags");
 
-  const onError = (error: Error) => {
-    toaster.create({
-      description: error.message,
-      title: translate("dags:runAndTaskActions.clear.error", { type: 
translate("taskInstance_one") }),
-      type: "error",
-    });
+  const onError = (error: unknown) => {
+    // Narrow the type safely
+    if (typeof error === "object" && error !== null) {
+      const apiError = error as ApiError;
+
+      const detail = typeof apiError.detail === "string" ? apiError.detail : 
"";
+      const ifDetailIsIncluded =
+        typeof detail === "string" && 
detail.includes("AirflowClearRunningTaskException");
+
+      if (detail !== "" && ifDetailIsIncluded === true) {

Review Comment:
   ```suggestion
         if (detail.includes("AirflowClearRunningTaskException")) {
   ```
   
   I feel like you can simplify this



##########
airflow-core/src/airflow/ui/src/queries/useClearTaskInstances.ts:
##########
@@ -46,14 +49,49 @@ export const useClearTaskInstances = ({
   const queryClient = useQueryClient();
   const { t: translate } = useTranslation("dags");
 
-  const onError = (error: Error) => {
-    toaster.create({
-      description: error.message,
-      title: translate("dags:runAndTaskActions.clear.error", { type: 
translate("taskInstance_one") }),
-      type: "error",
-    });
+  const onError = (error: unknown) => {
+    // Narrow the type safely
+    if (typeof error === "object" && error !== null) {
+      const apiError = error as ApiError;
+
+      const detail = typeof apiError.detail === "string" ? apiError.detail : 
"";
+      const ifDetailIsIncluded =
+        typeof detail === "string" && 
detail.includes("AirflowClearRunningTaskException");
+
+      if (detail !== "" && ifDetailIsIncluded === true) {

Review Comment:
   Additionally, only the description is changing. So let's make that a 
variable and then we only need to call toaster.create() once.



##########
airflow-core/src/airflow/ui/public/i18n/locales/en/dags.json:
##########
@@ -63,7 +63,12 @@
       "past": "Past",
       "queueNew": "Queue up new tasks",
       "runOnLatestVersion": "Run with latest bundle version",
-      "upstream": "Upstream"
+      "upstream": "Upstream",
+      "preventRunningTasks": "Prevent rerun if task is running"
+    },
+    "confirmationDialog": {
+      "title": " Cannot Clear Task Instance",

Review Comment:
   We shouldn't have a space here. If it's needed, lets add padding via styles



##########
airflow-core/src/airflow/ui/src/components/Clear/TaskInstance/ClearTaskInstanceConfirmationDialog.tsx:
##########
@@ -0,0 +1,126 @@
+
+import { useEffect, useState, useCallback } from "react";
+import { VStack, Icon, Text, Spinner } from "@chakra-ui/react";
+import { GoAlertFill } from "react-icons/go";
+import { useTranslation } from "react-i18next";
+import { Button, Dialog } from "src/components/ui";
+import { useClearTaskInstancesDryRun } from 
"src/queries/useClearTaskInstancesDryRun";
+import { getRelativeTime } from "src/utils/datetimeUtils";
+
+type Props = {
+  readonly dagDetails?: {
+    dagId: string;
+    dagRunId: string;
+    downstream?: boolean;
+    future?: boolean;
+    mapIndex?: number;
+    onlyFailed?: boolean;
+    past?: boolean;
+    taskId: string;
+    upstream?: boolean;
+  };
+  readonly onClose: () => void;
+  readonly onConfirm?: () => void;
+  readonly open: boolean;
+  readonly preventRunningTask: boolean;
+};
+
+const ClearTaskInstanceConfirmationDialog = ({
+  dagDetails,
+  onClose,
+  onConfirm,
+  open,
+  preventRunningTask,
+}: Props) => {
+  const { t: translate } = useTranslation();
+  const { data, isFetching } = useClearTaskInstancesDryRun({
+    dagId: dagDetails?.dagId ?? "",
+    options: {
+      enabled: open && Boolean(dagDetails),
+      gcTime: 0,
+      refetchOnMount: "always",
+      refetchOnWindowFocus: false,
+      staleTime: 0,
+    },
+    requestBody: {
+      dag_run_id: dagDetails?.dagRunId ?? "",
+      include_downstream: dagDetails?.downstream,
+      include_future: dagDetails?.future,
+      include_past: dagDetails?.past,
+      include_upstream: dagDetails?.upstream,
+      only_failed: dagDetails?.onlyFailed,
+      task_ids: [[dagDetails?.taskId ?? "", dagDetails?.mapIndex ?? 0]],
+    },
+  });
+
+  const [isReady, setIsReady] = useState(false);
+
+  const handleConfirm = useCallback(() => {
+    if (onConfirm) onConfirm();
+    onClose();
+  }, [onConfirm, onClose]);
+
+  const taskCurrentState = data?.task_instances?.[0]?.state;
+
+  useEffect(() => {
+    if (!isFetching && open && data) {
+      const isInTriggeringState =
+        taskCurrentState === "queued" || taskCurrentState === "scheduled";
+
+      if (!preventRunningTask || !isInTriggeringState) {
+        handleConfirm();
+      } else {
+        setIsReady(true);
+      }
+    }
+  }, [isFetching, data, open, handleConfirm, taskCurrentState, 
preventRunningTask]);
+
+  return (
+    <Dialog.Root lazyMount onOpenChange={onClose} open={open}>
+      <Dialog.Content backdrop>
+        {isFetching ? (
+          // Loading State — keeps the dialog mounted during fetch
+          <VStack align="center" gap={3} justify="center" py={8}>
+            <Spinner size="lg" />
+            <Text color="gray.600" fontSize="md">
+              {translate("common:loadingTaskDetails", "Loading task details…")}

Review Comment:
   Let's use the semantic token like `fg.solid` and then remove the backup 
translation text



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to