@adminforth/dashboard 1.5.0 → 1.6.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/custom/api/dashboardApi.ts +137 -1
- package/custom/model/dashboard.types.ts +32 -22
- package/custom/runtime/DashboardRuntime.vue +2 -3
- package/custom/skills/adminforth-dashboard/SKILL.md +66 -10
- package/custom/widgets/KpiCardWidget.vue +172 -9
- package/custom/widgets/chart/ChartWidget.vue +5 -5
- package/custom/widgets/registry.ts +4 -4
- package/dist/custom/api/dashboardApi.d.ts +46 -1
- package/dist/custom/api/dashboardApi.js +90 -0
- package/dist/custom/api/dashboardApi.ts +137 -1
- package/dist/custom/model/dashboard.types.d.ts +30 -14
- package/dist/custom/model/dashboard.types.js +2 -2
- package/dist/custom/model/dashboard.types.ts +32 -22
- package/dist/custom/queries/useDashboardConfig.d.ts +106 -104
- package/dist/custom/queries/useWidgetData.d.ts +106 -104
- package/dist/custom/runtime/DashboardRuntime.vue +2 -3
- package/dist/custom/skills/adminforth-dashboard/SKILL.md +66 -10
- package/dist/custom/widgets/KpiCardWidget.vue +172 -9
- package/dist/custom/widgets/chart/ChartWidget.vue +5 -5
- package/dist/custom/widgets/registry.js +4 -4
- package/dist/custom/widgets/registry.ts +4 -4
- package/dist/endpoint/widgets.js +99 -14
- package/dist/schema/api.d.ts +11426 -1634
- package/dist/schema/api.js +118 -21
- package/dist/schema/widget.d.ts +425 -1980
- package/dist/schema/widget.js +13 -374
- package/dist/schema/widgets/charts.d.ts +1689 -0
- package/dist/schema/widgets/charts.js +92 -0
- package/dist/schema/widgets/common.d.ts +275 -0
- package/dist/schema/widgets/common.js +171 -0
- package/dist/schema/widgets/gauge-card.d.ts +172 -0
- package/dist/schema/widgets/gauge-card.js +28 -0
- package/dist/schema/widgets/kpi-card.d.ts +212 -0
- package/dist/schema/widgets/kpi-card.js +43 -0
- package/dist/schema/widgets/pivot-table.d.ts +196 -0
- package/dist/schema/widgets/pivot-table.js +17 -0
- package/dist/schema/widgets/table.d.ts +130 -0
- package/dist/schema/widgets/table.js +12 -0
- package/dist/services/widgetDataService.js +96 -2
- package/endpoint/widgets.ts +173 -26
- package/package.json +1 -1
- package/schema/api.ts +148 -22
- package/schema/widget.ts +43 -425
- package/schema/widgets/charts.ts +113 -0
- package/schema/widgets/common.ts +194 -0
- package/schema/widgets/gauge-card.ts +34 -0
- package/schema/widgets/kpi-card.ts +49 -0
- package/schema/widgets/pivot-table.ts +24 -0
- package/schema/widgets/table.ts +18 -0
- package/services/widgetDataService.ts +129 -3
|
@@ -16,14 +16,68 @@
|
|
|
16
16
|
|
|
17
17
|
<div
|
|
18
18
|
v-else
|
|
19
|
-
class="grid gap-
|
|
19
|
+
class="grid gap-3"
|
|
20
20
|
>
|
|
21
|
-
<div class="
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
<div class="grid gap-1">
|
|
22
|
+
<div class="text-3xl font-bold text-lightNavbarText dark:text-darkNavbarText">
|
|
23
|
+
{{ formattedValue }}
|
|
24
|
+
</div>
|
|
25
|
+
<div class="flex flex-wrap items-center gap-2 text-sm text-lightListTableText dark:text-darkListTableText">
|
|
26
|
+
<span>{{ label }}</span>
|
|
27
|
+
<span
|
|
28
|
+
v-if="comparison"
|
|
29
|
+
class="rounded px-1.5 py-0.5 text-xs font-medium"
|
|
30
|
+
:class="comparisonClass"
|
|
31
|
+
:title="comparison.tooltip"
|
|
32
|
+
>
|
|
33
|
+
{{ comparison.label }}
|
|
34
|
+
</span>
|
|
35
|
+
</div>
|
|
26
36
|
</div>
|
|
37
|
+
<svg
|
|
38
|
+
v-if="sparklinePoints"
|
|
39
|
+
class="h-12 w-full overflow-visible"
|
|
40
|
+
viewBox="0 0 100 32"
|
|
41
|
+
preserveAspectRatio="none"
|
|
42
|
+
aria-hidden="true"
|
|
43
|
+
>
|
|
44
|
+
<defs v-if="usesSparklineGradient">
|
|
45
|
+
<linearGradient
|
|
46
|
+
:id="sparklineGradientId"
|
|
47
|
+
x1="0"
|
|
48
|
+
y1="0"
|
|
49
|
+
x2="0"
|
|
50
|
+
y2="1"
|
|
51
|
+
>
|
|
52
|
+
<stop
|
|
53
|
+
offset="0%"
|
|
54
|
+
stop-color="currentColor"
|
|
55
|
+
stop-opacity="0.24"
|
|
56
|
+
/>
|
|
57
|
+
<stop
|
|
58
|
+
offset="100%"
|
|
59
|
+
stop-color="currentColor"
|
|
60
|
+
stop-opacity="0"
|
|
61
|
+
/>
|
|
62
|
+
</linearGradient>
|
|
63
|
+
</defs>
|
|
64
|
+
<polygon
|
|
65
|
+
v-if="usesSparklineGradient"
|
|
66
|
+
class="text-lightPrimary dark:text-darkPrimary"
|
|
67
|
+
:points="sparklineFillPoints"
|
|
68
|
+
:fill="`url(#${sparklineGradientId})`"
|
|
69
|
+
/>
|
|
70
|
+
<polyline
|
|
71
|
+
class="text-lightPrimary dark:text-darkPrimary"
|
|
72
|
+
:points="sparklinePoints"
|
|
73
|
+
fill="none"
|
|
74
|
+
stroke="currentColor"
|
|
75
|
+
stroke-width="2"
|
|
76
|
+
stroke-linecap="round"
|
|
77
|
+
stroke-linejoin="round"
|
|
78
|
+
vector-effect="non-scaling-stroke"
|
|
79
|
+
/>
|
|
80
|
+
</svg>
|
|
27
81
|
</div>
|
|
28
82
|
</div>
|
|
29
83
|
</template>
|
|
@@ -61,11 +115,120 @@ watch(
|
|
|
61
115
|
const kpiConfig = computed(() => props.widget.target === 'kpi_card' ? props.widget.card : undefined)
|
|
62
116
|
const widgetData = computed(() => data.value?.data as DashboardWidgetTableData | null)
|
|
63
117
|
const columns = computed(() => widgetData.value?.columns ?? [])
|
|
64
|
-
const firstRow = computed(() => widgetData.value?.rows[0] ?? {})
|
|
118
|
+
const firstRow = computed(() => widgetData.value?.values ?? widgetData.value?.rows[0] ?? {})
|
|
65
119
|
const valueField = computed(() => kpiConfig.value?.value.field || columns.value[0])
|
|
66
120
|
const value = computed(() => toFiniteNumber(firstRow.value[valueField.value]))
|
|
67
121
|
const label = computed(() => kpiConfig.value?.subtitle?.field
|
|
68
|
-
?
|
|
122
|
+
? [kpiConfig.value.subtitle.text, formatValue(firstRow.value[kpiConfig.value.subtitle.field], kpiConfig.value.value.format)]
|
|
123
|
+
.filter(Boolean)
|
|
124
|
+
.join(': ')
|
|
69
125
|
: kpiConfig.value?.subtitle?.text ?? kpiConfig.value?.title ?? props.widget.label)
|
|
70
|
-
const formattedValue = computed(() => `${kpiConfig.value?.value.prefix ?? ''}${
|
|
126
|
+
const formattedValue = computed(() => `${kpiConfig.value?.value.prefix ?? ''}${formatValue(value.value, kpiConfig.value?.value.format)}${kpiConfig.value?.value.suffix ?? ''}`)
|
|
127
|
+
const comparisonValue = computed(() => toFiniteNumber(kpiConfig.value?.comparison?.field
|
|
128
|
+
? firstRow.value[kpiConfig.value.comparison.field]
|
|
129
|
+
: undefined))
|
|
130
|
+
const comparison = computed(() => {
|
|
131
|
+
const config = kpiConfig.value?.comparison
|
|
132
|
+
|
|
133
|
+
if (!config) {
|
|
134
|
+
return null
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const template = config.compact?.template ?? '{sign}{value}'
|
|
138
|
+
const tooltipTemplate = config.tooltip?.template
|
|
139
|
+
const valueText = formatValue(Math.abs(comparisonValue.value), config.format, { signed: false, compactTemplate: true })
|
|
140
|
+
const sign = comparisonValue.value > 0 ? '+' : comparisonValue.value < 0 ? '-' : ''
|
|
141
|
+
|
|
142
|
+
return {
|
|
143
|
+
value: comparisonValue.value,
|
|
144
|
+
label: config.compact?.show === false ? valueText : applyTemplate(template, sign, valueText),
|
|
145
|
+
tooltip: tooltipTemplate
|
|
146
|
+
? applyTemplate(tooltipTemplate, sign, valueText)
|
|
147
|
+
: config.tooltip?.label,
|
|
148
|
+
positiveIsGood: config.positive_is_good ?? true,
|
|
149
|
+
}
|
|
150
|
+
})
|
|
151
|
+
const comparisonClass = computed(() => {
|
|
152
|
+
if (!comparison.value || comparison.value.value === 0) {
|
|
153
|
+
return 'bg-lightListBorder text-lightListTableText dark:bg-darkListBorder dark:text-darkListTableText'
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const isGood = comparison.value.positiveIsGood
|
|
157
|
+
? comparison.value.value > 0
|
|
158
|
+
: comparison.value.value < 0
|
|
159
|
+
|
|
160
|
+
return isGood
|
|
161
|
+
? 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-300'
|
|
162
|
+
: 'bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-300'
|
|
163
|
+
})
|
|
164
|
+
const sparklineRows = computed(() => widgetData.value?.rows ?? [])
|
|
165
|
+
const sparklineConfig = computed(() => kpiConfig.value?.sparkline)
|
|
166
|
+
const sparklineGradientId = computed(() => `kpi-sparkline-${props.widget.id}`)
|
|
167
|
+
const usesSparklineGradient = computed(() => sparklineConfig.value?.fill?.type === 'gradient')
|
|
168
|
+
const sparklineCoordinates = computed(() => {
|
|
169
|
+
const field = sparklineConfig.value?.field
|
|
170
|
+
|
|
171
|
+
if (!field || sparklineRows.value.length < 2) {
|
|
172
|
+
return []
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const values = sparklineRows.value.map((row) => toFiniteNumber(row[field]))
|
|
176
|
+
const min = Math.min(...values)
|
|
177
|
+
const max = Math.max(...values)
|
|
178
|
+
const range = max - min || 1
|
|
179
|
+
|
|
180
|
+
return values.map((item, index) => ({
|
|
181
|
+
x: (index / Math.max(values.length - 1, 1)) * 100,
|
|
182
|
+
y: 30 - ((item - min) / range) * 28,
|
|
183
|
+
}))
|
|
184
|
+
})
|
|
185
|
+
const sparklinePoints = computed(() => sparklineCoordinates.value.length
|
|
186
|
+
? sparklineCoordinates.value.map((point) => `${point.x},${point.y}`).join(' ')
|
|
187
|
+
: '')
|
|
188
|
+
const sparklineFillPoints = computed(() => sparklineCoordinates.value.length
|
|
189
|
+
? `0,32 ${sparklinePoints.value} 100,32`
|
|
190
|
+
: '')
|
|
191
|
+
|
|
192
|
+
function applyTemplate(template: string, sign: string, value: string) {
|
|
193
|
+
return template
|
|
194
|
+
.replaceAll('{sign}', sign)
|
|
195
|
+
.replaceAll('{value}', value)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function formatValue(
|
|
199
|
+
rawValue: unknown,
|
|
200
|
+
format = 'number',
|
|
201
|
+
options: { signed?: boolean, compactTemplate?: boolean } = {},
|
|
202
|
+
) {
|
|
203
|
+
const numericValue = toFiniteNumber(rawValue)
|
|
204
|
+
const sign = options.signed && numericValue > 0 ? '+' : ''
|
|
205
|
+
const absoluteValue = options.signed ? Math.abs(numericValue) : numericValue
|
|
206
|
+
|
|
207
|
+
if (format === 'integer') {
|
|
208
|
+
return `${sign}${formatChartValue(absoluteValue, { maximumFractionDigits: 0 })}`
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (format === 'compact_number') {
|
|
212
|
+
return `${sign}${formatChartValue(absoluteValue, { notation: 'compact', maximumFractionDigits: 1 })}`
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (format === 'currency' || format === 'currency_delta') {
|
|
216
|
+
return `${sign}${formatChartValue(absoluteValue, {
|
|
217
|
+
style: 'currency',
|
|
218
|
+
currency: 'USD',
|
|
219
|
+
maximumFractionDigits: 2,
|
|
220
|
+
})}`
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (format === 'percent' || format === 'percent_delta') {
|
|
224
|
+
const value = formatChartValue(absoluteValue, { maximumFractionDigits: 1 })
|
|
225
|
+
return options.compactTemplate ? value : `${sign}${value}%`
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (format === 'number_delta') {
|
|
229
|
+
return `${sign}${formatChartValue(absoluteValue, { maximumFractionDigits: 2 })}`
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return `${sign}${formatChartValue(absoluteValue, { maximumFractionDigits: 2 })}`
|
|
233
|
+
}
|
|
71
234
|
</script>
|
|
@@ -83,11 +83,11 @@
|
|
|
83
83
|
import { computed, watch } from 'vue'
|
|
84
84
|
import { useWidgetData } from '../../queries/useWidgetData.js'
|
|
85
85
|
import type { ChartDashboardWidgetConfig, DashboardWidgetTableData } from '../../model/dashboard.types.js'
|
|
86
|
-
import BarChart from './
|
|
87
|
-
import FunnelChart from './
|
|
88
|
-
import LineChart from './
|
|
89
|
-
import PieChart from './
|
|
90
|
-
import StackedBarChart from './
|
|
86
|
+
import BarChart from './BarChart.vue'
|
|
87
|
+
import FunnelChart from './FunnelChart.vue'
|
|
88
|
+
import LineChart from './LineChart.vue'
|
|
89
|
+
import PieChart from './PieChart.vue'
|
|
90
|
+
import StackedBarChart from './StackedBarChart.vue'
|
|
91
91
|
import { toFiniteNumber } from './chart.utils.js'
|
|
92
92
|
|
|
93
93
|
const DEFAULT_WIDGET_HEIGHT = 500
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import ChartWidget from './chart/ChartWidget.vue';
|
|
2
|
-
import GaugeCardWidget from './
|
|
3
|
-
import KpiCardWidget from './
|
|
4
|
-
import PivotTableWidget from './
|
|
5
|
-
import TableWidget from './
|
|
2
|
+
import GaugeCardWidget from './GaugeCardWidget.vue';
|
|
3
|
+
import KpiCardWidget from './KpiCardWidget.vue';
|
|
4
|
+
import PivotTableWidget from './PivotTableWidget.vue';
|
|
5
|
+
import TableWidget from './TableWidget.vue';
|
|
6
6
|
export const widgetRegistry = [
|
|
7
7
|
{
|
|
8
8
|
type: 'table',
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { Component } from 'vue'
|
|
2
2
|
import type { DashboardWidgetTarget } from '../model/dashboard.types.js'
|
|
3
3
|
import ChartWidget from './chart/ChartWidget.vue'
|
|
4
|
-
import GaugeCardWidget from './
|
|
5
|
-
import KpiCardWidget from './
|
|
6
|
-
import PivotTableWidget from './
|
|
7
|
-
import TableWidget from './
|
|
4
|
+
import GaugeCardWidget from './GaugeCardWidget.vue'
|
|
5
|
+
import KpiCardWidget from './KpiCardWidget.vue'
|
|
6
|
+
import PivotTableWidget from './PivotTableWidget.vue'
|
|
7
|
+
import TableWidget from './TableWidget.vue'
|
|
8
8
|
|
|
9
9
|
export type DashboardWidgetType = DashboardWidgetTarget
|
|
10
10
|
|
package/dist/endpoint/widgets.js
CHANGED
|
@@ -8,7 +8,53 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
10
|
import { randomUUID } from 'crypto';
|
|
11
|
-
import { DashboardApiResponseSchema, DashboardWidgetDataResponseSchema, GroupIdRequestSchema, MoveWidgetRequestSchema, SetWidgetConfigRequestSchema, WidgetDataRequestSchema, WidgetIdRequestSchema, } from '../schema/api.js';
|
|
11
|
+
import { ConfigureBarChartWidgetRequestSchema, ConfigureFunnelChartWidgetRequestSchema, ConfigureGaugeCardWidgetRequestSchema, ConfigureHistogramChartWidgetRequestSchema, ConfigureKpiCardWidgetRequestSchema, ConfigureLineChartWidgetRequestSchema, ConfigurePieChartWidgetRequestSchema, ConfigurePivotTableWidgetRequestSchema, ConfigureStackedBarChartWidgetRequestSchema, ConfigureTableWidgetRequestSchema, DashboardApiResponseSchema, DashboardWidgetDataResponseSchema, GroupIdRequestSchema, MoveWidgetRequestSchema, SetWidgetConfigRequestSchema, WidgetDataRequestSchema, WidgetIdRequestSchema, } from '../schema/api.js';
|
|
12
|
+
function replaceWidgetConfig(ctx, slug, widgetId, widgetConfig) {
|
|
13
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
14
|
+
let mutationError = null;
|
|
15
|
+
const updatedDashboard = yield ctx.updateDashboardConfig(slug, (config) => {
|
|
16
|
+
const widget = config.widgets.find((item) => item.id === widgetId);
|
|
17
|
+
if (!widget) {
|
|
18
|
+
mutationError = 'Dashboard widget not found';
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
const nextWidget = Object.assign(Object.assign({}, widgetConfig), { id: widget.id, group_id: widget.group_id, order: widget.order });
|
|
22
|
+
return Object.assign(Object.assign({}, config), { widgets: config.widgets.map((item) => item.id === widgetId
|
|
23
|
+
? nextWidget
|
|
24
|
+
: item) });
|
|
25
|
+
});
|
|
26
|
+
return {
|
|
27
|
+
updatedDashboard,
|
|
28
|
+
mutationError,
|
|
29
|
+
};
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
function registerConfigureWidgetEndpoint(server, ctx, options) {
|
|
33
|
+
server.endpoint({
|
|
34
|
+
method: 'POST',
|
|
35
|
+
path: options.path,
|
|
36
|
+
description: options.description,
|
|
37
|
+
request_schema: options.requestSchema,
|
|
38
|
+
response_schema: DashboardApiResponseSchema,
|
|
39
|
+
handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, response }) {
|
|
40
|
+
if (!ctx.canEditDashboard(adminUser)) {
|
|
41
|
+
response.setStatus(403);
|
|
42
|
+
return { error: 'Dashboard edit is not allowed' };
|
|
43
|
+
}
|
|
44
|
+
const request = body;
|
|
45
|
+
const { updatedDashboard, mutationError } = yield replaceWidgetConfig(ctx, request.slug, request.widgetId, request.config);
|
|
46
|
+
if (!updatedDashboard) {
|
|
47
|
+
response.setStatus(404);
|
|
48
|
+
return { error: 'Dashboard not found' };
|
|
49
|
+
}
|
|
50
|
+
if (mutationError) {
|
|
51
|
+
response.setStatus(404);
|
|
52
|
+
return { error: mutationError };
|
|
53
|
+
}
|
|
54
|
+
return updatedDashboard;
|
|
55
|
+
}),
|
|
56
|
+
});
|
|
57
|
+
}
|
|
12
58
|
export function registerWidgetEndpoints(server, ctx) {
|
|
13
59
|
server.endpoint({
|
|
14
60
|
method: 'POST',
|
|
@@ -138,19 +184,8 @@ export function registerWidgetEndpoints(server, ctx) {
|
|
|
138
184
|
response.setStatus(403);
|
|
139
185
|
return { error: 'Dashboard edit is not allowed' };
|
|
140
186
|
}
|
|
141
|
-
|
|
142
|
-
const updatedDashboard = yield ctx.
|
|
143
|
-
const widget = config.widgets.find((item) => item.id === body.widgetId);
|
|
144
|
-
if (!widget) {
|
|
145
|
-
mutationError = 'Dashboard widget not found';
|
|
146
|
-
return null;
|
|
147
|
-
}
|
|
148
|
-
const typedWidgetConfig = body.config;
|
|
149
|
-
const nextWidget = Object.assign(Object.assign({}, typedWidgetConfig), { id: widget.id, group_id: widget.group_id, order: widget.order });
|
|
150
|
-
return Object.assign(Object.assign({}, config), { widgets: config.widgets.map((item) => item.id === body.widgetId
|
|
151
|
-
? nextWidget
|
|
152
|
-
: item) });
|
|
153
|
-
});
|
|
187
|
+
const request = body;
|
|
188
|
+
const { updatedDashboard, mutationError } = yield replaceWidgetConfig(ctx, request.slug, request.widgetId, request.config);
|
|
154
189
|
if (!updatedDashboard) {
|
|
155
190
|
response.setStatus(404);
|
|
156
191
|
return { error: 'Dashboard not found' };
|
|
@@ -162,6 +197,56 @@ export function registerWidgetEndpoints(server, ctx) {
|
|
|
162
197
|
return updatedDashboard;
|
|
163
198
|
}),
|
|
164
199
|
});
|
|
200
|
+
registerConfigureWidgetEndpoint(server, ctx, {
|
|
201
|
+
path: '/dashboard/configure_table_widget',
|
|
202
|
+
description: 'Configures an existing dashboard widget as a table. Superadmin only.',
|
|
203
|
+
requestSchema: ConfigureTableWidgetRequestSchema,
|
|
204
|
+
});
|
|
205
|
+
registerConfigureWidgetEndpoint(server, ctx, {
|
|
206
|
+
path: '/dashboard/configure_kpi_card_widget',
|
|
207
|
+
description: 'Configures an existing dashboard widget as a KPI card. Superadmin only.',
|
|
208
|
+
requestSchema: ConfigureKpiCardWidgetRequestSchema,
|
|
209
|
+
});
|
|
210
|
+
registerConfigureWidgetEndpoint(server, ctx, {
|
|
211
|
+
path: '/dashboard/configure_gauge_card_widget',
|
|
212
|
+
description: 'Configures an existing dashboard widget as a gauge card. Superadmin only.',
|
|
213
|
+
requestSchema: ConfigureGaugeCardWidgetRequestSchema,
|
|
214
|
+
});
|
|
215
|
+
registerConfigureWidgetEndpoint(server, ctx, {
|
|
216
|
+
path: '/dashboard/configure_pivot_table_widget',
|
|
217
|
+
description: 'Configures an existing dashboard widget as a pivot table. Superadmin only.',
|
|
218
|
+
requestSchema: ConfigurePivotTableWidgetRequestSchema,
|
|
219
|
+
});
|
|
220
|
+
registerConfigureWidgetEndpoint(server, ctx, {
|
|
221
|
+
path: '/dashboard/configure_line_chart_widget',
|
|
222
|
+
description: 'Configures an existing dashboard widget as a line chart. Superadmin only.',
|
|
223
|
+
requestSchema: ConfigureLineChartWidgetRequestSchema,
|
|
224
|
+
});
|
|
225
|
+
registerConfigureWidgetEndpoint(server, ctx, {
|
|
226
|
+
path: '/dashboard/configure_bar_chart_widget',
|
|
227
|
+
description: 'Configures an existing dashboard widget as a bar chart. Superadmin only.',
|
|
228
|
+
requestSchema: ConfigureBarChartWidgetRequestSchema,
|
|
229
|
+
});
|
|
230
|
+
registerConfigureWidgetEndpoint(server, ctx, {
|
|
231
|
+
path: '/dashboard/configure_stacked_bar_chart_widget',
|
|
232
|
+
description: 'Configures an existing dashboard widget as a stacked bar chart. Superadmin only.',
|
|
233
|
+
requestSchema: ConfigureStackedBarChartWidgetRequestSchema,
|
|
234
|
+
});
|
|
235
|
+
registerConfigureWidgetEndpoint(server, ctx, {
|
|
236
|
+
path: '/dashboard/configure_pie_chart_widget',
|
|
237
|
+
description: 'Configures an existing dashboard widget as a pie chart. Superadmin only.',
|
|
238
|
+
requestSchema: ConfigurePieChartWidgetRequestSchema,
|
|
239
|
+
});
|
|
240
|
+
registerConfigureWidgetEndpoint(server, ctx, {
|
|
241
|
+
path: '/dashboard/configure_histogram_chart_widget',
|
|
242
|
+
description: 'Configures an existing dashboard widget as a histogram chart. Superadmin only.',
|
|
243
|
+
requestSchema: ConfigureHistogramChartWidgetRequestSchema,
|
|
244
|
+
});
|
|
245
|
+
registerConfigureWidgetEndpoint(server, ctx, {
|
|
246
|
+
path: '/dashboard/configure_funnel_chart_widget',
|
|
247
|
+
description: 'Configures an existing dashboard widget as a funnel chart. Superadmin only.',
|
|
248
|
+
requestSchema: ConfigureFunnelChartWidgetRequestSchema,
|
|
249
|
+
});
|
|
165
250
|
server.endpoint({
|
|
166
251
|
method: 'POST',
|
|
167
252
|
path: '/dashboard/get_dashboard_widget_data',
|