From 152ca977198e750d268b89ca8b619c00bc3bb3d4 Mon Sep 17 00:00:00 2001 From: Mutasem Aldmour Date: Thu, 7 Nov 2024 12:43:26 +0100 Subject: [PATCH] feat: bring back UI --- packages/editor-ui/src/Interface.ts | 1 + packages/editor-ui/src/components/RunData.vue | 77 ++++++++++++++++++- .../src/components/RunDataAi/RunDataAi.vue | 1 + .../components/RunDataAi/RunDataAiContent.vue | 10 +++ .../WorkflowLMChat/WorkflowLMChat.vue | 10 +-- .../global/GlobalExecutionsListItem.vue | 8 +- .../src/composables/useExecutionHelpers.ts | 12 +++ .../src/composables/useNodeHelpers.ts | 42 ++++++---- 8 files changed, 132 insertions(+), 29 deletions(-) diff --git a/packages/editor-ui/src/Interface.ts b/packages/editor-ui/src/Interface.ts index aec2889070..04daff95d5 100644 --- a/packages/editor-ui/src/Interface.ts +++ b/packages/editor-ui/src/Interface.ts @@ -182,6 +182,7 @@ export interface IAiDataContent { metadata: { executionTime: number; startTime: number; + executionId?: string; }; } diff --git a/packages/editor-ui/src/components/RunData.vue b/packages/editor-ui/src/components/RunData.vue index a3d354d7e7..79f2072b30 100644 --- a/packages/editor-ui/src/components/RunData.vue +++ b/packages/editor-ui/src/components/RunData.vue @@ -58,12 +58,13 @@ import { useNodeTypesStore } from '@/stores/nodeTypes.store'; import { useNodeHelpers } from '@/composables/useNodeHelpers'; import { useNodeType } from '@/composables/useNodeType'; import { useToast } from '@/composables/useToast'; -import { isEqual, isObject } from 'lodash-es'; +import { get, isEqual, isObject } from 'lodash-es'; import { useExternalHooks } from '@/composables/useExternalHooks'; import { useSourceControlStore } from '@/stores/sourceControl.store'; import { useRootStore } from '@/stores/root.store'; import RunDataPinButton from '@/components/RunDataPinButton.vue'; import { getGenericHints } from '@/utils/nodeViewUtils'; +import { useExecutionHelpers } from '@/composables/useExecutionHelpers'; const LazyRunDataTable = defineAsyncComponent( async () => await import('@/components/RunDataTable.vue'), @@ -180,6 +181,7 @@ export default defineComponent({ const { isSubNodeType } = useNodeType({ node, }); + const { openExecutionInNewTab } = useExecutionHelpers(); return { ...useToast(), @@ -187,6 +189,7 @@ export default defineComponent({ nodeHelpers, pinnedData, isSubNodeType, + openExecutionInNewTab, }; }, data() { @@ -220,6 +223,43 @@ export default defineComponent({ useSourceControlStore, useRootStore, ), + subWorkflowData(): { executionId: string; workflowId?: string } | null { + if (!this.node) { + return null; + } + const metadata = get(this.workflowRunData, [this.node.name, this.runIndex, 'metadata'], null); + if (metadata?.executionId) { + return { + executionId: metadata?.executionId, + workflowId: metadata?.workflowId, + }; + } + return null; + }, + itemSubWorkflowData(): Array<{ + metadata?: { executionId?: string; workflowId?: string }; + itemIndex: number; + }> { + const items = this.getData(this.runIndex, this.currentOutputIndex); + return items + .map((item, itemIndex) => { + return { + metadata: item.metadata, + itemIndex, + }; + }) + .filter((item) => { + return !!item.metadata; + }); + }, + hasInputOverwrite(): boolean { + if (!this.node) { + return false; + } + const taskData = this.nodeHelpers.getNodeTaskData(this.node, this.runIndex); + if (taskData === null) return false; + return !!taskData.inputOverride; + }, isReadOnlyRoute() { return this.$route?.meta?.readOnlyCanvas === true; }, @@ -654,6 +694,15 @@ export default defineComponent({ this.hidePinDataDiscoveryTooltip(); }, methods: { + getData( + runIndex: number, + outputIndex: number, + connectionType: NodeConnectionType = NodeConnectionType.Main, + ) { + const rawInputData = this.getRawInputData(runIndex, outputIndex, connectionType); + const pinOrLiveData = this.getPinDataOrLiveData(rawInputData); + return this.getFilteredData(pinOrLiveData); + }, getResolvedNodeOutputs() { if (this.node && this.nodeType) { const workflowNode = this.workflow.getNode(this.node.name); @@ -1383,6 +1432,32 @@ export default defineComponent({ +
+ +
+ + + +
+
+
{ }} +
  • + EXECUTION ID {{ runMeta?.executionId }} +
  • {{ $locale.baseText('runData.aiContentBlock.tokens', { diff --git a/packages/editor-ui/src/components/WorkflowLMChat/WorkflowLMChat.vue b/packages/editor-ui/src/components/WorkflowLMChat/WorkflowLMChat.vue index e76ab8241a..b2576b908d 100644 --- a/packages/editor-ui/src/components/WorkflowLMChat/WorkflowLMChat.vue +++ b/packages/editor-ui/src/components/WorkflowLMChat/WorkflowLMChat.vue @@ -12,7 +12,6 @@ import { CHAT_TRIGGER_NODE_TYPE, MANUAL_CHAT_TRIGGER_NODE_TYPE, MODAL_CONFIRM, - VIEWS, WORKFLOW_LM_CHAT_MODAL_KEY, } from '@/constants'; @@ -58,6 +57,7 @@ import { usePinnedData } from '@/composables/usePinnedData'; import { get, last } from 'lodash-es'; import { isEmpty } from '@/utils/typesUtils'; import { chatEventBus } from '@n8n/chat/event-buses'; +import { useExecutionHelpers } from '@/composables/useExecutionHelpers'; const LazyRunDataAi = defineAsyncComponent( async () => await import('@/components/RunDataAi/RunDataAi.vue'), @@ -82,6 +82,8 @@ const workflowsStore = useWorkflowsStore(); const nodeTypesStore = useNodeTypesStore(); const uiStore = useUIStore(); +const executionHelpers = useExecutionHelpers(); + const { showError } = useToast(); const messages: Ref = ref([]); const currentSessionId = ref(String(Date.now())); @@ -428,11 +430,7 @@ async function sendMessage(message: string, files?: File[]) { } function displayExecution(executionId: string) { - const route = router.resolve({ - name: VIEWS.EXECUTION_PREVIEW, - params: { name: workflow.value.id, executionId }, - }); - window.open(route.href, '_blank'); + executionHelpers.openExecutionInNewTab(executionId, workflow.value.id); } function isTextMessage(message: ChatMessage): message is ChatMessageText { return message.type === 'text' || !message.type; diff --git a/packages/editor-ui/src/components/executions/global/GlobalExecutionsListItem.vue b/packages/editor-ui/src/components/executions/global/GlobalExecutionsListItem.vue index f23dbbc4ac..1b258f3e53 100644 --- a/packages/editor-ui/src/components/executions/global/GlobalExecutionsListItem.vue +++ b/packages/editor-ui/src/components/executions/global/GlobalExecutionsListItem.vue @@ -2,7 +2,7 @@ import { ref, computed, useCssModule } from 'vue'; import type { ExecutionSummary } from 'n8n-workflow'; import { useI18n } from '@/composables/useI18n'; -import { VIEWS, WAIT_TIME_UNLIMITED } from '@/constants'; +import { WAIT_TIME_UNLIMITED } from '@/constants'; import { useRouter } from 'vue-router'; import { convertToDisplayDate } from '@/utils/formatters/dateFormatter'; import { i18n as locale } from '@/plugins/i18n'; @@ -138,11 +138,7 @@ function formatDate(fullDate: Date | string | number) { } function displayExecution() { - const route = router.resolve({ - name: VIEWS.EXECUTION_PREVIEW, - params: { name: props.execution.workflowId, executionId: props.execution.id }, - }); - window.open(route.href, '_blank'); + executionHelpers.openExecutionInNewTab(props.execution.id, props.execution.workflowId); } function onStopExecution() { diff --git a/packages/editor-ui/src/composables/useExecutionHelpers.ts b/packages/editor-ui/src/composables/useExecutionHelpers.ts index c87006c92a..a68cfd85bf 100644 --- a/packages/editor-ui/src/composables/useExecutionHelpers.ts +++ b/packages/editor-ui/src/composables/useExecutionHelpers.ts @@ -1,6 +1,8 @@ import type { ExecutionSummary } from 'n8n-workflow'; import { convertToDisplayDate } from '@/utils/formatters/dateFormatter'; import { useI18n } from '@/composables/useI18n'; +import { useRouter } from 'vue-router'; +import { VIEWS } from '@/constants'; export interface IExecutionUIData { name: string; @@ -14,6 +16,7 @@ export interface IExecutionUIData { export function useExecutionHelpers() { const i18n = useI18n(); + const router = useRouter(); function getUIDetails(execution: ExecutionSummary): IExecutionUIData { const status = { @@ -69,9 +72,18 @@ export function useExecutionHelpers() { return ['crashed', 'error'].includes(execution.status) && !execution.retrySuccessId; } + function openExecutionInNewTab(executionId: string, workflowId?: string) { + const route = router.resolve({ + name: VIEWS.EXECUTION_PREVIEW, + params: { name: workflowId, executionId }, + }); + window.open(route.href, '_blank'); + } + return { getUIDetails, formatDate, isExecutionRetriable, + openExecutionInNewTab, }; } diff --git a/packages/editor-ui/src/composables/useNodeHelpers.ts b/packages/editor-ui/src/composables/useNodeHelpers.ts index f2f33a5ef7..42a573bdf2 100644 --- a/packages/editor-ui/src/composables/useNodeHelpers.ts +++ b/packages/editor-ui/src/composables/useNodeHelpers.ts @@ -565,6 +565,29 @@ export function useNodeHelpers() { } } + function getNodeTaskData(node: INodeUi | null, runIndex = 0) { + if (node === null) { + return null; + } + if (workflowsStore.getWorkflowExecution === null) { + return null; + } + + const executionData = workflowsStore.getWorkflowExecution.data; + if (!executionData?.resultData) { + // unknown status + return null; + } + const runData = executionData.resultData.runData; + + const taskData = get(runData, [node.name, runIndex]); + if (!taskData) { + return null; + } + + return taskData; + } + function getNodeInputData( node: INodeUi | null, runIndex = 0, @@ -582,22 +605,8 @@ export function useNodeHelpers() { runIndex = runIndex - 1; } - if (node === null) { - return []; - } - if (workflowsStore.getWorkflowExecution === null) { - return []; - } - - const executionData = workflowsStore.getWorkflowExecution.data; - if (!executionData?.resultData) { - // unknown status - return []; - } - const runData = executionData.resultData.runData; - - const taskData = get(runData, [node.name, runIndex]); - if (!taskData) { + const taskData = getNodeTaskData(node, runIndex); + if (taskData === null) { return []; } @@ -1281,5 +1290,6 @@ export function useNodeHelpers() { removeConnectionByConnectionInfo, addPinDataConnections, removePinDataConnections, + getNodeTaskData, }; }