mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-18 02:21:13 +00:00
refactor(editor): Migrate small components to composition API (#11509)
This commit is contained in:
@@ -1,7 +1,5 @@
|
||||
<script lang="ts">
|
||||
import type { PropType } from 'vue';
|
||||
import { defineComponent } from 'vue';
|
||||
import { mapStores } from 'pinia';
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue';
|
||||
import type { IN8nPromptResponse, ModalKey } from '@/Interface';
|
||||
import { VALID_EMAIL_REGEX } from '@/constants';
|
||||
import Modal from '@/components/Modal.vue';
|
||||
@@ -10,78 +8,69 @@ import { useRootStore } from '@/stores/root.store';
|
||||
import { createEventBus } from 'n8n-design-system/utils';
|
||||
import { useToast } from '@/composables/useToast';
|
||||
import { useNpsSurveyStore } from '@/stores/npsSurvey.store';
|
||||
import { useTelemetry } from '@/composables/useTelemetry';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ContactPromptModal',
|
||||
components: { Modal },
|
||||
props: {
|
||||
modalName: {
|
||||
type: String as PropType<ModalKey>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
return {
|
||||
...useToast(),
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
email: '',
|
||||
modalBus: createEventBus(),
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapStores(useRootStore, useSettingsStore, useNpsSurveyStore),
|
||||
title(): string {
|
||||
if (this.npsSurveyStore.promptsData?.title) {
|
||||
return this.npsSurveyStore.promptsData.title;
|
||||
defineProps<{
|
||||
modalName: ModalKey;
|
||||
}>();
|
||||
|
||||
const email = ref('');
|
||||
const modalBus = createEventBus();
|
||||
|
||||
const npsSurveyStore = useNpsSurveyStore();
|
||||
const rootStore = useRootStore();
|
||||
const settingsStore = useSettingsStore();
|
||||
|
||||
const toast = useToast();
|
||||
const telemetry = useTelemetry();
|
||||
|
||||
const title = computed(() => {
|
||||
if (npsSurveyStore.promptsData?.title) {
|
||||
return npsSurveyStore.promptsData.title;
|
||||
}
|
||||
|
||||
return 'You’re a power user 💪';
|
||||
},
|
||||
description(): string {
|
||||
if (this.npsSurveyStore.promptsData?.message) {
|
||||
return this.npsSurveyStore.promptsData.message;
|
||||
});
|
||||
|
||||
const description = computed(() => {
|
||||
if (npsSurveyStore.promptsData?.message) {
|
||||
return npsSurveyStore.promptsData.message;
|
||||
}
|
||||
|
||||
return 'Your experience with n8n can help us improve — for you and our entire community.';
|
||||
},
|
||||
isEmailValid(): boolean {
|
||||
return VALID_EMAIL_REGEX.test(String(this.email).toLowerCase());
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
closeDialog(): void {
|
||||
if (!this.isEmailValid) {
|
||||
this.$telemetry.track('User closed email modal', {
|
||||
instance_id: this.rootStore.instanceId,
|
||||
});
|
||||
|
||||
const isEmailValid = computed(() => {
|
||||
return VALID_EMAIL_REGEX.test(String(email.value).toLowerCase());
|
||||
});
|
||||
|
||||
const closeDialog = () => {
|
||||
if (!isEmailValid.value) {
|
||||
telemetry.track('User closed email modal', {
|
||||
instance_id: rootStore.instanceId,
|
||||
email: null,
|
||||
});
|
||||
}
|
||||
},
|
||||
async send() {
|
||||
if (this.isEmailValid) {
|
||||
const response = (await this.settingsStore.submitContactInfo(
|
||||
this.email,
|
||||
)) as IN8nPromptResponse;
|
||||
};
|
||||
|
||||
const send = async () => {
|
||||
if (isEmailValid.value) {
|
||||
const response = (await settingsStore.submitContactInfo(email.value)) as IN8nPromptResponse;
|
||||
|
||||
if (response.updated) {
|
||||
this.$telemetry.track('User closed email modal', {
|
||||
instance_id: this.rootStore.instanceId,
|
||||
email: this.email,
|
||||
telemetry.track('User closed email modal', {
|
||||
instance_id: rootStore.instanceId,
|
||||
email: email.value,
|
||||
});
|
||||
this.showMessage({
|
||||
toast.showMessage({
|
||||
title: 'Thanks!',
|
||||
message: "It's people like you that help make n8n better",
|
||||
type: 'success',
|
||||
});
|
||||
}
|
||||
this.modalBus.emit('close');
|
||||
modalBus.emit('close');
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,34 +1,36 @@
|
||||
<script lang="ts">
|
||||
import type { PropType } from 'vue';
|
||||
import { defineComponent } from 'vue';
|
||||
<script setup lang="ts">
|
||||
import { nextTick } from 'vue';
|
||||
import { onBeforeUnmount, onMounted, ref } from 'vue';
|
||||
import type { EventBus } from 'n8n-design-system/utils';
|
||||
import { createEventBus } from 'n8n-design-system/utils';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'IntersectionObserved',
|
||||
props: {
|
||||
enabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
eventBus: {
|
||||
type: Object as PropType<EventBus>,
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
enabled: boolean;
|
||||
eventBus: EventBus;
|
||||
}>(),
|
||||
{
|
||||
enabled: false,
|
||||
default: () => createEventBus(),
|
||||
},
|
||||
},
|
||||
async mounted() {
|
||||
if (!this.enabled) {
|
||||
);
|
||||
|
||||
const observed = ref<IntersectionObserver | null>(null);
|
||||
|
||||
onMounted(async () => {
|
||||
if (!props.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.$nextTick();
|
||||
this.eventBus.emit('observe', this.$refs.observed);
|
||||
},
|
||||
beforeUnmount() {
|
||||
if (this.enabled) {
|
||||
this.eventBus.emit('unobserve', this.$refs.observed);
|
||||
await nextTick();
|
||||
|
||||
props.eventBus.emit('observe', observed.value);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (props.enabled) {
|
||||
props.eventBus.emit('unobserve', observed.value);
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,67 +1,65 @@
|
||||
<script lang="ts">
|
||||
import type { PropType } from 'vue';
|
||||
import { defineComponent } from 'vue';
|
||||
<script setup lang="ts">
|
||||
import { onBeforeUnmount, onMounted, ref } from 'vue';
|
||||
import type { EventBus } from 'n8n-design-system/utils';
|
||||
import { createEventBus } from 'n8n-design-system/utils';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'IntersectionObserver',
|
||||
props: {
|
||||
threshold: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
enabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
eventBus: {
|
||||
type: Object as PropType<EventBus>,
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
threshold: number;
|
||||
enabled: boolean;
|
||||
eventBus: EventBus;
|
||||
}>(),
|
||||
{
|
||||
threshold: 0,
|
||||
enabled: false,
|
||||
default: () => createEventBus(),
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
observer: null as IntersectionObserver | null,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
if (!this.enabled) {
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
observed: [{ el: HTMLElement; isIntersecting: boolean }];
|
||||
}>();
|
||||
|
||||
const observer = ref<IntersectionObserver | null>(null);
|
||||
const root = ref<HTMLElement | null>(null);
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (props.enabled && observer.value) {
|
||||
observer.value.disconnect();
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
if (!props.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const options = {
|
||||
root: this.$refs.root as Element,
|
||||
root: root.value,
|
||||
rootMargin: '0px',
|
||||
threshold: this.threshold,
|
||||
threshold: props.threshold,
|
||||
};
|
||||
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
const intersectionObserver = new IntersectionObserver((entries) => {
|
||||
entries.forEach(({ target, isIntersecting }) => {
|
||||
this.$emit('observed', {
|
||||
el: target,
|
||||
emit('observed', {
|
||||
el: target as HTMLElement,
|
||||
isIntersecting,
|
||||
});
|
||||
});
|
||||
}, options);
|
||||
|
||||
this.observer = observer;
|
||||
observer.value = intersectionObserver;
|
||||
|
||||
this.eventBus.on('observe', (observed: Element) => {
|
||||
props.eventBus.on('observe', (observed: Element) => {
|
||||
if (observed) {
|
||||
observer.observe(observed);
|
||||
intersectionObserver.observe(observed);
|
||||
}
|
||||
});
|
||||
|
||||
this.eventBus.on('unobserve', (observed: Element) => {
|
||||
observer.unobserve(observed);
|
||||
props.eventBus.on('unobserve', (observed: Element) => {
|
||||
intersectionObserver.unobserve(observed);
|
||||
});
|
||||
},
|
||||
beforeUnmount() {
|
||||
if (this.enabled && this.observer) {
|
||||
this.observer.disconnect();
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,20 +1,14 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { mapStores } from 'pinia';
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { useRootStore } from '@/stores/root.store';
|
||||
import { useUIStore } from '@/stores/ui.store';
|
||||
|
||||
export default defineComponent({
|
||||
computed: {
|
||||
...mapStores(useRootStore, useUIStore),
|
||||
basePath(): string {
|
||||
return this.rootStore.baseUrl;
|
||||
},
|
||||
logoPath(): string {
|
||||
return this.basePath + this.uiStore.logo;
|
||||
},
|
||||
},
|
||||
});
|
||||
const rootStore = useRootStore();
|
||||
const uiStore = useUIStore();
|
||||
|
||||
const basePath = computed(() => rootStore.baseUrl);
|
||||
|
||||
const logoPath = computed(() => basePath.value + uiStore.logo);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,85 +1,71 @@
|
||||
<script lang="ts">
|
||||
<script setup lang="ts">
|
||||
import { useUIStore } from '@/stores/ui.store';
|
||||
import { mapStores } from 'pinia';
|
||||
import { defineComponent } from 'vue';
|
||||
import type { PropType } from 'vue';
|
||||
import { onBeforeUnmount, onMounted } from 'vue';
|
||||
import type { EventBus } from 'n8n-design-system';
|
||||
import { ElDrawer } from 'element-plus';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ModalDrawer',
|
||||
components: {
|
||||
ElDrawer,
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
name: string;
|
||||
beforeClose?: Function;
|
||||
eventBus?: EventBus;
|
||||
direction: 'ltr' | 'rtl' | 'ttb' | 'btt';
|
||||
modal?: boolean;
|
||||
width: string;
|
||||
wrapperClosable?: boolean;
|
||||
}>(),
|
||||
{
|
||||
modal: true,
|
||||
wrapperClosable: true,
|
||||
},
|
||||
props: {
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
beforeClose: {
|
||||
type: Function,
|
||||
},
|
||||
eventBus: {
|
||||
type: Object as PropType<EventBus>,
|
||||
},
|
||||
direction: {
|
||||
type: String as PropType<'ltr' | 'rtl' | 'ttb' | 'btt'>,
|
||||
required: true,
|
||||
},
|
||||
modal: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
},
|
||||
wrapperClosable: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
window.addEventListener('keydown', this.onWindowKeydown);
|
||||
this.eventBus?.on('close', this.close);
|
||||
);
|
||||
|
||||
const activeElement = document.activeElement as HTMLElement;
|
||||
if (activeElement) {
|
||||
activeElement.blur();
|
||||
const emit = defineEmits<{
|
||||
enter: [];
|
||||
}>();
|
||||
|
||||
const uiStore = useUIStore();
|
||||
|
||||
const handleEnter = () => {
|
||||
if (uiStore.isModalActiveById[props.name]) {
|
||||
emit('enter');
|
||||
}
|
||||
},
|
||||
beforeUnmount() {
|
||||
this.eventBus?.off('close', this.close);
|
||||
window.removeEventListener('keydown', this.onWindowKeydown);
|
||||
},
|
||||
computed: {
|
||||
...mapStores(useUIStore),
|
||||
},
|
||||
methods: {
|
||||
onWindowKeydown(event: KeyboardEvent) {
|
||||
if (!this.uiStore.isModalActiveById[this.name]) {
|
||||
};
|
||||
|
||||
const onWindowKeydown = (event: KeyboardEvent) => {
|
||||
if (!uiStore.isModalActiveById[props.name]) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event && event.keyCode === 13) {
|
||||
this.handleEnter();
|
||||
handleEnter();
|
||||
}
|
||||
},
|
||||
handleEnter() {
|
||||
if (this.uiStore.isModalActiveById[this.name]) {
|
||||
this.$emit('enter');
|
||||
}
|
||||
},
|
||||
async close() {
|
||||
if (this.beforeClose) {
|
||||
const shouldClose = await this.beforeClose();
|
||||
};
|
||||
|
||||
const close = async () => {
|
||||
if (props.beforeClose) {
|
||||
const shouldClose = await props.beforeClose();
|
||||
if (shouldClose === false) {
|
||||
// must be strictly false to stop modal from closing
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.uiStore.closeModal(this.name);
|
||||
},
|
||||
},
|
||||
uiStore.closeModal(props.name);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('keydown', onWindowKeydown);
|
||||
props.eventBus?.on('close', close);
|
||||
|
||||
const activeElement = document.activeElement as HTMLElement;
|
||||
if (activeElement) {
|
||||
activeElement.blur();
|
||||
}
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
props.eventBus?.off('close', close);
|
||||
window.removeEventListener('keydown', onWindowKeydown);
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,25 +1,23 @@
|
||||
<script lang="ts">
|
||||
import type { PropType } from 'vue';
|
||||
import { defineComponent } from 'vue';
|
||||
<script setup lang="ts">
|
||||
import { useUIStore } from '@/stores/ui.store';
|
||||
import { mapStores } from 'pinia';
|
||||
import type { ModalKey } from '@/Interface';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ModalRoot',
|
||||
props: {
|
||||
name: {
|
||||
type: String as PropType<ModalKey>,
|
||||
required: true,
|
||||
},
|
||||
keepAlive: {
|
||||
type: Boolean,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapStores(useUIStore),
|
||||
},
|
||||
});
|
||||
defineProps<{
|
||||
name: string;
|
||||
keepAlive?: boolean;
|
||||
}>();
|
||||
|
||||
defineSlots<{
|
||||
default: {
|
||||
modalName: string;
|
||||
active: boolean;
|
||||
open: boolean;
|
||||
activeId: string;
|
||||
mode: string;
|
||||
data: Record<string, unknown>;
|
||||
};
|
||||
}>();
|
||||
|
||||
const uiStore = useUIStore();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -69,6 +69,7 @@ import ProjectMoveResourceModal from '@/components/Projects/ProjectMoveResourceM
|
||||
import NewAssistantSessionModal from '@/components/AskAssistant/NewAssistantSessionModal.vue';
|
||||
import PromptMfaCodeModal from './PromptMfaCodeModal/PromptMfaCodeModal.vue';
|
||||
import CommunityPlusEnrollmentModal from '@/components/CommunityPlusEnrollmentModal.vue';
|
||||
import type { EventBus } from 'n8n-design-system';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -183,7 +184,15 @@ import CommunityPlusEnrollmentModal from '@/components/CommunityPlusEnrollmentMo
|
||||
</ModalRoot>
|
||||
|
||||
<ModalRoot :name="LOG_STREAM_MODAL_KEY">
|
||||
<template #default="{ modalName, data }">
|
||||
<template
|
||||
#default="{
|
||||
modalName,
|
||||
data,
|
||||
}: {
|
||||
modalName: string;
|
||||
data: { destination: Object; isNew: boolean; eventBus: EventBus };
|
||||
}"
|
||||
>
|
||||
<EventDestinationSettingsModal
|
||||
:modal-name="modalName"
|
||||
:destination="data.destination"
|
||||
|
||||
@@ -1,48 +1,40 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import NodeIcon from '@/components/NodeIcon.vue';
|
||||
import type { ITemplatesNode } from '@/Interface';
|
||||
import { filterTemplateNodes } from '@/utils/nodeTypesUtils';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'NodeList',
|
||||
components: {
|
||||
NodeIcon,
|
||||
},
|
||||
props: {
|
||||
nodes: {
|
||||
type: Array,
|
||||
},
|
||||
limit: {
|
||||
type: Number,
|
||||
default: 4,
|
||||
},
|
||||
size: {
|
||||
type: String,
|
||||
default: 'sm',
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
filteredCoreNodes() {
|
||||
return filterTemplateNodes(this.nodes as ITemplatesNode[]);
|
||||
},
|
||||
hiddenNodes(): number {
|
||||
return this.filteredCoreNodes.length - this.countNodesToBeSliced(this.filteredCoreNodes);
|
||||
},
|
||||
slicedNodes(): ITemplatesNode[] {
|
||||
return this.filteredCoreNodes.slice(0, this.countNodesToBeSliced(this.filteredCoreNodes));
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
countNodesToBeSliced(nodes: ITemplatesNode[]): number {
|
||||
if (nodes.length > this.limit) {
|
||||
return this.limit - 1;
|
||||
} else {
|
||||
return this.limit;
|
||||
}
|
||||
},
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
nodes: ITemplatesNode[];
|
||||
limit?: number;
|
||||
size?: string;
|
||||
}>(),
|
||||
{
|
||||
limit: 4,
|
||||
size: 'sm',
|
||||
},
|
||||
);
|
||||
|
||||
const filteredCoreNodes = computed(() => {
|
||||
return filterTemplateNodes(props.nodes);
|
||||
});
|
||||
|
||||
const hiddenNodes = computed(() => {
|
||||
return filteredCoreNodes.value.length - countNodesToBeSliced(filteredCoreNodes.value);
|
||||
});
|
||||
|
||||
const slicedNodes = computed(() => {
|
||||
return filteredCoreNodes.value.slice(0, countNodesToBeSliced(filteredCoreNodes.value));
|
||||
});
|
||||
|
||||
const countNodesToBeSliced = (nodes: ITemplatesNode[]): number => {
|
||||
if (nodes.length > props.limit) {
|
||||
return props.limit - 1;
|
||||
} else {
|
||||
return props.limit;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,32 +1,29 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
<script setup lang="ts">
|
||||
import Draggable from './Draggable.vue';
|
||||
import type { XYPosition } from '@/Interface';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
Draggable,
|
||||
},
|
||||
props: {
|
||||
canMoveRight: {
|
||||
type: Boolean,
|
||||
},
|
||||
canMoveLeft: {
|
||||
type: Boolean,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onDrag(e: XYPosition) {
|
||||
this.$emit('drag', e);
|
||||
},
|
||||
onDragStart() {
|
||||
this.$emit('dragstart');
|
||||
},
|
||||
onDragEnd() {
|
||||
this.$emit('dragend');
|
||||
},
|
||||
},
|
||||
});
|
||||
defineProps<{
|
||||
canMoveRight: boolean;
|
||||
canMoveLeft: boolean;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
drag: [e: XYPosition];
|
||||
dragstart: [];
|
||||
dragend: [];
|
||||
}>();
|
||||
|
||||
const onDrag = (e: XYPosition) => {
|
||||
emit('drag', e);
|
||||
};
|
||||
|
||||
const onDragEnd = () => {
|
||||
emit('dragend');
|
||||
};
|
||||
|
||||
const onDragStart = () => {
|
||||
emit('dragstart');
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
<script setup lang="ts">
|
||||
import TitledList from '@/components/TitledList.vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ParameterIssues',
|
||||
components: {
|
||||
TitledList,
|
||||
},
|
||||
props: ['issues'],
|
||||
});
|
||||
defineProps<{
|
||||
issues: string[];
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import type { PropType } from 'vue';
|
||||
<script setup lang="ts">
|
||||
import TemplateDetailsBlock from '@/components/TemplateDetailsBlock.vue';
|
||||
import NodeIcon from '@/components/NodeIcon.vue';
|
||||
import { filterTemplateNodes } from '@/utils/nodeTypesUtils';
|
||||
@@ -11,51 +9,32 @@ import type {
|
||||
ITemplatesNode,
|
||||
ITemplatesWorkflow,
|
||||
} from '@/Interface';
|
||||
import { mapStores } from 'pinia';
|
||||
import { useTemplatesStore } from '@/stores/templates.store';
|
||||
import TimeAgo from '@/components/TimeAgo.vue';
|
||||
import { isFullTemplatesCollection, isTemplatesWorkflow } from '@/utils/templates/typeGuards';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useI18n } from '@/composables/useI18n';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'TemplateDetails',
|
||||
components: {
|
||||
NodeIcon,
|
||||
TemplateDetailsBlock,
|
||||
TimeAgo,
|
||||
},
|
||||
props: {
|
||||
template: {
|
||||
type: Object as PropType<
|
||||
ITemplatesWorkflow | ITemplatesCollection | ITemplatesCollectionFull | null
|
||||
>,
|
||||
required: true,
|
||||
},
|
||||
blockTitle: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapStores(useTemplatesStore),
|
||||
},
|
||||
methods: {
|
||||
abbreviateNumber,
|
||||
filterTemplateNodes,
|
||||
redirectToCategory(id: string) {
|
||||
this.templatesStore.resetSessionId();
|
||||
void this.$router.push(`/templates?categories=${id}`);
|
||||
},
|
||||
redirectToSearchPage(node: ITemplatesNode) {
|
||||
this.templatesStore.resetSessionId();
|
||||
void this.$router.push(`/templates?search=${node.displayName}`);
|
||||
},
|
||||
isFullTemplatesCollection,
|
||||
isTemplatesWorkflow,
|
||||
},
|
||||
});
|
||||
defineProps<{
|
||||
template: ITemplatesWorkflow | ITemplatesCollection | ITemplatesCollectionFull | null;
|
||||
blockTitle: string;
|
||||
loading: boolean;
|
||||
}>();
|
||||
|
||||
const router = useRouter();
|
||||
const i18n = useI18n();
|
||||
|
||||
const templatesStore = useTemplatesStore();
|
||||
|
||||
const redirectToCategory = (id: string) => {
|
||||
templatesStore.resetSessionId();
|
||||
void router.push(`/templates?categories=${id}`);
|
||||
};
|
||||
|
||||
const redirectToSearchPage = (node: ITemplatesNode) => {
|
||||
templatesStore.resetSessionId();
|
||||
void router.push(`/templates?search=${node.displayName}`);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -91,13 +70,13 @@ export default defineComponent({
|
||||
|
||||
<TemplateDetailsBlock
|
||||
v-if="!loading && template"
|
||||
:title="$locale.baseText('template.details.details')"
|
||||
:title="i18n.baseText('template.details.details')"
|
||||
>
|
||||
<div :class="$style.text">
|
||||
<n8n-text v-if="isTemplatesWorkflow(template)" size="small" color="text-base">
|
||||
{{ $locale.baseText('template.details.created') }}
|
||||
{{ i18n.baseText('template.details.created') }}
|
||||
<TimeAgo :date="template.createdAt" />
|
||||
{{ $locale.baseText('template.details.by') }}
|
||||
{{ i18n.baseText('template.details.by') }}
|
||||
{{ template.user ? template.user.username : 'n8n team' }}
|
||||
</n8n-text>
|
||||
</div>
|
||||
@@ -107,9 +86,9 @@ export default defineComponent({
|
||||
size="small"
|
||||
color="text-base"
|
||||
>
|
||||
{{ $locale.baseText('template.details.viewed') }}
|
||||
{{ i18n.baseText('template.details.viewed') }}
|
||||
{{ abbreviateNumber(template.totalViews) }}
|
||||
{{ $locale.baseText('template.details.times') }}
|
||||
{{ i18n.baseText('template.details.times') }}
|
||||
</n8n-text>
|
||||
</div>
|
||||
</TemplateDetailsBlock>
|
||||
|
||||
@@ -1,103 +1,66 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
<script setup lang="ts">
|
||||
import { computed, onBeforeMount, onBeforeUnmount, onMounted } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { mapStores } from 'pinia';
|
||||
import type { WorkerStatus } from '@n8n/api-types';
|
||||
import type { ExecutionStatus } from 'n8n-workflow';
|
||||
|
||||
import PushConnectionTracker from '@/components/PushConnectionTracker.vue';
|
||||
import { useI18n } from '@/composables/useI18n';
|
||||
import { useToast } from '@/composables/useToast';
|
||||
import { useUIStore } from '@/stores/ui.store';
|
||||
import { useOrchestrationStore } from '@/stores/orchestration.store';
|
||||
import { useDocumentTitle } from '@/composables/useDocumentTitle';
|
||||
import WorkerCard from './Workers/WorkerCard.ee.vue';
|
||||
import { usePushConnection } from '@/composables/usePushConnection';
|
||||
import { usePushConnectionStore } from '@/stores/pushConnection.store';
|
||||
import { useRootStore } from '@/stores/root.store';
|
||||
import { useTelemetry } from '@/composables/useTelemetry';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default defineComponent({
|
||||
name: 'WorkerList',
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/naming-convention
|
||||
components: { PushConnectionTracker, WorkerCard },
|
||||
props: {
|
||||
autoRefreshEnabled: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
autoRefreshEnabled?: boolean;
|
||||
}>(),
|
||||
{
|
||||
autoRefreshEnabled: true,
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
);
|
||||
|
||||
const router = useRouter();
|
||||
const i18n = useI18n();
|
||||
const pushConnection = usePushConnection({ router });
|
||||
const documentTitle = useDocumentTitle();
|
||||
const telemetry = useTelemetry();
|
||||
|
||||
return {
|
||||
i18n,
|
||||
pushConnection,
|
||||
...useToast(),
|
||||
documentTitle: useDocumentTitle(),
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapStores(useRootStore, useUIStore, usePushConnectionStore, useOrchestrationStore),
|
||||
combinedWorkers(): WorkerStatus[] {
|
||||
const returnData: WorkerStatus[] = [];
|
||||
for (const workerId in this.orchestrationManagerStore.workers) {
|
||||
returnData.push(this.orchestrationManagerStore.workers[workerId]);
|
||||
}
|
||||
return returnData;
|
||||
},
|
||||
initialStatusReceived(): boolean {
|
||||
return this.orchestrationManagerStore.initialStatusReceived;
|
||||
},
|
||||
workerIds(): string[] {
|
||||
return Object.keys(this.orchestrationManagerStore.workers);
|
||||
},
|
||||
pageTitle() {
|
||||
return this.i18n.baseText('workerList.pageTitle');
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.documentTitle.set(this.pageTitle);
|
||||
const orchestrationManagerStore = useOrchestrationStore();
|
||||
const rootStore = useRootStore();
|
||||
const pushStore = usePushConnectionStore();
|
||||
|
||||
this.$telemetry.track('User viewed worker view', {
|
||||
instance_id: this.rootStore.instanceId,
|
||||
const initialStatusReceived = computed(() => orchestrationManagerStore.initialStatusReceived);
|
||||
|
||||
const workerIds = computed(() => Object.keys(orchestrationManagerStore.workers));
|
||||
|
||||
const pageTitle = computed(() => i18n.baseText('workerList.pageTitle'));
|
||||
|
||||
onMounted(() => {
|
||||
documentTitle.set(pageTitle.value);
|
||||
|
||||
telemetry.track('User viewed worker view', {
|
||||
instance_id: rootStore.instanceId,
|
||||
});
|
||||
},
|
||||
beforeMount() {
|
||||
});
|
||||
|
||||
onBeforeMount(() => {
|
||||
if (window.Cypress !== undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.pushConnection.initialize();
|
||||
this.pushStore.pushConnect();
|
||||
this.orchestrationManagerStore.startWorkerStatusPolling();
|
||||
},
|
||||
beforeUnmount() {
|
||||
pushConnection.initialize();
|
||||
pushStore.pushConnect();
|
||||
orchestrationManagerStore.startWorkerStatusPolling();
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (window.Cypress !== undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.orchestrationManagerStore.stopWorkerStatusPolling();
|
||||
this.pushStore.pushDisconnect();
|
||||
this.pushConnection.terminate();
|
||||
},
|
||||
methods: {
|
||||
averageLoadAvg(loads: number[]) {
|
||||
return (loads.reduce((prev, curr) => prev + curr, 0) / loads.length).toFixed(2);
|
||||
},
|
||||
getStatus(payload: WorkerStatus): ExecutionStatus {
|
||||
if (payload.runningJobsSummary.length > 0) {
|
||||
return 'running';
|
||||
} else {
|
||||
return 'success';
|
||||
}
|
||||
},
|
||||
getRowClass(payload: WorkerStatus): string {
|
||||
return [this.$style.execRow, this.$style[this.getStatus(payload)]].join(' ');
|
||||
},
|
||||
},
|
||||
orchestrationManagerStore.stopWorkerStatusPolling();
|
||||
pushStore.pushDisconnect();
|
||||
pushConnection.terminate();
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -111,7 +74,7 @@ export default defineComponent({
|
||||
<n8n-spinner />
|
||||
</div>
|
||||
<div v-else>
|
||||
<div v-if="workerIds.length === 0">{{ $locale.baseText('workerList.empty') }}</div>
|
||||
<div v-if="workerIds.length === 0">{{ i18n.baseText('workerList.empty') }}</div>
|
||||
<div v-else>
|
||||
<div v-for="workerId in workerIds" :key="workerId" :class="$style.card">
|
||||
<WorkerCard :worker-id="workerId" data-test-id="worker-card" />
|
||||
|
||||
Reference in New Issue
Block a user