mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-25 04:34:06 -08:00
feat(editor): Workflow history [WIP]- Add restore and clone into new workflow actions (no-changelog) (#7359)
This commit is contained in:
parent
77643e5ccb
commit
b3247e5935
|
@ -139,6 +139,16 @@
|
|||
<DebugPaywallModal data-test-id="debug-paywall-modal" :modalName="modalName" :data="data" />
|
||||
</template>
|
||||
</ModalRoot>
|
||||
|
||||
<ModalRoot :name="WORKFLOW_HISTORY_VERSION_RESTORE">
|
||||
<template #default="{ modalName, data }">
|
||||
<WorkflowHistoryVersionRestoreModal
|
||||
data-test-id="workflow-history-version-restore-modal"
|
||||
:modalName="modalName"
|
||||
:data="data"
|
||||
/>
|
||||
</template>
|
||||
</ModalRoot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -172,6 +182,7 @@ import {
|
|||
EXTERNAL_SECRETS_PROVIDER_MODAL_KEY,
|
||||
DEBUG_PAYWALL_MODAL_KEY,
|
||||
MFA_SETUP_MODAL_KEY,
|
||||
WORKFLOW_HISTORY_VERSION_RESTORE,
|
||||
} from '@/constants';
|
||||
|
||||
import AboutModal from './AboutModal.vue';
|
||||
|
@ -202,6 +213,7 @@ import SourceControlPushModal from '@/components/SourceControlPushModal.ee.vue';
|
|||
import SourceControlPullModal from '@/components/SourceControlPullModal.ee.vue';
|
||||
import ExternalSecretsProviderModal from '@/components/ExternalSecretsProviderModal.ee.vue';
|
||||
import DebugPaywallModal from '@/components/DebugPaywallModal.vue';
|
||||
import WorkflowHistoryVersionRestoreModal from '@/components/WorkflowHistory/WorkflowHistoryVersionRestoreModal.vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Modals',
|
||||
|
@ -234,6 +246,7 @@ export default defineComponent({
|
|||
ExternalSecretsProviderModal,
|
||||
DebugPaywallModal,
|
||||
MfaSetupModal,
|
||||
WorkflowHistoryVersionRestoreModal,
|
||||
},
|
||||
data: () => ({
|
||||
CHAT_EMBED_MODAL_KEY,
|
||||
|
@ -263,6 +276,7 @@ export default defineComponent({
|
|||
EXTERNAL_SECRETS_PROVIDER_MODAL_KEY,
|
||||
DEBUG_PAYWALL_MODAL_KEY,
|
||||
MFA_SETUP_MODAL_KEY,
|
||||
WORKFLOW_HISTORY_VERSION_RESTORE,
|
||||
}),
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -24,7 +24,11 @@ const props = defineProps<{
|
|||
const emit = defineEmits<{
|
||||
(
|
||||
event: 'action',
|
||||
value: { action: WorkflowHistoryActionTypes[number]; id: WorkflowVersionId },
|
||||
value: {
|
||||
action: WorkflowHistoryActionTypes[number];
|
||||
id: WorkflowVersionId;
|
||||
data: { formattedCreatedAt: string };
|
||||
},
|
||||
): void;
|
||||
(event: 'preview', value: { event: MouseEvent; id: WorkflowVersionId }): void;
|
||||
(event: 'loadMore', value: WorkflowHistoryRequestParams): void;
|
||||
|
@ -67,12 +71,14 @@ const observeElement = (element: Element) => {
|
|||
const onAction = ({
|
||||
action,
|
||||
id,
|
||||
data,
|
||||
}: {
|
||||
action: WorkflowHistoryActionTypes[number];
|
||||
id: WorkflowVersionId;
|
||||
data: { formattedCreatedAt: string };
|
||||
}) => {
|
||||
shouldAutoScroll.value = false;
|
||||
emit('action', { action, id });
|
||||
emit('action', { action, id, data });
|
||||
};
|
||||
|
||||
const onPreview = ({ event, id }: { event: MouseEvent; id: WorkflowVersionId }) => {
|
||||
|
|
|
@ -18,7 +18,11 @@ const props = defineProps<{
|
|||
const emit = defineEmits<{
|
||||
(
|
||||
event: 'action',
|
||||
value: { action: WorkflowHistoryActionTypes[number]; id: WorkflowVersionId },
|
||||
value: {
|
||||
action: WorkflowHistoryActionTypes[number];
|
||||
id: WorkflowVersionId;
|
||||
data: { formattedCreatedAt: string };
|
||||
},
|
||||
): void;
|
||||
(event: 'preview', value: { event: MouseEvent; id: WorkflowVersionId }): void;
|
||||
(event: 'mounted', value: { index: number; offsetTop: number; isActive: boolean }): void;
|
||||
|
@ -31,11 +35,11 @@ const itemElement = ref<HTMLElement | null>(null);
|
|||
const authorElement = ref<HTMLElement | null>(null);
|
||||
const isAuthorElementTruncated = ref(false);
|
||||
|
||||
const formattedCreatedAtDate = computed<string>(() => {
|
||||
const formattedCreatedAt = computed<string>(() => {
|
||||
const currentYear = new Date().getFullYear().toString();
|
||||
const [date, time] = dateformat(
|
||||
props.item.createdAt,
|
||||
`${props.item.createdAt.startsWith(currentYear) ? '' : 'yyyy '} mmm d"#"HH:MM`,
|
||||
`${props.item.createdAt.startsWith(currentYear) ? '' : 'yyyy '}mmm d"#"HH:MM`,
|
||||
).split('#');
|
||||
|
||||
return i18n.baseText('workflowHistory.item.createdAt', { interpolate: { date, time } });
|
||||
|
@ -60,7 +64,11 @@ const idLabel = computed<string>(() =>
|
|||
);
|
||||
|
||||
const onAction = (action: WorkflowHistoryActionTypes[number]) => {
|
||||
emit('action', { action, id: props.item.versionId });
|
||||
emit('action', {
|
||||
action,
|
||||
id: props.item.versionId,
|
||||
data: { formattedCreatedAt: formattedCreatedAt.value },
|
||||
});
|
||||
};
|
||||
|
||||
const onVisibleChange = (visible: boolean) => {
|
||||
|
@ -92,7 +100,7 @@ onMounted(() => {
|
|||
}"
|
||||
>
|
||||
<p @click="onItemClick">
|
||||
<time :datetime="item.createdAt">{{ formattedCreatedAtDate }}</time>
|
||||
<time :datetime="item.createdAt">{{ formattedCreatedAt }}</time>
|
||||
<n8n-tooltip placement="right-end" :disabled="authors.size < 2 && !isAuthorElementTruncated">
|
||||
<template #content>{{ props.item.authors }}</template>
|
||||
<span ref="authorElement">{{ authors.label }}</span>
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
<script lang="ts" setup>
|
||||
import { useI18n } from '@/composables';
|
||||
import Modal from '@/components/Modal.vue';
|
||||
import { useUIStore } from '@/stores';
|
||||
|
||||
const props = defineProps<{
|
||||
modalName: string;
|
||||
data: {
|
||||
isWorkflowActivated: boolean;
|
||||
formattedCreatedAt: string;
|
||||
beforeClose: () => void;
|
||||
buttons: Array<{
|
||||
text: string;
|
||||
type: string;
|
||||
action: () => void;
|
||||
}>;
|
||||
};
|
||||
}>();
|
||||
|
||||
const i18n = useI18n();
|
||||
const uiStore = useUIStore();
|
||||
|
||||
const closeModal = () => {
|
||||
uiStore.closeModal(props.modalName);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal width="500px" :name="props.modalName" :before-close="props.data.beforeClose">
|
||||
<template #header>
|
||||
<n8n-heading tag="h2" size="xlarge">
|
||||
{{ i18n.baseText('workflowHistory.action.restore.modal.title') }}
|
||||
</n8n-heading>
|
||||
</template>
|
||||
<template #content>
|
||||
<div>
|
||||
<n8n-text>
|
||||
<i18n-t keypath="workflowHistory.action.restore.modal.subtitle" tag="span">
|
||||
<template #date>
|
||||
<strong>{{ props.data.formattedCreatedAt }}</strong>
|
||||
</template>
|
||||
</i18n-t>
|
||||
<br />
|
||||
<br />
|
||||
<i18n-t
|
||||
v-if="props.data.isWorkflowActivated"
|
||||
keypath="workflowHistory.action.restore.modal.text"
|
||||
tag="span"
|
||||
>
|
||||
<template #buttonText>
|
||||
“{{
|
||||
i18n.baseText('workflowHistory.action.restore.modal.button.deactivateAndRestore')
|
||||
}}”
|
||||
</template>
|
||||
</i18n-t>
|
||||
</n8n-text>
|
||||
</div>
|
||||
</template>
|
||||
<template #footer>
|
||||
<div :class="$style.footer">
|
||||
<n8n-button
|
||||
v-for="(button, index) in props.data.buttons"
|
||||
size="medium"
|
||||
:key="index"
|
||||
:type="button.type"
|
||||
@click="
|
||||
() => {
|
||||
button.action();
|
||||
closeModal();
|
||||
}
|
||||
"
|
||||
>
|
||||
{{ button.text }}
|
||||
</n8n-button>
|
||||
</div>
|
||||
</template>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<style module lang="scss">
|
||||
.footer {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
|
||||
button {
|
||||
margin-left: var(--spacing-2xs);
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -91,10 +91,10 @@ describe('WorkflowHistoryList', () => {
|
|||
await userEvent.click(within(listItem).getByText(/ID: /));
|
||||
expect(emitted().preview).toEqual([
|
||||
[
|
||||
expect.objectContaining({
|
||||
{
|
||||
id: items[items.length - 1].versionId,
|
||||
event: expect.any(MouseEvent),
|
||||
}),
|
||||
},
|
||||
],
|
||||
]);
|
||||
|
||||
|
@ -141,7 +141,15 @@ describe('WorkflowHistoryList', () => {
|
|||
expect(actionsDropdown).toBeInTheDocument();
|
||||
|
||||
await userEvent.click(within(actionsDropdown).getByTestId(`action-${action}`));
|
||||
expect(emitted().action).toEqual([[{ action, id: items[index].versionId }]]);
|
||||
expect(emitted().action).toEqual([
|
||||
[
|
||||
{
|
||||
action,
|
||||
id: items[index].versionId,
|
||||
data: { formattedCreatedAt: expect.any(String) },
|
||||
},
|
||||
],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should show upgrade message', async () => {
|
||||
|
|
|
@ -70,7 +70,9 @@ describe('WorkflowHistoryListItem', () => {
|
|||
expect(getByTestId('action-toggle-dropdown')).toBeInTheDocument();
|
||||
|
||||
await userEvent.click(getByTestId(`action-${action}`));
|
||||
expect(emitted().action).toEqual([[{ action, id: item.versionId }]]);
|
||||
expect(emitted().action).toEqual([
|
||||
[{ action, id: item.versionId, data: { formattedCreatedAt: expect.any(String) } }],
|
||||
]);
|
||||
|
||||
expect(queryByText(/Latest saved/)).not.toBeInTheDocument();
|
||||
expect(emitted().mounted).toEqual([[{ index: 2, isActive: true, offsetTop: 0 }]]);
|
||||
|
|
|
@ -52,6 +52,7 @@ export const SOURCE_CONTROL_PUSH_MODAL_KEY = 'sourceControlPush';
|
|||
export const SOURCE_CONTROL_PULL_MODAL_KEY = 'sourceControlPull';
|
||||
export const DEBUG_PAYWALL_MODAL_KEY = 'debugPaywall';
|
||||
export const MFA_SETUP_MODAL_KEY = 'mfaSetup';
|
||||
export const WORKFLOW_HISTORY_VERSION_RESTORE = 'workflowHistoryVersionRestore';
|
||||
|
||||
export const EXTERNAL_SECRETS_PROVIDER_MODAL_KEY = 'externalSecretsProvider';
|
||||
|
||||
|
|
|
@ -1860,6 +1860,15 @@
|
|||
"workflowHistory.limit": "Version history is limited to {evaluatedPruneTime} days",
|
||||
"workflowHistory.upgrade": "{link} to activate full history",
|
||||
"workflowHistory.upgrade.link": "Upgrade plan",
|
||||
"workflowHistory.action.error.title": "Failed to {action}",
|
||||
"workflowHistory.action.restore.modal.title": "Restore previous workflow version?",
|
||||
"workflowHistory.action.restore.modal.subtitle": "Your workflow will revert to the version from {date}",
|
||||
"workflowHistory.action.restore.modal.text": "Your workflow is currently active, so production executions will immediately start using the restored version. If you'd like to deactivate it before restoring, click {buttonText}.",
|
||||
"workflowHistory.action.restore.modal.button.deactivateAndRestore": "Deactivate and restore",
|
||||
"workflowHistory.action.restore.modal.button.restore": "Restore",
|
||||
"workflowHistory.action.restore.modal.button.cancel": "Cancel",
|
||||
"workflowHistory.action.restore.success.title": "Successfully restored workflow version",
|
||||
"workflowHistory.action.clone.success.title": "Successfully cloned workflow version",
|
||||
"workflows.heading": "Workflows",
|
||||
"workflows.add": "Add Workflow",
|
||||
"workflows.menu.my": "My workflows",
|
||||
|
|
|
@ -36,6 +36,7 @@ import {
|
|||
SOURCE_CONTROL_PULL_MODAL_KEY,
|
||||
DEBUG_PAYWALL_MODAL_KEY,
|
||||
N8N_PRICING_PAGE_URL,
|
||||
WORKFLOW_HISTORY_VERSION_RESTORE,
|
||||
} from '@/constants';
|
||||
import type {
|
||||
CloudUpdateLinkSourceType,
|
||||
|
@ -157,6 +158,9 @@ export const useUIStore = defineStore(STORES.UI, {
|
|||
[DEBUG_PAYWALL_MODAL_KEY]: {
|
||||
open: false,
|
||||
},
|
||||
[WORKFLOW_HISTORY_VERSION_RESTORE]: {
|
||||
open: false,
|
||||
},
|
||||
},
|
||||
modalStack: [],
|
||||
sidebarMenuCollapsed: true,
|
||||
|
|
|
@ -1,17 +1,22 @@
|
|||
import { computed } from 'vue';
|
||||
import { defineStore } from 'pinia';
|
||||
import * as whApi from '@/api/workflowHistory';
|
||||
import { useRootStore } from '@/stores/n8nRoot.store';
|
||||
import { useSettingsStore } from '@/stores/settings.store';
|
||||
import { saveAs } from 'file-saver';
|
||||
import type { IWorkflowDataUpdate } from '@/Interface';
|
||||
import type {
|
||||
WorkflowHistory,
|
||||
WorkflowVersion,
|
||||
WorkflowHistoryRequestParams,
|
||||
WorkflowVersionId,
|
||||
} from '@/types/workflowHistory';
|
||||
import * as whApi from '@/api/workflowHistory';
|
||||
import { useRootStore } from '@/stores/n8nRoot.store';
|
||||
import { useSettingsStore } from '@/stores/settings.store';
|
||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||
|
||||
export const useWorkflowHistoryStore = defineStore('workflowHistory', () => {
|
||||
const rootStore = useRootStore();
|
||||
const settingsStore = useSettingsStore();
|
||||
const workflowsStore = useWorkflowsStore();
|
||||
|
||||
const licensePruneTime = computed(() => settingsStore.settings.workflowHistory.licensePruneTime);
|
||||
const pruneTime = computed(() => settingsStore.settings.workflowHistory.pruneTime);
|
||||
|
@ -40,9 +45,65 @@ export const useWorkflowHistoryStore = defineStore('workflowHistory', () => {
|
|||
return null;
|
||||
});
|
||||
|
||||
const downloadVersion = async (workflowId: string, workflowVersionId: WorkflowVersionId) => {
|
||||
const [workflow, workflowVersion] = await Promise.all([
|
||||
workflowsStore.fetchWorkflow(workflowId),
|
||||
getWorkflowVersion(workflowId, workflowVersionId),
|
||||
]);
|
||||
if (workflow && workflowVersion) {
|
||||
const { connections, nodes } = workflowVersion;
|
||||
const blob = new Blob([JSON.stringify({ ...workflow, nodes, connections }, null, 2)], {
|
||||
type: 'application/json;charset=utf-8',
|
||||
});
|
||||
saveAs(blob, `${workflow.name}-${workflowVersionId}.json`);
|
||||
}
|
||||
};
|
||||
|
||||
const cloneIntoNewWorkflow = async (
|
||||
workflowId: string,
|
||||
workflowVersionId: string,
|
||||
data: { formattedCreatedAt: string },
|
||||
) => {
|
||||
const [workflow, workflowVersion] = await Promise.all([
|
||||
workflowsStore.fetchWorkflow(workflowId),
|
||||
getWorkflowVersion(workflowId, workflowVersionId),
|
||||
]);
|
||||
if (workflow && workflowVersion) {
|
||||
const { connections, nodes } = workflowVersion;
|
||||
const { name } = workflow;
|
||||
const newWorkflowData: IWorkflowDataUpdate = {
|
||||
nodes,
|
||||
connections,
|
||||
name: `${name} (${data.formattedCreatedAt})`,
|
||||
};
|
||||
await workflowsStore.createNewWorkflow(newWorkflowData);
|
||||
}
|
||||
};
|
||||
|
||||
const restoreWorkflow = async (
|
||||
workflowId: string,
|
||||
workflowVersionId: string,
|
||||
shouldDeactivate: boolean,
|
||||
) => {
|
||||
const workflowVersion = await getWorkflowVersion(workflowId, workflowVersionId);
|
||||
if (workflowVersion?.nodes && workflowVersion?.connections) {
|
||||
const { connections, nodes } = workflowVersion;
|
||||
const updateData: IWorkflowDataUpdate = { connections, nodes };
|
||||
|
||||
if (shouldDeactivate) {
|
||||
updateData.active = false;
|
||||
}
|
||||
|
||||
await workflowsStore.updateWorkflow(workflowId, updateData, true);
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
getWorkflowHistory,
|
||||
getWorkflowVersion,
|
||||
downloadVersion,
|
||||
cloneIntoNewWorkflow,
|
||||
restoreWorkflow,
|
||||
evaluatedPruneTime,
|
||||
shouldUpgrade,
|
||||
};
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
<script setup lang="ts">
|
||||
import { saveAs } from 'file-saver';
|
||||
import { onBeforeMount, ref, watchEffect, computed } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import type { IWorkflowDb } from '@/Interface';
|
||||
import { VIEWS } from '@/constants';
|
||||
import { useI18n } from '@/composables';
|
||||
import { VIEWS, WORKFLOW_HISTORY_VERSION_RESTORE } from '@/constants';
|
||||
import { useI18n, useToast } from '@/composables';
|
||||
import type {
|
||||
WorkflowHistoryActionTypes,
|
||||
WorkflowVersionId,
|
||||
|
@ -22,6 +21,12 @@ type WorkflowHistoryActionRecord = {
|
|||
[K in Uppercase<WorkflowHistoryActionTypes[number]>]: Lowercase<K>;
|
||||
};
|
||||
|
||||
const enum WorkflowHistoryVersionRestoreModalActions {
|
||||
restore = 'restore',
|
||||
deactivateAndRestore = 'deactivateAndRestore',
|
||||
cancel = 'cancel',
|
||||
}
|
||||
|
||||
const workflowHistoryActionTypes: WorkflowHistoryActionTypes = [
|
||||
'restore',
|
||||
'clone',
|
||||
|
@ -36,6 +41,7 @@ const WORKFLOW_HISTORY_ACTIONS = workflowHistoryActionTypes.reduce(
|
|||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const i18n = useI18n();
|
||||
const toast = useToast();
|
||||
const workflowHistoryStore = useWorkflowHistoryStore();
|
||||
const uiStore = useUIStore();
|
||||
const workflowsStore = useWorkflowsStore();
|
||||
|
@ -102,42 +108,107 @@ const openInNewTab = (id: WorkflowVersionId) => {
|
|||
window.open(href, '_blank');
|
||||
};
|
||||
|
||||
const downloadVersion = async (id: WorkflowVersionId) => {
|
||||
const workflowVersion = await workflowHistoryStore.getWorkflowVersion(
|
||||
route.params.workflowId,
|
||||
id,
|
||||
);
|
||||
if (workflowVersion?.nodes && workflowVersion?.connections && activeWorkflow.value) {
|
||||
const { connections, nodes } = workflowVersion;
|
||||
const blob = new Blob(
|
||||
[JSON.stringify({ ...activeWorkflow.value, nodes, connections }, null, 2)],
|
||||
const openRestorationModal = async (
|
||||
isWorkflowActivated: boolean,
|
||||
formattedCreatedAt: string,
|
||||
): Promise<WorkflowHistoryVersionRestoreModalActions> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const buttons = [
|
||||
{
|
||||
type: 'application/json;charset=utf-8',
|
||||
text: i18n.baseText('workflowHistory.action.restore.modal.button.cancel'),
|
||||
type: 'tertiary',
|
||||
action: () => {
|
||||
resolve(WorkflowHistoryVersionRestoreModalActions.cancel);
|
||||
},
|
||||
},
|
||||
);
|
||||
saveAs(
|
||||
blob,
|
||||
`${activeWorkflow.value.name.replace(/[^a-zA-Z0-9]/gi, '_')}-${
|
||||
workflowVersion.versionId
|
||||
}.json`,
|
||||
);
|
||||
}
|
||||
];
|
||||
|
||||
if (isWorkflowActivated) {
|
||||
buttons.push({
|
||||
text: i18n.baseText('workflowHistory.action.restore.modal.button.deactivateAndRestore'),
|
||||
type: 'tertiary',
|
||||
action: () => {
|
||||
resolve(WorkflowHistoryVersionRestoreModalActions.deactivateAndRestore);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
buttons.push({
|
||||
text: i18n.baseText('workflowHistory.action.restore.modal.button.restore'),
|
||||
type: 'primary',
|
||||
action: () => {
|
||||
resolve(WorkflowHistoryVersionRestoreModalActions.restore);
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
uiStore.openModalWithData({
|
||||
name: WORKFLOW_HISTORY_VERSION_RESTORE,
|
||||
data: {
|
||||
beforeClose: () => {
|
||||
resolve(WorkflowHistoryVersionRestoreModalActions.cancel);
|
||||
},
|
||||
isWorkflowActivated,
|
||||
formattedCreatedAt,
|
||||
buttons,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const onAction = async ({
|
||||
action,
|
||||
id,
|
||||
data,
|
||||
}: {
|
||||
action: WorkflowHistoryActionTypes[number];
|
||||
id: WorkflowVersionId;
|
||||
data: { formattedCreatedAt: string };
|
||||
}) => {
|
||||
switch (action) {
|
||||
case WORKFLOW_HISTORY_ACTIONS.OPEN:
|
||||
openInNewTab(id);
|
||||
break;
|
||||
case WORKFLOW_HISTORY_ACTIONS.DOWNLOAD:
|
||||
await downloadVersion(id);
|
||||
break;
|
||||
try {
|
||||
switch (action) {
|
||||
case WORKFLOW_HISTORY_ACTIONS.OPEN:
|
||||
openInNewTab(id);
|
||||
break;
|
||||
case WORKFLOW_HISTORY_ACTIONS.DOWNLOAD:
|
||||
await workflowHistoryStore.downloadVersion(route.params.workflowId, id);
|
||||
break;
|
||||
case WORKFLOW_HISTORY_ACTIONS.CLONE:
|
||||
await workflowHistoryStore.cloneIntoNewWorkflow(route.params.workflowId, id, data);
|
||||
toast.showMessage({
|
||||
title: i18n.baseText('workflowHistory.action.clone.success.title'),
|
||||
type: 'success',
|
||||
});
|
||||
break;
|
||||
case WORKFLOW_HISTORY_ACTIONS.RESTORE:
|
||||
const workflow = await workflowsStore.fetchWorkflow(route.params.workflowId);
|
||||
const modalAction = await openRestorationModal(workflow.active, data.formattedCreatedAt);
|
||||
if (modalAction === WorkflowHistoryVersionRestoreModalActions.cancel) {
|
||||
break;
|
||||
}
|
||||
await workflowHistoryStore.restoreWorkflow(
|
||||
route.params.workflowId,
|
||||
id,
|
||||
modalAction === WorkflowHistoryVersionRestoreModalActions.deactivateAndRestore,
|
||||
);
|
||||
toast.showMessage({
|
||||
title: i18n.baseText('workflowHistory.action.restore.success.title'),
|
||||
type: 'success',
|
||||
});
|
||||
break;
|
||||
}
|
||||
} catch (error) {
|
||||
toast.showError(
|
||||
error,
|
||||
i18n.baseText('workflowHistory.action.error.title', {
|
||||
interpolate: {
|
||||
action: i18n.baseText(`workflowHistory.item.actions.${action}`).toLowerCase(),
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -161,10 +232,11 @@ const onUpgrade = () => {
|
|||
|
||||
watchEffect(async () => {
|
||||
if (route.params.versionId) {
|
||||
const workflowVersion = await workflowHistoryStore.getWorkflowVersion(
|
||||
route.params.workflowId,
|
||||
route.params.versionId,
|
||||
);
|
||||
const [workflow, workflowVersion] = await Promise.all([
|
||||
workflowsStore.fetchWorkflow(route.params.workflowId),
|
||||
workflowHistoryStore.getWorkflowVersion(route.params.workflowId, route.params.versionId),
|
||||
]);
|
||||
activeWorkflow.value = workflow;
|
||||
activeWorkflowVersion.value = workflowVersion;
|
||||
}
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue