n8n-editor-ui
Version:
Workflow Editor UI for n8n
972 lines (971 loc) • 40.1 kB
JavaScript
import { C as computed, Cn as toDisplayString, D as createElementBlock, E as createCommentVNode, Gt as unref, It as ref, L as h, M as createVNode, P as defineComponent, Pt as reactive, T as createBlock, Z as onMounted, _ as Fragment, _t as watch, bt as withCtx, et as openBlock, h as withModifiers, it as renderList, j as createTextVNode, ot as resolveComponent, q as onBeforeMount, vn as normalizeClass, vt as watchEffect, w as createBaseVNode } from "./vue.runtime.esm-bundler-tP5dCd7J.js";
import { _t as useI18n, vt as I18nT } from "./_MapCache-ficiegRb.js";
import { Sn as N8nButton_default, at as N8nLoading_default, ht as N8nTooltip_default, it as N8nActionToggle_default, lt as N8nBadge_default, mt as N8nLink_default, wn as N8nIcon_default, xn as N8nText_default, yn as N8nHeading_default } from "./src-DgvumQTi.js";
import "./en-CF30SCh2.js";
import "./preload-helper-CR0ecmWK.js";
import { t as __plugin_vue_export_helper_default } from "./_plugin-vue_export-helper-BwBpWJRZ.js";
import { T as createEventBus, v as useRoute, y as useRouter } from "./truncate-B1HVeveJ.js";
import { A as telemetry, Dr as useUsersStore, N as useWorkflowsStore, g as useWorkflowHistoryStore, oa as useUIStore, v as useToast } from "./builder.store-sBTWwxRU.js";
import "./empty-BuGRxzl4.js";
import "./sanitize-html-Cc45ZKm8.js";
import "./CalendarDate-zWqgZMlk.js";
import "./path-browserify-BtCDmZ3_.js";
import { Oo as getResourcePermissions, Ro as VIEWS, a as IS_DRAFT_PUBLISH_ENABLED, bs as WORKFLOW_HISTORY_VERSION_UNPUBLISH, vs as WORKFLOW_HISTORY_PUBLISH_MODAL_KEY, ys as WORKFLOW_HISTORY_VERSION_RESTORE } from "./constants-BbpucWL4.js";
import "./merge-k8vSyyXH.js";
import { r as useRootStore } from "./_baseOrderBy-B_6CV8x6.js";
import "./dateformat-CM9k0--B.js";
import "./useDebounce-Bnb8W2LR.js";
import "./executions.store-CXjGPxs0.js";
import "./versions.store-BwdT833-.js";
import { t as usePageRedirectionHelper } from "./usePageRedirectionHelper-fGNB6ZKK.js";
import { t as useWorkflowActivate } from "./useWorkflowActivate-BhSS_xpK.js";
import { i as getLastPublishedVersion, n as formatTimestamp, r as generateVersionName, t as computeTimelineEntries } from "./utils-C2GPI2gK.js";
import { t as WorkflowPreview_default } from "./WorkflowPreview-CMqGPBQJ.js";
import { t as useIntersectionObserver } from "./useIntersectionObserver-DXKoRNFj.js";
var WorkflowHistoryListItem_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
__name: "WorkflowHistoryListItem",
props: {
item: {},
index: {},
actions: {},
isSelected: {
type: Boolean,
default: false
},
isVersionActive: {
type: Boolean,
default: false
},
isGrouped: {
type: Boolean,
default: false
}
},
emits: [
"action",
"preview",
"mounted"
],
setup(__props, { emit: __emit }) {
const props = __props;
const emit = __emit;
const i18n = useI18n();
const usersStore = useUsersStore();
const actionsVisible = ref(false);
const itemElement = ref(null);
const authorElement = ref(null);
const isAuthorElementTruncated = ref(false);
const checkAuthorTruncation = () => {
const el = authorElement.value?.$el;
if (el instanceof HTMLElement) isAuthorElementTruncated.value = el.scrollWidth > el.clientWidth;
};
const formattedCreatedAt = computed(() => {
const { date, time } = formatTimestamp(props.item.createdAt);
return i18n.baseText("workflowHistory.item.createdAt", { interpolate: {
date,
time
} });
});
const authors = computed(() => {
const allAuthors = props.item.authors.split(", ");
let label$1 = allAuthors[0];
if (allAuthors.length > 1) label$1 = `${label$1} + ${allAuthors.length - 1}`;
return {
size: allAuthors.length,
label: label$1
};
});
const versionName = computed(() => {
if (props.item.name) return props.item.name;
return props.isVersionActive ? generateVersionName(props.item.versionId) : "";
});
const lastPublishInfo = computed(() => {
if (!props.isVersionActive) return null;
const lastPublishedByUser = getLastPublishedVersion(props.item.workflowPublishHistory);
if (!lastPublishedByUser) return null;
return lastPublishedByUser;
});
const publishedAt = computed(() => {
if (!lastPublishInfo.value) return null;
const { date, time } = formatTimestamp(lastPublishInfo.value.createdAt);
return i18n.baseText("workflowHistory.item.createdAt", { interpolate: {
date,
time
} });
});
const publishedByUserName = computed(() => {
const userId = lastPublishInfo.value?.userId;
if (!userId) return null;
const user = usersStore.usersById[userId];
return user?.fullName ?? user?.email ?? null;
});
const onAction = (value) => {
emit("action", {
action: value,
id: props.item.versionId,
data: {
formattedCreatedAt: formattedCreatedAt.value,
versionName: versionName.value,
description: props.item.description
}
});
};
const onVisibleChange = (visible) => {
actionsVisible.value = visible;
};
const onItemClick = (event) => {
emit("preview", {
event,
id: props.item.versionId
});
};
onMounted(() => {
emit("mounted", {
index: props.index,
offsetTop: itemElement.value?.offsetTop ?? 0,
isSelected: props.isSelected
});
checkAuthorTruncation();
});
return (_ctx, _cache) => {
return openBlock(), createElementBlock("li", {
ref_key: "itemElement",
ref: itemElement,
"data-test-id": "workflow-history-list-item",
role: "button",
class: normalizeClass({
[_ctx.$style.item]: true,
[_ctx.$style.selected]: props.isSelected,
[_ctx.$style.actionsVisible]: actionsVisible.value,
[_ctx.$style.grouped]: props.isGrouped
}),
onClick: onItemClick
}, [createBaseVNode("span", { class: normalizeClass(_ctx.$style.timelineColumn) }, [!props.isGrouped ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [props.isVersionActive ? (openBlock(), createBlock(unref(N8nIcon_default), {
key: 0,
size: "large",
icon: "circle-check",
color: "success"
})) : (openBlock(), createElementBlock("span", {
key: 1,
class: normalizeClass(_ctx.$style.timelineMarker)
}, null, 2))], 64)) : (openBlock(), createElementBlock("span", {
key: 1,
class: normalizeClass(_ctx.$style.timelineLine)
}, null, 2))], 2), createBaseVNode("div", { class: normalizeClass(_ctx.$style.wrapper) }, [createBaseVNode("div", { class: normalizeClass(_ctx.$style.content) }, [versionName.value ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [createBaseVNode("div", { class: normalizeClass(_ctx.$style.mainRow) }, [createVNode(unref(N8nText_default), {
size: "small",
bold: true,
color: "text-dark",
class: normalizeClass(_ctx.$style.mainLine)
}, {
default: withCtx(() => [createTextVNode(toDisplayString(versionName.value), 1)]),
_: 1
}, 8, ["class"]), props.isVersionActive ? (openBlock(), createBlock(unref(N8nTooltip_default), {
key: 0,
placement: "top",
disabled: !publishedAt.value
}, {
content: withCtx(() => [createBaseVNode("div", { class: normalizeClass(_ctx.$style.tooltipContent) }, [createVNode(unref(N8nText_default), { size: "small" }, {
default: withCtx(() => [createTextVNode(toDisplayString(unref(i18n).baseText("workflowHistory.item.publishedAtLabel")) + " " + toDisplayString(publishedAt.value), 1)]),
_: 1
}), publishedByUserName.value ? (openBlock(), createBlock(unref(N8nText_default), {
key: 0,
size: "small"
}, {
default: withCtx(() => [createTextVNode(toDisplayString(publishedByUserName.value), 1)]),
_: 1
})) : createCommentVNode("", true)], 2)]),
default: withCtx(() => [createVNode(unref(N8nBadge_default), {
size: "xsmall",
class: normalizeClass(_ctx.$style.publishedBadge),
"show-border": false
}, {
default: withCtx(() => [createTextVNode(toDisplayString(unref(i18n).baseText("workflowHistory.item.active")), 1)]),
_: 1
}, 8, ["class"])]),
_: 1
}, 8, ["disabled"])) : createCommentVNode("", true)], 2), createBaseVNode("div", { class: normalizeClass(_ctx.$style.metaRow) }, [createVNode(unref(N8nTooltip_default), {
placement: "right-end",
disabled: !isAuthorElementTruncated.value
}, {
content: withCtx(() => [createTextVNode(toDisplayString(props.item.authors), 1)]),
default: withCtx(() => [createVNode(unref(N8nText_default), {
ref_key: "authorElement",
ref: authorElement,
size: "small",
color: "text-base",
class: normalizeClass(_ctx.$style.metaItem)
}, {
default: withCtx(() => [createTextVNode(toDisplayString(authors.value.label) + ", ", 1)]),
_: 1
}, 8, ["class"])]),
_: 1
}, 8, ["disabled"]), createVNode(unref(N8nText_default), {
tag: "time",
size: "small",
color: "text-base",
class: normalizeClass(_ctx.$style.metaItem)
}, {
default: withCtx(() => [createTextVNode(toDisplayString(formattedCreatedAt.value), 1)]),
_: 1
}, 8, ["class"])], 2)], 64)) : (openBlock(), createElementBlock("div", {
key: 1,
class: normalizeClass(_ctx.$style.unnamedRow)
}, [createVNode(unref(N8nTooltip_default), {
placement: "right-end",
disabled: !isAuthorElementTruncated.value
}, {
content: withCtx(() => [createTextVNode(toDisplayString(props.item.authors), 1)]),
default: withCtx(() => [createVNode(unref(N8nText_default), {
ref_key: "authorElement",
ref: authorElement,
size: "small",
color: "text-base",
class: normalizeClass(_ctx.$style.unnamedAuthor)
}, {
default: withCtx(() => [createTextVNode(toDisplayString(authors.value.label) + ", ", 1)]),
_: 1
}, 8, ["class"])]),
_: 1
}, 8, ["disabled"]), createVNode(unref(N8nText_default), {
tag: "time",
size: "small",
color: "text-base",
class: normalizeClass(_ctx.$style.unnamedTime)
}, {
default: withCtx(() => [createTextVNode(toDisplayString(formattedCreatedAt.value), 1)]),
_: 1
}, 8, ["class"])], 2))], 2), createVNode(unref(N8nActionToggle_default), {
class: normalizeClass(_ctx.$style.actions),
actions: props.actions,
placement: "bottom-end",
onAction,
onClick: _cache[0] || (_cache[0] = withModifiers(() => {}, ["stop"])),
onVisibleChange
}, null, 8, ["class", "actions"])], 2)], 2);
};
}
});
var WorkflowHistoryListItem_vue_vue_type_style_index_0_lang_module_default = {
item: "_item_18356_123",
grouped: "_grouped_18356_134",
selected: "_selected_18356_134",
wrapper: "_wrapper_18356_152",
timelineColumn: "_timelineColumn_18356_165",
timelineMarker: "_timelineMarker_18356_176",
timelineLine: "_timelineLine_18356_184",
content: "_content_18356_192",
mainRow: "_mainRow_18356_200",
mainLine: "_mainLine_18356_206",
metaRow: "_metaRow_18356_212",
metaItem: "_metaItem_18356_219",
unnamedRow: "_unnamedRow_18356_226",
unnamedAuthor: "_unnamedAuthor_18356_231",
unnamedTime: "_unnamedTime_18356_238",
actions: "_actions_18356_242",
publishedBadge: "_publishedBadge_18356_247",
tooltipContent: "_tooltipContent_18356_252"
};
var WorkflowHistoryListItem_default = /* @__PURE__ */ __plugin_vue_export_helper_default(WorkflowHistoryListItem_vue_vue_type_script_setup_true_lang_default, [["__cssModules", { "$style": WorkflowHistoryListItem_vue_vue_type_style_index_0_lang_module_default }]]);
var _hoisted_1$2 = ["aria-expanded", "onClick"];
var _hoisted_2$1 = ["aria-label"];
var WorkflowHistoryList_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
__name: "WorkflowHistoryList",
props: {
items: {},
selectedItem: {},
actions: {},
requestNumberOfItems: {},
lastReceivedItemsLength: {},
evaluatedPruneDays: {},
shouldUpgrade: { type: Boolean },
isListLoading: { type: Boolean },
activeVersionId: {}
},
emits: [
"action",
"preview",
"loadMore",
"upgrade"
],
setup(__props, { emit: __emit }) {
const props = __props;
const emit = __emit;
const i18n = useI18n();
const listElement = ref(null);
const loadMoreSentinel = ref(null);
const shouldAutoScroll = ref(true);
const expandedGroups = reactive(/* @__PURE__ */ new Set());
const timelineEntries = computed(() => {
return computeTimelineEntries(props.items);
});
const toggleGroup = (groupId) => {
if (expandedGroups.has(groupId)) expandedGroups.delete(groupId);
else expandedGroups.add(groupId);
};
const hasMoreItems = computed(() => props.lastReceivedItemsLength === props.requestNumberOfItems);
const { observe: observeForLoadMore } = useIntersectionObserver({
root: listElement,
threshold: .01,
onIntersect: () => {
shouldAutoScroll.value = false;
emit("loadMore", {
take: props.requestNumberOfItems,
skip: props.items.length
});
}
});
watch([
loadMoreSentinel,
hasMoreItems,
() => props.items.length
], ([sentinel$1, canLoadMore]) => {
if (sentinel$1 && canLoadMore) observeForLoadMore(sentinel$1);
}, { immediate: true });
const getActions = (item$1, index) => {
let filteredActions = props.actions;
if (index === 0) filteredActions = filteredActions.filter((action) => action.value !== "restore");
if (item$1.versionId === props.activeVersionId) filteredActions = filteredActions.filter((action) => action.value !== "publish");
else filteredActions = filteredActions.filter((action) => action.value !== "unpublish");
return filteredActions;
};
const onAction = ({ action, id, data }) => {
shouldAutoScroll.value = false;
emit("action", {
action,
id,
data
});
};
const onPreview = ({ event, id }) => {
shouldAutoScroll.value = false;
emit("preview", {
event,
id
});
};
const onItemMounted = ({ offsetTop, isSelected }) => {
if (isSelected && shouldAutoScroll.value) {
shouldAutoScroll.value = false;
listElement.value?.scrollTo({
top: offsetTop,
behavior: "smooth"
});
}
};
return (_ctx, _cache) => {
return openBlock(), createElementBlock("ul", {
ref_key: "listElement",
ref: listElement,
class: normalizeClass(_ctx.$style.list),
"data-test-id": "workflow-history-list"
}, [
(openBlock(true), createElementBlock(Fragment, null, renderList(timelineEntries.value, (entry) => {
return openBlock(), createElementBlock(Fragment, { key: entry.type === "version" ? entry.item.versionId : entry.groupId }, [
entry.type === "group-header" ? (openBlock(), createElementBlock("li", {
key: 0,
class: normalizeClass(_ctx.$style.groupHeader),
"aria-expanded": expandedGroups.has(entry.groupId),
role: "button",
"data-test-id": "workflow-history-group-header",
onClick: ($event) => toggleGroup(entry.groupId)
}, [createVNode(unref(N8nIcon_default), {
class: normalizeClass([_ctx.$style.groupTimelineColumn, _ctx.$style.groupChevron]),
icon: expandedGroups.has(entry.groupId) ? "chevron-down" : "chevron-right",
size: "small"
}, null, 8, ["class", "icon"]), createVNode(unref(N8nText_default), {
color: "text-base",
size: "small"
}, {
default: withCtx(() => [createTextVNode(toDisplayString(unref(i18n).baseText("workflowHistory.group.unnamedVersions", {
adjustToNumber: entry.count,
interpolate: { count: String(entry.count) }
})), 1)]),
_: 2
}, 1024)], 10, _hoisted_1$2)) : createCommentVNode("", true),
entry.type === "group-header" && expandedGroups.has(entry.groupId) ? (openBlock(true), createElementBlock(Fragment, { key: 1 }, renderList(entry.versions, (versionEntry) => {
return openBlock(), createBlock(WorkflowHistoryListItem_default, {
key: versionEntry.item.versionId,
index: versionEntry.originalIndex,
item: versionEntry.item,
"is-selected": versionEntry.item.versionId === props.selectedItem?.versionId,
"is-version-active": versionEntry.item.versionId === props.activeVersionId,
actions: getActions(versionEntry.item, versionEntry.originalIndex),
"is-grouped": true,
onAction,
onPreview,
onMounted: onItemMounted
}, null, 8, [
"index",
"item",
"is-selected",
"is-version-active",
"actions"
]);
}), 128)) : createCommentVNode("", true),
entry.type === "version" ? (openBlock(), createBlock(WorkflowHistoryListItem_default, {
key: 2,
index: entry.originalIndex,
item: entry.item,
"is-selected": entry.item.versionId === props.selectedItem?.versionId,
"is-version-active": entry.item.versionId === props.activeVersionId,
actions: getActions(entry.item, entry.originalIndex),
onAction,
onPreview,
onMounted: onItemMounted
}, null, 8, [
"index",
"item",
"is-selected",
"is-version-active",
"actions"
])) : createCommentVNode("", true)
], 64);
}), 128)),
props.items.length && hasMoreItems.value ? (openBlock(), createElementBlock("li", {
key: 0,
ref_key: "loadMoreSentinel",
ref: loadMoreSentinel,
class: normalizeClass(_ctx.$style.sentinel),
"aria-hidden": "true"
}, null, 2)) : createCommentVNode("", true),
props.isListLoading ? (openBlock(), createElementBlock("li", {
key: 1,
class: normalizeClass(_ctx.$style.loader),
role: "status",
"aria-live": "polite",
"aria-busy": "true",
"aria-label": unref(i18n).baseText("generic.loading")
}, [
createVNode(unref(N8nLoading_default), {
rows: 3,
class: "mb-xs"
}),
createVNode(unref(N8nLoading_default), {
rows: 3,
class: "mb-xs"
}),
createVNode(unref(N8nLoading_default), {
rows: 3,
class: "mb-xs"
})
], 10, _hoisted_2$1)) : createCommentVNode("", true),
props.shouldUpgrade ? (openBlock(), createElementBlock("li", {
key: 2,
class: normalizeClass(_ctx.$style.retention)
}, [createBaseVNode("span", null, toDisplayString(unref(i18n).baseText("workflowHistory.limit", { interpolate: { days: String(props.evaluatedPruneDays) } })), 1), createVNode(unref(I18nT), {
keypath: "workflowHistory.upgrade",
tag: "span",
scope: "global"
}, {
link: withCtx(() => [createBaseVNode("a", {
href: "#",
onClick: _cache[0] || (_cache[0] = ($event) => emit("upgrade"))
}, toDisplayString(unref(i18n).baseText("workflowHistory.upgrade.link")), 1)]),
_: 1
})], 2)) : createCommentVNode("", true)
], 2);
};
}
});
var WorkflowHistoryList_vue_vue_type_style_index_0_lang_module_default = {
list: "_list_1dkdi_123",
loader: "_loader_1dkdi_133",
sentinel: "_sentinel_1dkdi_137",
retention: "_retention_1dkdi_141",
groupHeader: "_groupHeader_1dkdi_149",
groupTimelineColumn: "_groupTimelineColumn_1dkdi_168",
groupChevron: "_groupChevron_1dkdi_172"
};
var WorkflowHistoryList_default = /* @__PURE__ */ __plugin_vue_export_helper_default(WorkflowHistoryList_vue_vue_type_script_setup_true_lang_default, [["__cssModules", { "$style": WorkflowHistoryList_vue_vue_type_style_index_0_lang_module_default }]]);
var _hoisted_1$1 = ["datetime"];
var _hoisted_2 = ["value"];
var MAX_DESCRIPTION_LENGTH = 200;
var WorkflowHistoryContent_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
__name: "WorkflowHistoryContent",
props: {
workflow: {},
workflowVersion: {},
actions: {},
isVersionActive: { type: Boolean },
isListLoading: { type: Boolean },
isFirstItemShown: { type: Boolean }
},
emits: ["action"],
setup(__props, { emit: __emit }) {
const i18n = useI18n();
const props = __props;
const emit = __emit;
const isDraftPublishEnabled = true;
const workflowVersionPreview = computed(() => {
if (!props.workflowVersion || !props.workflow) return;
const { pinData,...workflow } = props.workflow;
return {
...workflow,
nodes: props.workflowVersion.nodes,
connections: props.workflowVersion.connections
};
});
const formattedCreatedAt = computed(() => {
if (!props.workflowVersion) return "";
const { date, time } = formatTimestamp(props.workflowVersion.createdAt);
return i18n.baseText("workflowHistory.item.createdAt", { interpolate: {
date,
time
} });
});
const versionNameDisplay = computed(() => {
if (props.workflowVersion?.name) return props.workflowVersion.name;
return props.isVersionActive && props.workflowVersion ? generateVersionName(props.workflowVersion.versionId) : formattedCreatedAt.value;
});
const isDescriptionExpanded = ref(false);
const description = computed(() => props.workflowVersion?.description ?? "");
const isDescriptionLong = computed(() => description.value.length > MAX_DESCRIPTION_LENGTH);
const displayDescription = computed(() => {
if (!isDescriptionLong.value || isDescriptionExpanded.value) return description.value;
return description.value.substring(0, MAX_DESCRIPTION_LENGTH) + "... ";
});
const toggleDescription = () => {
isDescriptionExpanded.value = !isDescriptionExpanded.value;
};
const actions$2 = computed(() => {
let filteredActions = props.actions;
if (props.isFirstItemShown) filteredActions = filteredActions.filter((action) => action.value !== "restore");
if (props.isVersionActive) filteredActions = filteredActions.filter((action) => action.value !== "publish");
else filteredActions = filteredActions.filter((action) => action.value !== "unpublish");
return filteredActions;
});
const onAction = (value) => {
if (!props.workflowVersion) return;
emit("action", {
action: value,
id: props.workflowVersion.versionId,
data: {
formattedCreatedAt: formattedCreatedAt.value,
versionName: versionNameDisplay.value,
description: description.value
}
});
};
watch(() => props.workflowVersion, () => {
isDescriptionExpanded.value = false;
});
return (_ctx, _cache) => {
return openBlock(), createElementBlock("div", { class: normalizeClass(_ctx.$style.content) }, [props.workflowVersion ? (openBlock(), createBlock(WorkflowPreview_default, {
key: 0,
workflow: workflowVersionPreview.value,
loading: props.isListLoading,
"loader-type": "spinner"
}, null, 8, ["workflow", "loading"])) : createCommentVNode("", true), props.workflowVersion ? (openBlock(), createElementBlock("div", {
key: 1,
class: normalizeClass(_ctx.$style.info)
}, [createBaseVNode("div", { class: normalizeClass(_ctx.$style.card) }, [unref(isDraftPublishEnabled) ? (openBlock(), createElementBlock("div", {
key: 0,
class: normalizeClass(_ctx.$style.descriptionBox)
}, [versionNameDisplay.value ? (openBlock(), createBlock(unref(N8nTooltip_default), {
key: 0,
content: versionNameDisplay.value
}, {
default: withCtx(() => [createVNode(unref(N8nText_default), {
class: normalizeClass(_ctx.$style.mainLine),
bold: "",
color: "text-dark"
}, {
default: withCtx(() => [createTextVNode(toDisplayString(versionNameDisplay.value), 1)]),
_: 1
}, 8, ["class"])]),
_: 1
}, 8, ["content"])) : createCommentVNode("", true), description.value ? (openBlock(), createBlock(unref(N8nText_default), {
key: 1,
size: "small",
color: "text-base"
}, {
default: withCtx(() => [createTextVNode(toDisplayString(displayDescription.value) + " ", 1), isDescriptionLong.value ? (openBlock(), createBlock(unref(N8nLink_default), {
key: 0,
size: "small",
onClick: toggleDescription
}, {
default: withCtx(() => [createTextVNode(toDisplayString(isDescriptionExpanded.value ? unref(i18n).baseText("generic.showLess") : unref(i18n).baseText("generic.showMore")), 1)]),
_: 1
})) : createCommentVNode("", true)]),
_: 1
})) : createCommentVNode("", true)], 2)) : (openBlock(), createElementBlock("section", {
key: 1,
class: normalizeClass(_ctx.$style.textOld)
}, [
createBaseVNode("p", null, [createBaseVNode("span", { class: normalizeClass(_ctx.$style.label) }, toDisplayString(unref(i18n).baseText("workflowHistory.content.title")) + ": ", 3), createBaseVNode("time", { datetime: props.workflowVersion.createdAt }, toDisplayString(formattedCreatedAt.value), 9, _hoisted_1$1)]),
createBaseVNode("p", null, [createBaseVNode("span", { class: normalizeClass(_ctx.$style.label) }, toDisplayString(unref(i18n).baseText("workflowHistory.content.editedBy")) + ": ", 3), createBaseVNode("span", null, toDisplayString(props.workflowVersion.authors), 1)]),
createBaseVNode("p", null, [createBaseVNode("span", { class: normalizeClass(_ctx.$style.label) }, toDisplayString(unref(i18n).baseText("workflowHistory.content.versionId")) + ": ", 3), createBaseVNode("data", { value: props.workflowVersion.versionId }, toDisplayString(props.workflowVersion.versionId), 9, _hoisted_2)])
], 2)), createVNode(unref(N8nActionToggle_default), {
class: normalizeClass(_ctx.$style.actions),
actions: actions$2.value,
placement: "bottom-end",
"data-test-id": "workflow-history-content-actions",
onAction
}, {
default: withCtx(() => [createVNode(unref(N8nButton_default), {
type: "tertiary",
size: "large",
"data-test-id": "action-toggle-button"
}, {
default: withCtx(() => [createTextVNode(toDisplayString(unref(i18n).baseText("workflowHistory.content.actions")) + " ", 1), createVNode(unref(N8nIcon_default), {
class: "ml-3xs",
icon: "chevron-down",
size: "small"
})]),
_: 1
})]),
_: 1
}, 8, ["class", "actions"])], 2)], 2)) : createCommentVNode("", true)], 2);
};
}
});
var WorkflowHistoryContent_vue_vue_type_style_index_0_lang_module_default = {
content: "_content_1m14m_123",
info: "_info_1m14m_133",
card: "_card_1m14m_141",
descriptionBox: "_descriptionBox_1m14m_148",
mainLine: "_mainLine_1m14m_160",
textOld: "_textOld_1m14m_167",
label: "_label_1m14m_202",
actions: "_actions_1m14m_207"
};
var WorkflowHistoryContent_default = /* @__PURE__ */ __plugin_vue_export_helper_default(WorkflowHistoryContent_vue_vue_type_script_setup_true_lang_default, [["__cssModules", { "$style": WorkflowHistoryContent_vue_vue_type_style_index_0_lang_module_default }]]);
var _hoisted_1 = { key: 0 };
var WorkflowHistoryVersionRestoreModalActions = /* @__PURE__ */ function(WorkflowHistoryVersionRestoreModalActions$1) {
WorkflowHistoryVersionRestoreModalActions$1["restore"] = "restore";
WorkflowHistoryVersionRestoreModalActions$1["deactivateAndRestore"] = "deactivateAndRestore";
WorkflowHistoryVersionRestoreModalActions$1["cancel"] = "cancel";
return WorkflowHistoryVersionRestoreModalActions$1;
}(WorkflowHistoryVersionRestoreModalActions || {});
var WorkflowHistory_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
__name: "WorkflowHistory",
setup(__props) {
const workflowHistoryActionTypes = [
"restore",
"publish",
"unpublish",
"clone",
"open",
"download"
];
const WORKFLOW_HISTORY_ACTIONS = workflowHistoryActionTypes.reduce((record, key) => ({
...record,
[key.toUpperCase()]: key
}), {});
const route = useRoute();
const router = useRouter();
const i18n = useI18n();
const toast = useToast();
const pageRedirectionHelper = usePageRedirectionHelper();
const workflowHistoryStore = useWorkflowHistoryStore();
const uiStore = useUIStore();
const workflowsStore = useWorkflowsStore();
const workflowActivate = useWorkflowActivate();
const canRender = ref(true);
const isListLoading = ref(true);
const requestNumberOfItems = ref(20);
const lastReceivedItemsLength = ref(0);
const activeWorkflow = ref(null);
const workflowHistory = ref([]);
const selectedWorkflowVersion = ref(null);
const workflowId = computed(() => normalizeSingleRouteParam("workflowId"));
const versionId = computed(() => normalizeSingleRouteParam("versionId"));
const editorRoute = computed(() => ({
name: VIEWS.WORKFLOW,
params: { name: workflowId.value }
}));
const workflowPermissions = computed(() => getResourcePermissions(workflowsStore.getWorkflowById(workflowId.value)?.scopes).workflow);
const workflowActiveVersionId = computed(() => {
return workflowsStore.getWorkflowById(workflowId.value)?.activeVersion?.versionId;
});
const actions$2 = computed(() => workflowHistoryActionTypes.filter((value) => !(value === "publish" && activeWorkflow.value?.isArchived)).map((value) => ({
label: i18n.baseText(`workflowHistory.item.actions.${value}`),
disabled: value === "clone" && !workflowPermissions.value.create || value === "restore" && !workflowPermissions.value.update || (value === "publish" || value === "unpublish") && !workflowPermissions.value.update,
value
})));
const isFirstItemShown = computed(() => workflowHistory.value[0]?.versionId === versionId.value);
const evaluatedPruneDays = computed(() => Math.floor(workflowHistoryStore.evaluatedPruneTime / 24));
const sendTelemetry = (event) => {
telemetry.track(event, {
instance_id: useRootStore().instanceId,
workflow_id: workflowId.value
});
};
const loadMore = async (queryParams) => {
const history = await workflowHistoryStore.getWorkflowHistory(workflowId.value, queryParams);
lastReceivedItemsLength.value = history.length;
workflowHistory.value = workflowHistory.value.concat(history);
};
onBeforeMount(async () => {
sendTelemetry("User opened workflow history");
try {
const [workflow] = await Promise.all([workflowsStore.fetchWorkflow(workflowId.value), loadMore({ take: requestNumberOfItems.value })]);
activeWorkflow.value = workflow;
isListLoading.value = false;
if (!versionId.value && workflowHistory.value.length) await router.replace({
name: VIEWS.WORKFLOW_HISTORY,
params: {
workflowId: workflowId.value,
versionId: workflowHistory.value[0].versionId
}
});
} catch (error) {
canRender.value = false;
toast.showError(error, i18n.baseText("workflowHistory.title"));
}
});
const normalizeSingleRouteParam = (name) => {
const param = route.params[name];
if (typeof param === "string") return param;
return param?.[0] ?? "";
};
const openInNewTab = (id) => {
const { href } = router.resolve({
name: VIEWS.WORKFLOW_HISTORY,
params: {
workflowId: workflowId.value,
versionId: id
}
});
window.open(href, "_blank");
};
const cloneWorkflowVersion = async (id, data) => {
const clonedWorkflow = await workflowHistoryStore.cloneIntoNewWorkflow(workflowId.value, id, data);
const { href } = router.resolve({
name: VIEWS.WORKFLOW,
params: { name: clonedWorkflow.id }
});
toast.showMessage({
title: i18n.baseText("workflowHistory.action.clone.success.title"),
message: h("a", {
href,
target: "_blank"
}, i18n.baseText("workflowHistory.action.clone.success.message")),
type: "success",
duration: 1e4
});
};
const restoreWorkflowVersion = async (id, data) => {
const workflow = await workflowsStore.fetchWorkflow(workflowId.value);
let deactivateAndRestore = false;
const versionIdBeforeRestore = workflow.versionId;
activeWorkflow.value = await workflowHistoryStore.restoreWorkflow(workflowId.value, id, deactivateAndRestore);
if (activeWorkflow.value.versionId === versionIdBeforeRestore) {
toast.showMessage({
title: i18n.baseText("workflowHistory.action.restore.alreadyRestored"),
type: "info"
});
return;
}
workflowHistory.value = (await workflowHistoryStore.getWorkflowHistory(workflowId.value, { take: 1 })).concat(workflowHistory.value);
toast.showMessage({
title: i18n.baseText("workflowHistory.action.restore.success.title"),
type: "success"
});
};
const publishWorkflowVersion = (id, data) => {
const publishEventBus = createEventBus();
publishEventBus.once("publish", (publishData) => {
activeWorkflow.value = workflowsStore.getWorkflowById(workflowId.value);
const historyItem = workflowHistory.value.find((item$1) => item$1.versionId === publishData.versionId);
if (historyItem) {
historyItem.name = publishData.name;
historyItem.description = publishData.description;
if (activeWorkflow.value?.activeVersion?.workflowPublishHistory) historyItem.workflowPublishHistory = activeWorkflow.value.activeVersion.workflowPublishHistory;
}
if (selectedWorkflowVersion.value?.versionId === publishData.versionId) selectedWorkflowVersion.value = {
...selectedWorkflowVersion.value,
name: publishData.name,
description: publishData.description,
workflowPublishHistory: activeWorkflow.value?.activeVersion?.workflowPublishHistory ?? selectedWorkflowVersion.value.workflowPublishHistory
};
sendTelemetry("User published version from history");
});
uiStore.openModalWithData({
name: WORKFLOW_HISTORY_PUBLISH_MODAL_KEY,
data: {
versionId: id,
workflowId: workflowId.value,
formattedCreatedAt: data.formattedCreatedAt,
versionName: data.versionName,
description: data.description,
eventBus: publishEventBus
}
});
};
const unpublishWorkflowVersion = (id, data) => {
if (workflowActiveVersionId.value !== id) return;
const unpublishEventBus = createEventBus();
unpublishEventBus.once("unpublish", async () => {
const success = await workflowActivate.unpublishWorkflowFromHistory(workflowId.value);
uiStore.closeModal(WORKFLOW_HISTORY_VERSION_UNPUBLISH);
if (!success) return;
activeWorkflow.value = workflowsStore.getWorkflowById(workflowId.value);
toast.showMessage({
title: i18n.baseText("workflowHistory.action.unpublish.success.title"),
type: "success"
});
sendTelemetry("User unpublished workflow from history");
});
uiStore.openModalWithData({
name: WORKFLOW_HISTORY_VERSION_UNPUBLISH,
data: {
versionName: data.versionName,
eventBus: unpublishEventBus
}
});
};
const onAction = async ({ action, id, data }) => {
try {
switch (action) {
case WORKFLOW_HISTORY_ACTIONS.OPEN:
openInNewTab(id);
sendTelemetry("User opened version in new tab");
break;
case WORKFLOW_HISTORY_ACTIONS.DOWNLOAD:
await workflowHistoryStore.downloadVersion(workflowId.value, id, data);
sendTelemetry("User downloaded version");
break;
case WORKFLOW_HISTORY_ACTIONS.CLONE:
await cloneWorkflowVersion(id, data);
sendTelemetry("User cloned version");
break;
case WORKFLOW_HISTORY_ACTIONS.RESTORE:
await restoreWorkflowVersion(id, data);
sendTelemetry("User restored version");
break;
case WORKFLOW_HISTORY_ACTIONS.PUBLISH:
publishWorkflowVersion(id, data);
break;
case WORKFLOW_HISTORY_ACTIONS.UNPUBLISH:
unpublishWorkflowVersion(id, data);
break;
}
} catch (error) {
toast.showError(error, i18n.baseText("workflowHistory.action.error.title", { interpolate: { action: i18n.baseText(`workflowHistory.item.actions.${action}`).toLowerCase() } }));
}
};
const onPreview = async ({ event, id }) => {
if (event.metaKey || event.ctrlKey) {
openInNewTab(id);
sendTelemetry("User opened version in new tab");
} else await router.push({
name: VIEWS.WORKFLOW_HISTORY,
params: {
workflowId: workflowId.value,
versionId: id
}
});
};
const onUpgrade = () => {
pageRedirectionHelper.goToUpgrade("workflow-history", "upgrade-workflow-history");
};
watchEffect(async () => {
if (!versionId.value) return;
try {
const [workflowVersion, workflow] = await Promise.all([workflowHistoryStore.getWorkflowVersion(workflowId.value, versionId.value), workflowsStore.fetchWorkflow(workflowId.value)]);
selectedWorkflowVersion.value = workflowVersion;
activeWorkflow.value = workflow;
sendTelemetry("User selected version");
} catch (error) {
if (error.message?.includes("version")) toast.showError(/* @__PURE__ */ new Error(`${error.message} "${versionId.value}" `), i18n.baseText("workflowHistory.title"));
else {
canRender.value = false;
toast.showError(error, i18n.baseText("workflowHistory.title"));
}
}
});
return (_ctx, _cache) => {
const _component_RouterLink = resolveComponent("RouterLink");
return openBlock(), createElementBlock("div", { class: normalizeClass(_ctx.$style.view) }, [
createBaseVNode("div", { class: normalizeClass(_ctx.$style.header) }, [createVNode(unref(N8nHeading_default), {
tag: "h2",
size: "medium"
}, {
default: withCtx(() => [createTextVNode(toDisplayString(activeWorkflow.value?.name), 1)]),
_: 1
}), activeWorkflow.value?.isArchived ? (openBlock(), createElementBlock("span", _hoisted_1, [createVNode(unref(N8nBadge_default), {
class: "ml-s",
theme: "tertiary",
bold: "",
"data-test-id": "workflow-archived-tag"
}, {
default: withCtx(() => [createTextVNode(toDisplayString(unref(i18n).baseText("workflows.item.archived")), 1)]),
_: 1
})])) : createCommentVNode("", true)], 2),
createBaseVNode("div", { class: normalizeClass(_ctx.$style.corner) }, [createVNode(unref(N8nHeading_default), {
tag: "h2",
size: "medium",
bold: ""
}, {
default: withCtx(() => [createTextVNode(toDisplayString(unref(i18n).baseText("workflowHistory.title")), 1)]),
_: 1
}), createVNode(_component_RouterLink, {
to: editorRoute.value,
"data-test-id": "workflow-history-close-button"
}, {
default: withCtx(() => [createVNode(unref(N8nButton_default), {
type: "tertiary",
icon: "x",
size: "small",
text: "",
square: ""
})]),
_: 1
}, 8, ["to"])], 2),
createBaseVNode("div", { class: normalizeClass(_ctx.$style.listComponentWrapper) }, [canRender.value ? (openBlock(), createBlock(WorkflowHistoryList_default, {
key: 0,
items: workflowHistory.value,
"last-received-items-length": lastReceivedItemsLength.value,
"selected-item": selectedWorkflowVersion.value,
actions: actions$2.value,
"request-number-of-items": requestNumberOfItems.value,
"should-upgrade": unref(workflowHistoryStore).shouldUpgrade,
"evaluated-prune-days": evaluatedPruneDays.value,
"is-list-loading": isListLoading.value,
"active-version-id": workflowActiveVersionId.value,
onAction,
onPreview,
onLoadMore: loadMore,
onUpgrade
}, null, 8, [
"items",
"last-received-items-length",
"selected-item",
"actions",
"request-number-of-items",
"should-upgrade",
"evaluated-prune-days",
"is-list-loading",
"active-version-id"
])) : createCommentVNode("", true)], 2),
createBaseVNode("div", { class: normalizeClass(_ctx.$style.contentComponentWrapper) }, [canRender.value ? (openBlock(), createBlock(WorkflowHistoryContent_default, {
key: 0,
workflow: activeWorkflow.value,
"workflow-version": selectedWorkflowVersion.value,
"is-version-active": selectedWorkflowVersion.value?.versionId === workflowActiveVersionId.value,
actions: actions$2.value,
"is-list-loading": isListLoading.value,
"is-first-item-shown": isFirstItemShown.value,
onAction
}, null, 8, [
"workflow",
"workflow-version",
"is-version-active",
"actions",
"is-list-loading",
"is-first-item-shown"
])) : createCommentVNode("", true)], 2)
], 2);
};
}
});
var WorkflowHistory_vue_vue_type_style_index_0_lang_module_default = {
view: "_view_1q9dy_123",
header: "_header_1q9dy_133",
corner: "_corner_1q9dy_141",
contentComponentWrapper: "_contentComponentWrapper_1q9dy_152",
listComponentWrapper: "_listComponentWrapper_1q9dy_157"
};
var WorkflowHistory_default = /* @__PURE__ */ __plugin_vue_export_helper_default(WorkflowHistory_vue_vue_type_script_setup_true_lang_default, [["__cssModules", { "$style": WorkflowHistory_vue_vue_type_style_index_0_lang_module_default }]]);
export { WorkflowHistory_default as default };