@adminforth/dashboard 1.0.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 (107) hide show
  1. package/.woodpecker/buildRelease.sh +13 -0
  2. package/.woodpecker/buildSlackNotify.sh +46 -0
  3. package/.woodpecker/release.yml +57 -0
  4. package/README.md +59 -0
  5. package/custom/api/dashboardApi.ts +213 -0
  6. package/custom/composables/useElementSize.ts +41 -0
  7. package/custom/model/dashboard.types.ts +73 -0
  8. package/custom/package.json +9 -0
  9. package/custom/pnpm-lock.yaml +24 -0
  10. package/custom/queries/useDashboardConfig.ts +51 -0
  11. package/custom/queries/useWidgetData.ts +51 -0
  12. package/custom/runtime/DashboardGroup.vue +185 -0
  13. package/custom/runtime/DashboardPage.vue +122 -0
  14. package/custom/runtime/DashboardRuntime.vue +435 -0
  15. package/custom/runtime/WidgetRenderer.vue +60 -0
  16. package/custom/runtime/WidgetShell.vue +152 -0
  17. package/custom/skills/adminforth-dashboard/SKILL.md +125 -0
  18. package/custom/widgets/chart/ChartWidget.vue +188 -0
  19. package/custom/widgets/chart/bar/BarChart.vue +167 -0
  20. package/custom/widgets/chart/chart.types.ts +34 -0
  21. package/custom/widgets/chart/chart.utils.ts +54 -0
  22. package/custom/widgets/chart/funnel/FunnelChart.vue +197 -0
  23. package/custom/widgets/chart/histogram/HistogramChart.vue +21 -0
  24. package/custom/widgets/chart/line/LineChart.vue +175 -0
  25. package/custom/widgets/chart/pie/PieChart.vue +161 -0
  26. package/custom/widgets/chart/stacked-bar/StackedBarChart.vue +256 -0
  27. package/custom/widgets/gauge-card/GaugeCardWidget.vue +107 -0
  28. package/custom/widgets/kpi-card/KpiCardWidget.vue +73 -0
  29. package/custom/widgets/pivot-table/PivotTableWidget.vue +122 -0
  30. package/custom/widgets/registry.ts +51 -0
  31. package/custom/widgets/table/TableWidget.vue +110 -0
  32. package/dist/custom/api/dashboardApi.d.ts +32 -0
  33. package/dist/custom/api/dashboardApi.js +179 -0
  34. package/dist/custom/api/dashboardApi.ts +213 -0
  35. package/dist/custom/composables/useElementSize.d.ts +8 -0
  36. package/dist/custom/composables/useElementSize.js +30 -0
  37. package/dist/custom/composables/useElementSize.ts +41 -0
  38. package/dist/custom/model/dashboard.types.d.ts +45 -0
  39. package/dist/custom/model/dashboard.types.js +14 -0
  40. package/dist/custom/model/dashboard.types.ts +73 -0
  41. package/dist/custom/package.json +9 -0
  42. package/dist/custom/pnpm-lock.yaml +24 -0
  43. package/dist/custom/queries/useDashboardConfig.d.ts +112 -0
  44. package/dist/custom/queries/useDashboardConfig.js +57 -0
  45. package/dist/custom/queries/useDashboardConfig.ts +51 -0
  46. package/dist/custom/queries/useWidgetData.d.ts +90 -0
  47. package/dist/custom/queries/useWidgetData.js +57 -0
  48. package/dist/custom/queries/useWidgetData.ts +51 -0
  49. package/dist/custom/runtime/DashboardGroup.vue +185 -0
  50. package/dist/custom/runtime/DashboardPage.vue +122 -0
  51. package/dist/custom/runtime/DashboardRuntime.vue +435 -0
  52. package/dist/custom/runtime/WidgetRenderer.vue +60 -0
  53. package/dist/custom/runtime/WidgetShell.vue +152 -0
  54. package/dist/custom/skills/adminforth-dashboard/SKILL.md +125 -0
  55. package/dist/custom/widgets/chart/ChartWidget.vue +188 -0
  56. package/dist/custom/widgets/chart/bar/BarChart.vue +167 -0
  57. package/dist/custom/widgets/chart/chart.types.d.ts +25 -0
  58. package/dist/custom/widgets/chart/chart.types.js +2 -0
  59. package/dist/custom/widgets/chart/chart.types.ts +34 -0
  60. package/dist/custom/widgets/chart/chart.utils.d.ts +5 -0
  61. package/dist/custom/widgets/chart/chart.utils.js +52 -0
  62. package/dist/custom/widgets/chart/chart.utils.ts +54 -0
  63. package/dist/custom/widgets/chart/funnel/FunnelChart.vue +197 -0
  64. package/dist/custom/widgets/chart/histogram/HistogramChart.vue +21 -0
  65. package/dist/custom/widgets/chart/line/LineChart.vue +175 -0
  66. package/dist/custom/widgets/chart/pie/PieChart.vue +161 -0
  67. package/dist/custom/widgets/chart/stacked-bar/StackedBarChart.vue +256 -0
  68. package/dist/custom/widgets/gauge-card/GaugeCardWidget.vue +107 -0
  69. package/dist/custom/widgets/kpi-card/KpiCardWidget.vue +73 -0
  70. package/dist/custom/widgets/pivot-table/PivotTableWidget.vue +122 -0
  71. package/dist/custom/widgets/registry.d.ts +11 -0
  72. package/dist/custom/widgets/registry.js +47 -0
  73. package/dist/custom/widgets/registry.ts +51 -0
  74. package/dist/custom/widgets/table/TableWidget.vue +110 -0
  75. package/dist/endpoint/dashboard.d.ts +7 -0
  76. package/dist/endpoint/dashboard.js +29 -0
  77. package/dist/endpoint/groups.d.ts +30 -0
  78. package/dist/endpoint/groups.js +131 -0
  79. package/dist/endpoint/widgets.d.ts +15 -0
  80. package/dist/endpoint/widgets.js +182 -0
  81. package/dist/index.d.ts +13 -0
  82. package/dist/index.js +124 -0
  83. package/dist/schema/api.d.ts +1205 -0
  84. package/dist/schema/api.js +84 -0
  85. package/dist/schema/widget.d.ts +514 -0
  86. package/dist/schema/widget.js +133 -0
  87. package/dist/services/dashboardConfigService.d.ts +35 -0
  88. package/dist/services/dashboardConfigService.js +79 -0
  89. package/dist/services/widgetConfigValidator.d.ts +8 -0
  90. package/dist/services/widgetConfigValidator.js +65 -0
  91. package/dist/services/widgetDataService.d.ts +20 -0
  92. package/dist/services/widgetDataService.js +32 -0
  93. package/dist/types.d.ts +8 -0
  94. package/dist/types.js +1 -0
  95. package/endpoint/dashboard.ts +32 -0
  96. package/endpoint/groups.ts +213 -0
  97. package/endpoint/widgets.ts +255 -0
  98. package/index.ts +141 -0
  99. package/package.json +64 -0
  100. package/schema/api.ts +99 -0
  101. package/schema/widget.ts +159 -0
  102. package/services/dashboardConfigService.ts +136 -0
  103. package/services/widgetConfigValidator.ts +93 -0
  104. package/services/widgetDataService.ts +57 -0
  105. package/shims-vue.d.ts +5 -0
  106. package/tsconfig.json +18 -0
  107. package/types.ts +8 -0
@@ -0,0 +1,110 @@
1
+ <template>
2
+ <div class="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 table data
15
+ </div>
16
+
17
+ <div
18
+ v-else-if="!tableData?.rows.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
32
+ v-for="column in columns"
33
+ :key="column"
34
+ class="px-3 py-2 font-semibold"
35
+ >
36
+ {{ column }}
37
+ </th>
38
+ </tr>
39
+ </thead>
40
+
41
+ <tbody>
42
+ <tr
43
+ v-for="(row, index) in tableData.rows"
44
+ :key="index"
45
+ class="border-t border-lightListBorder odd:bg-lightTableOddBackground even:bg-lightTableEvenBackground dark:border-darkListBorder odd:dark:bg-darkTableOddBackground even:dark:bg-darkTableEvenBackground"
46
+ >
47
+ <td
48
+ v-for="column in columns"
49
+ :key="column"
50
+ class="px-3 py-2 text-lightListTableText dark:text-darkListTableText"
51
+ >
52
+ {{ formatCell(row[column]) }}
53
+ </td>
54
+ </tr>
55
+ </tbody>
56
+ </table>
57
+ </div>
58
+ </div>
59
+ </template>
60
+
61
+
62
+
63
+ <script setup lang="ts">
64
+ import { computed, watch } from 'vue'
65
+ import { useWidgetData } from '../../queries/useWidgetData.js'
66
+ import type { DashboardWidgetConfig, DashboardWidgetTableData } from '../../model/dashboard.types.js'
67
+
68
+ const props = defineProps<{
69
+ dashboardSlug: string
70
+ widget: DashboardWidgetConfig
71
+ }>()
72
+
73
+ const dashboardSlugRef = computed(() => props.dashboardSlug)
74
+ const widgetIdRef = computed(() => props.widget.id)
75
+ const {
76
+ data,
77
+ isLoading,
78
+ error,
79
+ refetch,
80
+ } = useWidgetData(dashboardSlugRef, widgetIdRef)
81
+
82
+ watch(
83
+ () => props.widget,
84
+ () => {
85
+ void refetch()
86
+ },
87
+ { deep: true },
88
+ )
89
+
90
+ const tableData = computed(() => {
91
+ return data.value?.data as DashboardWidgetTableData | null
92
+ })
93
+
94
+ const columns = computed(() => {
95
+ const configuredColumns = (props.widget.table as { columns?: string[] } | undefined)?.columns
96
+ return configuredColumns ?? tableData.value?.columns ?? []
97
+ })
98
+
99
+ function formatCell(value: unknown) {
100
+ if (value === null || value === undefined) {
101
+ return ''
102
+ }
103
+
104
+ if (typeof value === 'object') {
105
+ return JSON.stringify(value)
106
+ }
107
+
108
+ return String(value)
109
+ }
110
+ </script>
@@ -0,0 +1,32 @@
1
+ import type { DashboardConfig, DashboardGroupConfig, DashboardGroupMoveDirection, DashboardWidgetConfig, DashboardWidgetMoveDirection } from '../model/dashboard.types.js';
2
+ export type DashboardWidgetConfigValidationError = {
3
+ field: string;
4
+ message: string;
5
+ };
6
+ export type DashboardResponse = {
7
+ id: string;
8
+ slug: string;
9
+ label: string;
10
+ revision: number;
11
+ config: DashboardConfig;
12
+ };
13
+ export type DashboardWidgetDataResponse = {
14
+ widget: DashboardWidgetConfig;
15
+ data: unknown;
16
+ };
17
+ export declare class DashboardApiError extends Error {
18
+ validationErrors: DashboardWidgetConfigValidationError[];
19
+ constructor(message: string, validationErrors?: DashboardWidgetConfigValidationError[]);
20
+ }
21
+ export declare const dashboardApi: {
22
+ getDashboardConfig(slug: string): Promise<DashboardResponse>;
23
+ addDashboardGroup(slug: string): Promise<DashboardResponse>;
24
+ moveDashboardGroup(slug: string, groupId: string, direction: DashboardGroupMoveDirection): Promise<DashboardResponse>;
25
+ removeDashboardGroup(slug: string, groupId: string): Promise<DashboardResponse>;
26
+ setDashboardGroupConfig(slug: string, groupId: string, config: DashboardGroupConfig): Promise<DashboardResponse>;
27
+ addDashboardWidget(slug: string, groupId: string): Promise<DashboardResponse>;
28
+ moveDashboardWidget(slug: string, widgetId: string, direction: DashboardWidgetMoveDirection): Promise<DashboardResponse>;
29
+ removeDashboardWidget(slug: string, widgetId: string): Promise<DashboardResponse>;
30
+ setWidgetConfig(slug: string, widgetId: string, config: DashboardWidgetConfig): Promise<DashboardResponse>;
31
+ getDashboardWidgetData(slug: string, widgetId: string): Promise<DashboardWidgetDataResponse>;
32
+ };
@@ -0,0 +1,179 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.dashboardApi = exports.DashboardApiError = void 0;
13
+ class DashboardApiError extends Error {
14
+ constructor(message, validationErrors = []) {
15
+ super(message);
16
+ this.name = 'DashboardApiError';
17
+ this.validationErrors = validationErrors;
18
+ }
19
+ }
20
+ exports.DashboardApiError = DashboardApiError;
21
+ function normalizeValidationErrors(response) {
22
+ if (Array.isArray(response === null || response === void 0 ? void 0 : response.validationErrors)) {
23
+ return response.validationErrors;
24
+ }
25
+ if (Array.isArray(response === null || response === void 0 ? void 0 : response.details)) {
26
+ return response.details.map((detail) => ({
27
+ field: Array.isArray(detail.instancePath)
28
+ ? detail.instancePath.join('.')
29
+ : String(detail.instancePath || detail.path || 'config').replace(/^\//, '').replaceAll('/', '.'),
30
+ message: String(detail.message || 'Invalid value'),
31
+ }));
32
+ }
33
+ return [];
34
+ }
35
+ function parseDashboardResponse(rawResponse) {
36
+ return __awaiter(this, void 0, void 0, function* () {
37
+ const responseText = yield rawResponse.text();
38
+ if (!responseText) {
39
+ return null;
40
+ }
41
+ try {
42
+ return JSON.parse(responseText);
43
+ }
44
+ catch (_a) {
45
+ return {
46
+ error: responseText,
47
+ };
48
+ }
49
+ });
50
+ }
51
+ function callDashboardApi(path, body) {
52
+ return __awaiter(this, void 0, void 0, function* () {
53
+ const rawResponse = yield fetch(path, {
54
+ method: 'POST',
55
+ headers: {
56
+ 'Content-Type': 'application/json',
57
+ 'accept-language': localStorage.getItem('af_lang') || 'en',
58
+ },
59
+ body: JSON.stringify(body),
60
+ });
61
+ const response = yield parseDashboardResponse(rawResponse);
62
+ if (!rawResponse.ok) {
63
+ throw new DashboardApiError((response === null || response === void 0 ? void 0 : response.error) || rawResponse.statusText || `Dashboard request failed (${rawResponse.status})`, normalizeValidationErrors(response));
64
+ }
65
+ if (!response || response.error) {
66
+ throw new DashboardApiError((response === null || response === void 0 ? void 0 : response.error) || 'Dashboard request failed', normalizeValidationErrors(response));
67
+ }
68
+ return {
69
+ id: response.id,
70
+ slug: response.slug,
71
+ label: response.label,
72
+ revision: response.revision,
73
+ config: response.config,
74
+ };
75
+ });
76
+ }
77
+ function callDashboardWidgetDataApi(path, body) {
78
+ return __awaiter(this, void 0, void 0, function* () {
79
+ const rawResponse = yield fetch(path, {
80
+ method: 'POST',
81
+ headers: {
82
+ 'Content-Type': 'application/json',
83
+ 'accept-language': localStorage.getItem('af_lang') || 'en',
84
+ },
85
+ body: JSON.stringify(body),
86
+ });
87
+ const response = yield parseDashboardResponse(rawResponse);
88
+ if (!rawResponse.ok) {
89
+ throw new DashboardApiError((response === null || response === void 0 ? void 0 : response.error) || rawResponse.statusText || `Dashboard request failed (${rawResponse.status})`, normalizeValidationErrors(response));
90
+ }
91
+ if (!response || response.error) {
92
+ throw new DashboardApiError((response === null || response === void 0 ? void 0 : response.error) || 'Dashboard request failed', normalizeValidationErrors(response));
93
+ }
94
+ return {
95
+ widget: response.widget,
96
+ data: response.data,
97
+ };
98
+ });
99
+ }
100
+ exports.dashboardApi = {
101
+ getDashboardConfig(slug) {
102
+ return __awaiter(this, void 0, void 0, function* () {
103
+ return callDashboardApi('/adminapi/v1/dashboard/get-config', { slug });
104
+ });
105
+ },
106
+ addDashboardGroup(slug) {
107
+ return __awaiter(this, void 0, void 0, function* () {
108
+ return callDashboardApi('/adminapi/v1/dashboard/add_dashboard_group', { slug });
109
+ });
110
+ },
111
+ moveDashboardGroup(slug, groupId, direction) {
112
+ return __awaiter(this, void 0, void 0, function* () {
113
+ return callDashboardApi('/adminapi/v1/dashboard/move_dashboard_group', {
114
+ slug,
115
+ groupId,
116
+ direction,
117
+ });
118
+ });
119
+ },
120
+ removeDashboardGroup(slug, groupId) {
121
+ return __awaiter(this, void 0, void 0, function* () {
122
+ return callDashboardApi('/adminapi/v1/dashboard/remove_dashboard_group', {
123
+ slug,
124
+ groupId,
125
+ });
126
+ });
127
+ },
128
+ setDashboardGroupConfig(slug, groupId, config) {
129
+ return __awaiter(this, void 0, void 0, function* () {
130
+ return callDashboardApi('/adminapi/v1/dashboard/set_dashboard_group_config', {
131
+ slug,
132
+ groupId,
133
+ config,
134
+ });
135
+ });
136
+ },
137
+ addDashboardWidget(slug, groupId) {
138
+ return __awaiter(this, void 0, void 0, function* () {
139
+ return callDashboardApi('/adminapi/v1/dashboard/add_dashboard_widget', {
140
+ slug,
141
+ groupId,
142
+ });
143
+ });
144
+ },
145
+ moveDashboardWidget(slug, widgetId, direction) {
146
+ return __awaiter(this, void 0, void 0, function* () {
147
+ return callDashboardApi('/adminapi/v1/dashboard/move_dashboard_widget', {
148
+ slug,
149
+ widgetId,
150
+ direction,
151
+ });
152
+ });
153
+ },
154
+ removeDashboardWidget(slug, widgetId) {
155
+ return __awaiter(this, void 0, void 0, function* () {
156
+ return callDashboardApi('/adminapi/v1/dashboard/remove_dashboard_widget', {
157
+ slug,
158
+ widgetId,
159
+ });
160
+ });
161
+ },
162
+ setWidgetConfig(slug, widgetId, config) {
163
+ return __awaiter(this, void 0, void 0, function* () {
164
+ return callDashboardApi('/adminapi/v1/dashboard/set_widget_config', {
165
+ slug,
166
+ widgetId,
167
+ config,
168
+ });
169
+ });
170
+ },
171
+ getDashboardWidgetData(slug, widgetId) {
172
+ return __awaiter(this, void 0, void 0, function* () {
173
+ return callDashboardWidgetDataApi('/adminapi/v1/dashboard/get_dashboard_widget_data', {
174
+ slug,
175
+ widgetId,
176
+ });
177
+ });
178
+ },
179
+ };
@@ -0,0 +1,213 @@
1
+ import type {
2
+ DashboardConfig,
3
+ DashboardGroupConfig,
4
+ DashboardGroupMoveDirection,
5
+ DashboardWidgetConfig,
6
+ DashboardWidgetMoveDirection,
7
+ } from '../model/dashboard.types.js'
8
+
9
+ export type DashboardWidgetConfigValidationError = {
10
+ field: string
11
+ message: string
12
+ }
13
+
14
+ export type DashboardResponse = {
15
+ id: string
16
+ slug: string
17
+ label: string
18
+ revision: number
19
+ config: DashboardConfig
20
+ }
21
+
22
+ export type DashboardWidgetDataResponse = {
23
+ widget: DashboardWidgetConfig
24
+ data: unknown
25
+ }
26
+
27
+ export class DashboardApiError extends Error {
28
+ validationErrors: DashboardWidgetConfigValidationError[]
29
+
30
+ constructor(message: string, validationErrors: DashboardWidgetConfigValidationError[] = []) {
31
+ super(message)
32
+ this.name = 'DashboardApiError'
33
+ this.validationErrors = validationErrors
34
+ }
35
+ }
36
+
37
+ function normalizeValidationErrors(response: any): DashboardWidgetConfigValidationError[] {
38
+ if (Array.isArray(response?.validationErrors)) {
39
+ return response.validationErrors
40
+ }
41
+
42
+ if (Array.isArray(response?.details)) {
43
+ return response.details.map((detail: any) => ({
44
+ field: Array.isArray(detail.instancePath)
45
+ ? detail.instancePath.join('.')
46
+ : String(detail.instancePath || detail.path || 'config').replace(/^\//, '').replaceAll('/', '.'),
47
+ message: String(detail.message || 'Invalid value'),
48
+ }))
49
+ }
50
+
51
+ return []
52
+ }
53
+
54
+ async function parseDashboardResponse(rawResponse: Response) {
55
+ const responseText = await rawResponse.text()
56
+
57
+ if (!responseText) {
58
+ return null
59
+ }
60
+
61
+ try {
62
+ return JSON.parse(responseText)
63
+ } catch {
64
+ return {
65
+ error: responseText,
66
+ }
67
+ }
68
+ }
69
+
70
+ async function callDashboardApi(path: string, body: Record<string, unknown>): Promise<DashboardResponse> {
71
+ const rawResponse = await fetch(path, {
72
+ method: 'POST',
73
+ headers: {
74
+ 'Content-Type': 'application/json',
75
+ 'accept-language': localStorage.getItem('af_lang') || 'en',
76
+ },
77
+ body: JSON.stringify(body),
78
+ })
79
+
80
+ const response = await parseDashboardResponse(rawResponse)
81
+
82
+ if (!rawResponse.ok) {
83
+ throw new DashboardApiError(
84
+ response?.error || rawResponse.statusText || `Dashboard request failed (${rawResponse.status})`,
85
+ normalizeValidationErrors(response),
86
+ )
87
+ }
88
+
89
+ if (!response || response.error) {
90
+ throw new DashboardApiError(response?.error || 'Dashboard request failed', normalizeValidationErrors(response))
91
+ }
92
+
93
+ return {
94
+ id: response.id,
95
+ slug: response.slug,
96
+ label: response.label,
97
+ revision: response.revision,
98
+ config: response.config,
99
+ }
100
+ }
101
+
102
+ async function callDashboardWidgetDataApi(
103
+ path: string,
104
+ body: Record<string, unknown>,
105
+ ): Promise<DashboardWidgetDataResponse> {
106
+ const rawResponse = await fetch(path, {
107
+ method: 'POST',
108
+ headers: {
109
+ 'Content-Type': 'application/json',
110
+ 'accept-language': localStorage.getItem('af_lang') || 'en',
111
+ },
112
+ body: JSON.stringify(body),
113
+ })
114
+
115
+ const response = await parseDashboardResponse(rawResponse)
116
+
117
+ if (!rawResponse.ok) {
118
+ throw new DashboardApiError(
119
+ response?.error || rawResponse.statusText || `Dashboard request failed (${rawResponse.status})`,
120
+ normalizeValidationErrors(response),
121
+ )
122
+ }
123
+
124
+ if (!response || response.error) {
125
+ throw new DashboardApiError(response?.error || 'Dashboard request failed', normalizeValidationErrors(response))
126
+ }
127
+
128
+ return {
129
+ widget: response.widget,
130
+ data: response.data,
131
+ }
132
+ }
133
+
134
+ export const dashboardApi = {
135
+ async getDashboardConfig(slug: string): Promise<DashboardResponse> {
136
+ return callDashboardApi('/adminapi/v1/dashboard/get-config', { slug })
137
+ },
138
+
139
+ async addDashboardGroup(slug: string): Promise<DashboardResponse> {
140
+ return callDashboardApi('/adminapi/v1/dashboard/add_dashboard_group', { slug })
141
+ },
142
+
143
+ async moveDashboardGroup(
144
+ slug: string,
145
+ groupId: string,
146
+ direction: DashboardGroupMoveDirection,
147
+ ): Promise<DashboardResponse> {
148
+ return callDashboardApi('/adminapi/v1/dashboard/move_dashboard_group', {
149
+ slug,
150
+ groupId,
151
+ direction,
152
+ })
153
+ },
154
+
155
+ async removeDashboardGroup(slug: string, groupId: string): Promise<DashboardResponse> {
156
+ return callDashboardApi('/adminapi/v1/dashboard/remove_dashboard_group', {
157
+ slug,
158
+ groupId,
159
+ })
160
+ },
161
+
162
+ async setDashboardGroupConfig(slug: string, groupId: string, config: DashboardGroupConfig): Promise<DashboardResponse> {
163
+ return callDashboardApi('/adminapi/v1/dashboard/set_dashboard_group_config', {
164
+ slug,
165
+ groupId,
166
+ config,
167
+ })
168
+ },
169
+
170
+ async addDashboardWidget(slug: string, groupId: string): Promise<DashboardResponse> {
171
+ return callDashboardApi('/adminapi/v1/dashboard/add_dashboard_widget', {
172
+ slug,
173
+ groupId,
174
+ })
175
+ },
176
+
177
+ async moveDashboardWidget(
178
+ slug: string,
179
+ widgetId: string,
180
+ direction: DashboardWidgetMoveDirection,
181
+ ): Promise<DashboardResponse> {
182
+ return callDashboardApi('/adminapi/v1/dashboard/move_dashboard_widget', {
183
+ slug,
184
+ widgetId,
185
+ direction,
186
+ })
187
+ },
188
+
189
+ async removeDashboardWidget(slug: string, widgetId: string): Promise<DashboardResponse> {
190
+ return callDashboardApi('/adminapi/v1/dashboard/remove_dashboard_widget', {
191
+ slug,
192
+ widgetId,
193
+ })
194
+ },
195
+
196
+ async setWidgetConfig(slug: string, widgetId: string, config: DashboardWidgetConfig): Promise<DashboardResponse> {
197
+ return callDashboardApi('/adminapi/v1/dashboard/set_widget_config', {
198
+ slug,
199
+ widgetId,
200
+ config,
201
+ })
202
+ },
203
+
204
+ async getDashboardWidgetData(
205
+ slug: string,
206
+ widgetId: string,
207
+ ): Promise<DashboardWidgetDataResponse> {
208
+ return callDashboardWidgetDataApi('/adminapi/v1/dashboard/get_dashboard_widget_data', {
209
+ slug,
210
+ widgetId,
211
+ })
212
+ },
213
+ }
@@ -0,0 +1,8 @@
1
+ import type { Ref, ShallowRef } from 'vue';
2
+ type ElementSizeState<T extends HTMLElement> = {
3
+ el: ShallowRef<T | null>;
4
+ width: Ref<number>;
5
+ height: Ref<number>;
6
+ };
7
+ export declare function useElementSize<T extends HTMLElement>(): ElementSizeState<T>;
8
+ export {};
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useElementSize = useElementSize;
4
+ const vue_1 = require("vue");
5
+ function useElementSize() {
6
+ const el = (0, vue_1.shallowRef)(null);
7
+ const width = (0, vue_1.ref)(0);
8
+ const height = (0, vue_1.ref)(0);
9
+ let observer;
10
+ (0, vue_1.onMounted)(() => {
11
+ observer = new ResizeObserver(([entry]) => {
12
+ if (!entry) {
13
+ return;
14
+ }
15
+ width.value = Math.floor(entry.contentRect.width);
16
+ height.value = Math.floor(entry.contentRect.height);
17
+ });
18
+ if (el.value) {
19
+ observer.observe(el.value);
20
+ }
21
+ });
22
+ (0, vue_1.onBeforeUnmount)(() => {
23
+ observer === null || observer === void 0 ? void 0 : observer.disconnect();
24
+ });
25
+ return {
26
+ el: el,
27
+ width,
28
+ height,
29
+ };
30
+ }
@@ -0,0 +1,41 @@
1
+ import { onBeforeUnmount, onMounted, ref, shallowRef } from 'vue'
2
+ import type { Ref, ShallowRef } from 'vue'
3
+
4
+ type ElementSizeState<T extends HTMLElement> = {
5
+ el: ShallowRef<T | null>
6
+ width: Ref<number>
7
+ height: Ref<number>
8
+ }
9
+
10
+ export function useElementSize<T extends HTMLElement>(): ElementSizeState<T> {
11
+ const el = shallowRef<T | null>(null)
12
+ const width = ref(0)
13
+ const height = ref(0)
14
+
15
+ let observer: ResizeObserver | undefined
16
+
17
+ onMounted(() => {
18
+ observer = new ResizeObserver(([entry]) => {
19
+ if (!entry) {
20
+ return
21
+ }
22
+
23
+ width.value = Math.floor(entry.contentRect.width)
24
+ height.value = Math.floor(entry.contentRect.height)
25
+ })
26
+
27
+ if (el.value) {
28
+ observer.observe(el.value)
29
+ }
30
+ })
31
+
32
+ onBeforeUnmount(() => {
33
+ observer?.disconnect()
34
+ })
35
+
36
+ return {
37
+ el: el as ShallowRef<T | null>,
38
+ width,
39
+ height,
40
+ }
41
+ }
@@ -0,0 +1,45 @@
1
+ import type { ChartWidgetConfig } from '../widgets/chart/chart.types.js';
2
+ export type DashboardConfig = {
3
+ version: number;
4
+ groups: DashboardGroupConfig[];
5
+ widgets: DashboardWidgetConfig[];
6
+ };
7
+ export type DashboardGroupConfig = {
8
+ id: string;
9
+ label: string;
10
+ order: number;
11
+ };
12
+ export type DashboardGroupMoveDirection = 'up' | 'down';
13
+ export type DashboardWidgetMoveDirection = 'up' | 'down';
14
+ export type DashboardWidgetTarget = 'empty' | 'table' | 'chart' | 'kpi_card' | 'pivot_table' | 'gauge_card';
15
+ export type DashboardWidgetSize = 'small' | 'medium' | 'large' | 'wide' | 'full';
16
+ export type WidgetLayout = {
17
+ size?: DashboardWidgetSize;
18
+ width?: number;
19
+ minWidth?: number;
20
+ maxWidth?: number | null;
21
+ height?: number;
22
+ };
23
+ export type DashboardWidgetConfig = {
24
+ id: string;
25
+ group_id: string;
26
+ label?: string;
27
+ size?: DashboardWidgetSize;
28
+ width?: number;
29
+ height?: number;
30
+ minWidth?: number;
31
+ maxWidth?: number | null;
32
+ order: number;
33
+ target: DashboardWidgetTarget;
34
+ chart?: ChartWidgetConfig;
35
+ table?: unknown;
36
+ kpi_card?: unknown;
37
+ pivot_table?: unknown;
38
+ gauge_card?: unknown;
39
+ query?: unknown;
40
+ };
41
+ export type DashboardWidgetTableData = {
42
+ columns: string[];
43
+ rows: Record<string, unknown>[];
44
+ };
45
+ export declare function normalizeDashboardConfig(config: unknown): DashboardConfig;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalizeDashboardConfig = normalizeDashboardConfig;
4
+ function normalizeDashboardConfig(config) {
5
+ const value = isRecord(config) ? config : {};
6
+ return {
7
+ version: typeof value.version === 'number' ? value.version : 1,
8
+ groups: Array.isArray(value.groups) ? value.groups : [],
9
+ widgets: Array.isArray(value.widgets) ? value.widgets : [],
10
+ };
11
+ }
12
+ function isRecord(value) {
13
+ return typeof value === 'object' && value !== null;
14
+ }