Skip to content

Commit

Permalink
[24.0] Do not save workflow on Run without user confirmation
Browse files Browse the repository at this point in the history
This adds a modal when the user runs a workflow with changes, that asks for user confirmation on whether to proceed without saving changes or to save changes and then proceed. Currently, we _always_ save the workflow when it is run (even if there are no changes at all).

Fixes #17903
  • Loading branch information
ahmedhamidawan committed Apr 4, 2024
1 parent d308444 commit ad6af8d
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 4 deletions.
20 changes: 16 additions & 4 deletions client/src/components/Workflow/Editor/Index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
@onRefactor="onRefactor"
@onShow="hideModal" />
<MessagesModal :title="messageTitle" :message="messageBody" :error="messageIsError" @onHidden="resetMessage" />
<SaveChangesModal :nav-url.sync="navUrl" :show-modal.sync="showSaveChangesModal" @on-proceed="onNavigate" />
<b-modal
v-model="showSaveAsModal"
title="Save As a New Workflow"
Expand Down Expand Up @@ -167,7 +168,7 @@
<script>
import { Toast } from "composables/toast";
import { storeToRefs } from "pinia";
import Vue, { computed, onUnmounted, ref, unref } from "vue";
import Vue, { computed, nextTick, onUnmounted, ref, unref } from "vue";
import { getUntypedWorkflowParameters } from "@/components/Workflow/Editor/modules/parameters";
import { ConfirmDialog } from "@/composables/confirmDialog";
Expand All @@ -192,6 +193,7 @@ import WorkflowLint from "./Lint.vue";
import MessagesModal from "./MessagesModal.vue";
import WorkflowOptions from "./Options.vue";
import RefactorConfirmationModal from "./RefactorConfirmationModal.vue";
import SaveChangesModal from "./SaveChangesModal.vue";
import StateUpgradeModal from "./StateUpgradeModal.vue";
import WorkflowGraph from "./WorkflowGraph.vue";
import MarkdownEditor from "@/components/Markdown/MarkdownEditor.vue";
Expand All @@ -204,6 +206,7 @@ export default {
components: {
MarkdownEditor,
FlexPanel,
SaveChangesModal,
StateUpgradeModal,
ToolPanel,
FormDefault,
Expand Down Expand Up @@ -337,6 +340,8 @@ export default {
showSaveAsModal: false,
transform: { x: 0, y: 0, k: 1 },
graphOffset: { left: 0, top: 0, width: 0, height: 0 },
showSaveChangesModal: false,
navUrl: "",
};
},
computed: {
Expand Down Expand Up @@ -677,14 +682,21 @@ export default {
const runUrl = `/workflows/run?id=${this.id}`;
this.onNavigate(runUrl);
},
async onNavigate(url) {
async onNavigate(url, forceSave = false, ignoreChanges = false) {
if (this.isNewTempWorkflow) {
await this.onCreate();
} else {
await this.onSave(true);
} else if (this.hasChanges && !forceSave && !ignoreChanges) {
// if there are changes, prompt user to save or discard or cancel
this.navUrl = url;
this.showSaveChangesModal = true;
return;
} else if (forceSave) {
// when forceSave is true, save the workflow before navigating
await this.onSave();
}
this.hasChanges = false;
await nextTick();
this.$router.push(url);
},
onSave(hideProgress = false) {
Expand Down
97 changes: 97 additions & 0 deletions client/src/components/Workflow/Editor/SaveChangesModal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<script setup lang="ts">
import { library } from "@fortawesome/fontawesome-svg-core";
import { faSave, faTimes, faTrash } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { BButton, BModal } from "bootstrap-vue";
import { ref } from "vue";
import localize from "@/utils/localization";
library.add(faSave, faTimes, faTrash);
interface Props {
/** Show the save changes modal */
showModal: boolean;
/** The URL to navigate to before saving/ignoring changes */
navUrl: string;
}
const props = withDefaults(defineProps<Props>(), {
showModal: false,
});
const busy = ref(false);
const emit = defineEmits<{
/** Proceed with or without saving the changes */
(e: "on-proceed", url: string, forceSave: boolean, ignoreChanges: boolean): void;
/** Update the nav URL prop */
(e: "update:nav-url", url: string): void;
/** Update the show modal boolean prop */
(e: "update:show-modal", showModal: boolean): void;
}>();
const title = localize("You have unsaved changes. Do you want to save them before proceeding?");
const body = localize(
"Click 'Save' to save your changes and proceed, 'Don't Save' to discard them and proceed, or 'Cancel' to return to the editor."
);
const buttonTitles = {
cancel: localize("Do not run proceed and return to editor"),
dontSave: localize("Discard changes and proceed"),
save: localize("Save changes and proceed"),
};
function closeModal() {
emit("update:show-modal", false);
emit("update:nav-url", "");
}
function dontSave() {
busy.value = true;
emit("on-proceed", props.navUrl, false, true);
}
function saveChanges() {
busy.value = true;
closeModal();
emit("on-proceed", props.navUrl, true, false);
}
</script>

<template>
<BModal :title="title" :visible="props.showModal" @close="closeModal" @hide="closeModal">
<div>
{{ body }}
</div>
<template v-slot:modal-footer>
<BButton
v-b-tooltip.noninteractive.hover
:title="buttonTitles['cancel']"
variant="secondary"
:disabled="busy"
@click="closeModal">
<FontAwesomeIcon :icon="faTimes" />
{{ localize("Cancel") }}
</BButton>
<BButton
v-b-tooltip.noninteractive.hover
:title="buttonTitles['dontSave']"
variant="danger"
:disabled="busy"
@click="dontSave">
<FontAwesomeIcon :icon="faTrash" />
{{ localize("Don't Save") }}
</BButton>
<BButton
v-b-tooltip.noninteractive.hover
:title="buttonTitles['save']"
variant="primary"
:disabled="busy"
@click="saveChanges">
<FontAwesomeIcon :icon="faSave" />
{{ localize("Save") }}
</BButton>
</template>
</BModal>
</template>

0 comments on commit ad6af8d

Please sign in to comment.