diff --git a/airflow/ui/src/components/TriggerDag/TriggerDAGForm.tsx b/airflow/ui/src/components/TriggerDag/TriggerDAGForm.tsx index a11cad61aefd2..8288849ecf1f1 100644 --- a/airflow/ui/src/components/TriggerDag/TriggerDAGForm.tsx +++ b/airflow/ui/src/components/TriggerDag/TriggerDAGForm.tsx @@ -16,11 +16,11 @@ * specific language governing permissions and limitations * under the License. */ -import { Input, Button, Box, Text, Spacer, HStack } from "@chakra-ui/react"; +import { Input, Button, Box, Spacer, HStack, Field } from "@chakra-ui/react"; import { json } from "@codemirror/lang-json"; import { githubLight, githubDark } from "@uiw/codemirror-themes-all"; import CodeMirror from "@uiw/react-codemirror"; -import { useEffect, useMemo, useState } from "react"; +import { useEffect, useState } from "react"; import { useForm, Controller } from "react-hook-form"; import { FiPlay } from "react-icons/fi"; @@ -32,9 +32,9 @@ import { ErrorAlert } from "../ErrorAlert"; import { Accordion } from "../ui"; type TriggerDAGFormProps = { - dagId: string; - onClose: () => void; - open: boolean; + readonly dagId: string; + readonly onClose: () => void; + readonly open: boolean; }; export type DagRunTriggerParams = { @@ -45,26 +45,11 @@ export type DagRunTriggerParams = { note: string; }; -const TriggerDAGForm: React.FC = ({ - dagId, - onClose, - open, -}) => { - const [errors, setErrors] = useState<{ conf?: string; date?: string }>({}); +const TriggerDAGForm = ({ dagId, onClose, open }: TriggerDAGFormProps) => { + const [errors, setErrors] = useState<{ conf?: string; date?: unknown }>({}); const conf = useDagParams(dagId, open); const { error: errorTrigger, isPending, triggerDagRun } = useTrigger(onClose); - const dagRunRequestBody: DagRunTriggerParams = useMemo( - () => ({ - conf, - dagRunId: "", - dataIntervalEnd: "", - dataIntervalStart: "", - note: "", - }), - [conf], - ); - const { control, formState: { isDirty }, @@ -72,14 +57,47 @@ const TriggerDAGForm: React.FC = ({ reset, setValue, watch, - } = useForm({ defaultValues: dagRunRequestBody }); + } = useForm({ + defaultValues: { + conf, + dagRunId: "", + dataIntervalEnd: "", + dataIntervalStart: "", + note: "", + }, + }); + + // Automatically reset form when conf is fetched + useEffect(() => { + if (conf) { + reset({ conf }); + } + }, [conf, reset]); const dataIntervalStart = watch("dataIntervalStart"); const dataIntervalEnd = watch("dataIntervalEnd"); - useEffect(() => { - reset(dagRunRequestBody); - }, [dagRunRequestBody, reset]); + const handleReset = () => { + setErrors({ conf: undefined, date: undefined }); + reset(); + }; + + const onSubmit = (data: DagRunTriggerParams) => { + if (Boolean(data.dataIntervalStart) !== Boolean(data.dataIntervalEnd)) { + setErrors((prev) => ({ + ...prev, + date: { + body: { + detail: + "Either both Data Interval Start and End must be provided, or both must be empty.", + }, + }, + })); + + return; + } + triggerDagRun(dagId, data); + }; const validateAndPrettifyJson = (value: string) => { try { @@ -101,18 +119,6 @@ const TriggerDAGForm: React.FC = ({ } }; - const onSubmit = (data: DagRunTriggerParams) => { - if (Boolean(data.dataIntervalStart) !== Boolean(data.dataIntervalEnd)) { - setErrors((prev) => ({ - ...prev, - date: "Either both Data Interval Start and End must be provided, or both must be empty.", - })); - - return; - } - triggerDagRun(dagId, data); - }; - const validateDates = ( fieldName: "dataIntervalEnd" | "dataIntervalStart", ) => { @@ -136,73 +142,74 @@ const TriggerDAGForm: React.FC = ({ return ( <> - - + Advanced Options - - Data Interval Start Date - ( - validateDates("dataIntervalStart")} - placeholder="yyyy-mm-ddThh:mm" - size="sm" - type="datetime-local" - /> + + + Data Interval Start Date + + validateDates("dataIntervalStart")} + placeholder="yyyy-mm-ddThh:mm" + size="sm" + type="datetime-local" + /> + )} /> - - Data Interval End Date - ( - validateDates("dataIntervalEnd")} - placeholder="yyyy-mm-ddThh:mm" - size="sm" - type="datetime-local" - /> + + + Data Interval End Date + + validateDates("dataIntervalEnd")} + placeholder="yyyy-mm-ddThh:mm" + size="sm" + type="datetime-local" + /> + )} /> - - Run ID - ( - + + Run ID + + )} /> - - Configuration JSON - ( - + + Configuration JSON = ({ extensions={[json()]} height="200px" onBlur={() => { - const prettifiedJson = validateAndPrettifyJson( - field.value, - ); - - field.onChange(prettifiedJson); + field.onChange(validateAndPrettifyJson(field.value)); }} style={{ border: "1px solid #CBD5E0", borderRadius: "8px", outline: "none", padding: "2px", + width: "100%", }} theme={colorMode === "dark" ? githubDark : githubLight} /> - {Boolean(errors.conf) && ( - - {errors.conf} - - )} - + {Boolean(errors.conf) ? ( + {errors.conf} + ) : undefined} + )} /> - - Dag Run Notes - ( - + + Dag Run Notes + + )} /> - {Boolean(errors.date) && ( - - {errors.date} - - )} + {isDirty ? ( - ) : undefined} diff --git a/airflow/ui/src/queries/useTrigger.ts b/airflow/ui/src/queries/useTrigger.ts index 8ee004e7d3f06..b454d0176a78f 100644 --- a/airflow/ui/src/queries/useTrigger.ts +++ b/airflow/ui/src/queries/useTrigger.ts @@ -43,15 +43,14 @@ export const useTrigger = (onClose: () => void) => { queryKeys.map((key) => queryClient.invalidateQueries({ queryKey: [key] }), ), - ); - - toaster.create({ - description: "DAG run has been successfully triggered.", - title: "DAG Run Request Submitted", - type: "success", + ).then(() => { + toaster.create({ + description: "DAG run has been successfully triggered.", + title: "DAG Run Request Submitted", + type: "success", + }); + onClose(); }); - - onClose(); }; const onError = (_error: unknown) => {