mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-18 10:31:15 +00:00
fix(editor): Support 'View Execution' links with multiple branches (#14345)
This commit is contained in:
@@ -86,11 +86,11 @@ import {
|
|||||||
} from '@n8n/design-system';
|
} from '@n8n/design-system';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import { useExecutionHelpers } from '@/composables/useExecutionHelpers';
|
|
||||||
import { useUIStore } from '@/stores/ui.store';
|
import { useUIStore } from '@/stores/ui.store';
|
||||||
import { useSchemaPreviewStore } from '@/stores/schemaPreview.store';
|
import { useSchemaPreviewStore } from '@/stores/schemaPreview.store';
|
||||||
import { asyncComputed } from '@vueuse/core';
|
import { asyncComputed } from '@vueuse/core';
|
||||||
import { usePostHog } from '@/stores/posthog.store';
|
import { usePostHog } from '@/stores/posthog.store';
|
||||||
|
import ViewSubExecution from './ViewSubExecution.vue';
|
||||||
|
|
||||||
const LazyRunDataTable = defineAsyncComponent(
|
const LazyRunDataTable = defineAsyncComponent(
|
||||||
async () => await import('@/components/RunDataTable.vue'),
|
async () => await import('@/components/RunDataTable.vue'),
|
||||||
@@ -200,7 +200,6 @@ const nodeHelpers = useNodeHelpers();
|
|||||||
const externalHooks = useExternalHooks();
|
const externalHooks = useExternalHooks();
|
||||||
const telemetry = useTelemetry();
|
const telemetry = useTelemetry();
|
||||||
const i18n = useI18n();
|
const i18n = useI18n();
|
||||||
const { trackOpeningRelatedExecution, resolveRelatedExecutionUrl } = useExecutionHelpers();
|
|
||||||
|
|
||||||
const node = toRef(props, 'node');
|
const node = toRef(props, 'node');
|
||||||
|
|
||||||
@@ -558,12 +557,6 @@ const activeTaskMetadata = computed((): ITaskMetadata | null => {
|
|||||||
return workflowRunData.value?.[node.value.name]?.[props.runIndex]?.metadata ?? null;
|
return workflowRunData.value?.[node.value.name]?.[props.runIndex]?.metadata ?? null;
|
||||||
});
|
});
|
||||||
|
|
||||||
const hasRelatedExecution = computed(() => {
|
|
||||||
return Boolean(
|
|
||||||
activeTaskMetadata.value?.subExecution ?? activeTaskMetadata.value?.parentExecution,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
const hasInputOverwrite = computed((): boolean => {
|
const hasInputOverwrite = computed((): boolean => {
|
||||||
if (!node.value) {
|
if (!node.value) {
|
||||||
return false;
|
return false;
|
||||||
@@ -1313,26 +1306,6 @@ function onSearchClear() {
|
|||||||
document.dispatchEvent(new KeyboardEvent('keyup', { key: '/' }));
|
document.dispatchEvent(new KeyboardEvent('keyup', { key: '/' }));
|
||||||
}
|
}
|
||||||
|
|
||||||
function getExecutionLinkLabel(task: ITaskMetadata): string | undefined {
|
|
||||||
if (task.parentExecution) {
|
|
||||||
return i18n.baseText('runData.openParentExecution', {
|
|
||||||
interpolate: { id: task.parentExecution.executionId },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (task.subExecution) {
|
|
||||||
if (activeTaskMetadata.value?.subExecutionsCount === 1) {
|
|
||||||
return i18n.baseText('runData.openSubExecutionSingle');
|
|
||||||
} else {
|
|
||||||
return i18n.baseText('runData.openSubExecutionWithId', {
|
|
||||||
interpolate: { id: task.subExecution.executionId },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
defineExpose({ enterEditMode });
|
defineExpose({ enterEditMode });
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -1504,20 +1477,11 @@ defineExpose({ enterEditMode });
|
|||||||
|
|
||||||
<slot name="run-info"></slot>
|
<slot name="run-info"></slot>
|
||||||
</div>
|
</div>
|
||||||
|
<ViewSubExecution
|
||||||
<a
|
v-if="activeTaskMetadata && !(paneType === 'input' && hasInputOverwrite)"
|
||||||
v-if="
|
:task-metadata="activeTaskMetadata"
|
||||||
activeTaskMetadata && hasRelatedExecution && !(paneType === 'input' && hasInputOverwrite)
|
:display-mode="displayMode"
|
||||||
"
|
/>
|
||||||
:class="$style.relatedExecutionInfo"
|
|
||||||
data-test-id="related-execution-link"
|
|
||||||
:href="resolveRelatedExecutionUrl(activeTaskMetadata)"
|
|
||||||
target="_blank"
|
|
||||||
@click.stop="trackOpeningRelatedExecution(activeTaskMetadata, displayMode)"
|
|
||||||
>
|
|
||||||
<N8nIcon icon="external-link-alt" size="xsmall" />
|
|
||||||
{{ getExecutionLinkLabel(activeTaskMetadata) }}
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<slot v-if="!displaysMultipleNodes" name="before-data" />
|
<slot v-if="!displaysMultipleNodes" name="before-data" />
|
||||||
@@ -1544,6 +1508,11 @@ defineExpose({ enterEditMode });
|
|||||||
data-test-id="branches"
|
data-test-id="branches"
|
||||||
>
|
>
|
||||||
<slot v-if="inputSelectLocation === 'outputs'" name="input-select"></slot>
|
<slot v-if="inputSelectLocation === 'outputs'" name="input-select"></slot>
|
||||||
|
<ViewSubExecution
|
||||||
|
v-if="activeTaskMetadata && !(paneType === 'input' && hasInputOverwrite)"
|
||||||
|
:task-metadata="activeTaskMetadata"
|
||||||
|
:display-mode="displayMode"
|
||||||
|
/>
|
||||||
|
|
||||||
<div :class="$style.tabs">
|
<div :class="$style.tabs">
|
||||||
<N8nTabs
|
<N8nTabs
|
||||||
@@ -1594,20 +1563,11 @@ defineExpose({ enterEditMode });
|
|||||||
}}
|
}}
|
||||||
</span>
|
</span>
|
||||||
</N8nText>
|
</N8nText>
|
||||||
|
<ViewSubExecution
|
||||||
<a
|
v-if="activeTaskMetadata && !(paneType === 'input' && hasInputOverwrite)"
|
||||||
v-if="
|
:task-metadata="activeTaskMetadata"
|
||||||
activeTaskMetadata && hasRelatedExecution && !(paneType === 'input' && hasInputOverwrite)
|
:display-mode="displayMode"
|
||||||
"
|
/>
|
||||||
:class="$style.relatedExecutionInfo"
|
|
||||||
data-test-id="related-execution-link"
|
|
||||||
:href="resolveRelatedExecutionUrl(activeTaskMetadata)"
|
|
||||||
target="_blank"
|
|
||||||
@click.stop="trackOpeningRelatedExecution(activeTaskMetadata, displayMode)"
|
|
||||||
>
|
|
||||||
<N8nIcon icon="external-link-alt" size="xsmall" />
|
|
||||||
{{ getExecutionLinkLabel(activeTaskMetadata) }}
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div ref="dataContainerRef" :class="$style.dataContainer" data-test-id="ndv-data-container">
|
<div ref="dataContainerRef" :class="$style.dataContainer" data-test-id="ndv-data-container">
|
||||||
@@ -2304,15 +2264,6 @@ defineExpose({ enterEditMode });
|
|||||||
.schema {
|
.schema {
|
||||||
padding: 0 var(--spacing-s);
|
padding: 0 var(--spacing-s);
|
||||||
}
|
}
|
||||||
|
|
||||||
.relatedExecutionInfo {
|
|
||||||
font-size: var(--font-size-s);
|
|
||||||
margin-left: var(--spacing-3xs);
|
|
||||||
|
|
||||||
svg {
|
|
||||||
padding-bottom: 2px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ import type { INodeTypeDescription, NodeConnectionType, NodeError } from 'n8n-wo
|
|||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
import NodeIcon from '@/components/NodeIcon.vue';
|
import NodeIcon from '@/components/NodeIcon.vue';
|
||||||
import AiRunContentBlock from './AiRunContentBlock.vue';
|
import AiRunContentBlock from './AiRunContentBlock.vue';
|
||||||
import { useExecutionHelpers } from '@/composables/useExecutionHelpers';
|
|
||||||
import { useI18n } from '@/composables/useI18n';
|
import { useI18n } from '@/composables/useI18n';
|
||||||
import { formatTokenUsageCount, getConsumedTokens } from '@/components/RunDataAi/utils';
|
import { formatTokenUsageCount, getConsumedTokens } from '@/components/RunDataAi/utils';
|
||||||
import ConsumedTokensDetails from '@/components/ConsumedTokensDetails.vue';
|
import ConsumedTokensDetails from '@/components/ConsumedTokensDetails.vue';
|
||||||
|
import ViewSubExecution from '../ViewSubExecution.vue';
|
||||||
|
|
||||||
interface RunMeta {
|
interface RunMeta {
|
||||||
startTimeMs: number;
|
startTimeMs: number;
|
||||||
@@ -30,7 +30,6 @@ const props = defineProps<{
|
|||||||
const nodeTypesStore = useNodeTypesStore();
|
const nodeTypesStore = useNodeTypesStore();
|
||||||
const workflowsStore = useWorkflowsStore();
|
const workflowsStore = useWorkflowsStore();
|
||||||
|
|
||||||
const { trackOpeningRelatedExecution, resolveRelatedExecutionUrl } = useExecutionHelpers();
|
|
||||||
const i18n = useI18n();
|
const i18n = useI18n();
|
||||||
|
|
||||||
const consumedTokensSum = computed(() => {
|
const consumedTokensSum = computed(() => {
|
||||||
@@ -105,15 +104,8 @@ const outputError = computed(() => {
|
|||||||
}}
|
}}
|
||||||
</n8n-tooltip>
|
</n8n-tooltip>
|
||||||
</li>
|
</li>
|
||||||
<li v-if="runMeta?.subExecution">
|
<li v-if="runMeta">
|
||||||
<a
|
<ViewSubExecution :task-metadata="runMeta" :display-mode="'ai'" :inline="true" />
|
||||||
:href="resolveRelatedExecutionUrl(runMeta)"
|
|
||||||
target="_blank"
|
|
||||||
@click.stop="trackOpeningRelatedExecution(runMeta, 'ai')"
|
|
||||||
>
|
|
||||||
<N8nIcon icon="external-link-alt" size="xsmall" />
|
|
||||||
{{ i18n.baseText('runData.openSubExecutionSingle') }}
|
|
||||||
</a>
|
|
||||||
</li>
|
</li>
|
||||||
<li v-if="(consumedTokensSum?.totalTokens ?? 0) > 0" :class="$style.tokensUsage">
|
<li v-if="(consumedTokensSum?.totalTokens ?? 0) > 0" :class="$style.tokensUsage">
|
||||||
{{
|
{{
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { useExecutionHelpers } from '@/composables/useExecutionHelpers';
|
||||||
|
import { useI18n } from '@/composables/useI18n';
|
||||||
|
import type { IRunDataDisplayMode } from '@/Interface';
|
||||||
|
import type { ITaskMetadata } from 'n8n-workflow';
|
||||||
|
import { computed } from 'vue';
|
||||||
|
|
||||||
|
const { trackOpeningRelatedExecution, resolveRelatedExecutionUrl } = useExecutionHelpers();
|
||||||
|
const i18n = useI18n();
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
taskMetadata: ITaskMetadata;
|
||||||
|
displayMode: IRunDataDisplayMode;
|
||||||
|
inline?: boolean;
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
inline: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const hasRelatedExecution = computed(() => {
|
||||||
|
return Boolean(props.taskMetadata.subExecution ?? props.taskMetadata.parentExecution);
|
||||||
|
});
|
||||||
|
|
||||||
|
function getExecutionLinkLabel(task: ITaskMetadata): string | undefined {
|
||||||
|
if (task.parentExecution) {
|
||||||
|
return i18n.baseText('runData.openParentExecution', {
|
||||||
|
interpolate: { id: task.parentExecution.executionId },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (task.subExecution) {
|
||||||
|
if (props.taskMetadata.subExecutionsCount === 1) {
|
||||||
|
return i18n.baseText('runData.openSubExecutionSingle');
|
||||||
|
} else {
|
||||||
|
return i18n.baseText('runData.openSubExecutionWithId', {
|
||||||
|
interpolate: { id: task.subExecution.executionId },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<a
|
||||||
|
v-if="hasRelatedExecution"
|
||||||
|
:class="{ [$style.relatedExecutionInfo]: !inline }"
|
||||||
|
data-test-id="related-execution-link"
|
||||||
|
:href="resolveRelatedExecutionUrl(taskMetadata)"
|
||||||
|
target="_blank"
|
||||||
|
@click.stop="trackOpeningRelatedExecution(taskMetadata, displayMode)"
|
||||||
|
>
|
||||||
|
<N8nIcon icon="external-link-alt" size="xsmall" />
|
||||||
|
{{ getExecutionLinkLabel(taskMetadata) }}
|
||||||
|
</a>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" module>
|
||||||
|
.relatedExecutionInfo {
|
||||||
|
font-size: var(--font-size-s);
|
||||||
|
margin-left: var(--spacing-3xs);
|
||||||
|
|
||||||
|
svg {
|
||||||
|
padding-bottom: var(--spacing-5xs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user