@adminforth/dashboard 1.4.0 → 1.4.1

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 (78) hide show
  1. package/README.md +23 -4
  2. package/custom/api/dashboardApi.ts +6 -9
  3. package/custom/model/dashboard.types.ts +60 -275
  4. package/custom/model/dashboardTopics.ts +5 -0
  5. package/custom/runtime/DashboardGroup.vue +2 -2
  6. package/custom/runtime/DashboardPage.vue +17 -7
  7. package/custom/runtime/DashboardRuntime.vue +20 -8
  8. package/custom/runtime/WidgetRenderer.vue +1 -2
  9. package/custom/runtime/WidgetShell.vue +3 -3
  10. package/custom/skills/adminforth-dashboard/SKILL.md +2 -2
  11. package/custom/widgets/{gauge-card/GaugeCardWidget.vue → GaugeCardWidget.vue} +63 -61
  12. package/custom/widgets/{kpi-card/KpiCardWidget.vue → KpiCardWidget.vue} +35 -33
  13. package/custom/widgets/{pivot-table/PivotTableWidget.vue → PivotTableWidget.vue} +71 -68
  14. package/custom/widgets/{table/TableWidget.vue → TableWidget.vue} +5 -5
  15. package/custom/widgets/chart/{bar/BarChart.vue → BarChart.vue} +2 -2
  16. package/custom/widgets/chart/ChartWidget.vue +4 -15
  17. package/{dist/custom/widgets/chart/funnel → custom/widgets/chart}/FunnelChart.vue +80 -78
  18. package/{dist/custom/widgets/chart/line → custom/widgets/chart}/LineChart.vue +2 -2
  19. package/custom/widgets/chart/{pie/PieChart.vue → PieChart.vue} +2 -2
  20. package/{dist/custom/widgets/chart/stacked-bar → custom/widgets/chart}/StackedBarChart.vue +97 -95
  21. package/custom/widgets/chart/chart.types.ts +0 -28
  22. package/dist/custom/api/dashboardApi.d.ts +4 -8
  23. package/dist/custom/api/dashboardApi.ts +6 -9
  24. package/dist/custom/model/dashboard.types.d.ts +38 -32
  25. package/dist/custom/model/dashboard.types.js +2 -155
  26. package/dist/custom/model/dashboard.types.ts +60 -275
  27. package/dist/custom/model/dashboardTopics.d.ts +2 -0
  28. package/dist/custom/model/dashboardTopics.js +8 -0
  29. package/dist/custom/model/dashboardTopics.ts +5 -0
  30. package/dist/custom/queries/useDashboardConfig.d.ts +96 -96
  31. package/dist/custom/queries/useWidgetData.d.ts +96 -96
  32. package/dist/custom/runtime/DashboardGroup.vue +2 -2
  33. package/dist/custom/runtime/DashboardPage.vue +17 -7
  34. package/dist/custom/runtime/DashboardRuntime.vue +20 -8
  35. package/dist/custom/runtime/WidgetRenderer.vue +1 -2
  36. package/dist/custom/runtime/WidgetShell.vue +3 -3
  37. package/dist/custom/skills/adminforth-dashboard/SKILL.md +2 -2
  38. package/dist/custom/widgets/{gauge-card/GaugeCardWidget.vue → GaugeCardWidget.vue} +63 -61
  39. package/dist/custom/widgets/{kpi-card/KpiCardWidget.vue → KpiCardWidget.vue} +35 -33
  40. package/dist/custom/widgets/{pivot-table/PivotTableWidget.vue → PivotTableWidget.vue} +71 -68
  41. package/dist/custom/widgets/{table/TableWidget.vue → TableWidget.vue} +5 -5
  42. package/dist/custom/widgets/chart/{bar/BarChart.vue → BarChart.vue} +2 -2
  43. package/dist/custom/widgets/chart/ChartWidget.vue +4 -15
  44. package/{custom/widgets/chart/funnel → dist/custom/widgets/chart}/FunnelChart.vue +80 -78
  45. package/{custom/widgets/chart/line → dist/custom/widgets/chart}/LineChart.vue +2 -2
  46. package/dist/custom/widgets/chart/{pie/PieChart.vue → PieChart.vue} +2 -2
  47. package/{custom/widgets/chart/stacked-bar → dist/custom/widgets/chart}/StackedBarChart.vue +97 -95
  48. package/dist/custom/widgets/chart/chart.types.d.ts +0 -2
  49. package/dist/custom/widgets/chart/chart.types.js +0 -23
  50. package/dist/custom/widgets/chart/chart.types.ts +0 -28
  51. package/dist/endpoint/dashboard.d.ts +2 -3
  52. package/dist/endpoint/dashboard.js +12 -32
  53. package/dist/endpoint/groups.d.ts +2 -21
  54. package/dist/endpoint/groups.js +18 -16
  55. package/dist/endpoint/widgets.d.ts +0 -3
  56. package/dist/endpoint/widgets.js +27 -74
  57. package/dist/index.js +1 -3
  58. package/dist/schema/api.d.ts +2090 -511
  59. package/dist/schema/api.js +18 -15
  60. package/dist/schema/widget.d.ts +1003 -250
  61. package/dist/schema/widget.js +102 -46
  62. package/dist/services/dashboardConfigService.d.ts +0 -10
  63. package/dist/services/dashboardConfigService.js +6 -21
  64. package/dist/services/widgetDataService.js +226 -196
  65. package/endpoint/dashboard.ts +13 -46
  66. package/endpoint/groups.ts +25 -42
  67. package/endpoint/widgets.ts +36 -95
  68. package/index.ts +0 -3
  69. package/package.json +3 -3
  70. package/schema/api.ts +19 -15
  71. package/schema/widget.ts +113 -52
  72. package/services/dashboardConfigService.ts +6 -25
  73. package/services/widgetDataService.ts +304 -229
  74. package/custom/widgets/chart/histogram/HistogramChart.vue +0 -21
  75. package/dist/custom/widgets/chart/histogram/HistogramChart.vue +0 -21
  76. package/dist/services/widgetConfigValidator.d.ts +0 -8
  77. package/dist/services/widgetConfigValidator.js +0 -27
  78. package/services/widgetConfigValidator.ts +0 -61
