@adminforth/dashboard 1.0.0 → 1.2.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.
- package/README.md +99 -54
- package/custom/api/dashboardApi.ts +9 -0
- package/custom/model/dashboard.types.ts +353 -2
- package/custom/queries/useWidgetData.ts +8 -4
- package/custom/runtime/DashboardRuntime.vue +2 -1
- package/custom/runtime/WidgetRenderer.vue +2 -1
- package/custom/runtime/WidgetShell.vue +8 -4
- package/custom/skills/adminforth-dashboard/SKILL.md +4 -4
- package/custom/widgets/chart/ChartWidget.vue +45 -12
- package/custom/widgets/chart/chart.types.ts +83 -0
- package/custom/widgets/chart/chart.utils.ts +2 -2
- package/custom/widgets/gauge-card/GaugeCardWidget.vue +63 -12
- package/custom/widgets/kpi-card/KpiCardWidget.vue +6 -8
- package/custom/widgets/pivot-table/PivotTableWidget.vue +32 -12
- package/custom/widgets/table/TableWidget.vue +155 -30
- package/dist/custom/api/dashboardApi.d.ts +7 -1
- package/dist/custom/api/dashboardApi.js +4 -6
- package/dist/custom/api/dashboardApi.ts +9 -0
- package/dist/custom/model/dashboard.types.d.ts +70 -1
- package/dist/custom/model/dashboard.types.js +173 -1
- package/dist/custom/model/dashboard.types.ts +353 -2
- package/dist/custom/queries/useDashboardConfig.d.ts +42 -2
- package/dist/custom/queries/useWidgetData.d.ts +44 -3
- package/dist/custom/queries/useWidgetData.js +3 -3
- package/dist/custom/queries/useWidgetData.ts +8 -4
- package/dist/custom/runtime/DashboardRuntime.vue +2 -1
- package/dist/custom/runtime/WidgetRenderer.vue +2 -1
- package/dist/custom/runtime/WidgetShell.vue +8 -4
- package/dist/custom/skills/adminforth-dashboard/SKILL.md +4 -4
- package/dist/custom/widgets/chart/ChartWidget.vue +45 -12
- package/dist/custom/widgets/chart/chart.types.d.ts +15 -0
- package/dist/custom/widgets/chart/chart.types.js +46 -0
- package/dist/custom/widgets/chart/chart.types.ts +83 -0
- package/dist/custom/widgets/chart/chart.utils.d.ts +1 -1
- package/dist/custom/widgets/chart/chart.utils.js +2 -2
- package/dist/custom/widgets/chart/chart.utils.ts +2 -2
- package/dist/custom/widgets/gauge-card/GaugeCardWidget.vue +63 -12
- package/dist/custom/widgets/kpi-card/KpiCardWidget.vue +6 -8
- package/dist/custom/widgets/pivot-table/PivotTableWidget.vue +32 -12
- package/dist/custom/widgets/table/TableWidget.vue +155 -30
- package/dist/endpoint/widgets.d.ts +6 -1
- package/dist/endpoint/widgets.js +41 -6
- package/dist/schema/api.d.ts +874 -444
- package/dist/schema/api.js +11 -2
- package/dist/schema/widget.d.ts +538 -132
- package/dist/schema/widget.js +138 -14
- package/dist/services/widgetConfigValidator.js +26 -40
- package/dist/services/widgetDataService.d.ts +7 -14
- package/dist/services/widgetDataService.js +115 -11
- package/endpoint/widgets.ts +56 -6
- package/package.json +1 -1
- package/schema/api.ts +11 -1
- package/schema/widget.ts +145 -15
- package/services/widgetConfigValidator.ts +36 -44
- package/services/widgetDataService.ts +175 -28
|
@@ -23,6 +23,27 @@ export declare function useDashboardConfig(slug: Ref<string>): {
|
|
|
23
23
|
maxWidth?: number | null | undefined;
|
|
24
24
|
order: number;
|
|
25
25
|
target: import("../model/dashboard.types.js").DashboardWidgetTarget;
|
|
26
|
+
dataSource?: {
|
|
27
|
+
type: "resource";
|
|
28
|
+
resourceId: string;
|
|
29
|
+
columns?: string[] | undefined;
|
|
30
|
+
sort?: unknown;
|
|
31
|
+
filters?: unknown;
|
|
32
|
+
} | {
|
|
33
|
+
type: "aggregate";
|
|
34
|
+
resourceId: string;
|
|
35
|
+
aggregations: Record<string, import("../model/dashboard.types.js").AggregationRule>;
|
|
36
|
+
groupBy?: {
|
|
37
|
+
type: "field";
|
|
38
|
+
field: string;
|
|
39
|
+
} | {
|
|
40
|
+
type: "date_trunc";
|
|
41
|
+
field: string;
|
|
42
|
+
truncation: "day" | "week" | "month" | "year";
|
|
43
|
+
timezone?: string | undefined;
|
|
44
|
+
} | undefined;
|
|
45
|
+
filters?: unknown;
|
|
46
|
+
} | undefined;
|
|
26
47
|
chart?: {
|
|
27
48
|
type: import("../widgets/chart/chart.types.js").ChartWidgetType;
|
|
28
49
|
title?: string | undefined;
|
|
@@ -49,7 +70,6 @@ export declare function useDashboardConfig(slug: Ref<string>): {
|
|
|
49
70
|
kpi_card?: unknown;
|
|
50
71
|
pivot_table?: unknown;
|
|
51
72
|
gauge_card?: unknown;
|
|
52
|
-
query?: unknown;
|
|
53
73
|
}[];
|
|
54
74
|
};
|
|
55
75
|
} | null, import("../api/dashboardApi.js").DashboardResponse | {
|
|
@@ -75,6 +95,27 @@ export declare function useDashboardConfig(slug: Ref<string>): {
|
|
|
75
95
|
maxWidth?: number | null | undefined;
|
|
76
96
|
order: number;
|
|
77
97
|
target: import("../model/dashboard.types.js").DashboardWidgetTarget;
|
|
98
|
+
dataSource?: {
|
|
99
|
+
type: "resource";
|
|
100
|
+
resourceId: string;
|
|
101
|
+
columns?: string[] | undefined;
|
|
102
|
+
sort?: unknown;
|
|
103
|
+
filters?: unknown;
|
|
104
|
+
} | {
|
|
105
|
+
type: "aggregate";
|
|
106
|
+
resourceId: string;
|
|
107
|
+
aggregations: Record<string, import("../model/dashboard.types.js").AggregationRule>;
|
|
108
|
+
groupBy?: {
|
|
109
|
+
type: "field";
|
|
110
|
+
field: string;
|
|
111
|
+
} | {
|
|
112
|
+
type: "date_trunc";
|
|
113
|
+
field: string;
|
|
114
|
+
truncation: "day" | "week" | "month" | "year";
|
|
115
|
+
timezone?: string | undefined;
|
|
116
|
+
} | undefined;
|
|
117
|
+
filters?: unknown;
|
|
118
|
+
} | undefined;
|
|
78
119
|
chart?: {
|
|
79
120
|
type: import("../widgets/chart/chart.types.js").ChartWidgetType;
|
|
80
121
|
title?: string | undefined;
|
|
@@ -101,7 +142,6 @@ export declare function useDashboardConfig(slug: Ref<string>): {
|
|
|
101
142
|
kpi_card?: unknown;
|
|
102
143
|
pivot_table?: unknown;
|
|
103
144
|
gauge_card?: unknown;
|
|
104
|
-
query?: unknown;
|
|
105
145
|
}[];
|
|
106
146
|
};
|
|
107
147
|
} | null>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Ref } from 'vue';
|
|
2
|
-
|
|
2
|
+
import { type DashboardWidgetDataRequest } from '../api/dashboardApi.js';
|
|
3
|
+
export declare function useWidgetData(slug: Ref<string>, widgetId: Ref<string>, request?: Ref<DashboardWidgetDataRequest>): {
|
|
3
4
|
data: Ref<{
|
|
4
5
|
widget: {
|
|
5
6
|
id: string;
|
|
@@ -12,6 +13,27 @@ export declare function useWidgetData(slug: Ref<string>, widgetId: Ref<string>):
|
|
|
12
13
|
maxWidth?: number | null | undefined;
|
|
13
14
|
order: number;
|
|
14
15
|
target: import("../model/dashboard.types.js").DashboardWidgetTarget;
|
|
16
|
+
dataSource?: {
|
|
17
|
+
type: "resource";
|
|
18
|
+
resourceId: string;
|
|
19
|
+
columns?: string[] | undefined;
|
|
20
|
+
sort?: unknown;
|
|
21
|
+
filters?: unknown;
|
|
22
|
+
} | {
|
|
23
|
+
type: "aggregate";
|
|
24
|
+
resourceId: string;
|
|
25
|
+
aggregations: Record<string, import("../model/dashboard.types.js").AggregationRule>;
|
|
26
|
+
groupBy?: {
|
|
27
|
+
type: "field";
|
|
28
|
+
field: string;
|
|
29
|
+
} | {
|
|
30
|
+
type: "date_trunc";
|
|
31
|
+
field: string;
|
|
32
|
+
truncation: "day" | "week" | "month" | "year";
|
|
33
|
+
timezone?: string | undefined;
|
|
34
|
+
} | undefined;
|
|
35
|
+
filters?: unknown;
|
|
36
|
+
} | undefined;
|
|
15
37
|
chart?: {
|
|
16
38
|
type: import("../widgets/chart/chart.types.js").ChartWidgetType;
|
|
17
39
|
title?: string | undefined;
|
|
@@ -38,7 +60,6 @@ export declare function useWidgetData(slug: Ref<string>, widgetId: Ref<string>):
|
|
|
38
60
|
kpi_card?: unknown;
|
|
39
61
|
pivot_table?: unknown;
|
|
40
62
|
gauge_card?: unknown;
|
|
41
|
-
query?: unknown;
|
|
42
63
|
};
|
|
43
64
|
data: unknown;
|
|
44
65
|
} | null, import("../api/dashboardApi.js").DashboardWidgetDataResponse | {
|
|
@@ -53,6 +74,27 @@ export declare function useWidgetData(slug: Ref<string>, widgetId: Ref<string>):
|
|
|
53
74
|
maxWidth?: number | null | undefined;
|
|
54
75
|
order: number;
|
|
55
76
|
target: import("../model/dashboard.types.js").DashboardWidgetTarget;
|
|
77
|
+
dataSource?: {
|
|
78
|
+
type: "resource";
|
|
79
|
+
resourceId: string;
|
|
80
|
+
columns?: string[] | undefined;
|
|
81
|
+
sort?: unknown;
|
|
82
|
+
filters?: unknown;
|
|
83
|
+
} | {
|
|
84
|
+
type: "aggregate";
|
|
85
|
+
resourceId: string;
|
|
86
|
+
aggregations: Record<string, import("../model/dashboard.types.js").AggregationRule>;
|
|
87
|
+
groupBy?: {
|
|
88
|
+
type: "field";
|
|
89
|
+
field: string;
|
|
90
|
+
} | {
|
|
91
|
+
type: "date_trunc";
|
|
92
|
+
field: string;
|
|
93
|
+
truncation: "day" | "week" | "month" | "year";
|
|
94
|
+
timezone?: string | undefined;
|
|
95
|
+
} | undefined;
|
|
96
|
+
filters?: unknown;
|
|
97
|
+
} | undefined;
|
|
56
98
|
chart?: {
|
|
57
99
|
type: import("../widgets/chart/chart.types.js").ChartWidgetType;
|
|
58
100
|
title?: string | undefined;
|
|
@@ -79,7 +121,6 @@ export declare function useWidgetData(slug: Ref<string>, widgetId: Ref<string>):
|
|
|
79
121
|
kpi_card?: unknown;
|
|
80
122
|
pivot_table?: unknown;
|
|
81
123
|
gauge_card?: unknown;
|
|
82
|
-
query?: unknown;
|
|
83
124
|
};
|
|
84
125
|
data: unknown;
|
|
85
126
|
} | null>;
|
|
@@ -12,7 +12,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
12
12
|
exports.useWidgetData = useWidgetData;
|
|
13
13
|
const vue_1 = require("vue");
|
|
14
14
|
const dashboardApi_js_1 = require("../api/dashboardApi.js");
|
|
15
|
-
function useWidgetData(slug, widgetId) {
|
|
15
|
+
function useWidgetData(slug, widgetId, request) {
|
|
16
16
|
const data = (0, vue_1.ref)(null);
|
|
17
17
|
const isLoading = (0, vue_1.ref)(false);
|
|
18
18
|
const isFetching = (0, vue_1.ref)(false);
|
|
@@ -29,7 +29,7 @@ function useWidgetData(slug, widgetId) {
|
|
|
29
29
|
isLoading.value = true;
|
|
30
30
|
}
|
|
31
31
|
try {
|
|
32
|
-
const response = yield dashboardApi_js_1.dashboardApi.getDashboardWidgetData(slug.value, widgetId.value);
|
|
32
|
+
const response = yield dashboardApi_js_1.dashboardApi.getDashboardWidgetData(slug.value, widgetId.value, request === null || request === void 0 ? void 0 : request.value);
|
|
33
33
|
data.value = response;
|
|
34
34
|
error.value = null;
|
|
35
35
|
return response;
|
|
@@ -44,7 +44,7 @@ function useWidgetData(slug, widgetId) {
|
|
|
44
44
|
}
|
|
45
45
|
});
|
|
46
46
|
}
|
|
47
|
-
(0, vue_1.watch)([slug, widgetId], () => {
|
|
47
|
+
(0, vue_1.watch)(request ? [slug, widgetId, request] : [slug, widgetId], () => {
|
|
48
48
|
void refetch();
|
|
49
49
|
}, { immediate: true });
|
|
50
50
|
return {
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { ref, watch, type Ref } from 'vue'
|
|
2
|
-
import { dashboardApi } from '../api/dashboardApi.js'
|
|
2
|
+
import { dashboardApi, type DashboardWidgetDataRequest } from '../api/dashboardApi.js'
|
|
3
3
|
|
|
4
|
-
export function useWidgetData(
|
|
4
|
+
export function useWidgetData(
|
|
5
|
+
slug: Ref<string>,
|
|
6
|
+
widgetId: Ref<string>,
|
|
7
|
+
request?: Ref<DashboardWidgetDataRequest>,
|
|
8
|
+
) {
|
|
5
9
|
const data = ref<Awaited<ReturnType<typeof dashboardApi.getDashboardWidgetData>> | null>(null)
|
|
6
10
|
const isLoading = ref(false)
|
|
7
11
|
const isFetching = ref(false)
|
|
@@ -20,7 +24,7 @@ export function useWidgetData(slug: Ref<string>, widgetId: Ref<string>) {
|
|
|
20
24
|
}
|
|
21
25
|
|
|
22
26
|
try {
|
|
23
|
-
const response = await dashboardApi.getDashboardWidgetData(slug.value, widgetId.value)
|
|
27
|
+
const response = await dashboardApi.getDashboardWidgetData(slug.value, widgetId.value, request?.value)
|
|
24
28
|
data.value = response
|
|
25
29
|
error.value = null
|
|
26
30
|
return response
|
|
@@ -34,7 +38,7 @@ export function useWidgetData(slug: Ref<string>, widgetId: Ref<string>) {
|
|
|
34
38
|
}
|
|
35
39
|
|
|
36
40
|
watch(
|
|
37
|
-
[slug, widgetId],
|
|
41
|
+
request ? [slug, widgetId, request] : [slug, widgetId],
|
|
38
42
|
() => {
|
|
39
43
|
void refetch()
|
|
40
44
|
},
|
|
@@ -211,6 +211,7 @@ import type {
|
|
|
211
211
|
DashboardWidgetConfig,
|
|
212
212
|
DashboardWidgetMoveDirection,
|
|
213
213
|
} from '../model/dashboard.types.js'
|
|
214
|
+
import { serializeDashboardWidgetConfigForEditor } from '../model/dashboard.types.js'
|
|
214
215
|
|
|
215
216
|
const props = defineProps<{
|
|
216
217
|
dashboardSlug: string
|
|
@@ -388,7 +389,7 @@ async function removeWidget(widgetId: string) {
|
|
|
388
389
|
|
|
389
390
|
function editWidget(widget: DashboardWidgetConfig) {
|
|
390
391
|
editingWidgetId.value = widget.id
|
|
391
|
-
widgetConfigCode.value = stringifyYaml(widget)
|
|
392
|
+
widgetConfigCode.value = stringifyYaml(serializeDashboardWidgetConfigForEditor(widget))
|
|
392
393
|
widgetConfigError.value = ''
|
|
393
394
|
widgetConfigFieldErrors.value = []
|
|
394
395
|
}
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
<script setup lang="ts">
|
|
27
27
|
import { computed } from 'vue'
|
|
28
28
|
import type { DashboardWidgetConfig } from '../model/dashboard.types.js'
|
|
29
|
+
import { normalizeChartWidgetConfig } from '../widgets/chart/chart.types.js'
|
|
29
30
|
import { getWidgetLabel, getWidgetRegistration } from '../widgets/registry.js'
|
|
30
31
|
|
|
31
32
|
const props = defineProps<{
|
|
@@ -52,7 +53,7 @@ const widgetTitle = computed(() => {
|
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
if (props.widget.target === 'chart') {
|
|
55
|
-
return props.widget.chart?.title || 'Untitled chart'
|
|
56
|
+
return normalizeChartWidgetConfig(props.widget.chart)?.title || 'Untitled chart'
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
return getWidgetLabel(props.widget.target)
|
|
@@ -135,11 +135,11 @@ const widgetLayoutVars = computed<CSSProperties>(() => {
|
|
|
135
135
|
const fixedWidth = formatWidth(props.layout?.width)
|
|
136
136
|
|
|
137
137
|
return {
|
|
138
|
-
'--widget-basis': fixedWidth ?? basis,
|
|
139
|
-
'--widget-min-width': fixedWidth ?? formatWidth(props.layout?.minWidth) ?? basis,
|
|
138
|
+
'--widget-basis': clampToContainerWidth(fixedWidth ?? basis),
|
|
139
|
+
'--widget-min-width': clampToContainerWidth(fixedWidth ?? formatWidth(props.layout?.minWidth) ?? basis),
|
|
140
140
|
'--widget-max-width': props.layout?.maxWidth === null
|
|
141
|
-
? '
|
|
142
|
-
: fixedWidth ?? formatWidth(props.layout?.maxWidth) ?? '
|
|
141
|
+
? '100%'
|
|
142
|
+
: clampToContainerWidth(fixedWidth ?? formatWidth(props.layout?.maxWidth) ?? '100%'),
|
|
143
143
|
height: formatWidth(props.layout?.height ?? DEFAULT_WIDGET_HEIGHT),
|
|
144
144
|
}
|
|
145
145
|
})
|
|
@@ -149,4 +149,8 @@ function formatWidth(value: number | undefined) {
|
|
|
149
149
|
return `${value}px`
|
|
150
150
|
}
|
|
151
151
|
}
|
|
152
|
+
|
|
153
|
+
function clampToContainerWidth(value: string) {
|
|
154
|
+
return `min(${value}, 100%)`
|
|
155
|
+
}
|
|
152
156
|
</script>
|
|
@@ -119,7 +119,7 @@ Use the current schema keys exactly:
|
|
|
119
119
|
|
|
120
120
|
- Use `target`, not `type`.
|
|
121
121
|
- Use `label`, not `title`.
|
|
122
|
-
- Use `
|
|
123
|
-
- Use `
|
|
124
|
-
- Use `
|
|
125
|
-
- Use `
|
|
122
|
+
- Use `data_source`, not `dataSource`.
|
|
123
|
+
- Use `resource_id`, not `resourceId`.
|
|
124
|
+
- Use `group_by`, not `groupBy`.
|
|
125
|
+
- Use `page_size`, not `pageSize`.
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
:rows="rows"
|
|
27
27
|
:x-field="xField"
|
|
28
28
|
:y-field="yField"
|
|
29
|
-
:series-name="chartConfig.
|
|
29
|
+
:series-name="chartConfig.seriesName"
|
|
30
30
|
:color="chartConfig.color"
|
|
31
31
|
:height="chartHeight"
|
|
32
32
|
/>
|
|
@@ -91,6 +91,7 @@
|
|
|
91
91
|
import { computed, watch } from 'vue'
|
|
92
92
|
import { useWidgetData } from '../../queries/useWidgetData.js'
|
|
93
93
|
import type { DashboardWidgetConfig, DashboardWidgetTableData } from '../../model/dashboard.types.js'
|
|
94
|
+
import { normalizeChartWidgetConfig } from './chart.types.js'
|
|
94
95
|
import BarChart from './bar/BarChart.vue'
|
|
95
96
|
import FunnelChart from './funnel/FunnelChart.vue'
|
|
96
97
|
import HistogramChart from './histogram/HistogramChart.vue'
|
|
@@ -126,13 +127,45 @@ watch(
|
|
|
126
127
|
const chartData = computed(() => data.value?.data as DashboardWidgetTableData | null)
|
|
127
128
|
const rows = computed(() => chartData.value?.rows ?? [])
|
|
128
129
|
const columns = computed(() => chartData.value?.columns ?? [])
|
|
129
|
-
const chartConfig = computed(() => props.widget.chart)
|
|
130
|
-
const
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
130
|
+
const chartConfig = computed(() => normalizeChartWidgetConfig(props.widget.chart))
|
|
131
|
+
const aggregateGroupField = computed(() => {
|
|
132
|
+
const dataSource = props.widget.dataSource
|
|
133
|
+
|
|
134
|
+
if (dataSource?.type !== 'aggregate' || !dataSource.groupBy) {
|
|
135
|
+
return undefined
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return dataSource.groupBy.field
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
function resolveChartDimensionField(field: string | undefined, fallbackField: string | undefined) {
|
|
142
|
+
const resolvedField = field ?? fallbackField
|
|
143
|
+
|
|
144
|
+
if (!resolvedField) {
|
|
145
|
+
return ''
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (columns.value.includes(resolvedField)) {
|
|
149
|
+
return resolvedField
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (
|
|
153
|
+
aggregateGroupField.value
|
|
154
|
+
&& resolvedField === aggregateGroupField.value
|
|
155
|
+
&& columns.value.includes('group')
|
|
156
|
+
) {
|
|
157
|
+
return 'group'
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return resolvedField
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const xField = computed(() => resolveChartDimensionField(chartConfig.value?.xField, columns.value[0]))
|
|
164
|
+
const yField = computed(() => chartConfig.value?.yField || columns.value[1])
|
|
165
|
+
const labelField = computed(() => resolveChartDimensionField(chartConfig.value?.labelField, columns.value[0]))
|
|
166
|
+
const valueField = computed(() => chartConfig.value?.valueField || columns.value[1])
|
|
134
167
|
const pieRows = computed(() => {
|
|
135
|
-
if (chartConfig.value?.
|
|
168
|
+
if (chartConfig.value?.valueField) {
|
|
136
169
|
return rows.value
|
|
137
170
|
}
|
|
138
171
|
|
|
@@ -147,10 +180,10 @@ const pieRows = computed(() => {
|
|
|
147
180
|
|
|
148
181
|
return Array.from(groupedRows.values())
|
|
149
182
|
})
|
|
150
|
-
const pieLabelField = computed(() => chartConfig.value?.
|
|
151
|
-
const pieValueField = computed(() => chartConfig.value?.
|
|
183
|
+
const pieLabelField = computed(() => chartConfig.value?.valueField ? labelField.value : 'label')
|
|
184
|
+
const pieValueField = computed(() => chartConfig.value?.valueField ? valueField.value : 'value')
|
|
152
185
|
const barRows = computed(() => {
|
|
153
|
-
const bucketField = chartConfig.value?.
|
|
186
|
+
const bucketField = chartConfig.value?.bucketField
|
|
154
187
|
|
|
155
188
|
if (!bucketField) {
|
|
156
189
|
return rows.value
|
|
@@ -167,8 +200,8 @@ const barRows = computed(() => {
|
|
|
167
200
|
}).length,
|
|
168
201
|
}))
|
|
169
202
|
})
|
|
170
|
-
const barLabelField = computed(() => chartConfig.value?.
|
|
171
|
-
const barValueField = computed(() => chartConfig.value?.
|
|
203
|
+
const barLabelField = computed(() => chartConfig.value?.bucketField ? 'label' : labelField.value)
|
|
204
|
+
const barValueField = computed(() => chartConfig.value?.bucketField ? 'count' : valueField.value)
|
|
172
205
|
const stackedBarSeries = computed(() => {
|
|
173
206
|
if (chartConfig.value?.series?.length) {
|
|
174
207
|
return chartConfig.value.series
|
|
@@ -23,3 +23,18 @@ export type ChartWidgetConfig = {
|
|
|
23
23
|
color?: string;
|
|
24
24
|
colors?: string[];
|
|
25
25
|
};
|
|
26
|
+
export type NormalizedChartWidgetConfig = {
|
|
27
|
+
type: ChartWidgetType;
|
|
28
|
+
title?: string;
|
|
29
|
+
xField?: string;
|
|
30
|
+
yField?: string;
|
|
31
|
+
labelField?: string;
|
|
32
|
+
valueField?: string;
|
|
33
|
+
bucketField?: string;
|
|
34
|
+
buckets?: ChartWidgetBucketConfig[];
|
|
35
|
+
series?: ChartWidgetSeriesConfig[];
|
|
36
|
+
seriesName?: string;
|
|
37
|
+
color?: string;
|
|
38
|
+
colors?: string[];
|
|
39
|
+
};
|
|
40
|
+
export declare function normalizeChartWidgetConfig(value: unknown): NormalizedChartWidgetConfig | undefined;
|
|
@@ -1,2 +1,48 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.normalizeChartWidgetConfig = normalizeChartWidgetConfig;
|
|
4
|
+
function normalizeChartWidgetConfig(value) {
|
|
5
|
+
const config = asChartWidgetConfigRecord(value);
|
|
6
|
+
if (!config) {
|
|
7
|
+
return undefined;
|
|
8
|
+
}
|
|
9
|
+
const type = normalizeChartWidgetType(config.type);
|
|
10
|
+
if (!type) {
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
const xField = getStringField(config, 'x_field');
|
|
14
|
+
const yField = getStringField(config, 'y_field');
|
|
15
|
+
const labelField = getStringField(config, 'label_field');
|
|
16
|
+
const valueField = getStringField(config, 'value_field');
|
|
17
|
+
const bucketField = getStringField(config, 'bucket_field');
|
|
18
|
+
const seriesName = getStringField(config, 'series_name');
|
|
19
|
+
const title = getStringField(config, 'title');
|
|
20
|
+
const color = getStringField(config, 'color');
|
|
21
|
+
const colors = Array.isArray(config.colors) ? config.colors : undefined;
|
|
22
|
+
const buckets = Array.isArray(config.buckets) ? config.buckets : undefined;
|
|
23
|
+
const series = Array.isArray(config.series) ? config.series : undefined;
|
|
24
|
+
return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ type }, (title !== undefined ? { title } : {})), (xField !== undefined ? { xField } : {})), (yField !== undefined ? { yField } : {})), (labelField !== undefined ? { labelField } : {})), (valueField !== undefined ? { valueField } : {})), (bucketField !== undefined ? { bucketField } : {})), (buckets !== undefined ? { buckets } : {})), (series !== undefined ? { series } : {})), (seriesName !== undefined ? { seriesName } : {})), (color !== undefined ? { color } : {})), (colors !== undefined ? { colors } : {}));
|
|
25
|
+
}
|
|
26
|
+
function normalizeChartWidgetType(value) {
|
|
27
|
+
switch (value) {
|
|
28
|
+
case 'line':
|
|
29
|
+
case 'pie':
|
|
30
|
+
case 'bar':
|
|
31
|
+
case 'stacked_bar':
|
|
32
|
+
case 'funnel':
|
|
33
|
+
case 'histogram':
|
|
34
|
+
return value;
|
|
35
|
+
default:
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function asChartWidgetConfigRecord(value) {
|
|
40
|
+
return isRecord(value) ? value : undefined;
|
|
41
|
+
}
|
|
42
|
+
function getStringField(record, key) {
|
|
43
|
+
const value = record[key];
|
|
44
|
+
return typeof value === 'string' ? value : undefined;
|
|
45
|
+
}
|
|
46
|
+
function isRecord(value) {
|
|
47
|
+
return typeof value === 'object' && value !== null;
|
|
48
|
+
}
|
|
@@ -32,3 +32,86 @@ export type ChartWidgetConfig = {
|
|
|
32
32
|
color?: string
|
|
33
33
|
colors?: string[]
|
|
34
34
|
}
|
|
35
|
+
|
|
36
|
+
export type NormalizedChartWidgetConfig = {
|
|
37
|
+
type: ChartWidgetType
|
|
38
|
+
title?: string
|
|
39
|
+
xField?: string
|
|
40
|
+
yField?: string
|
|
41
|
+
labelField?: string
|
|
42
|
+
valueField?: string
|
|
43
|
+
bucketField?: string
|
|
44
|
+
buckets?: ChartWidgetBucketConfig[]
|
|
45
|
+
series?: ChartWidgetSeriesConfig[]
|
|
46
|
+
seriesName?: string
|
|
47
|
+
color?: string
|
|
48
|
+
colors?: string[]
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function normalizeChartWidgetConfig(value: unknown): NormalizedChartWidgetConfig | undefined {
|
|
52
|
+
const config = asChartWidgetConfigRecord(value)
|
|
53
|
+
|
|
54
|
+
if (!config) {
|
|
55
|
+
return undefined
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const type = normalizeChartWidgetType(config.type)
|
|
59
|
+
|
|
60
|
+
if (!type) {
|
|
61
|
+
return undefined
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const xField = getStringField(config, 'x_field')
|
|
65
|
+
const yField = getStringField(config, 'y_field')
|
|
66
|
+
const labelField = getStringField(config, 'label_field')
|
|
67
|
+
const valueField = getStringField(config, 'value_field')
|
|
68
|
+
const bucketField = getStringField(config, 'bucket_field')
|
|
69
|
+
const seriesName = getStringField(config, 'series_name')
|
|
70
|
+
const title = getStringField(config, 'title')
|
|
71
|
+
const color = getStringField(config, 'color')
|
|
72
|
+
const colors = Array.isArray(config.colors) ? config.colors as string[] : undefined
|
|
73
|
+
const buckets = Array.isArray(config.buckets) ? config.buckets as ChartWidgetBucketConfig[] : undefined
|
|
74
|
+
const series = Array.isArray(config.series) ? config.series as ChartWidgetSeriesConfig[] : undefined
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
type,
|
|
78
|
+
...(title !== undefined ? { title } : {}),
|
|
79
|
+
...(xField !== undefined ? { xField } : {}),
|
|
80
|
+
...(yField !== undefined ? { yField } : {}),
|
|
81
|
+
...(labelField !== undefined ? { labelField } : {}),
|
|
82
|
+
...(valueField !== undefined ? { valueField } : {}),
|
|
83
|
+
...(bucketField !== undefined ? { bucketField } : {}),
|
|
84
|
+
...(buckets !== undefined ? { buckets } : {}),
|
|
85
|
+
...(series !== undefined ? { series } : {}),
|
|
86
|
+
...(seriesName !== undefined ? { seriesName } : {}),
|
|
87
|
+
...(color !== undefined ? { color } : {}),
|
|
88
|
+
...(colors !== undefined ? { colors } : {}),
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function normalizeChartWidgetType(value: unknown): ChartWidgetType | undefined {
|
|
93
|
+
switch (value) {
|
|
94
|
+
case 'line':
|
|
95
|
+
case 'pie':
|
|
96
|
+
case 'bar':
|
|
97
|
+
case 'stacked_bar':
|
|
98
|
+
case 'funnel':
|
|
99
|
+
case 'histogram':
|
|
100
|
+
return value
|
|
101
|
+
default:
|
|
102
|
+
return undefined
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function asChartWidgetConfigRecord(value: unknown): Record<string, unknown> | undefined {
|
|
107
|
+
return isRecord(value) ? value : undefined
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function getStringField(record: Record<string, unknown>, key: string) {
|
|
111
|
+
const value = record[key]
|
|
112
|
+
return typeof value === 'string' ? value : undefined
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
116
|
+
return typeof value === 'object' && value !== null
|
|
117
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export declare const CHART_COLORS: string[];
|
|
2
2
|
export declare function toFiniteNumber(value: unknown): number;
|
|
3
|
-
export declare function formatChartValue(value: number): string;
|
|
3
|
+
export declare function formatChartValue(value: number, options?: Intl.NumberFormatOptions): string;
|
|
4
4
|
export declare function formatChartLabel(value: unknown): string;
|
|
5
5
|
export declare function formatChartAxisLabel(value: unknown, maxLength?: number): string;
|
|
@@ -19,8 +19,8 @@ function toFiniteNumber(value) {
|
|
|
19
19
|
const numberValue = typeof value === 'number' ? value : Number(value);
|
|
20
20
|
return Number.isFinite(numberValue) ? numberValue : 0;
|
|
21
21
|
}
|
|
22
|
-
function formatChartValue(value) {
|
|
23
|
-
return new Intl.NumberFormat().format(value);
|
|
22
|
+
function formatChartValue(value, options = {}) {
|
|
23
|
+
return new Intl.NumberFormat(undefined, options).format(value);
|
|
24
24
|
}
|
|
25
25
|
function formatChartLabel(value) {
|
|
26
26
|
if (typeof value !== 'string') {
|
|
@@ -14,8 +14,8 @@ export function toFiniteNumber(value: unknown) {
|
|
|
14
14
|
return Number.isFinite(numberValue) ? numberValue : 0
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
export function formatChartValue(value: number) {
|
|
18
|
-
return new Intl.NumberFormat().format(value)
|
|
17
|
+
export function formatChartValue(value: number, options: Intl.NumberFormatOptions = {}) {
|
|
18
|
+
return new Intl.NumberFormat(undefined, options).format(value)
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
export function formatChartLabel(value: unknown) {
|