@n8n/frontend-module-insights 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/LICENSE.md +88 -0
  2. package/README.md +64 -0
  3. package/biome.jsonc +4 -0
  4. package/eslint.config.mjs +33 -0
  5. package/package.json +72 -0
  6. package/src/__tests__/setup.ts +4 -0
  7. package/src/chartjs.utils.ts +208 -0
  8. package/src/components/InsightsDashboard.test.ts +891 -0
  9. package/src/components/InsightsDashboard.vue +426 -0
  10. package/src/components/InsightsDataRangePicker.vue +195 -0
  11. package/src/components/InsightsDateRangeAlert.test.ts +134 -0
  12. package/src/components/InsightsDateRangeAlert.vue +89 -0
  13. package/src/components/InsightsPaywall.vue +42 -0
  14. package/src/components/InsightsSummary.test.ts +206 -0
  15. package/src/components/InsightsSummary.vue +357 -0
  16. package/src/components/InsightsUpgradeModal.vue +74 -0
  17. package/src/components/__snapshots__/InsightsSummary.test.ts.snap +446 -0
  18. package/src/components/charts/InsightsChartAverageRuntime.vue +67 -0
  19. package/src/components/charts/InsightsChartFailed.vue +57 -0
  20. package/src/components/charts/InsightsChartFailureRate.vue +58 -0
  21. package/src/components/charts/InsightsChartTimeSaved.vue +74 -0
  22. package/src/components/charts/InsightsChartTotal.vue +58 -0
  23. package/src/components/charts/insightChartProps.ts +9 -0
  24. package/src/components/tables/InsightsTableWorkflows.test.ts +509 -0
  25. package/src/components/tables/InsightsTableWorkflows.vue +279 -0
  26. package/src/index.ts +4 -0
  27. package/src/insights.api.test.ts +435 -0
  28. package/src/insights.api.ts +71 -0
  29. package/src/insights.constants.ts +69 -0
  30. package/src/insights.module.test.ts +59 -0
  31. package/src/insights.module.ts +45 -0
  32. package/src/insights.store.test.ts +118 -0
  33. package/src/insights.store.ts +88 -0
  34. package/src/insights.types.ts +16 -0
  35. package/src/insights.utils.test.ts +339 -0
  36. package/src/insights.utils.ts +172 -0
  37. package/stylelint.config.mjs +9 -0
  38. package/vite.config.ts +29 -0
