Repository: kestra-io/kestra
Issue: Allow creating a new revision of a flow instead of throwing an error
Labels: enhancement
frontend

### Feature description

## Current behavior
When you try to create a flow and this flow already exists, you'll get an error:

![image](https://github.com/kestra-io/kestra/assets/86264395/a2273f34-c24a-4f9f-ab20-fef0b65d539f)

## Feature request
Instead of only giving that error, allow the option to overwrite that flow with a new revision of this flow. 

E.g. a prompt window: "Flow with this ID already exists. Do you want to create a new revision?"

Issue comments:

Pull request review comments:

Patch that resolved the issue:
diff --git a/ui/src/components/inputs/EditorView.vue b/ui/src/components/inputs/EditorView.vue
index 2fb5473c82..5313b0807f 100644
--- a/ui/src/components/inputs/EditorView.vue
+++ b/ui/src/components/inputs/EditorView.vue
@@ -1,5 +1,5 @@
 <script setup>
-    import {computed, getCurrentInstance, onBeforeUnmount, onMounted, ref, watch} from "vue";
+    import {computed, getCurrentInstance, h, onBeforeUnmount, onMounted, ref, watch} from "vue";
     import {useStore} from "vuex"
 
     // Icons
@@ -24,6 +24,7 @@
     import {apiUrl} from "override/utils/route";
     import EditorButtons from "./EditorButtons.vue";
     import Drawer from "../Drawer.vue";
+    import {ElMessageBox} from "element-plus";
 
     const store = useStore();
     const router = getCurrentInstance().appContext.config.globalProperties.$router;
@@ -566,28 +567,48 @@
                 title: t("invalid flow"),
                 message: t("invalid yaml"),
             })
+
             return;
         }
-
+        const overrideFlow = ref(false);
         if (flowErrors.value) {
-            saveAsDraft(flowErrors.value);
-            return;
+            if (props.flowValidation.outdated && props.isCreating) {
+                overrideFlow.value = await ElMessageBox({
+                    title: t("override.title"),
+                    message: () => {
+                        return h("div", null, [
+                            h("p", null, t("override.details")),
+
+                        ])
+                    },
+                    showCancelButton: true,
+                    confirmButtonText: t("ok"),
+                    cancelButtonText: t("cancel"),
+                    center: false,
+                    showClose: false,
+                }).then(() => {
+                    overrideFlow.value = true;
+                    console.log("pop")
+                    return true;
+                }).catch(() => {
+                    return false
+                })
+
+            }
+
+            if (!overrideFlow.value) {
+                saveAsDraft(flowErrors.value);
+
+                return;
+            }
         }
 
-        if (props.isCreating) {
+
+        if (props.isCreating && !overrideFlow.value) {
             await store.dispatch("flow/createFlow", {flow: flowYaml.value})
                 .then((response) => {
                     toast.saved(response.id);
                     store.dispatch("core/isUnsaved", false);
-                    router.push({
-                        name: "flows/update",
-                        params: {
-                            id: flowParsed.value.id,
-                            namespace: flowParsed.value.namespace,
-                            tab: "editor",
-                            tenant: routeParams.tenant
-                        }
-                    });
                 })
         } else {
             await store.dispatch("flow/saveFlow", {flow: flowYaml.value})
@@ -597,6 +618,18 @@
                 })
         }
 
+        if (props.isCreating || overrideFlow.value) {
+            router.push({
+                name: "flows/update",
+                params: {
+                    id: flowParsed.value.id,
+                    namespace: flowParsed.value.namespace,
+                    tab: "editor",
+                    tenant: routeParams.tenant
+                }
+            });
+        }
+
         haveChange.value = false;
         await store.dispatch("flow/validateFlow", {flow: yamlWithNextRevision.value})
     }
diff --git a/ui/src/translations/en.json b/ui/src/translations/en.json
index 0ff9a06246..799c432cb9 100644
--- a/ui/src/translations/en.json
+++ b/ui/src/translations/en.json
@@ -31,9 +31,13 @@
       "create": {
         "title": "Flow already exists",
         "description": "A Flow with the same id / namespace already exists.",
-        "details": "Check the Flows menu for more details about the existing flow."
+        "details": "Saving to override and force the creation of a new revision."
       }
     },
+    "override": {
+      "title": "You are going to override the current flow",
+      "details": "Previous flow will be recoverable through the revisions tab."
+    },
     "is deprecated": "is deprecated",
     "success": "Success",
     "save": "Save",
diff --git a/ui/src/translations/fr.json b/ui/src/translations/fr.json
index 2d59d274b3..c96209abda 100644
--- a/ui/src/translations/fr.json
+++ b/ui/src/translations/fr.json
@@ -30,9 +30,13 @@
       },
       "create": {
         "description": "Un Flow avec le même id / namespace existe déjà.",
-        "details": "Regardez le menu Flows pour plus de détails sur le Flow existant."
+        "details": "Sauvegarder pour l'écraser et forcer la création d'une révision."
       }
     },
+    "override": {
+      "title": "Vous allez écraser le flow actuel",
+      "details": "Vous pourrez toujours le restaurer via les révisions."
+    },
     "is deprecated": "est déprécié(e)",
     "success": "Succès",
     "save": "Enregistrer",


Test patch: