feat(editor): Workflow history [WIP]- Create workflow history item preview component (no-changelog) (#7378)
Co-authored-by: Giulio Andreini <g.andreini@gmail.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { onBeforeMount, ref, watchEffect, computed } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import type { IWorkflowDb } from '@/Interface';
|
||||
import type { IWorkflowDb, UserAction } from '@/Interface';
|
||||
import { VIEWS, WORKFLOW_HISTORY_VERSION_RESTORE } from '@/constants';
|
||||
import { useI18n, useToast } from '@/composables';
|
||||
import type {
|
||||
@@ -46,6 +46,7 @@ const workflowHistoryStore = useWorkflowHistoryStore();
|
||||
const uiStore = useUIStore();
|
||||
const workflowsStore = useWorkflowsStore();
|
||||
|
||||
const canRender = ref(true);
|
||||
const isListLoading = ref(true);
|
||||
const requestNumberOfItems = ref(20);
|
||||
const lastReceivedItemsLength = ref(0);
|
||||
@@ -58,16 +59,13 @@ const editorRoute = computed(() => ({
|
||||
const activeWorkflow = ref<IWorkflowDb | null>(null);
|
||||
const workflowHistory = ref<WorkflowHistory[]>([]);
|
||||
const activeWorkflowVersion = ref<WorkflowVersion | null>(null);
|
||||
const activeWorkflowVersionPreview = computed<IWorkflowDb | null>(() => {
|
||||
if (activeWorkflowVersion.value && activeWorkflow.value) {
|
||||
return {
|
||||
...activeWorkflow.value,
|
||||
nodes: activeWorkflowVersion.value.nodes,
|
||||
connections: activeWorkflowVersion.value.connections,
|
||||
};
|
||||
}
|
||||
return null;
|
||||
});
|
||||
const actions = computed<UserAction[]>(() =>
|
||||
workflowHistoryActionTypes.map((value) => ({
|
||||
label: i18n.baseText(`workflowHistory.item.actions.${value}`),
|
||||
disabled: false,
|
||||
value,
|
||||
})),
|
||||
);
|
||||
|
||||
const loadMore = async (queryParams: WorkflowHistoryRequestParams) => {
|
||||
const history = await workflowHistoryStore.getWorkflowHistory(
|
||||
@@ -75,25 +73,30 @@ const loadMore = async (queryParams: WorkflowHistoryRequestParams) => {
|
||||
queryParams,
|
||||
);
|
||||
lastReceivedItemsLength.value = history.length;
|
||||
workflowHistory.value.push(...history);
|
||||
workflowHistory.value = workflowHistory.value.concat(history);
|
||||
};
|
||||
|
||||
onBeforeMount(async () => {
|
||||
const [workflow] = await Promise.all([
|
||||
workflowsStore.fetchWorkflow(route.params.workflowId),
|
||||
loadMore({ take: requestNumberOfItems.value }),
|
||||
]);
|
||||
activeWorkflow.value = workflow;
|
||||
isListLoading.value = false;
|
||||
try {
|
||||
const [workflow] = await Promise.all([
|
||||
workflowsStore.fetchWorkflow(route.params.workflowId),
|
||||
loadMore({ take: requestNumberOfItems.value }),
|
||||
]);
|
||||
activeWorkflow.value = workflow;
|
||||
isListLoading.value = false;
|
||||
|
||||
if (!route.params.versionId && workflowHistory.value.length) {
|
||||
await router.replace({
|
||||
name: VIEWS.WORKFLOW_HISTORY,
|
||||
params: {
|
||||
workflowId: route.params.workflowId,
|
||||
versionId: workflowHistory.value[0].versionId,
|
||||
},
|
||||
});
|
||||
if (!route.params.versionId && workflowHistory.value.length) {
|
||||
await router.replace({
|
||||
name: VIEWS.WORKFLOW_HISTORY,
|
||||
params: {
|
||||
workflowId: route.params.workflowId,
|
||||
versionId: workflowHistory.value[0].versionId,
|
||||
},
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
canRender.value = false;
|
||||
toast.showError(error, i18n.baseText('workflowHistory.title'));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -174,7 +177,7 @@ const onAction = async ({
|
||||
openInNewTab(id);
|
||||
break;
|
||||
case WORKFLOW_HISTORY_ACTIONS.DOWNLOAD:
|
||||
await workflowHistoryStore.downloadVersion(route.params.workflowId, id);
|
||||
await workflowHistoryStore.downloadVersion(route.params.workflowId, id, data);
|
||||
break;
|
||||
case WORKFLOW_HISTORY_ACTIONS.CLONE:
|
||||
await workflowHistoryStore.cloneIntoNewWorkflow(route.params.workflowId, id, data);
|
||||
@@ -194,6 +197,10 @@ const onAction = async ({
|
||||
id,
|
||||
modalAction === WorkflowHistoryVersionRestoreModalActions.deactivateAndRestore,
|
||||
);
|
||||
const history = await workflowHistoryStore.getWorkflowHistory(route.params.workflowId, {
|
||||
take: 1,
|
||||
});
|
||||
workflowHistory.value = history.concat(workflowHistory.value);
|
||||
toast.showMessage({
|
||||
title: i18n.baseText('workflowHistory.action.restore.success.title'),
|
||||
type: 'success',
|
||||
@@ -231,13 +238,26 @@ const onUpgrade = () => {
|
||||
};
|
||||
|
||||
watchEffect(async () => {
|
||||
if (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;
|
||||
if (!route.params.versionId) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
activeWorkflowVersion.value = await workflowHistoryStore.getWorkflowVersion(
|
||||
route.params.workflowId,
|
||||
route.params.versionId,
|
||||
);
|
||||
} catch (error) {
|
||||
toast.showError(
|
||||
new Error(`${error.message} "${route.params.versionId}" `),
|
||||
i18n.baseText('workflowHistory.title'),
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
activeWorkflow.value = await workflowsStore.fetchWorkflow(route.params.workflowId);
|
||||
} catch (error) {
|
||||
canRender.value = false;
|
||||
toast.showError(error, i18n.baseText('workflowHistory.title'));
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -254,15 +274,13 @@ watchEffect(async () => {
|
||||
<n8n-button type="tertiary" icon="times" size="small" text square />
|
||||
</router-link>
|
||||
</div>
|
||||
<div :class="$style.contentComponentWrapper">
|
||||
<workflow-history-content :workflow-version="activeWorkflowVersionPreview" />
|
||||
</div>
|
||||
<div :class="$style.listComponentWrapper">
|
||||
<workflow-history-list
|
||||
v-if="canRender"
|
||||
:items="workflowHistory"
|
||||
:lastReceivedItemsLength="lastReceivedItemsLength"
|
||||
:activeItem="activeWorkflowVersion"
|
||||
:actionTypes="workflowHistoryActionTypes"
|
||||
:actions="actions"
|
||||
:requestNumberOfItems="requestNumberOfItems"
|
||||
:shouldUpgrade="workflowHistoryStore.shouldUpgrade"
|
||||
:evaluatedPruneTime="workflowHistoryStore.evaluatedPruneTime"
|
||||
@@ -273,6 +291,16 @@ watchEffect(async () => {
|
||||
@upgrade="onUpgrade"
|
||||
/>
|
||||
</div>
|
||||
<div :class="$style.contentComponentWrapper">
|
||||
<workflow-history-content
|
||||
v-if="canRender"
|
||||
:workflow="activeWorkflow"
|
||||
:workflow-version="activeWorkflowVersion"
|
||||
:actions="actions"
|
||||
:isListLoading="isListLoading"
|
||||
@action="onAction"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<style module lang="scss">
|
||||
@@ -308,13 +336,11 @@ watchEffect(async () => {
|
||||
.contentComponentWrapper {
|
||||
grid-area: content;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.listComponentWrapper {
|
||||
grid-area: list;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
|
||||
@@ -9,12 +9,14 @@ import { createComponentRenderer } from '@/__tests__/render';
|
||||
import { SETTINGS_STORE_DEFAULT_STATE } from '@/__tests__/utils';
|
||||
import WorkflowHistoryPage from '@/views/WorkflowHistory.vue';
|
||||
import { useWorkflowHistoryStore } from '@/stores/workflowHistory.store';
|
||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||
import { STORES, VIEWS } from '@/constants';
|
||||
import {
|
||||
workflowHistoryDataFactory,
|
||||
workflowVersionDataFactory,
|
||||
} from '@/stores/__tests__/utils/workflowHistoryTestUtils';
|
||||
import type { WorkflowVersion } from '@/types/workflowHistory';
|
||||
import type { IWorkflowDb } from '@/Interface';
|
||||
|
||||
vi.mock('vue-router', () => {
|
||||
const params = {};
|
||||
@@ -63,6 +65,7 @@ let pinia: ReturnType<typeof createTestingPinia>;
|
||||
let router: ReturnType<typeof useRouter>;
|
||||
let route: ReturnType<typeof useRoute>;
|
||||
let workflowHistoryStore: ReturnType<typeof useWorkflowHistoryStore>;
|
||||
let workflowsStore: ReturnType<typeof useWorkflowsStore>;
|
||||
let windowOpenSpy: SpyInstance;
|
||||
|
||||
describe('WorkflowHistory', () => {
|
||||
@@ -73,9 +76,11 @@ describe('WorkflowHistory', () => {
|
||||
},
|
||||
});
|
||||
workflowHistoryStore = useWorkflowHistoryStore();
|
||||
workflowsStore = useWorkflowsStore();
|
||||
route = useRoute();
|
||||
router = useRouter();
|
||||
|
||||
vi.spyOn(workflowsStore, 'fetchWorkflow').mockResolvedValue({} as IWorkflowDb);
|
||||
vi.spyOn(workflowHistoryStore, 'getWorkflowHistory').mockResolvedValue(historyData);
|
||||
vi.spyOn(workflowHistoryStore, 'getWorkflowVersion').mockResolvedValue(versionData);
|
||||
windowOpenSpy = vi.spyOn(window, 'open').mockImplementation(() => null);
|
||||
|
||||
Reference in New Issue
Block a user