@@ -0,0 +1,57 @@
1
+ <script lang="ts" setup>
2
+ import { useI18n } from '@n8n/i18n';
3
+ import { smartDecimal } from '@n8n/utils/number/smart-decimal';
4
+ import { useCssVar } from '@vueuse/core';
5
+ import type { ChartData } from 'chart.js';
6
+ import { computed } from 'vue';
7
+ import { Bar } from 'vue-chartjs';
8
+
9
+ import type { ChartProps } from './insightChartProps';
10
+ import { generateBarChartOptions } from '../../chartjs.utils';
11
+ import { GRANULARITY_DATE_FORMAT_MASK } from '../../insights.constants';
12
+
13
+ const props = defineProps<ChartProps>();
14
+
15
+ const i18n = useI18n();
16
+
17
+ const colorPrimary = useCssVar('--color--primary', document.body);
18
+ const chartOptions = computed(() =>
19
+ generateBarChartOptions({
20
+ plugins: {
21
+ tooltip: {
22
+ callbacks: {
23
+ label: (context) => {
24
+ const label = context.dataset.label ?? '';
25
+ return `${label} ${smartDecimal(context.parsed.y)}`;
26
+ },
27
+ },
28
+ },
29
+ },
30
+ }),
31
+ );
32
+
33
+ const chartData = computed<ChartData<'bar'>>(() => {
34
+ const labels: string[] = [];
35
+ const data: number[] = [];
36
+
37
+ for (const entry of props.data) {
38
+ labels.push(GRANULARITY_DATE_FORMAT_MASK[props.granularity](entry.date));
39
+ data.push(entry.values.failed);
40
+ }
41
+
42
+ return {
43
+ labels,
44
+ datasets: [
45
+ {
46
+ label: i18n.baseText('insights.chart.failed'),
47
+ data,
48
+ backgroundColor: colorPrimary.value,
49
+ },
50
+ ],
51
+ };
52
+ });
53
+ </script>
54
+
55
+ <template>
56
+ <Bar data-test-id="insights-chart-failed" :data="chartData" :options="chartOptions" />
57
+ </template>
@@ -0,0 +1,58 @@
1
+ <script lang="ts" setup>
2
+ import { useI18n } from '@n8n/i18n';
3
+ import { smartDecimal } from '@n8n/utils/number/smart-decimal';
4
+ import { useCssVar } from '@vueuse/core';
5
+ import type { ChartData } from 'chart.js';
6
+ import { computed } from 'vue';
7
+ import { Bar } from 'vue-chartjs';
8
+
9
+ import type { ChartProps } from './insightChartProps';
10
+ import { generateBarChartOptions } from '../../chartjs.utils';
11
+ import { GRANULARITY_DATE_FORMAT_MASK, INSIGHTS_UNIT_MAPPING } from '../../insights.constants';
12
+ import { transformInsightsFailureRate } from '../../insights.utils';
13
+
14
+ const props = defineProps<ChartProps>();
15
+
16
+ const i18n = useI18n();
17
+
18
+ const colorPrimary = useCssVar('--color--primary', document.body);
19
+ const chartOptions = computed(() =>
20
+ generateBarChartOptions({
21
+ plugins: {
22
+ tooltip: {
23
+ callbacks: {
24
+ label: (context) => {
25
+ const label = context.dataset.label ?? '';
26
+ return `${label} ${smartDecimal(context.parsed.y)}${INSIGHTS_UNIT_MAPPING[props.type](context.parsed.y)}`;
27
+ },
28
+ },
29
+ },
30
+ },
31
+ }),
32
+ );
33
+
34
+ const chartData = computed<ChartData<'bar'>>(() => {
35
+ const labels: string[] = [];
36
+ const data: number[] = [];
37
+
38
+ for (const entry of props.data) {
39
+ labels.push(GRANULARITY_DATE_FORMAT_MASK[props.granularity](entry.date));
40
+ data.push(transformInsightsFailureRate(entry.values.failureRate));
41
+ }
42
+
43
+ return {
44
+ labels,
45
+ datasets: [
46
+ {
47
+ label: i18n.baseText('insights.banner.title.failureRate'),
48
+ data,
49
+ backgroundColor: colorPrimary.value,
50
+ },
51
+ ],
52
+ };
53
+ });
54
+ </script>
55
+
56
+ <template>
57
+ <Bar data-test-id="insights-chart-failure-rate" :data="chartData" :options="chartOptions" />
58
+ </template>
@@ -0,0 +1,74 @@
1
+ <script lang="ts" setup>
2
+ import { useI18n } from '@n8n/i18n';
3
+ import { type ChartData, Filler, type ScriptableContext } from 'chart.js';
4
+ import { computed } from 'vue';
5
+ import { Line } from 'vue-chartjs';
6
+
7
+ import type { ChartProps } from './insightChartProps';
8
+ import { generateLinearGradient, generateLineChartOptions } from '../../chartjs.utils';
9
+ import { GRANULARITY_DATE_FORMAT_MASK, INSIGHTS_UNIT_MAPPING } from '../../insights.constants';
10
+ import { transformInsightsTimeSaved } from '../../insights.utils';
11
+
12
+ const props = defineProps<ChartProps>();
13
+ const i18n = useI18n();
14
+
15
+ const chartOptions = computed(() =>
16
+ generateLineChartOptions({
17
+ plugins: {
18
+ tooltip: {
19
+ callbacks: {
20
+ label: (context) => {
21
+ const label = context.dataset.label ?? '';
22
+ const value = Number(context.parsed.y);
23
+ return `${label} ${transformInsightsTimeSaved(value).toLocaleString('en-US')}${INSIGHTS_UNIT_MAPPING[props.type](value)}`;
24
+ },
25
+ },
26
+ },
27
+ },
28
+ scales: {
29
+ y: {
30
+ ticks: {
31
+ // eslint-disable-next-line id-denylist
32
+ callback(tickValue) {
33
+ return transformInsightsTimeSaved(Number(tickValue));
34
+ },
35
+ },
36
+ },
37
+ },
38
+ }),
39
+ );
40
+
41
+ const chartData = computed<ChartData<'line'>>(() => {
42
+ const labels: string[] = [];
43
+ const data: number[] = [];
44
+
45
+ for (const entry of props.data) {
46
+ labels.push(GRANULARITY_DATE_FORMAT_MASK[props.granularity](entry.date));
47
+ data.push(entry.values.timeSaved);
48
+ }
49
+
50
+ return {
51
+ labels,
52
+ datasets: [
53
+ {
54
+ label: i18n.baseText('insights.banner.title.timeSaved'),
55
+ data,
56
+ fill: 'origin',
57
+ cubicInterpolationMode: 'monotone' as const,
58
+ backgroundColor: (ctx: ScriptableContext<'line'>) =>
59
+ generateLinearGradient(ctx.chart.ctx, 292),
60
+ borderColor: 'rgba(255, 64, 39, 1)',
61
+ },
62
+ ],
63
+ };
64
+ });
65
+ </script>
66
+
67
+ <template>
68
+ <Line
69
+ data-test-id="insights-chart-time-saved"
70
+ :data="chartData"
71
+ :options="chartOptions"
72
+ :plugins="[Filler]"
73
+ />
74
+ </template>
@@ -0,0 +1,58 @@
1
+ <script lang="ts" setup>
2
+ import { useI18n } from '@n8n/i18n';
3
+ import { useCssVar } from '@vueuse/core';
4
+ import type { ChartData } from 'chart.js';
5
+ import { computed } from 'vue';
6
+ import { Bar } from 'vue-chartjs';
7
+
8
+ import type { ChartProps } from './insightChartProps';
9
+ import { generateBarChartOptions } from '../../chartjs.utils';
10
+ import { GRANULARITY_DATE_FORMAT_MASK } from '../../insights.constants';
11
+
12
+ const props = defineProps<ChartProps>();
13
+
14
+ const i18n = useI18n();
15
+
16
+ const colorPrimary = useCssVar('--color--primary', document.body);
17
+ const chartOptions = computed(() =>
18
+ generateBarChartOptions({
19
+ plugins: {
20
+ tooltip: {
21
+ itemSort: (a) => (a.dataset.label === i18n.baseText('insights.chart.succeeded') ? -1 : 1),
22
+ },
23
+ },
24
+ }),
25
+ );
26
+
27
+ const chartData = computed<ChartData<'bar'>>(() => {
28
+ const labels: string[] = [];
29
+ const succeededData: number[] = [];
30
+ const failedData: number[] = [];
31
+
32
+ for (const entry of props.data) {
33
+ labels.push(GRANULARITY_DATE_FORMAT_MASK[props.granularity](entry.date));
34
+ succeededData.push(entry.values.succeeded);
35
+ failedData.push(entry.values.failed);
36
+ }
37
+
38
+ return {
39
+ labels,
40
+ datasets: [
41
+ {
42
+ label: i18n.baseText('insights.chart.failed'),
43
+ data: failedData,
44
+ backgroundColor: colorPrimary.value,
45
+ },
46
+ {
47
+ label: i18n.baseText('insights.chart.succeeded'),
48
+ data: succeededData,
49
+ backgroundColor: '#3E999F',
50
+ },
51
+ ],
52
+ };
53
+ });
54
+ </script>
55
+
56
+ <template>
57
+ <Bar data-test-id="insights-chart-total" :data="chartData" :options="chartOptions" />
58
+ </template>
@@ -0,0 +1,9 @@
1
+ import type { InsightsByTime, InsightsSummaryType, InsightsDateRange } from '@n8n/api-types';
2
+
3
+ export type ChartProps = {
4
+ data: InsightsByTime[];
5
+ type: InsightsSummaryType;
6
+ granularity: InsightsDateRange['granularity'];
7
+ startDate: string;
8
+ endDate: string;
9
+ };