fix(editor): Implement insight's design feedback 4 (#14550)

This commit is contained in:
Raúl Gómez Morales
2025-04-11 16:41:59 +02:00
committed by GitHub
parent d390258001
commit cc089bebd4
4 changed files with 59 additions and 6 deletions

View File

@@ -104,7 +104,22 @@ watch(
/> />
<div v-if="insightsStore.isInsightsEnabled" :class="$style.insightsContent"> <div v-if="insightsStore.isInsightsEnabled" :class="$style.insightsContent">
<div :class="$style.insightsChartWrapper"> <div :class="$style.insightsChartWrapper">
<template v-if="insightsStore.charts.isLoading"> loading </template> <div v-if="insightsStore.charts.isLoading" :class="$style.chartLoader">
<svg
width="22"
height="22"
viewBox="0 0 22 22"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M21 11C21 16.5228 16.5228 21 11 21C5.47715 21 1 16.5228 1 11C1 5.47715 5.47715 1 11 1C11.6293 1 12.245 1.05813 12.8421 1.16931"
stroke="currentColor"
stroke-width="2"
/>
</svg>
{{ i18n.baseText('insights.chart.loading') }}
</div>
<component <component
:is="chartComponents[props.insightType]" :is="chartComponents[props.insightType]"
v-else v-else
@@ -169,4 +184,13 @@ watch(
.insightsTableWrapper { .insightsTableWrapper {
padding: var(--spacing-l) var(--spacing-l) 0; padding: var(--spacing-l) var(--spacing-l) 0;
} }
.chartLoader {
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 9px;
}
</style> </style>

View File

@@ -15,7 +15,7 @@ import { smartDecimal } from '@n8n/utils/number/smartDecimal';
import { useTelemetry } from '@/composables/useTelemetry'; import { useTelemetry } from '@/composables/useTelemetry';
import { VIEWS } from '@/constants'; import { VIEWS } from '@/constants';
import { computed, ref, watch } from 'vue'; import { computed, ref, watch } from 'vue';
import { type RouteLocationRaw } from 'vue-router'; import { type RouteLocationRaw, type LocationQueryRaw } from 'vue-router';
const props = defineProps<{ const props = defineProps<{
data: InsightsByWorkflow; data: InsightsByWorkflow;
@@ -81,7 +81,7 @@ const headers = ref<Array<TableHeader<Item>>>([
}, },
}, },
{ {
title: 'Project name', title: i18n.baseText('insights.dashboard.table.projectName'),
key: 'projectName', key: 'projectName',
disableSort: true, disableSort: true,
}, },
@@ -101,11 +101,12 @@ const emit = defineEmits<{
]; ];
}>(); }>();
const getWorkflowLink = (item: Item): RouteLocationRaw => ({ const getWorkflowLink = (item: Item, query?: LocationQueryRaw): RouteLocationRaw => ({
name: VIEWS.WORKFLOW, name: VIEWS.WORKFLOW,
params: { params: {
name: item.workflowId, name: item.workflowId,
}, },
query,
}); });
const trackWorkflowClick = (item: Item) => { const trackWorkflowClick = (item: Item) => {
@@ -146,6 +147,18 @@ watch(sortBy, (newValue) => {
</N8nTooltip> </N8nTooltip>
</router-link> </router-link>
</template> </template>
<template #[`item.timeSaved`]="{ item, value }">
<router-link
v-if="!item.timeSaved"
:to="getWorkflowLink(item, { settings: 'true' })"
class="link"
>
{{ i18n.baseText('insights.dashboard.table.estimate') }}
</router-link>
<template v-else>
{{ value }}
</template>
</template>
<template #[`item.projectName`]="{ item }"> <template #[`item.projectName`]="{ item }">
<N8nTooltip v-if="item.projectName" :content="item.projectName" placement="top"> <N8nTooltip v-if="item.projectName" :content="item.projectName" placement="top">
<div class="ellipsis"> <div class="ellipsis">
@@ -164,13 +177,20 @@ watch(sortBy, (newValue) => {
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
line-height: 1.2; line-height: 1.2;
width: fit-content;
max-width: 100%;
} }
.link { .link {
display: flex; display: inline-flex;
height: 100%; height: 100%;
align-items: center; align-items: center;
text-decoration: none; text-decoration: none;
color: var(--color-text-base); color: var(--color-text-base);
text-decoration: underline;
max-width: 100%;
&:hover {
color: var(--color-text-dark);
}
} }
</style> </style>

View File

@@ -3087,9 +3087,12 @@
"insights.banner.title.timeSaved": "Time saved", "insights.banner.title.timeSaved": "Time saved",
"insights.banner.title.timeSavedDailyAverage": "Time saved daily avg.", "insights.banner.title.timeSavedDailyAverage": "Time saved daily avg.",
"insights.banner.title.averageRunTime": "Run time (avg.)", "insights.banner.title.averageRunTime": "Run time (avg.)",
"insights.dashboard.table.projectName": "Project name",
"insights.dashboard.table.estimate": "Estimate",
"insights.dashboard.title": "Insights", "insights.dashboard.title": "Insights",
"insights.banner.title.timeSaved.tooltip": "Total time saved calculated from your estimated time savings per execution across all workflows", "insights.banner.title.timeSaved.tooltip": "Total time saved calculated from your estimated time savings per execution across all workflows",
"insights.banner.failureRate.deviation.tooltip": "Percentage point change from previous period", "insights.banner.failureRate.deviation.tooltip": "Percentage point change from previous period",
"insights.chart.failed": "Failed", "insights.chart.failed": "Failed",
"insights.chart.succeeded": "Successful" "insights.chart.succeeded": "Successful",
"insights.chart.loading": "Loading data..."
} }

View File

@@ -64,6 +64,7 @@ import {
VALID_WORKFLOW_IMPORT_URL_REGEX, VALID_WORKFLOW_IMPORT_URL_REGEX,
VIEWS, VIEWS,
AI_CREDITS_EXPERIMENT, AI_CREDITS_EXPERIMENT,
WORKFLOW_SETTINGS_MODAL_KEY,
} from '@/constants'; } from '@/constants';
import { useSourceControlStore } from '@/stores/sourceControl.store'; import { useSourceControlStore } from '@/stores/sourceControl.store';
import { useNodeCreatorStore } from '@/stores/nodeCreator.store'; import { useNodeCreatorStore } from '@/stores/nodeCreator.store';
@@ -1681,6 +1682,11 @@ onMounted(() => {
// Once view is initialized, pick up all toast notifications // Once view is initialized, pick up all toast notifications
// waiting in the store and display them // waiting in the store and display them
toast.showNotificationForViews([VIEWS.WORKFLOW, VIEWS.NEW_WORKFLOW]); toast.showNotificationForViews([VIEWS.WORKFLOW, VIEWS.NEW_WORKFLOW]);
if (route.query.settings) {
uiStore.openModal(WORKFLOW_SETTINGS_MODAL_KEY);
void router.replace({ query: { settings: undefined } });
}
}) })
.finally(() => { .finally(() => {
isLoading.value = false; isLoading.value = false;