@@ -207,6 +207,8 @@ import { DashboardApiError, dashboardApi, type DashboardResponse } from '../api/
207
207
  import type {
208
208
  DashboardConfig,
209
209
  DashboardGroupConfig,
210
+ EditableDashboardGroupConfig,
211
+ EditableDashboardWidgetConfig,
210
212
  DashboardGroupMoveDirection,
211
213
  DashboardWidgetConfig,
212
214
  DashboardWidgetMoveDirection,
@@ -252,13 +254,15 @@ const sortedGroups = computed(() => {
252
254
  return [...draftConfig.value.groups].sort((a, b) => a.order - b.order)
253
255
  })
254
256
 
255
- const widgetsByGroupId = computed<Map<string, DashboardWidgetConfig[]>>(() => {
257
+ function groupWidgetsByGroupId(widgets: DashboardWidgetConfig[]) {
256
258
  const result = new Map<string, DashboardWidgetConfig[]>()
257
259
 
258
- for (const widget of draftConfig.value.widgets) {
259
- const widgets = result.get(widget.group_id) ?? []
260
- widgets.push(widget)
261
- result.set(widget.group_id, widgets)
260
+ for (const widget of widgets) {
261
+ const nextWidgets = result.get(widget.group_id)
262
+ ? [...result.get(widget.group_id)!, widget]
263
+ : [widget]
264
+
265
+ result.set(widget.group_id, nextWidgets)
262
266
  }
263
267
 
264
268
  for (const [groupId, widgets] of result.entries()) {
@@ -266,6 +270,10 @@ const widgetsByGroupId = computed<Map<string, DashboardWidgetConfig[]>>(() => {
266
270
  }
267
271
 
268
272
  return result
273
+ }
274
+
275
+ const widgetsByGroupId = computed<Map<string, DashboardWidgetConfig[]>>(() => {
276
+ return groupWidgetsByGroupId(draftConfig.value.widgets as DashboardWidgetConfig[])
269
277
  })
270
278
 
271
279
  const visibleGroups = computed(() => {
@@ -329,8 +337,12 @@ async function removeGroup(groupId: string) {
329
337
  }
330
338
 
331
339
  function editGroup(group: DashboardGroupConfig) {
340
+ const editableGroupConfig: EditableDashboardGroupConfig = {
341
+ label: group.label,
342
+ }
343
+
332
344
  editingGroupId.value = group.id
333
- groupConfigCode.value = stringifyYaml(group)
345
+ groupConfigCode.value = stringifyYaml(editableGroupConfig)
334
346
  groupConfigError.value = ''
335
347
  }
336
348
 
@@ -340,7 +352,7 @@ async function saveGroupConfig() {
340
352
  }
341
353
 
342
354
  try {
343
- const groupConfig = parseYaml(groupConfigCode.value) as DashboardGroupConfig
355
+ const groupConfig = parseYaml(groupConfigCode.value) as EditableDashboardGroupConfig
344
356
 
345
357
  applyDashboardResponse(
346
358
  await dashboardApi.setDashboardGroupConfig(
@@ -402,7 +414,7 @@ async function saveWidgetConfig() {
402
414
  try {
403
415
  widgetConfigError.value = ''
404
416
  widgetConfigFieldErrors.value = []
405
- const widgetConfig = parseYaml(widgetConfigCode.value) as DashboardWidgetConfig
417
+ const widgetConfig = parseYaml(widgetConfigCode.value) as EditableDashboardWidgetConfig
406
418
 
407
419
  applyDashboardResponse(
408
420
  await dashboardApi.setWidgetConfig(
@@ -26,7 +26,6 @@
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'
30
29
  import { getWidgetLabel, getWidgetRegistration } from '../widgets/registry.js'
31
30
 
32
31
  const props = defineProps<{
@@ -53,7 +52,7 @@ const widgetTitle = computed(() => {
53
52
  }
54
53
 
55
54
  if (props.widget.target === 'chart') {
56
- return normalizeChartWidgetConfig(props.widget.chart)?.title || 'Untitled chart'
55
+ return props.widget.chart.title || 'Untitled chart'
57
56
  }
58
57
 
59
58
  return getWidgetLabel(props.widget.target)
@@ -136,10 +136,10 @@ const widgetLayoutVars = computed<CSSProperties>(() => {
136
136
 
137
137
  return {
138
138
  '--widget-basis': clampToContainerWidth(fixedWidth ?? basis),
139
- '--widget-min-width': clampToContainerWidth(fixedWidth ?? formatWidth(props.layout?.minWidth) ?? basis),
140
- '--widget-max-width': props.layout?.maxWidth === null
139
+ '--widget-min-width': clampToContainerWidth(fixedWidth ?? formatWidth(props.layout?.min_width) ?? basis),
140
+ '--widget-max-width': props.layout?.max_width === null
141
141
  ? '100%'
142
- : clampToContainerWidth(fixedWidth ?? formatWidth(props.layout?.maxWidth) ?? '100%'),
142
+ : clampToContainerWidth(fixedWidth ?? formatWidth(props.layout?.max_width) ?? '100%'),
143
143
  height: formatWidth(props.layout?.height ?? DEFAULT_WIDGET_HEIGHT),
144
144
  }
145
145
  })
@@ -139,8 +139,8 @@ Use the current schema keys exactly:
139
139
 
140
140
  - Use `target`, not `type`.
141
141
  - Use `label`, not `title`.
142
- - Use `query`, not `data_source`.
143
- - Use `resource`, not `resource_id`.
142
+ - Use `query`, not `dataSource`.
143
+ - Use `resource`, not `resourceId`.
144
144
  - Use `group_by`, not `groupBy`.
145
145
  - Use `order_by`, not `orderBy`.
146
146
  - Use `page_size`, not `pageSize`.
@@ -1,8 +1,66 @@
1
+ <template>
2
+ <div class="mt-3 rounded-lg border border-lightListBorder bg-lightTableBackground p-4 dark:border-darkListBorder dark:bg-darkTableBackground">
3
+ <div
4
+ v-if="isLoading"
5
+ class="text-sm text-lightListTableText dark:text-darkListTableText"
6
+ >
7
+ Loading...
8
+ </div>
9
+
10
+ <div
11
+ v-else-if="error"
12
+ class="text-sm text-lightInputErrorColor"
13
+ >
14
+ Failed to load gauge data
15
+ </div>
16
+
17
+ <div
18
+ v-else
19
+ class="flex flex-col items-center gap-2"
20
+ >
21
+ <svg
22
+ width="180"
23
+ height="104"
24
+ viewBox="0 0 180 104"
25
+ role="img"
26
+ :aria-label="valueField"
27
+ >
28
+ <path
29
+ d="M18 90a72 72 0 0 1 144 0"
30
+ class="text-lightListBorder dark:text-darkListBorder"
31
+ fill="none"
32
+ stroke="currentColor"
33
+ stroke-linecap="round"
34
+ stroke-width="18"
35
+ />
36
+ <path
37
+ d="M18 90a72 72 0 0 1 144 0"
38
+ fill="none"
39
+ :stroke="gaugeColor"
40
+ stroke-linecap="round"
41
+ stroke-width="18"
42
+ :stroke-dasharray="circumference"
43
+ :stroke-dashoffset="strokeDashoffset"
44
+ />
45
+ </svg>
46
+
47
+ <div class="text-3xl font-bold text-lightNavbarText dark:text-darkNavbarText">
48
+ {{ gaugeConfig?.value.prefix ?? '' }}{{ formattedValue }}{{ gaugeConfig?.value.suffix ?? '' }}
49
+ </div>
50
+ <div class="text-sm text-lightListTableText dark:text-darkListTableText">
51
+ {{ formattedMinValue }} - {{ formattedMaxValue }}
52
+ </div>
53
+ </div>
54
+ </div>
55
+ </template>
56
+
57
+
58
+
1
59
  <script setup lang="ts">
2
60
  import { computed, watch } from 'vue'
3
- import { useWidgetData } from '../../queries/useWidgetData.js'
4
- import type { DashboardWidgetConfig, DashboardWidgetTableData } from '../../model/dashboard.types.js'
5
- import { CHART_COLORS, formatChartValue, toFiniteNumber } from '../chart/chart.utils.js'
61
+ import { useWidgetData } from '../queries/useWidgetData.js'
62
+ import type { DashboardWidgetConfig, DashboardWidgetTableData } from '../model/dashboard.types.js'
63
+ import { CHART_COLORS, formatChartValue, toFiniteNumber } from './chart/chart.utils.js'
6
64
 
7
65
  const props = defineProps<{
8
66
  dashboardSlug: string
@@ -57,13 +115,13 @@ const widgetData = computed(() => data.value?.data as DashboardWidgetTableData |
57
115
  const columns = computed(() => widgetData.value?.columns ?? [])
58
116
  const firstRow = computed(() => widgetData.value?.rows[0] ?? {})
59
117
  const valueField = computed(() => gaugeConfig.value?.value.field || columns.value[0])
60
- const targetField = computed(() => gaugeConfig.value?.target?.field ?? gaugeConfig.value?.progress?.targetField)
118
+ const targetField = computed(() => gaugeConfig.value?.target?.field ?? gaugeConfig.value?.progress?.target_field)
61
119
  const minValue = computed(() => {
62
120
  return 0
63
121
  })
64
122
  const maxValue = computed(() => {
65
123
  const dynamicMax = targetField.value ? parseOptionalNumber(firstRow.value[targetField.value]) : undefined
66
- return dynamicMax ?? parseOptionalNumber(gaugeConfig.value?.target?.value ?? gaugeConfig.value?.progress?.targetValue) ?? 100
124
+ return dynamicMax ?? parseOptionalNumber(gaugeConfig.value?.target?.value ?? gaugeConfig.value?.progress?.target_value) ?? 100
67
125
  })
68
126
  const value = computed(() => toFiniteNumber(firstRow.value[valueField.value]))
69
127
  const fractionDigits = computed(() => Math.min([
@@ -95,59 +153,3 @@ const circumference = Math.PI * radius
95
153
  const strokeDashoffset = computed(() => circumference * (1 - progress.value))
96
154
  const gaugeColor = computed(() => gaugeConfig.value?.color || CHART_COLORS[0])
97
155
  </script>
98
-
99
- <template>
100
- <div class="mt-3 rounded-lg border border-lightListBorder bg-lightTableBackground p-4 dark:border-darkListBorder dark:bg-darkTableBackground">
101
- <div
102
- v-if="isLoading"
103
- class="text-sm text-lightListTableText dark:text-darkListTableText"
104
- >
105
- Loading...
106
- </div>
107
-
108
- <div
109
- v-else-if="error"
110
- class="text-sm text-lightInputErrorColor"
111
- >
112
- Failed to load gauge data
113
- </div>
114
-
115
- <div
116
- v-else
117
- class="flex flex-col items-center gap-2"
118
- >
119
- <svg
120
- width="180"
121
- height="104"
122
- viewBox="0 0 180 104"
123
- role="img"
124
- :aria-label="valueField"
125
- >
126
- <path
127
- d="M18 90a72 72 0 0 1 144 0"
128
- class="text-lightListBorder dark:text-darkListBorder"
129
- fill="none"
130
- stroke="currentColor"
131
- stroke-linecap="round"
132
- stroke-width="18"
133
- />
134
- <path
135
- d="M18 90a72 72 0 0 1 144 0"
136
- fill="none"
137
- :stroke="gaugeColor"
138
- stroke-linecap="round"
139
- stroke-width="18"
140
- :stroke-dasharray="circumference"
141
- :stroke-dashoffset="strokeDashoffset"
142
- />
143
- </svg>
144
-
145
- <div class="text-3xl font-bold text-lightNavbarText dark:text-darkNavbarText">
146
- {{ gaugeConfig?.value.prefix ?? '' }}{{ formattedValue }}{{ gaugeConfig?.value.suffix ?? '' }}
147
- </div>
148
- <div class="text-sm text-lightListTableText dark:text-darkListTableText">
149
- {{ formattedMinValue }} - {{ formattedMaxValue }}
150
- </div>
151
- </div>
152
- </div>
153
- </template>
@@ -1,8 +1,40 @@
1
+ <template>
2
+ <div class="mt-3 rounded-lg border border-lightListBorder bg-lightTableBackground p-4 dark:border-darkListBorder dark:bg-darkTableBackground">
3
+ <div
4
+ v-if="isLoading"
5
+ class="text-sm text-lightListTableText dark:text-darkListTableText"
6
+ >
7
+ Loading...
8
+ </div>
9
+
10
+ <div
11
+ v-else-if="error"
12
+ class="text-sm text-lightInputErrorColor"
13
+ >
14
+ Failed to load KPI data
15
+ </div>
16
+
17
+ <div
18
+ v-else
19
+ class="grid gap-1"
20
+ >
21
+ <div class="text-3xl font-bold text-lightNavbarText dark:text-darkNavbarText">
22
+ {{ formattedValue }}
23
+ </div>
24
+ <div class="text-sm text-lightListTableText dark:text-darkListTableText">
25
+ {{ label }}
26
+ </div>
27
+ </div>
28
+ </div>
29
+ </template>
30
+
31
+
32
+
1
33
  <script setup lang="ts">
2
34
  import { computed, watch } from 'vue'
3
- import { useWidgetData } from '../../queries/useWidgetData.js'
4
- import type { DashboardWidgetConfig, DashboardWidgetTableData } from '../../model/dashboard.types.js'
5
- import { formatChartValue, toFiniteNumber } from '../chart/chart.utils.js'
35
+ import { useWidgetData } from '../queries/useWidgetData.js'
36
+ import type { DashboardWidgetConfig, DashboardWidgetTableData } from '../model/dashboard.types.js'
37
+ import { formatChartValue, toFiniteNumber } from './chart/chart.utils.js'
6
38
 
7
39
  const props = defineProps<{
8
40
  dashboardSlug: string
@@ -37,33 +69,3 @@ const label = computed(() => kpiConfig.value?.subtitle?.field
37
69
  : kpiConfig.value?.subtitle?.text ?? kpiConfig.value?.title ?? props.widget.label)
38
70
  const formattedValue = computed(() => `${kpiConfig.value?.value.prefix ?? ''}${formatChartValue(value.value)}${kpiConfig.value?.value.suffix ?? ''}`)
39
71
  </script>
40
-
41
- <template>
42
- <div class="mt-3 rounded-lg border border-lightListBorder bg-lightTableBackground p-4 dark:border-darkListBorder dark:bg-darkTableBackground">
43
- <div
44
- v-if="isLoading"
45
- class="text-sm text-lightListTableText dark:text-darkListTableText"
46
- >
47
- Loading...
48
- </div>
49
-
50
- <div
51
- v-else-if="error"
52
- class="text-sm text-lightInputErrorColor"
53
- >
54
- Failed to load KPI data
55
- </div>
56
-
57
- <div
58
- v-else
59
- class="grid gap-1"
60
- >
61
- <div class="text-3xl font-bold text-lightNavbarText dark:text-darkNavbarText">
62
- {{ formattedValue }}
63
- </div>
64
- <div class="text-sm text-lightListTableText dark:text-darkListTableText">
65
- {{ label }}
66
- </div>
67
- </div>
68
- </div>
69
- </template>
@@ -1,11 +1,78 @@
1
+ <template>
2
+ <div class="mt-3 flex h-full min-h-0 flex-col overflow-hidden rounded-lg border border-lightListBorder bg-lightTableBackground dark:border-darkListBorder dark:bg-darkTableBackground">
3
+ <div
4
+ v-if="isLoading"
5
+ class="p-4 text-sm text-lightListTableText dark:text-darkListTableText"
6
+ >
7
+ Loading...
8
+ </div>
9
+
10
+ <div
11
+ v-else-if="error"
12
+ class="p-4 text-sm text-lightInputErrorColor"
13
+ >
14
+ Failed to load pivot data
15
+ </div>
16
+
17
+ <div
18
+ v-else-if="!pivotRows.length"
19
+ class="p-4 text-sm text-lightListTableText dark:text-darkListTableText"
20
+ >
21
+ No data available
22
+ </div>
23
+
24
+ <div
25
+ v-else
26
+ class="min-h-0 flex-1 overflow-auto"
27
+ >
28
+ <table class="min-w-max w-full border-collapse text-left text-sm">
29
+ <thead class="bg-lightTableHeadingBackground text-xs uppercase text-lightTableHeadingText dark:bg-darkTableHeadingBackground dark:text-darkTableHeadingText">
30
+ <tr>
31
+ <th class="px-3 py-2 font-semibold">
32
+ {{ rowField }}
33
+ </th>
34
+ <th
35
+ v-for="column in pivotColumnLabels"
36
+ :key="column"
37
+ class="px-3 py-2 text-right font-semibold"
38
+ >
39
+ {{ column }}
40
+ </th>
41
+ </tr>
42
+ </thead>
43
+ <tbody>
44
+ <tr
45
+ v-for="row in pivotRows"
46
+ :key="String(row.label)"
47
+ class="border-t border-lightListBorder odd:bg-lightTableOddBackground even:bg-lightTableEvenBackground dark:border-darkListBorder odd:dark:bg-darkTableOddBackground even:dark:bg-darkTableEvenBackground"
48
+ >
49
+ <td class="px-3 py-2 font-medium text-lightNavbarText dark:text-darkNavbarText">
50
+ {{ row.label }}
51
+ </td>
52
+ <td
53
+ v-for="column in pivotColumnLabels"
54
+ :key="column"
55
+ class="px-3 py-2 text-right text-lightListTableText dark:text-darkListTableText"
56
+ >
57
+ {{ formatChartValue(typeof row[column] === 'number' ? row[column] : 0) }}
58
+ </td>
59
+ </tr>
60
+ </tbody>
61
+ </table>
62
+ </div>
63
+ </div>
64
+ </template>
65
+
66
+
67
+
1
68
  <script setup lang="ts">
2
69
  import { computed, watch } from 'vue'
3
- import { useWidgetData } from '../../queries/useWidgetData.js'
70
+ import { useWidgetData } from '../queries/useWidgetData.js'
4
71
  import {
5
72
  getFieldRefField,
6
- } from '../../model/dashboard.types.js'
7
- import type { DashboardWidgetConfig, DashboardWidgetData } from '../../model/dashboard.types.js'
8
- import { formatChartLabel, formatChartValue, toFiniteNumber } from '../chart/chart.utils.js'
73
+ } from '../model/dashboard.types.js'
74
+ import type { DashboardWidgetConfig, DashboardWidgetData } from '../model/dashboard.types.js'
75
+ import { formatChartLabel, formatChartValue, toFiniteNumber } from './chart/chart.utils.js'
9
76
 
10
77
  const props = defineProps<{
11
78
  dashboardSlug: string
@@ -77,67 +144,3 @@ const pivotRows = computed(() => {
77
144
  })
78
145
  </script>
79
146
 
80
- <template>
81
- <div class="mt-3 flex h-full min-h-0 flex-col overflow-hidden rounded-lg border border-lightListBorder bg-lightTableBackground dark:border-darkListBorder dark:bg-darkTableBackground">
82
- <div
83
- v-if="isLoading"
84
- class="p-4 text-sm text-lightListTableText dark:text-darkListTableText"
85
- >
86
- Loading...
87
- </div>
88
-
89
- <div
90
- v-else-if="error"
91
- class="p-4 text-sm text-lightInputErrorColor"
92
- >
93
- Failed to load pivot data
94
- </div>
95
-
96
- <div
97
- v-else-if="!pivotRows.length"
98
- class="p-4 text-sm text-lightListTableText dark:text-darkListTableText"
99
- >
100
- No data available
101
- </div>
102
-
103
- <div
104
- v-else
105
- class="min-h-0 flex-1 overflow-auto"
106
- >
107
- <table class="min-w-max w-full border-collapse text-left text-sm">
108
- <thead class="bg-lightTableHeadingBackground text-xs uppercase text-lightTableHeadingText dark:bg-darkTableHeadingBackground dark:text-darkTableHeadingText">
109
- <tr>
110
- <th class="px-3 py-2 font-semibold">
111
- {{ rowField }}
112
- </th>
113
- <th
114
- v-for="column in pivotColumnLabels"
115
- :key="column"
116
- class="px-3 py-2 text-right font-semibold"
117
- >
118
- {{ column }}
119
- </th>
120
- </tr>
121
- </thead>
122
- <tbody>
123
- <tr
124
- v-for="row in pivotRows"
125
- :key="String(row.label)"
126
- class="border-t border-lightListBorder odd:bg-lightTableOddBackground even:bg-lightTableEvenBackground dark:border-darkListBorder odd:dark:bg-darkTableOddBackground even:dark:bg-darkTableEvenBackground"
127
- >
128
- <td class="px-3 py-2 font-medium text-lightNavbarText dark:text-darkNavbarText">
129
- {{ row.label }}
130
- </td>
131
- <td
132
- v-for="column in pivotColumnLabels"
133
- :key="column"
134
- class="px-3 py-2 text-right text-lightListTableText dark:text-darkListTableText"
135
- >
136
- {{ formatChartValue(typeof row[column] === 'number' ? row[column] : 0) }}
137
- </td>
138
- </tr>
139
- </tbody>
140
- </table>
141
- </div>
142
- </div>
143
- </template>
@@ -112,14 +112,14 @@
112
112
 
113
113
  <script setup lang="ts">
114
114
  import { computed, ref, watch } from 'vue'
115
- import { useWidgetData } from '../../queries/useWidgetData.js'
116
- import { getFieldRefField } from '../../model/dashboard.types.js'
117
- import type { DashboardWidgetConfig, DashboardWidgetTableData, FieldRef } from '../../model/dashboard.types.js'
115
+ import { useWidgetData } from '../queries/useWidgetData.js'
116
+ import { getFieldRefField } from '../model/dashboard.types.js'
117
+ import type { DashboardWidgetConfig, DashboardWidgetTableData, FieldRef } from '../model/dashboard.types.js'
118
118
 
119
119
  type TableWidgetConfig = {
120
120
  columns?: FieldRef[]
121
121
  pagination?: boolean
122
- pageSize?: number
122
+ page_size?: number
123
123
  }
124
124
 
125
125
  const DEFAULT_PAGE_SIZE = 10
@@ -133,7 +133,7 @@ const currentPage = ref(1)
133
133
  const currentPageInput = ref(1)
134
134
  const tableConfig = computed(() => props.widget.table as TableWidgetConfig | undefined)
135
135
  const isPaginationEnabled = computed(() => tableConfig.value?.pagination !== false)
136
- const pageSize = computed(() => tableConfig.value?.pageSize ?? DEFAULT_PAGE_SIZE)
136
+ const pageSize = computed(() => tableConfig.value?.page_size ?? DEFAULT_PAGE_SIZE)
137
137
  const dashboardSlugRef = computed(() => props.dashboardSlug)
138
138
  const widgetIdRef = computed(() => props.widget.id)
139
139
  const widgetDataRequest = computed(() => (
@@ -82,7 +82,7 @@
82
82
 
83
83
  <script setup lang="ts">
84
84
  import { computed } from 'vue'
85
- import { useElementSize } from '../../../composables/useElementSize.js'
85
+ import { useElementSize } from '../../composables/useElementSize.js'
86
86
  import {
87
87
  CHART_COLORS,
88
88
  formatChartAxisLabel,
@@ -90,7 +90,7 @@ import {
90
90
  formatChartValue,
91
91
  getChartYAxisWidth,
92
92
  toFiniteNumber,
93
- } from '../chart.utils.js'
93
+ } from './chart.utils.js'
94
94
 
95
95
  const props = withDefaults(defineProps<{
96
96
  rows: Record<string, unknown>[]
@@ -41,16 +41,7 @@
41
41
  />
42
42
 
43
43
  <BarChart
44
- v-else-if="chartConfig?.type === 'bar'"
45
- :rows="barRows"
46
- :label-field="barLabelField"
47
- :value-field="barValueField"
48
- :color="chartConfig.color"
49
- :height="chartHeight"
50
- />
51
-
52
- <HistogramChart
53
- v-else-if="chartConfig?.type === 'histogram'"
44
+ v-else-if="chartConfig?.type === 'bar' || chartConfig?.type === 'histogram'"
54
45
  :rows="barRows"
55
46
  :label-field="barLabelField"
56
47
  :value-field="barValueField"
@@ -91,11 +82,9 @@
91
82
  <script setup lang="ts">
92
83
  import { computed, watch } from 'vue'
93
84
  import { useWidgetData } from '../../queries/useWidgetData.js'
94
- import type { DashboardWidgetConfig, DashboardWidgetTableData } from '../../model/dashboard.types.js'
95
- import { normalizeChartWidgetConfig } from './chart.types.js'
85
+ import type { ChartDashboardWidgetConfig, DashboardWidgetTableData } from '../../model/dashboard.types.js'
96
86
  import BarChart from './bar/BarChart.vue'
97
87
  import FunnelChart from './funnel/FunnelChart.vue'
98
- import HistogramChart from './histogram/HistogramChart.vue'
99
88
  import LineChart from './line/LineChart.vue'
100
89
  import PieChart from './pie/PieChart.vue'
101
90
  import StackedBarChart from './stacked-bar/StackedBarChart.vue'
@@ -105,7 +94,7 @@ const DEFAULT_WIDGET_HEIGHT = 500
105
94
 
106
95
  const props = defineProps<{
107
96
  dashboardSlug: string
108
- widget: DashboardWidgetConfig
97
+ widget: ChartDashboardWidgetConfig
109
98
  }>()
110
99
 
111
100
  const dashboardSlugRef = computed(() => props.dashboardSlug)
@@ -128,7 +117,7 @@ watch(
128
117
  const chartData = computed(() => data.value?.data as DashboardWidgetTableData | null)
129
118
  const rows = computed(() => chartData.value?.rows ?? [])
130
119
  const columns = computed(() => chartData.value?.columns ?? [])
131
- const chartConfig = computed(() => normalizeChartWidgetConfig(props.widget.chart))
120
+ const chartConfig = computed(() => props.widget.chart)
132
121
 
133
122
  function resolveChartDimensionField(field: string | undefined, fallbackField: string | undefined) {
134
123
  const resolvedField = field ?? fallbackField