@praxisui/charts 1.0.0-beta.68
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 +126 -0
- package/fesm2022/praxisui-charts.mjs +2662 -0
- package/fesm2022/praxisui-charts.mjs.map +1 -0
- package/index.d.ts +639 -0
- package/package.json +46 -0
|
@@ -0,0 +1,2662 @@
|
|
|
1
|
+
import { CommonModule } from '@angular/common';
|
|
2
|
+
import * as i0 from '@angular/core';
|
|
3
|
+
import { Injectable, Inject, InjectionToken, input, output, viewChild, inject, DestroyRef, signal, computed, effect, ChangeDetectionStrategy, Component, ENVIRONMENT_INITIALIZER } from '@angular/core';
|
|
4
|
+
import * as i1$1 from '@praxisui/core';
|
|
5
|
+
import { buildApiUrl, API_URL, PraxisI18nService, ComponentMetadataRegistry, createDefaultTableConfig, DynamicWidgetPageComponent, DynamicGridPageComponent } from '@praxisui/core';
|
|
6
|
+
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
7
|
+
import { use, init } from 'echarts/core';
|
|
8
|
+
import { BarChart, LineChart, PieChart } from 'echarts/charts';
|
|
9
|
+
import { AriaComponent, DatasetComponent, GridComponent, LegendComponent, TitleComponent, TooltipComponent, TransformComponent } from 'echarts/components';
|
|
10
|
+
import { CanvasRenderer } from 'echarts/renderers';
|
|
11
|
+
import * as i1 from '@angular/common/http';
|
|
12
|
+
import { HttpErrorResponse } from '@angular/common/http';
|
|
13
|
+
import { throwError, map } from 'rxjs';
|
|
14
|
+
import { catchError } from 'rxjs/operators';
|
|
15
|
+
import { PraxisTable } from '@praxisui/table';
|
|
16
|
+
|
|
17
|
+
class PraxisChartDataTransformerService {
|
|
18
|
+
transform(config, rows) {
|
|
19
|
+
if (config.type === 'pie' || config.type === 'donut') {
|
|
20
|
+
return this.transformPie(config, rows);
|
|
21
|
+
}
|
|
22
|
+
return this.transformCartesian(config, rows);
|
|
23
|
+
}
|
|
24
|
+
transformCartesian(config, rows) {
|
|
25
|
+
const categoryField = config.axes?.x?.field;
|
|
26
|
+
if (!categoryField || !rows.length || !config.series.length) {
|
|
27
|
+
return {
|
|
28
|
+
mode: 'cartesian',
|
|
29
|
+
categories: [],
|
|
30
|
+
series: [],
|
|
31
|
+
slices: [],
|
|
32
|
+
hasData: false,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
const categories = Array.from(new Set(rows.map((row) => this.normalizeCategory(row[categoryField]))));
|
|
36
|
+
const series = config.series.map((seriesConfig) => this.buildCartesianSeries(config, seriesConfig, rows, categories));
|
|
37
|
+
return {
|
|
38
|
+
mode: 'cartesian',
|
|
39
|
+
categories,
|
|
40
|
+
series,
|
|
41
|
+
slices: [],
|
|
42
|
+
hasData: categories.length > 0 && series.some((item) => item.points.some((point) => point !== null)),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
transformPie(config, rows) {
|
|
46
|
+
const seriesConfig = config.series[0];
|
|
47
|
+
const categoryField = seriesConfig?.categoryField ?? config.axes?.x?.field;
|
|
48
|
+
if (!seriesConfig || !categoryField || !rows.length) {
|
|
49
|
+
return {
|
|
50
|
+
mode: 'pie',
|
|
51
|
+
categories: [],
|
|
52
|
+
series: [],
|
|
53
|
+
slices: [],
|
|
54
|
+
hasData: false,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
const bucket = new Map();
|
|
58
|
+
for (const row of rows) {
|
|
59
|
+
const key = this.normalizeCategory(row[categoryField]);
|
|
60
|
+
const nextValue = this.extractMetricValue(row, seriesConfig);
|
|
61
|
+
bucket.set(key, (bucket.get(key) ?? 0) + nextValue);
|
|
62
|
+
}
|
|
63
|
+
const slices = Array.from(bucket.entries()).map(([name, value]) => ({
|
|
64
|
+
id: `${seriesConfig.id}:${name}`,
|
|
65
|
+
name,
|
|
66
|
+
value,
|
|
67
|
+
color: seriesConfig.color,
|
|
68
|
+
}));
|
|
69
|
+
return {
|
|
70
|
+
mode: 'pie',
|
|
71
|
+
categories: slices.map((slice) => slice.name),
|
|
72
|
+
series: [],
|
|
73
|
+
slices,
|
|
74
|
+
hasData: slices.some((slice) => slice.value > 0),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
buildCartesianSeries(config, seriesConfig, rows, categories) {
|
|
78
|
+
const categoryField = config.axes?.x?.field;
|
|
79
|
+
const byCategory = new Map();
|
|
80
|
+
for (const row of rows) {
|
|
81
|
+
const key = this.normalizeCategory(row[categoryField]);
|
|
82
|
+
const bucket = byCategory.get(key) ?? [];
|
|
83
|
+
bucket.push(row);
|
|
84
|
+
byCategory.set(key, bucket);
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
id: seriesConfig.id,
|
|
88
|
+
name: seriesConfig.name ?? seriesConfig.metric?.label ?? seriesConfig.id,
|
|
89
|
+
type: config.type === 'bar' || config.type === 'stacked-bar' ? 'bar' : 'line',
|
|
90
|
+
stack: config.type === 'stacked-bar' ? seriesConfig.stackId ?? 'stack-1' : undefined,
|
|
91
|
+
smooth: seriesConfig.smooth ?? config.type === 'area',
|
|
92
|
+
area: config.type === 'area',
|
|
93
|
+
color: seriesConfig.color,
|
|
94
|
+
labelsVisible: seriesConfig.labels?.visible ?? false,
|
|
95
|
+
points: categories.map((category) => {
|
|
96
|
+
const bucket = byCategory.get(category) ?? [];
|
|
97
|
+
if (!bucket.length)
|
|
98
|
+
return null;
|
|
99
|
+
return this.aggregate(bucket, seriesConfig);
|
|
100
|
+
}),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
aggregate(rows, seriesConfig) {
|
|
104
|
+
const aggregation = seriesConfig.metric?.aggregation ?? 'sum';
|
|
105
|
+
if (aggregation === 'count') {
|
|
106
|
+
return rows.length;
|
|
107
|
+
}
|
|
108
|
+
const values = rows
|
|
109
|
+
.map((row) => this.extractMetricValue(row, seriesConfig))
|
|
110
|
+
.filter((value) => Number.isFinite(value));
|
|
111
|
+
if (!values.length)
|
|
112
|
+
return 0;
|
|
113
|
+
switch (aggregation) {
|
|
114
|
+
case 'avg':
|
|
115
|
+
return values.reduce((sum, value) => sum + value, 0) / values.length;
|
|
116
|
+
case 'min':
|
|
117
|
+
return Math.min(...values);
|
|
118
|
+
case 'max':
|
|
119
|
+
return Math.max(...values);
|
|
120
|
+
case 'sum':
|
|
121
|
+
default:
|
|
122
|
+
return values.reduce((sum, value) => sum + value, 0);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
extractMetricValue(row, seriesConfig) {
|
|
126
|
+
const field = seriesConfig.metric?.field;
|
|
127
|
+
if (!field) {
|
|
128
|
+
return 1;
|
|
129
|
+
}
|
|
130
|
+
const value = row[field];
|
|
131
|
+
if (typeof value === 'number')
|
|
132
|
+
return value;
|
|
133
|
+
if (typeof value === 'string') {
|
|
134
|
+
const parsed = Number(value);
|
|
135
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
136
|
+
}
|
|
137
|
+
return 0;
|
|
138
|
+
}
|
|
139
|
+
normalizeCategory(value) {
|
|
140
|
+
if (value instanceof Date) {
|
|
141
|
+
return value.toISOString();
|
|
142
|
+
}
|
|
143
|
+
if (value === null || value === undefined || value === '') {
|
|
144
|
+
return 'undefined';
|
|
145
|
+
}
|
|
146
|
+
return String(value);
|
|
147
|
+
}
|
|
148
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisChartDataTransformerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
149
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisChartDataTransformerService, providedIn: 'root' });
|
|
150
|
+
}
|
|
151
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisChartDataTransformerService, decorators: [{
|
|
152
|
+
type: Injectable,
|
|
153
|
+
args: [{ providedIn: 'root' }]
|
|
154
|
+
}] });
|
|
155
|
+
|
|
156
|
+
class PraxisChartOptionBuilderService {
|
|
157
|
+
transformer;
|
|
158
|
+
constructor(transformer) {
|
|
159
|
+
this.transformer = transformer;
|
|
160
|
+
}
|
|
161
|
+
build(config, rows) {
|
|
162
|
+
const transformed = this.transformer.transform(config, rows);
|
|
163
|
+
const palette = config.theme?.palette;
|
|
164
|
+
const tooltipEnabled = config.theme?.tooltip?.enabled ?? true;
|
|
165
|
+
const legendVisible = config.theme?.legend?.visible ?? true;
|
|
166
|
+
if (transformed.mode === 'pie') {
|
|
167
|
+
const pieSeries = {
|
|
168
|
+
type: 'pie',
|
|
169
|
+
radius: config.type === 'donut' ? ['45%', '70%'] : '70%',
|
|
170
|
+
label: {
|
|
171
|
+
show: config.series[0]?.labels?.visible ?? false,
|
|
172
|
+
},
|
|
173
|
+
data: transformed.slices.map((slice) => ({
|
|
174
|
+
name: slice.name,
|
|
175
|
+
value: slice.value,
|
|
176
|
+
itemStyle: slice.color ? { color: slice.color } : undefined,
|
|
177
|
+
})),
|
|
178
|
+
};
|
|
179
|
+
return {
|
|
180
|
+
backgroundColor: config.theme?.backgroundColor,
|
|
181
|
+
color: palette,
|
|
182
|
+
title: {
|
|
183
|
+
text: this.resolveText(config.title),
|
|
184
|
+
subtext: this.resolveText(config.subtitle),
|
|
185
|
+
left: 'center',
|
|
186
|
+
},
|
|
187
|
+
tooltip: tooltipEnabled ? { trigger: 'item' } : undefined,
|
|
188
|
+
legend: legendVisible
|
|
189
|
+
? { show: true, orient: 'horizontal', bottom: 0 }
|
|
190
|
+
: { show: false },
|
|
191
|
+
series: [pieSeries],
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
return {
|
|
195
|
+
backgroundColor: config.theme?.backgroundColor,
|
|
196
|
+
color: palette,
|
|
197
|
+
title: {
|
|
198
|
+
text: this.resolveText(config.title),
|
|
199
|
+
subtext: this.resolveText(config.subtitle),
|
|
200
|
+
left: 'left',
|
|
201
|
+
},
|
|
202
|
+
tooltip: tooltipEnabled
|
|
203
|
+
? { trigger: config.theme?.tooltip?.trigger ?? 'axis' }
|
|
204
|
+
: undefined,
|
|
205
|
+
legend: legendVisible
|
|
206
|
+
? { show: true, top: 0 }
|
|
207
|
+
: { show: false },
|
|
208
|
+
grid: {
|
|
209
|
+
top: 56,
|
|
210
|
+
right: 24,
|
|
211
|
+
bottom: 32,
|
|
212
|
+
left: 48,
|
|
213
|
+
containLabel: true,
|
|
214
|
+
},
|
|
215
|
+
xAxis: {
|
|
216
|
+
type: config.axes?.x?.type ?? 'category',
|
|
217
|
+
name: config.axes?.x?.label,
|
|
218
|
+
data: transformed.categories,
|
|
219
|
+
axisLabel: {
|
|
220
|
+
show: config.axes?.x?.labels?.visible ?? true,
|
|
221
|
+
rotate: config.axes?.x?.labels?.rotate ?? 0,
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
yAxis: {
|
|
225
|
+
type: config.axes?.y?.type ?? 'value',
|
|
226
|
+
name: config.axes?.y?.label,
|
|
227
|
+
min: config.axes?.y?.min,
|
|
228
|
+
max: config.axes?.y?.max,
|
|
229
|
+
axisLabel: {
|
|
230
|
+
show: config.axes?.y?.labels?.visible ?? true,
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
series: transformed.series.map((series) => ({
|
|
234
|
+
id: series.id,
|
|
235
|
+
name: series.name,
|
|
236
|
+
type: series.type,
|
|
237
|
+
stack: series.stack,
|
|
238
|
+
smooth: series.smooth,
|
|
239
|
+
areaStyle: series.area ? {} : undefined,
|
|
240
|
+
label: { show: series.labelsVisible },
|
|
241
|
+
itemStyle: series.color ? { color: series.color } : undefined,
|
|
242
|
+
data: series.points,
|
|
243
|
+
})),
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
resolveText(value) {
|
|
247
|
+
if (!value)
|
|
248
|
+
return undefined;
|
|
249
|
+
if (typeof value === 'string')
|
|
250
|
+
return value;
|
|
251
|
+
if (typeof value === 'object' && value && 'text' in value) {
|
|
252
|
+
const text = value.text;
|
|
253
|
+
return text || undefined;
|
|
254
|
+
}
|
|
255
|
+
return undefined;
|
|
256
|
+
}
|
|
257
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisChartOptionBuilderService, deps: [{ token: PraxisChartDataTransformerService }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
258
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisChartOptionBuilderService, providedIn: 'root' });
|
|
259
|
+
}
|
|
260
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisChartOptionBuilderService, decorators: [{
|
|
261
|
+
type: Injectable,
|
|
262
|
+
args: [{ providedIn: 'root' }]
|
|
263
|
+
}], ctorParameters: () => [{ type: PraxisChartDataTransformerService }] });
|
|
264
|
+
|
|
265
|
+
use([
|
|
266
|
+
AriaComponent,
|
|
267
|
+
BarChart,
|
|
268
|
+
CanvasRenderer,
|
|
269
|
+
DatasetComponent,
|
|
270
|
+
GridComponent,
|
|
271
|
+
LegendComponent,
|
|
272
|
+
LineChart,
|
|
273
|
+
PieChart,
|
|
274
|
+
TitleComponent,
|
|
275
|
+
TooltipComponent,
|
|
276
|
+
TransformComponent,
|
|
277
|
+
]);
|
|
278
|
+
class EChartsEngineAdapter {
|
|
279
|
+
optionBuilder;
|
|
280
|
+
chart;
|
|
281
|
+
constructor(optionBuilder) {
|
|
282
|
+
this.optionBuilder = optionBuilder;
|
|
283
|
+
}
|
|
284
|
+
render(host, payload) {
|
|
285
|
+
if (!this.chart) {
|
|
286
|
+
this.chart = init(host);
|
|
287
|
+
}
|
|
288
|
+
const option = this.optionBuilder.build(payload.config, payload.data);
|
|
289
|
+
this.chart?.setOption(option, true);
|
|
290
|
+
this.chart?.off('click');
|
|
291
|
+
this.chart?.on('click', (params) => {
|
|
292
|
+
payload.onPointClick?.({
|
|
293
|
+
chartId: payload.config.id,
|
|
294
|
+
seriesId: params?.seriesId,
|
|
295
|
+
seriesName: params?.seriesName,
|
|
296
|
+
category: params?.name,
|
|
297
|
+
value: params?.value,
|
|
298
|
+
data: params?.data,
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
resize() {
|
|
303
|
+
this.chart?.resize();
|
|
304
|
+
}
|
|
305
|
+
destroy() {
|
|
306
|
+
this.chart?.dispose();
|
|
307
|
+
this.chart = undefined;
|
|
308
|
+
}
|
|
309
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: EChartsEngineAdapter, deps: [{ token: PraxisChartOptionBuilderService }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
310
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: EChartsEngineAdapter });
|
|
311
|
+
}
|
|
312
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: EChartsEngineAdapter, decorators: [{
|
|
313
|
+
type: Injectable
|
|
314
|
+
}], ctorParameters: () => [{ type: PraxisChartOptionBuilderService }] });
|
|
315
|
+
|
|
316
|
+
class PraxisChartStatsApiService {
|
|
317
|
+
http;
|
|
318
|
+
apiUrl;
|
|
319
|
+
constructor(http, apiUrl) {
|
|
320
|
+
this.http = http;
|
|
321
|
+
this.apiUrl = apiUrl;
|
|
322
|
+
}
|
|
323
|
+
execute(event, config) {
|
|
324
|
+
const query = event.query;
|
|
325
|
+
const statsRequest = query?.statsRequest;
|
|
326
|
+
if (!query?.statsPath || !statsRequest) {
|
|
327
|
+
return throwError(() => new Error('PraxisChartStatsApiService requires query.statsPath and query.statsRequest for praxis.stats execution.'));
|
|
328
|
+
}
|
|
329
|
+
const categoryField = this.resolveCategoryField(config, statsRequest);
|
|
330
|
+
const metricField = this.resolveMetricField(config);
|
|
331
|
+
const url = this.buildStatsUrl(query.statsPath);
|
|
332
|
+
return this.http
|
|
333
|
+
.post(url, statsRequest)
|
|
334
|
+
.pipe(map((response) => this.toChartRows(response?.data, statsRequest, categoryField, metricField)), catchError((error) => this.handleHttpError(error)));
|
|
335
|
+
}
|
|
336
|
+
toChartRows(response, request, categoryField, metricField) {
|
|
337
|
+
if (!response) {
|
|
338
|
+
return [];
|
|
339
|
+
}
|
|
340
|
+
if ('points' in response) {
|
|
341
|
+
return response.points.map((point) => {
|
|
342
|
+
const category = point.label ?? point.start ?? point.end ?? '';
|
|
343
|
+
const metricValue = this.resolveMetricValue(point.value, point.count);
|
|
344
|
+
return {
|
|
345
|
+
[categoryField]: category,
|
|
346
|
+
[metricField]: metricValue,
|
|
347
|
+
key: point.start ?? point.label ?? point.end ?? category,
|
|
348
|
+
label: point.label ?? category,
|
|
349
|
+
value: point.value ?? null,
|
|
350
|
+
count: point.count ?? null,
|
|
351
|
+
start: point.start ?? null,
|
|
352
|
+
end: point.end ?? null,
|
|
353
|
+
granularity: response.granularity ?? null,
|
|
354
|
+
};
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
if ('buckets' in response) {
|
|
358
|
+
return response.buckets.map((bucket) => {
|
|
359
|
+
const category = this.resolveBucketCategory(bucket);
|
|
360
|
+
const metricValue = this.resolveMetricValue(bucket.value, bucket.count);
|
|
361
|
+
return {
|
|
362
|
+
[categoryField]: category,
|
|
363
|
+
[metricField]: metricValue,
|
|
364
|
+
key: bucket.key ?? null,
|
|
365
|
+
label: bucket.label ?? category,
|
|
366
|
+
value: bucket.value ?? null,
|
|
367
|
+
count: bucket.count ?? null,
|
|
368
|
+
from: bucket.from ?? null,
|
|
369
|
+
to: bucket.to ?? null,
|
|
370
|
+
mode: 'mode' in response ? response.mode ?? null : null,
|
|
371
|
+
requestMode: 'mode' in request ? request.mode : null,
|
|
372
|
+
};
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
return [];
|
|
376
|
+
}
|
|
377
|
+
resolveBucketCategory(bucket) {
|
|
378
|
+
if (bucket.label !== null && bucket.label !== undefined && bucket.label !== '') {
|
|
379
|
+
return String(bucket.label);
|
|
380
|
+
}
|
|
381
|
+
if (bucket.key !== null && bucket.key !== undefined && bucket.key !== '') {
|
|
382
|
+
return String(bucket.key);
|
|
383
|
+
}
|
|
384
|
+
if (bucket.from !== null || bucket.to !== null) {
|
|
385
|
+
return `${bucket.from ?? ''} - ${bucket.to ?? ''}`.trim();
|
|
386
|
+
}
|
|
387
|
+
return '';
|
|
388
|
+
}
|
|
389
|
+
resolveMetricValue(value, count) {
|
|
390
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
391
|
+
return value;
|
|
392
|
+
}
|
|
393
|
+
if (typeof count === 'number' && Number.isFinite(count)) {
|
|
394
|
+
return count;
|
|
395
|
+
}
|
|
396
|
+
return 0;
|
|
397
|
+
}
|
|
398
|
+
resolveCategoryField(config, request) {
|
|
399
|
+
const firstSeries = config.series[0];
|
|
400
|
+
return (firstSeries?.categoryField
|
|
401
|
+
|| config.axes?.x?.field
|
|
402
|
+
|| request.field
|
|
403
|
+
|| 'category');
|
|
404
|
+
}
|
|
405
|
+
resolveMetricField(config) {
|
|
406
|
+
return config.series[0]?.metric?.field || 'value';
|
|
407
|
+
}
|
|
408
|
+
buildStatsUrl(statsPath) {
|
|
409
|
+
const base = this.buildDefaultApiBase();
|
|
410
|
+
const normalizedStatsPath = this.normalizePath(statsPath);
|
|
411
|
+
const resolvedStatsPath = this.shouldStripLeadingApiSegment(base)
|
|
412
|
+
? this.stripLeadingApiSegment(normalizedStatsPath)
|
|
413
|
+
: normalizedStatsPath;
|
|
414
|
+
return `${base}/${resolvedStatsPath}`;
|
|
415
|
+
}
|
|
416
|
+
buildDefaultApiBase() {
|
|
417
|
+
const entry = this.apiUrl?.default ?? {};
|
|
418
|
+
return buildApiUrl(entry).replace(/\/+$/, '');
|
|
419
|
+
}
|
|
420
|
+
normalizePath(value) {
|
|
421
|
+
return String(value || '').trim().replace(/^\/+|\/+$/g, '');
|
|
422
|
+
}
|
|
423
|
+
stripLeadingApiSegment(value) {
|
|
424
|
+
return value.replace(/^api\/+/i, '');
|
|
425
|
+
}
|
|
426
|
+
shouldStripLeadingApiSegment(base) {
|
|
427
|
+
return /\/api$/i.test(base);
|
|
428
|
+
}
|
|
429
|
+
handleHttpError(error) {
|
|
430
|
+
if (error instanceof HttpErrorResponse) {
|
|
431
|
+
const detail = typeof error.message === 'string' && error.message.trim()
|
|
432
|
+
? error.message
|
|
433
|
+
: `HTTP ${error.status || 0}`;
|
|
434
|
+
return throwError(() => new Error(detail));
|
|
435
|
+
}
|
|
436
|
+
return throwError(() => error instanceof Error ? error : new Error('Unexpected failure during praxis.stats execution.'));
|
|
437
|
+
}
|
|
438
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisChartStatsApiService, deps: [{ token: i1.HttpClient }, { token: API_URL }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
439
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisChartStatsApiService, providedIn: 'root' });
|
|
440
|
+
}
|
|
441
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisChartStatsApiService, decorators: [{
|
|
442
|
+
type: Injectable,
|
|
443
|
+
args: [{ providedIn: 'root' }]
|
|
444
|
+
}], ctorParameters: () => [{ type: i1.HttpClient }, { type: undefined, decorators: [{
|
|
445
|
+
type: Inject,
|
|
446
|
+
args: [API_URL]
|
|
447
|
+
}] }] });
|
|
448
|
+
|
|
449
|
+
const PRAXIS_CHART_ENGINE = new InjectionToken('PRAXIS_CHART_ENGINE');
|
|
450
|
+
|
|
451
|
+
class PraxisChartComponent {
|
|
452
|
+
config = input.required(...(ngDevMode ? [{ debugName: "config" }] : []));
|
|
453
|
+
data = input(null, ...(ngDevMode ? [{ debugName: "data" }] : []));
|
|
454
|
+
pointClick = output();
|
|
455
|
+
queryRequest = output();
|
|
456
|
+
loadStateChange = output();
|
|
457
|
+
chartHost = viewChild('chartHost', ...(ngDevMode ? [{ debugName: "chartHost" }] : []));
|
|
458
|
+
engine = inject(PRAXIS_CHART_ENGINE);
|
|
459
|
+
transformer = inject(PraxisChartDataTransformerService);
|
|
460
|
+
statsApi = inject(PraxisChartStatsApiService);
|
|
461
|
+
i18n = inject(PraxisI18nService);
|
|
462
|
+
destroyRef = inject(DestroyRef);
|
|
463
|
+
resizeObserver = signal(null, ...(ngDevMode ? [{ debugName: "resizeObserver" }] : []));
|
|
464
|
+
currentLoadState = signal('idle', ...(ngDevMode ? [{ debugName: "currentLoadState" }] : []));
|
|
465
|
+
remoteResolvedData = signal(null, ...(ngDevMode ? [{ debugName: "remoteResolvedData" }] : []));
|
|
466
|
+
remoteRuntimeState = signal('idle', ...(ngDevMode ? [{ debugName: "remoteRuntimeState" }] : []));
|
|
467
|
+
remoteTechnicalError = signal(null, ...(ngDevMode ? [{ debugName: "remoteTechnicalError" }] : []));
|
|
468
|
+
previousRemoteSignature = null;
|
|
469
|
+
resolvedData = computed(() => {
|
|
470
|
+
const inputData = this.data();
|
|
471
|
+
if (inputData !== null && inputData !== undefined) {
|
|
472
|
+
return inputData;
|
|
473
|
+
}
|
|
474
|
+
const remoteData = this.remoteResolvedData();
|
|
475
|
+
if (remoteData !== null) {
|
|
476
|
+
return remoteData;
|
|
477
|
+
}
|
|
478
|
+
const dataSource = this.config().dataSource;
|
|
479
|
+
if (dataSource?.kind === 'local') {
|
|
480
|
+
return dataSource.items ?? [];
|
|
481
|
+
}
|
|
482
|
+
return [];
|
|
483
|
+
}, ...(ngDevMode ? [{ debugName: "resolvedData" }] : []));
|
|
484
|
+
resolvedHeight = computed(() => {
|
|
485
|
+
const value = this.config().height;
|
|
486
|
+
if (typeof value === 'number')
|
|
487
|
+
return `${value}px`;
|
|
488
|
+
return value || '320px';
|
|
489
|
+
}, ...(ngDevMode ? [{ debugName: "resolvedHeight" }] : []));
|
|
490
|
+
renderConfig = computed(() => {
|
|
491
|
+
const config = this.config();
|
|
492
|
+
const explicitData = this.data();
|
|
493
|
+
const remoteState = this.remoteRuntimeState();
|
|
494
|
+
const remoteData = this.remoteResolvedData();
|
|
495
|
+
if (explicitData !== null && explicitData !== undefined) {
|
|
496
|
+
return config;
|
|
497
|
+
}
|
|
498
|
+
if (config.dataSource?.kind !== 'remote') {
|
|
499
|
+
return config;
|
|
500
|
+
}
|
|
501
|
+
if (remoteState === 'loading') {
|
|
502
|
+
return {
|
|
503
|
+
...config,
|
|
504
|
+
preferredLoadState: 'loading',
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
if (remoteState === 'error') {
|
|
508
|
+
return {
|
|
509
|
+
...config,
|
|
510
|
+
preferredLoadState: 'error',
|
|
511
|
+
state: config.state?.error
|
|
512
|
+
? {
|
|
513
|
+
...config.state,
|
|
514
|
+
error: {
|
|
515
|
+
...config.state.error,
|
|
516
|
+
technicalDetails: this.remoteTechnicalError() ?? config.state.error.technicalDetails,
|
|
517
|
+
},
|
|
518
|
+
}
|
|
519
|
+
: config.state,
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
if (remoteData !== null) {
|
|
523
|
+
return {
|
|
524
|
+
...config,
|
|
525
|
+
preferredLoadState: undefined,
|
|
526
|
+
series: config.series.map((series) => ({
|
|
527
|
+
...series,
|
|
528
|
+
metric: {
|
|
529
|
+
...series.metric,
|
|
530
|
+
aggregation: 'sum',
|
|
531
|
+
field: series.metric?.field || 'value',
|
|
532
|
+
},
|
|
533
|
+
})),
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
return config;
|
|
537
|
+
}, ...(ngDevMode ? [{ debugName: "renderConfig" }] : []));
|
|
538
|
+
loadingLabel = computed(() => this.i18n.resolve(this.renderConfig().state?.loadingLabel), ...(ngDevMode ? [{ debugName: "loadingLabel" }] : []));
|
|
539
|
+
emptyTitle = computed(() => this.i18n.resolve(this.renderConfig().emptyState?.title), ...(ngDevMode ? [{ debugName: "emptyTitle" }] : []));
|
|
540
|
+
emptyDescription = computed(() => this.i18n.resolve(this.renderConfig().emptyState?.description), ...(ngDevMode ? [{ debugName: "emptyDescription" }] : []));
|
|
541
|
+
errorTitle = computed(() => this.i18n.resolve(this.renderConfig().state?.error?.title), ...(ngDevMode ? [{ debugName: "errorTitle" }] : []));
|
|
542
|
+
errorDescription = computed(() => this.i18n.resolve(this.renderConfig().state?.error?.description), ...(ngDevMode ? [{ debugName: "errorDescription" }] : []));
|
|
543
|
+
loadState = computed(() => {
|
|
544
|
+
const config = this.renderConfig();
|
|
545
|
+
if (config.preferredLoadState === 'loading')
|
|
546
|
+
return 'loading';
|
|
547
|
+
if (config.preferredLoadState === 'error')
|
|
548
|
+
return 'error';
|
|
549
|
+
const dataSource = config.dataSource;
|
|
550
|
+
const explicitData = this.data();
|
|
551
|
+
if (dataSource?.kind === 'remote' && (explicitData === null || explicitData === undefined)) {
|
|
552
|
+
return this.remoteRuntimeState() === 'ready' ? 'ready' : 'loading';
|
|
553
|
+
}
|
|
554
|
+
const transformed = this.transformer.transform(config, this.resolvedData());
|
|
555
|
+
return transformed.hasData ? 'ready' : 'empty';
|
|
556
|
+
}, ...(ngDevMode ? [{ debugName: "loadState" }] : []));
|
|
557
|
+
constructor() {
|
|
558
|
+
effect(() => {
|
|
559
|
+
const config = this.config();
|
|
560
|
+
const explicitData = this.data();
|
|
561
|
+
const nextSignature = explicitData === null || explicitData === undefined
|
|
562
|
+
? this.buildRemoteSignature(config)
|
|
563
|
+
: null;
|
|
564
|
+
if (nextSignature === this.previousRemoteSignature) {
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
this.previousRemoteSignature = nextSignature;
|
|
568
|
+
this.remoteResolvedData.set(null);
|
|
569
|
+
this.remoteRuntimeState.set('idle');
|
|
570
|
+
this.remoteTechnicalError.set(null);
|
|
571
|
+
});
|
|
572
|
+
effect(() => {
|
|
573
|
+
const nextState = this.loadState();
|
|
574
|
+
if (this.currentLoadState() !== nextState) {
|
|
575
|
+
this.currentLoadState.set(nextState);
|
|
576
|
+
this.loadStateChange.emit(nextState);
|
|
577
|
+
}
|
|
578
|
+
});
|
|
579
|
+
effect(() => {
|
|
580
|
+
const config = this.config();
|
|
581
|
+
const explicitData = this.data();
|
|
582
|
+
if (config.dataSource?.kind !== 'remote' || (explicitData !== null && explicitData !== undefined)) {
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
if (this.remoteRuntimeState() === 'loading' || this.remoteResolvedData() !== null) {
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
const event = {
|
|
589
|
+
chartId: config.id,
|
|
590
|
+
dataSource: config.dataSource,
|
|
591
|
+
query: config.dataSource.query,
|
|
592
|
+
};
|
|
593
|
+
this.queryRequest.emit(event);
|
|
594
|
+
this.remoteRuntimeState.set('loading');
|
|
595
|
+
this.remoteResolvedData.set([]);
|
|
596
|
+
this.remoteTechnicalError.set(null);
|
|
597
|
+
this.statsApi.execute(event, config)
|
|
598
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
599
|
+
.subscribe({
|
|
600
|
+
next: (rows) => {
|
|
601
|
+
this.remoteResolvedData.set(rows);
|
|
602
|
+
this.remoteRuntimeState.set('ready');
|
|
603
|
+
this.remoteTechnicalError.set(null);
|
|
604
|
+
},
|
|
605
|
+
error: (error) => {
|
|
606
|
+
this.remoteResolvedData.set([]);
|
|
607
|
+
this.remoteRuntimeState.set('error');
|
|
608
|
+
this.remoteTechnicalError.set(error instanceof Error && error.message.trim()
|
|
609
|
+
? error.message
|
|
610
|
+
: 'praxis.stats request failed');
|
|
611
|
+
},
|
|
612
|
+
});
|
|
613
|
+
});
|
|
614
|
+
effect(() => {
|
|
615
|
+
if (this.loadState() !== 'ready') {
|
|
616
|
+
this.engine.destroy();
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
619
|
+
const host = this.chartHost()?.nativeElement;
|
|
620
|
+
if (!host)
|
|
621
|
+
return;
|
|
622
|
+
this.engine.render(host, {
|
|
623
|
+
config: this.renderConfig(),
|
|
624
|
+
data: this.resolvedData(),
|
|
625
|
+
onPointClick: (event) => this.pointClick.emit(event),
|
|
626
|
+
});
|
|
627
|
+
this.ensureResizeObserver(host);
|
|
628
|
+
});
|
|
629
|
+
this.destroyRef.onDestroy(() => {
|
|
630
|
+
this.resizeObserver()?.disconnect();
|
|
631
|
+
this.engine.destroy();
|
|
632
|
+
});
|
|
633
|
+
}
|
|
634
|
+
ensureResizeObserver(host) {
|
|
635
|
+
if (this.resizeObserver()) {
|
|
636
|
+
return;
|
|
637
|
+
}
|
|
638
|
+
const observer = new ResizeObserver(() => {
|
|
639
|
+
this.engine.resize();
|
|
640
|
+
});
|
|
641
|
+
observer.observe(host);
|
|
642
|
+
this.resizeObserver.set(observer);
|
|
643
|
+
}
|
|
644
|
+
buildRemoteSignature(config) {
|
|
645
|
+
if (config.dataSource?.kind !== 'remote') {
|
|
646
|
+
return null;
|
|
647
|
+
}
|
|
648
|
+
return JSON.stringify({
|
|
649
|
+
id: config.id,
|
|
650
|
+
resourcePath: config.dataSource.resourcePath,
|
|
651
|
+
query: config.dataSource.query,
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisChartComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
655
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: PraxisChartComponent, isStandalone: true, selector: "praxis-chart", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { pointClick: "pointClick", queryRequest: "queryRequest", loadStateChange: "loadStateChange" }, providers: [
|
|
656
|
+
EChartsEngineAdapter,
|
|
657
|
+
{
|
|
658
|
+
provide: PRAXIS_CHART_ENGINE,
|
|
659
|
+
useExisting: EChartsEngineAdapter,
|
|
660
|
+
},
|
|
661
|
+
], viewQueries: [{ propertyName: "chartHost", first: true, predicate: ["chartHost"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
662
|
+
<section class="praxis-chart-shell" [style.height]="resolvedHeight()">
|
|
663
|
+
@if (loadState() === 'loading') {
|
|
664
|
+
<div class="praxis-chart-state praxis-chart-state-loading">
|
|
665
|
+
<div class="praxis-chart-spinner" aria-hidden="true"></div>
|
|
666
|
+
@if (loadingLabel()) {
|
|
667
|
+
<div class="praxis-chart-state-title">{{ loadingLabel() }}</div>
|
|
668
|
+
}
|
|
669
|
+
</div>
|
|
670
|
+
} @else if (loadState() === 'error') {
|
|
671
|
+
<div class="praxis-chart-state praxis-chart-state-error">
|
|
672
|
+
@if (errorTitle()) {
|
|
673
|
+
<div class="praxis-chart-state-title">{{ errorTitle() }}</div>
|
|
674
|
+
}
|
|
675
|
+
@if (errorDescription()) {
|
|
676
|
+
<div class="praxis-chart-state-description">{{ errorDescription() }}</div>
|
|
677
|
+
}
|
|
678
|
+
</div>
|
|
679
|
+
} @else if (loadState() === 'empty') {
|
|
680
|
+
<div class="praxis-chart-state praxis-chart-state-empty">
|
|
681
|
+
@if (emptyTitle()) {
|
|
682
|
+
<div class="praxis-chart-state-title">{{ emptyTitle() }}</div>
|
|
683
|
+
}
|
|
684
|
+
@if (emptyDescription()) {
|
|
685
|
+
<div class="praxis-chart-state-description">{{ emptyDescription() }}</div>
|
|
686
|
+
}
|
|
687
|
+
</div>
|
|
688
|
+
} @else {
|
|
689
|
+
<div #chartHost class="praxis-chart-host"></div>
|
|
690
|
+
}
|
|
691
|
+
</section>
|
|
692
|
+
`, isInline: true, styles: [":host{display:block;min-width:0}.praxis-chart-shell{position:relative;width:100%;min-height:240px;border-radius:18px;overflow:hidden;background:radial-gradient(circle at top left,rgba(18,99,180,.12),transparent 38%),linear-gradient(180deg,#1263b408,#1263b400);border:1px solid color-mix(in srgb,var(--md-sys-color-outline, #c5c7ce) 72%,transparent)}.praxis-chart-host{width:100%;height:100%}.praxis-chart-state{height:100%;min-height:240px;display:grid;place-content:center;gap:10px;padding:24px;text-align:center}.praxis-chart-state-title{font-size:1rem;font-weight:600;color:var(--md-sys-color-on-surface, #1a1b20)}.praxis-chart-state-description{font-size:.925rem;color:var(--md-sys-color-on-surface-variant, #5a5d67);max-width:36rem}.praxis-chart-spinner{width:34px;height:34px;margin-inline:auto;border-radius:999px;border:3px solid rgba(18,99,180,.18);border-top-color:#1263b4d1;animation:praxis-chart-spin .8s linear infinite}@keyframes praxis-chart-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
693
|
+
}
|
|
694
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisChartComponent, decorators: [{
|
|
695
|
+
type: Component,
|
|
696
|
+
args: [{ selector: 'praxis-chart', standalone: true, imports: [CommonModule], providers: [
|
|
697
|
+
EChartsEngineAdapter,
|
|
698
|
+
{
|
|
699
|
+
provide: PRAXIS_CHART_ENGINE,
|
|
700
|
+
useExisting: EChartsEngineAdapter,
|
|
701
|
+
},
|
|
702
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
703
|
+
<section class="praxis-chart-shell" [style.height]="resolvedHeight()">
|
|
704
|
+
@if (loadState() === 'loading') {
|
|
705
|
+
<div class="praxis-chart-state praxis-chart-state-loading">
|
|
706
|
+
<div class="praxis-chart-spinner" aria-hidden="true"></div>
|
|
707
|
+
@if (loadingLabel()) {
|
|
708
|
+
<div class="praxis-chart-state-title">{{ loadingLabel() }}</div>
|
|
709
|
+
}
|
|
710
|
+
</div>
|
|
711
|
+
} @else if (loadState() === 'error') {
|
|
712
|
+
<div class="praxis-chart-state praxis-chart-state-error">
|
|
713
|
+
@if (errorTitle()) {
|
|
714
|
+
<div class="praxis-chart-state-title">{{ errorTitle() }}</div>
|
|
715
|
+
}
|
|
716
|
+
@if (errorDescription()) {
|
|
717
|
+
<div class="praxis-chart-state-description">{{ errorDescription() }}</div>
|
|
718
|
+
}
|
|
719
|
+
</div>
|
|
720
|
+
} @else if (loadState() === 'empty') {
|
|
721
|
+
<div class="praxis-chart-state praxis-chart-state-empty">
|
|
722
|
+
@if (emptyTitle()) {
|
|
723
|
+
<div class="praxis-chart-state-title">{{ emptyTitle() }}</div>
|
|
724
|
+
}
|
|
725
|
+
@if (emptyDescription()) {
|
|
726
|
+
<div class="praxis-chart-state-description">{{ emptyDescription() }}</div>
|
|
727
|
+
}
|
|
728
|
+
</div>
|
|
729
|
+
} @else {
|
|
730
|
+
<div #chartHost class="praxis-chart-host"></div>
|
|
731
|
+
}
|
|
732
|
+
</section>
|
|
733
|
+
`, styles: [":host{display:block;min-width:0}.praxis-chart-shell{position:relative;width:100%;min-height:240px;border-radius:18px;overflow:hidden;background:radial-gradient(circle at top left,rgba(18,99,180,.12),transparent 38%),linear-gradient(180deg,#1263b408,#1263b400);border:1px solid color-mix(in srgb,var(--md-sys-color-outline, #c5c7ce) 72%,transparent)}.praxis-chart-host{width:100%;height:100%}.praxis-chart-state{height:100%;min-height:240px;display:grid;place-content:center;gap:10px;padding:24px;text-align:center}.praxis-chart-state-title{font-size:1rem;font-weight:600;color:var(--md-sys-color-on-surface, #1a1b20)}.praxis-chart-state-description{font-size:.925rem;color:var(--md-sys-color-on-surface-variant, #5a5d67);max-width:36rem}.praxis-chart-spinner{width:34px;height:34px;margin-inline:auto;border-radius:999px;border:3px solid rgba(18,99,180,.18);border-top-color:#1263b4d1;animation:praxis-chart-spin .8s linear infinite}@keyframes praxis-chart-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"] }]
|
|
734
|
+
}], ctorParameters: () => [], propDecorators: { config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: true }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], pointClick: [{ type: i0.Output, args: ["pointClick"] }], queryRequest: [{ type: i0.Output, args: ["queryRequest"] }], loadStateChange: [{ type: i0.Output, args: ["loadStateChange"] }], chartHost: [{ type: i0.ViewChild, args: ['chartHost', { isSignal: true }] }] } });
|
|
735
|
+
|
|
736
|
+
const PRAXIS_CHART_DRILLDOWN_DATA_BY_MONTH = {
|
|
737
|
+
Jan: [
|
|
738
|
+
{ segment: 'Enterprise', total: 52000 },
|
|
739
|
+
{ segment: 'Mid-market', total: 38000 },
|
|
740
|
+
{ segment: 'SMB', total: 30000 },
|
|
741
|
+
],
|
|
742
|
+
Fev: [
|
|
743
|
+
{ segment: 'Enterprise', total: 61000 },
|
|
744
|
+
{ segment: 'Mid-market', total: 42000 },
|
|
745
|
+
{ segment: 'SMB', total: 35000 },
|
|
746
|
+
],
|
|
747
|
+
Mar: [
|
|
748
|
+
{ segment: 'Enterprise', total: 68000 },
|
|
749
|
+
{ segment: 'Mid-market', total: 47000 },
|
|
750
|
+
{ segment: 'SMB', total: 36000 },
|
|
751
|
+
],
|
|
752
|
+
Abr: [
|
|
753
|
+
{ segment: 'Enterprise', total: 64000 },
|
|
754
|
+
{ segment: 'Mid-market', total: 49500 },
|
|
755
|
+
{ segment: 'SMB', total: 36000 },
|
|
756
|
+
],
|
|
757
|
+
};
|
|
758
|
+
|
|
759
|
+
class PraxisChartDrilldownPanelComponent {
|
|
760
|
+
title = input('Detalhamento por segmento', ...(ngDevMode ? [{ debugName: "title" }] : []));
|
|
761
|
+
selection = input(null, ...(ngDevMode ? [{ debugName: "selection" }] : []));
|
|
762
|
+
activeCategory = computed(() => {
|
|
763
|
+
const raw = this.selection()?.category;
|
|
764
|
+
return typeof raw === 'string' ? raw : null;
|
|
765
|
+
}, ...(ngDevMode ? [{ debugName: "activeCategory" }] : []));
|
|
766
|
+
detailData = computed(() => {
|
|
767
|
+
const category = this.activeCategory();
|
|
768
|
+
if (!category)
|
|
769
|
+
return [];
|
|
770
|
+
return PRAXIS_CHART_DRILLDOWN_DATA_BY_MONTH[category] ?? [];
|
|
771
|
+
}, ...(ngDevMode ? [{ debugName: "detailData" }] : []));
|
|
772
|
+
detailChartConfig = computed(() => {
|
|
773
|
+
const category = this.activeCategory();
|
|
774
|
+
return {
|
|
775
|
+
id: 'chart-drilldown-detail',
|
|
776
|
+
type: 'donut',
|
|
777
|
+
title: { text: category ? `Mix de receita em ${category}` : 'Aguardando selecao' },
|
|
778
|
+
subtitle: { text: category ? 'Drill-down local com JSON mockado' : 'Selecione um ponto no chart principal' },
|
|
779
|
+
height: 280,
|
|
780
|
+
series: [
|
|
781
|
+
{
|
|
782
|
+
id: 'segmentMix',
|
|
783
|
+
categoryField: 'segment',
|
|
784
|
+
metric: { field: 'total', aggregation: 'sum' },
|
|
785
|
+
labels: { visible: true },
|
|
786
|
+
},
|
|
787
|
+
],
|
|
788
|
+
emptyState: {
|
|
789
|
+
title: { text: 'Nenhum recorte selecionado' },
|
|
790
|
+
description: { text: 'O painel de drill-down usa mocks locais e reage ao pointClick do chart principal.' },
|
|
791
|
+
},
|
|
792
|
+
theme: {
|
|
793
|
+
palette: ['#1263b4', '#2b8a3e', '#f08c00', '#7b61ff'],
|
|
794
|
+
},
|
|
795
|
+
};
|
|
796
|
+
}, ...(ngDevMode ? [{ debugName: "detailChartConfig" }] : []));
|
|
797
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisChartDrilldownPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
798
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: PraxisChartDrilldownPanelComponent, isStandalone: true, selector: "praxis-chart-drilldown-panel", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, selection: { classPropertyName: "selection", publicName: "selection", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
|
|
799
|
+
<section class="drilldown-shell">
|
|
800
|
+
<header class="drilldown-header">
|
|
801
|
+
<p class="drilldown-eyebrow">Drill-down local</p>
|
|
802
|
+
<h3>{{ title() }}</h3>
|
|
803
|
+
@if (activeCategory()) {
|
|
804
|
+
<p class="drilldown-description">Recorte atual: {{ activeCategory() }}</p>
|
|
805
|
+
} @else {
|
|
806
|
+
<p class="drilldown-description">Clique em uma barra do chart principal para abrir o detalhamento local.</p>
|
|
807
|
+
}
|
|
808
|
+
</header>
|
|
809
|
+
|
|
810
|
+
<praxis-chart
|
|
811
|
+
[config]="detailChartConfig()"
|
|
812
|
+
[data]="detailData()"
|
|
813
|
+
></praxis-chart>
|
|
814
|
+
</section>
|
|
815
|
+
`, isInline: true, styles: [":host{display:block}.drilldown-shell{display:grid;gap:14px}.drilldown-header h3,.drilldown-eyebrow,.drilldown-description{margin:0}.drilldown-eyebrow{font-size:.75rem;letter-spacing:.14em;text-transform:uppercase;color:var(--md-sys-color-on-surface-variant, #5a5d67)}.drilldown-header h3{font-size:1.2rem;color:var(--md-sys-color-on-surface, #1a1b20)}.drilldown-description{color:var(--md-sys-color-on-surface-variant, #5a5d67)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: PraxisChartComponent, selector: "praxis-chart", inputs: ["config", "data"], outputs: ["pointClick", "queryRequest", "loadStateChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
816
|
+
}
|
|
817
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisChartDrilldownPanelComponent, decorators: [{
|
|
818
|
+
type: Component,
|
|
819
|
+
args: [{ selector: 'praxis-chart-drilldown-panel', standalone: true, imports: [CommonModule, PraxisChartComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
820
|
+
<section class="drilldown-shell">
|
|
821
|
+
<header class="drilldown-header">
|
|
822
|
+
<p class="drilldown-eyebrow">Drill-down local</p>
|
|
823
|
+
<h3>{{ title() }}</h3>
|
|
824
|
+
@if (activeCategory()) {
|
|
825
|
+
<p class="drilldown-description">Recorte atual: {{ activeCategory() }}</p>
|
|
826
|
+
} @else {
|
|
827
|
+
<p class="drilldown-description">Clique em uma barra do chart principal para abrir o detalhamento local.</p>
|
|
828
|
+
}
|
|
829
|
+
</header>
|
|
830
|
+
|
|
831
|
+
<praxis-chart
|
|
832
|
+
[config]="detailChartConfig()"
|
|
833
|
+
[data]="detailData()"
|
|
834
|
+
></praxis-chart>
|
|
835
|
+
</section>
|
|
836
|
+
`, styles: [":host{display:block}.drilldown-shell{display:grid;gap:14px}.drilldown-header h3,.drilldown-eyebrow,.drilldown-description{margin:0}.drilldown-eyebrow{font-size:.75rem;letter-spacing:.14em;text-transform:uppercase;color:var(--md-sys-color-on-surface-variant, #5a5d67)}.drilldown-header h3{font-size:1.2rem;color:var(--md-sys-color-on-surface, #1a1b20)}.drilldown-description{color:var(--md-sys-color-on-surface-variant, #5a5d67)}\n"] }]
|
|
837
|
+
}], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], selection: [{ type: i0.Input, args: [{ isSignal: true, alias: "selection", required: false }] }] } });
|
|
838
|
+
|
|
839
|
+
class PraxisChartStateProbeComponent {
|
|
840
|
+
title = input('Chart Runtime Probe', ...(ngDevMode ? [{ debugName: "title" }] : []));
|
|
841
|
+
value = input(null, ...(ngDevMode ? [{ debugName: "value" }] : []));
|
|
842
|
+
serializedValue = computed(() => JSON.stringify(this.value(), null, 2), ...(ngDevMode ? [{ debugName: "serializedValue" }] : []));
|
|
843
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisChartStateProbeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
844
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: PraxisChartStateProbeComponent, isStandalone: true, selector: "praxis-chart-state-probe", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
|
|
845
|
+
<section class="probe-shell">
|
|
846
|
+
<header class="probe-header">
|
|
847
|
+
<p class="probe-eyebrow">Probe</p>
|
|
848
|
+
<h3>{{ title() }}</h3>
|
|
849
|
+
</header>
|
|
850
|
+
|
|
851
|
+
@if (value() === null || value() === undefined || value() === '') {
|
|
852
|
+
<div class="probe-empty">Aguardando eventos do runtime.</div>
|
|
853
|
+
} @else {
|
|
854
|
+
<pre>{{ serializedValue() }}</pre>
|
|
855
|
+
}
|
|
856
|
+
</section>
|
|
857
|
+
`, isInline: true, styles: [":host{display:block}.probe-shell{display:grid;gap:12px;min-height:220px;padding:18px;border-radius:20px;background:linear-gradient(180deg,#0b111ff5,#0b111fe0),radial-gradient(circle at top right,rgba(18,99,180,.32),transparent 35%);color:#d7e6ff}.probe-header h3,.probe-eyebrow{margin:0}.probe-eyebrow{font-size:.75rem;letter-spacing:.12em;text-transform:uppercase;color:#d7e6ffb3}.probe-empty{color:#d7e6ffc7}pre{margin:0;overflow:auto;white-space:pre-wrap;word-break:break-word;font-size:.84rem;line-height:1.45}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
858
|
+
}
|
|
859
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisChartStateProbeComponent, decorators: [{
|
|
860
|
+
type: Component,
|
|
861
|
+
args: [{ selector: 'praxis-chart-state-probe', standalone: true, imports: [CommonModule], changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
862
|
+
<section class="probe-shell">
|
|
863
|
+
<header class="probe-header">
|
|
864
|
+
<p class="probe-eyebrow">Probe</p>
|
|
865
|
+
<h3>{{ title() }}</h3>
|
|
866
|
+
</header>
|
|
867
|
+
|
|
868
|
+
@if (value() === null || value() === undefined || value() === '') {
|
|
869
|
+
<div class="probe-empty">Aguardando eventos do runtime.</div>
|
|
870
|
+
} @else {
|
|
871
|
+
<pre>{{ serializedValue() }}</pre>
|
|
872
|
+
}
|
|
873
|
+
</section>
|
|
874
|
+
`, styles: [":host{display:block}.probe-shell{display:grid;gap:12px;min-height:220px;padding:18px;border-radius:20px;background:linear-gradient(180deg,#0b111ff5,#0b111fe0),radial-gradient(circle at top right,rgba(18,99,180,.32),transparent 35%);color:#d7e6ff}.probe-header h3,.probe-eyebrow{margin:0}.probe-eyebrow{font-size:.75rem;letter-spacing:.12em;text-transform:uppercase;color:#d7e6ffb3}.probe-empty{color:#d7e6ffc7}pre{margin:0;overflow:auto;white-space:pre-wrap;word-break:break-word;font-size:.84rem;line-height:1.45}\n"] }]
|
|
875
|
+
}], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }] } });
|
|
876
|
+
|
|
877
|
+
const PRAXIS_CHART_COMPONENT_METADATA = {
|
|
878
|
+
id: 'praxis-chart',
|
|
879
|
+
componentType: 'praxis-chart',
|
|
880
|
+
displayName: 'Praxis Chart',
|
|
881
|
+
selector: 'praxis-chart',
|
|
882
|
+
component: PraxisChartComponent,
|
|
883
|
+
friendlyName: 'Praxis Chart',
|
|
884
|
+
description: 'Chart component for analytics and metadata-driven visualizations in Praxis UI.',
|
|
885
|
+
icon: 'bar_chart',
|
|
886
|
+
tags: ['chart', 'analytics', 'widget', 'visualization'],
|
|
887
|
+
lib: '@praxisui/charts',
|
|
888
|
+
inputs: [
|
|
889
|
+
{
|
|
890
|
+
name: 'config',
|
|
891
|
+
type: 'PraxisChartConfig',
|
|
892
|
+
description: 'Declarative chart configuration with metadata-oriented semantics.',
|
|
893
|
+
},
|
|
894
|
+
{
|
|
895
|
+
name: 'data',
|
|
896
|
+
type: 'PraxisChartDataRow[]',
|
|
897
|
+
description: 'Optional local dataset that takes precedence over local datasource items.',
|
|
898
|
+
},
|
|
899
|
+
],
|
|
900
|
+
outputs: [
|
|
901
|
+
{
|
|
902
|
+
name: 'pointClick',
|
|
903
|
+
type: 'PraxisChartPointEvent',
|
|
904
|
+
description: 'Emitted when the host wants to react to a point/series click.',
|
|
905
|
+
},
|
|
906
|
+
{
|
|
907
|
+
name: 'queryRequest',
|
|
908
|
+
type: 'PraxisChartQueryRequestEvent',
|
|
909
|
+
description: 'Emitted before a remote praxis.stats datasource is resolved, allowing host-side observability or overrides.',
|
|
910
|
+
},
|
|
911
|
+
{
|
|
912
|
+
name: 'loadStateChange',
|
|
913
|
+
type: 'PraxisChartLoadState',
|
|
914
|
+
description: 'Emitted when the chart state changes.',
|
|
915
|
+
},
|
|
916
|
+
],
|
|
917
|
+
};
|
|
918
|
+
function providePraxisChartsMetadata() {
|
|
919
|
+
return {
|
|
920
|
+
provide: ENVIRONMENT_INITIALIZER,
|
|
921
|
+
multi: true,
|
|
922
|
+
useFactory: (registry) => () => {
|
|
923
|
+
registry.register(PRAXIS_CHART_COMPONENT_METADATA);
|
|
924
|
+
},
|
|
925
|
+
deps: [ComponentMetadataRegistry],
|
|
926
|
+
};
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
const PRAXIS_CHART_DRILLDOWN_PANEL_METADATA = {
|
|
930
|
+
id: 'praxis-chart-drilldown-panel',
|
|
931
|
+
componentType: 'praxis-chart-drilldown-panel',
|
|
932
|
+
displayName: 'Praxis Chart Drilldown Panel',
|
|
933
|
+
selector: 'praxis-chart-drilldown-panel',
|
|
934
|
+
component: PraxisChartDrilldownPanelComponent,
|
|
935
|
+
friendlyName: 'Praxis Chart Drilldown Panel',
|
|
936
|
+
description: 'Local drill-down panel that consumes chart point events and renders a derived detail chart.',
|
|
937
|
+
icon: 'query_stats',
|
|
938
|
+
tags: ['chart', 'drilldown', 'widget', 'analytics'],
|
|
939
|
+
lib: '@praxisui/charts',
|
|
940
|
+
inputs: [
|
|
941
|
+
{
|
|
942
|
+
name: 'title',
|
|
943
|
+
type: 'string',
|
|
944
|
+
description: 'Panel title for the drill-down view.',
|
|
945
|
+
},
|
|
946
|
+
{
|
|
947
|
+
name: 'selection',
|
|
948
|
+
type: 'PraxisChartPointEvent',
|
|
949
|
+
description: 'Event emitted by a source chart and used to resolve local detail datasets.',
|
|
950
|
+
},
|
|
951
|
+
],
|
|
952
|
+
};
|
|
953
|
+
function providePraxisChartDrilldownPanelMetadata() {
|
|
954
|
+
return {
|
|
955
|
+
provide: ENVIRONMENT_INITIALIZER,
|
|
956
|
+
multi: true,
|
|
957
|
+
useFactory: (registry) => () => {
|
|
958
|
+
registry.register(PRAXIS_CHART_DRILLDOWN_PANEL_METADATA);
|
|
959
|
+
},
|
|
960
|
+
deps: [ComponentMetadataRegistry],
|
|
961
|
+
};
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
const PRAXIS_CHART_STATE_PROBE_COMPONENT_METADATA = {
|
|
965
|
+
id: 'praxis-chart-state-probe',
|
|
966
|
+
componentType: 'praxis-chart-state-probe',
|
|
967
|
+
displayName: 'Praxis Chart State Probe',
|
|
968
|
+
selector: 'praxis-chart-state-probe',
|
|
969
|
+
component: PraxisChartStateProbeComponent,
|
|
970
|
+
friendlyName: 'Praxis Chart State Probe',
|
|
971
|
+
description: 'Diagnostic widget used to inspect chart events and runtime payloads during local validation.',
|
|
972
|
+
icon: 'monitoring',
|
|
973
|
+
tags: ['chart', 'probe', 'debug', 'widget'],
|
|
974
|
+
lib: '@praxisui/charts',
|
|
975
|
+
inputs: [
|
|
976
|
+
{
|
|
977
|
+
name: 'title',
|
|
978
|
+
type: 'string',
|
|
979
|
+
description: 'Probe panel title.',
|
|
980
|
+
},
|
|
981
|
+
{
|
|
982
|
+
name: 'value',
|
|
983
|
+
type: 'unknown',
|
|
984
|
+
description: 'Payload rendered as formatted JSON.',
|
|
985
|
+
},
|
|
986
|
+
],
|
|
987
|
+
};
|
|
988
|
+
function providePraxisChartStateProbeMetadata() {
|
|
989
|
+
return {
|
|
990
|
+
provide: ENVIRONMENT_INITIALIZER,
|
|
991
|
+
multi: true,
|
|
992
|
+
useFactory: (registry) => () => {
|
|
993
|
+
registry.register(PRAXIS_CHART_STATE_PROBE_COMPONENT_METADATA);
|
|
994
|
+
},
|
|
995
|
+
deps: [ComponentMetadataRegistry],
|
|
996
|
+
};
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
class PraxisChartShowcaseWidgetComponent {
|
|
1000
|
+
config = input.required(...(ngDevMode ? [{ debugName: "config" }] : []));
|
|
1001
|
+
data = input(null, ...(ngDevMode ? [{ debugName: "data" }] : []));
|
|
1002
|
+
viewMode = input('chart', ...(ngDevMode ? [{ debugName: "viewMode" }] : []));
|
|
1003
|
+
pointClick = output();
|
|
1004
|
+
queryRequest = output();
|
|
1005
|
+
loadStateChange = output();
|
|
1006
|
+
statsApi = inject(PraxisChartStatsApiService);
|
|
1007
|
+
destroyRef = inject(DestroyRef);
|
|
1008
|
+
remoteResolvedRows = signal(null, ...(ngDevMode ? [{ debugName: "remoteResolvedRows" }] : []));
|
|
1009
|
+
remoteRuntimeState = signal('idle', ...(ngDevMode ? [{ debugName: "remoteRuntimeState" }] : []));
|
|
1010
|
+
remoteTechnicalError = signal(null, ...(ngDevMode ? [{ debugName: "remoteTechnicalError" }] : []));
|
|
1011
|
+
previousRemoteSignature = null;
|
|
1012
|
+
resolvedRows = computed(() => {
|
|
1013
|
+
const explicitData = this.data();
|
|
1014
|
+
if (explicitData !== null && explicitData !== undefined) {
|
|
1015
|
+
return explicitData;
|
|
1016
|
+
}
|
|
1017
|
+
return this.remoteResolvedRows() ?? [];
|
|
1018
|
+
}, ...(ngDevMode ? [{ debugName: "resolvedRows" }] : []));
|
|
1019
|
+
tableId = computed(() => `${this.config().id || 'praxis-chart'}-showcase-table`, ...(ngDevMode ? [{ debugName: "tableId" }] : []));
|
|
1020
|
+
resolvedTitle = computed(() => resolveText(this.config().title) || 'Tabela analitica', ...(ngDevMode ? [{ debugName: "resolvedTitle" }] : []));
|
|
1021
|
+
resolvedSubtitle = computed(() => resolveText(this.config().subtitle) || 'Mesmo dataset em visualizacao tabular', ...(ngDevMode ? [{ debugName: "resolvedSubtitle" }] : []));
|
|
1022
|
+
loadingLabel = computed(() => resolveText(this.config().state?.loadingLabel) || 'Carregando analytics...', ...(ngDevMode ? [{ debugName: "loadingLabel" }] : []));
|
|
1023
|
+
errorTitle = computed(() => this.remoteTechnicalError()
|
|
1024
|
+
|| resolveText(this.config().state?.error?.title)
|
|
1025
|
+
|| 'Falha no contrato analitico', ...(ngDevMode ? [{ debugName: "errorTitle" }] : []));
|
|
1026
|
+
errorDescription = computed(() => resolveText(this.config().state?.error?.description), ...(ngDevMode ? [{ debugName: "errorDescription" }] : []));
|
|
1027
|
+
stateMode = computed(() => resolveShowcaseStateMode(this.config(), this.remoteRuntimeState()), ...(ngDevMode ? [{ debugName: "stateMode" }] : []));
|
|
1028
|
+
tableConfig = computed(() => buildTableConfig(this.config(), this.resolvedRows()), ...(ngDevMode ? [{ debugName: "tableConfig" }] : []));
|
|
1029
|
+
constructor() {
|
|
1030
|
+
effect(() => {
|
|
1031
|
+
const config = this.config();
|
|
1032
|
+
const explicitData = this.data();
|
|
1033
|
+
const nextSignature = explicitData === null || explicitData === undefined
|
|
1034
|
+
? buildRemoteSignature(config)
|
|
1035
|
+
: null;
|
|
1036
|
+
if (nextSignature === this.previousRemoteSignature) {
|
|
1037
|
+
return;
|
|
1038
|
+
}
|
|
1039
|
+
this.previousRemoteSignature = nextSignature;
|
|
1040
|
+
this.remoteResolvedRows.set(null);
|
|
1041
|
+
this.remoteRuntimeState.set('idle');
|
|
1042
|
+
this.remoteTechnicalError.set(null);
|
|
1043
|
+
});
|
|
1044
|
+
effect(() => {
|
|
1045
|
+
const config = this.config();
|
|
1046
|
+
const explicitData = this.data();
|
|
1047
|
+
if (this.viewMode() !== 'table') {
|
|
1048
|
+
return;
|
|
1049
|
+
}
|
|
1050
|
+
if (config.dataSource?.kind !== 'remote' || (explicitData !== null && explicitData !== undefined)) {
|
|
1051
|
+
return;
|
|
1052
|
+
}
|
|
1053
|
+
if (this.remoteRuntimeState() === 'loading' || this.remoteResolvedRows() !== null) {
|
|
1054
|
+
return;
|
|
1055
|
+
}
|
|
1056
|
+
const event = {
|
|
1057
|
+
chartId: config.id,
|
|
1058
|
+
dataSource: config.dataSource,
|
|
1059
|
+
query: config.dataSource.query,
|
|
1060
|
+
};
|
|
1061
|
+
this.queryRequest.emit(event);
|
|
1062
|
+
this.remoteRuntimeState.set('loading');
|
|
1063
|
+
this.remoteResolvedRows.set([]);
|
|
1064
|
+
this.remoteTechnicalError.set(null);
|
|
1065
|
+
this.statsApi.execute(event, config)
|
|
1066
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
1067
|
+
.subscribe({
|
|
1068
|
+
next: (rows) => {
|
|
1069
|
+
this.remoteResolvedRows.set(rows);
|
|
1070
|
+
this.remoteRuntimeState.set('ready');
|
|
1071
|
+
this.remoteTechnicalError.set(null);
|
|
1072
|
+
},
|
|
1073
|
+
error: (error) => {
|
|
1074
|
+
this.remoteResolvedRows.set([]);
|
|
1075
|
+
this.remoteRuntimeState.set('error');
|
|
1076
|
+
this.remoteTechnicalError.set(error instanceof Error && error.message.trim()
|
|
1077
|
+
? error.message
|
|
1078
|
+
: 'praxis.stats request failed');
|
|
1079
|
+
},
|
|
1080
|
+
});
|
|
1081
|
+
});
|
|
1082
|
+
}
|
|
1083
|
+
handleRowClick(event) {
|
|
1084
|
+
const row = event?.row;
|
|
1085
|
+
if (!row) {
|
|
1086
|
+
return;
|
|
1087
|
+
}
|
|
1088
|
+
const config = this.config();
|
|
1089
|
+
const primarySeries = config.series[0];
|
|
1090
|
+
const categoryField = primarySeries?.categoryField || config.axes?.x?.field;
|
|
1091
|
+
const metricField = primarySeries?.metric?.field;
|
|
1092
|
+
this.pointClick.emit({
|
|
1093
|
+
chartId: config.id,
|
|
1094
|
+
seriesId: primarySeries?.id,
|
|
1095
|
+
seriesName: primarySeries?.name,
|
|
1096
|
+
category: categoryField ? stringifyCell(row[categoryField]) : undefined,
|
|
1097
|
+
value: metricField ? row[metricField] : undefined,
|
|
1098
|
+
data: row,
|
|
1099
|
+
});
|
|
1100
|
+
}
|
|
1101
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisChartShowcaseWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1102
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: PraxisChartShowcaseWidgetComponent, isStandalone: true, selector: "praxis-chart-showcase-widget", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, viewMode: { classPropertyName: "viewMode", publicName: "viewMode", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { pointClick: "pointClick", queryRequest: "queryRequest", loadStateChange: "loadStateChange" }, ngImport: i0, template: `
|
|
1103
|
+
@if (viewMode() === 'chart') {
|
|
1104
|
+
<praxis-chart
|
|
1105
|
+
[config]="config()"
|
|
1106
|
+
[data]="data()"
|
|
1107
|
+
(pointClick)="pointClick.emit($event)"
|
|
1108
|
+
(queryRequest)="queryRequest.emit($event)"
|
|
1109
|
+
(loadStateChange)="loadStateChange.emit($event)"
|
|
1110
|
+
></praxis-chart>
|
|
1111
|
+
} @else if (stateMode() === 'loading') {
|
|
1112
|
+
<section class="showcase-state-card">
|
|
1113
|
+
<h4>{{ loadingLabel() }}</h4>
|
|
1114
|
+
</section>
|
|
1115
|
+
} @else if (stateMode() === 'error') {
|
|
1116
|
+
<section class="showcase-state-card showcase-state-card-error">
|
|
1117
|
+
<h4>{{ errorTitle() }}</h4>
|
|
1118
|
+
@if (errorDescription()) {
|
|
1119
|
+
<p>{{ errorDescription() }}</p>
|
|
1120
|
+
}
|
|
1121
|
+
</section>
|
|
1122
|
+
} @else {
|
|
1123
|
+
<praxis-table
|
|
1124
|
+
[config]="tableConfig()"
|
|
1125
|
+
[data]="resolvedRows()"
|
|
1126
|
+
[tableId]="tableId()"
|
|
1127
|
+
[title]="resolvedTitle()"
|
|
1128
|
+
[subtitle]="resolvedSubtitle()"
|
|
1129
|
+
[icon]="'table_view'"
|
|
1130
|
+
(rowClick)="handleRowClick($event)"
|
|
1131
|
+
></praxis-table>
|
|
1132
|
+
}
|
|
1133
|
+
`, isInline: true, styles: [":host{display:block;min-width:0}.showcase-state-card{min-height:240px;display:grid;place-content:center;gap:8px;padding:24px;border-radius:18px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline, #c5c7ce) 72%,transparent);background:linear-gradient(180deg,#fffffff5,#f4f7fbfa);text-align:center}.showcase-state-card h4{margin:0;font-size:1rem;font-weight:600;color:var(--md-sys-color-on-surface, #1a1b20)}.showcase-state-card p{margin:0;color:var(--md-sys-color-on-surface-variant, #5a5d67)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: PraxisChartComponent, selector: "praxis-chart", inputs: ["config", "data"], outputs: ["pointClick", "queryRequest", "loadStateChange"] }, { kind: "component", type: PraxisTable, selector: "praxis-table", inputs: ["config", "resourcePath", "data", "tableId", "componentInstanceId", "title", "subtitle", "icon", "autoDelete", "notifyIfOutdated", "snoozeMs", "autoOpenSettingsOnOutdated", "crudContext", "dslFunctionRegistry", "editModeEnabled", "dense"], outputs: ["rowClick", "rowDoubleClick", "rowExpansionChange", "rowAction", "toolbarAction", "bulkAction", "columnReorder", "columnReorderAttempt", "beforeDelete", "afterDelete", "deleteError", "beforeBulkDelete", "afterBulkDelete", "bulkDeleteError", "schemaStatusChange", "metadataChange", "loadingStateChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1134
|
+
}
|
|
1135
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisChartShowcaseWidgetComponent, decorators: [{
|
|
1136
|
+
type: Component,
|
|
1137
|
+
args: [{ selector: 'praxis-chart-showcase-widget', standalone: true, imports: [CommonModule, PraxisChartComponent, PraxisTable], changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
1138
|
+
@if (viewMode() === 'chart') {
|
|
1139
|
+
<praxis-chart
|
|
1140
|
+
[config]="config()"
|
|
1141
|
+
[data]="data()"
|
|
1142
|
+
(pointClick)="pointClick.emit($event)"
|
|
1143
|
+
(queryRequest)="queryRequest.emit($event)"
|
|
1144
|
+
(loadStateChange)="loadStateChange.emit($event)"
|
|
1145
|
+
></praxis-chart>
|
|
1146
|
+
} @else if (stateMode() === 'loading') {
|
|
1147
|
+
<section class="showcase-state-card">
|
|
1148
|
+
<h4>{{ loadingLabel() }}</h4>
|
|
1149
|
+
</section>
|
|
1150
|
+
} @else if (stateMode() === 'error') {
|
|
1151
|
+
<section class="showcase-state-card showcase-state-card-error">
|
|
1152
|
+
<h4>{{ errorTitle() }}</h4>
|
|
1153
|
+
@if (errorDescription()) {
|
|
1154
|
+
<p>{{ errorDescription() }}</p>
|
|
1155
|
+
}
|
|
1156
|
+
</section>
|
|
1157
|
+
} @else {
|
|
1158
|
+
<praxis-table
|
|
1159
|
+
[config]="tableConfig()"
|
|
1160
|
+
[data]="resolvedRows()"
|
|
1161
|
+
[tableId]="tableId()"
|
|
1162
|
+
[title]="resolvedTitle()"
|
|
1163
|
+
[subtitle]="resolvedSubtitle()"
|
|
1164
|
+
[icon]="'table_view'"
|
|
1165
|
+
(rowClick)="handleRowClick($event)"
|
|
1166
|
+
></praxis-table>
|
|
1167
|
+
}
|
|
1168
|
+
`, styles: [":host{display:block;min-width:0}.showcase-state-card{min-height:240px;display:grid;place-content:center;gap:8px;padding:24px;border-radius:18px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline, #c5c7ce) 72%,transparent);background:linear-gradient(180deg,#fffffff5,#f4f7fbfa);text-align:center}.showcase-state-card h4{margin:0;font-size:1rem;font-weight:600;color:var(--md-sys-color-on-surface, #1a1b20)}.showcase-state-card p{margin:0;color:var(--md-sys-color-on-surface-variant, #5a5d67)}\n"] }]
|
|
1169
|
+
}], ctorParameters: () => [], propDecorators: { config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: true }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], viewMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "viewMode", required: false }] }], pointClick: [{ type: i0.Output, args: ["pointClick"] }], queryRequest: [{ type: i0.Output, args: ["queryRequest"] }], loadStateChange: [{ type: i0.Output, args: ["loadStateChange"] }] } });
|
|
1170
|
+
function resolveShowcaseStateMode(config, remoteRuntimeState = 'idle') {
|
|
1171
|
+
if (config.preferredLoadState === 'loading') {
|
|
1172
|
+
return 'loading';
|
|
1173
|
+
}
|
|
1174
|
+
if (config.preferredLoadState === 'error') {
|
|
1175
|
+
return 'error';
|
|
1176
|
+
}
|
|
1177
|
+
if (config.dataSource?.kind === 'remote' && remoteRuntimeState === 'error') {
|
|
1178
|
+
return 'error';
|
|
1179
|
+
}
|
|
1180
|
+
if (config.dataSource?.kind === 'remote' && remoteRuntimeState !== 'ready') {
|
|
1181
|
+
return 'loading';
|
|
1182
|
+
}
|
|
1183
|
+
return 'ready';
|
|
1184
|
+
}
|
|
1185
|
+
function buildTableConfig(config, rows) {
|
|
1186
|
+
const tableConfig = createDefaultTableConfig();
|
|
1187
|
+
const toolbar = tableConfig.toolbar;
|
|
1188
|
+
const behavior = tableConfig.behavior;
|
|
1189
|
+
const pagination = behavior.pagination;
|
|
1190
|
+
const sorting = behavior.sorting;
|
|
1191
|
+
const filtering = behavior.filtering;
|
|
1192
|
+
const selection = behavior.selection;
|
|
1193
|
+
const localDataMode = behavior.localDataMode;
|
|
1194
|
+
const actions = tableConfig.actions;
|
|
1195
|
+
const rowActions = actions.row;
|
|
1196
|
+
const bulkActions = actions.bulk;
|
|
1197
|
+
const appearance = tableConfig.appearance;
|
|
1198
|
+
const categoryField = config.axes?.x?.field || config.series[0]?.categoryField;
|
|
1199
|
+
const categoryLabel = config.axes?.x?.label || categoryField;
|
|
1200
|
+
const sampleRow = rows[0] || {};
|
|
1201
|
+
const seriesColumns = config.series
|
|
1202
|
+
.map((series) => {
|
|
1203
|
+
const field = series.metric?.field || series.categoryField;
|
|
1204
|
+
if (!field) {
|
|
1205
|
+
return null;
|
|
1206
|
+
}
|
|
1207
|
+
return {
|
|
1208
|
+
field,
|
|
1209
|
+
header: series.name || series.metric?.label || field,
|
|
1210
|
+
};
|
|
1211
|
+
})
|
|
1212
|
+
.filter((column) => column !== null);
|
|
1213
|
+
const fallbackColumns = Object.keys(sampleRow).map((field) => ({
|
|
1214
|
+
field,
|
|
1215
|
+
header: field,
|
|
1216
|
+
}));
|
|
1217
|
+
const categoryColumn = categoryField
|
|
1218
|
+
? [{
|
|
1219
|
+
field: categoryField,
|
|
1220
|
+
header: categoryLabel || categoryField,
|
|
1221
|
+
}]
|
|
1222
|
+
: [];
|
|
1223
|
+
const dedupedColumns = [...categoryColumn, ...seriesColumns, ...fallbackColumns].filter((column, index, source) => source.findIndex((candidate) => candidate.field === column.field) === index);
|
|
1224
|
+
tableConfig.columns = dedupedColumns;
|
|
1225
|
+
toolbar.visible = false;
|
|
1226
|
+
pagination.enabled = rows.length > 10;
|
|
1227
|
+
sorting.enabled = true;
|
|
1228
|
+
filtering.enabled = false;
|
|
1229
|
+
selection.enabled = false;
|
|
1230
|
+
localDataMode.enabled = true;
|
|
1231
|
+
rowActions.enabled = false;
|
|
1232
|
+
rowActions.actions = [];
|
|
1233
|
+
bulkActions.enabled = false;
|
|
1234
|
+
bulkActions.actions = [];
|
|
1235
|
+
appearance.density = 'comfortable';
|
|
1236
|
+
return tableConfig;
|
|
1237
|
+
}
|
|
1238
|
+
function resolveText(value) {
|
|
1239
|
+
if (typeof value === 'string') {
|
|
1240
|
+
return value;
|
|
1241
|
+
}
|
|
1242
|
+
if (value && typeof value === 'object' && 'text' in value) {
|
|
1243
|
+
const text = value.text;
|
|
1244
|
+
return typeof text === 'string' ? text : '';
|
|
1245
|
+
}
|
|
1246
|
+
return '';
|
|
1247
|
+
}
|
|
1248
|
+
function stringifyCell(value) {
|
|
1249
|
+
if (value === null || value === undefined) {
|
|
1250
|
+
return undefined;
|
|
1251
|
+
}
|
|
1252
|
+
return String(value);
|
|
1253
|
+
}
|
|
1254
|
+
function buildRemoteSignature(config) {
|
|
1255
|
+
if (config.dataSource?.kind !== 'remote') {
|
|
1256
|
+
return null;
|
|
1257
|
+
}
|
|
1258
|
+
return JSON.stringify({
|
|
1259
|
+
id: config.id,
|
|
1260
|
+
resourcePath: config.dataSource.resourcePath,
|
|
1261
|
+
query: config.dataSource.query,
|
|
1262
|
+
});
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
const PRAXIS_CHART_SHOWCASE_WIDGET_METADATA = {
|
|
1266
|
+
id: 'praxis-chart-showcase-widget',
|
|
1267
|
+
componentType: 'praxis-chart-showcase-widget',
|
|
1268
|
+
displayName: 'Praxis Chart Showcase Widget',
|
|
1269
|
+
selector: 'praxis-chart-showcase-widget',
|
|
1270
|
+
component: PraxisChartShowcaseWidgetComponent,
|
|
1271
|
+
friendlyName: 'Praxis Chart Showcase Widget',
|
|
1272
|
+
description: 'Showcase-only widget that toggles between Praxis chart and Praxis table using the same dataset.',
|
|
1273
|
+
icon: 'swap_horiz',
|
|
1274
|
+
tags: ['chart', 'table', 'showcase', 'analytics'],
|
|
1275
|
+
lib: '@praxisui/charts',
|
|
1276
|
+
inputs: [
|
|
1277
|
+
{
|
|
1278
|
+
name: 'config',
|
|
1279
|
+
type: 'PraxisChartConfig',
|
|
1280
|
+
description: 'Chart configuration preserved across chart and table views.',
|
|
1281
|
+
},
|
|
1282
|
+
{
|
|
1283
|
+
name: 'data',
|
|
1284
|
+
type: 'PraxisChartDataRow[]',
|
|
1285
|
+
description: 'Local dataset shared by the chart and table renderers.',
|
|
1286
|
+
},
|
|
1287
|
+
{
|
|
1288
|
+
name: 'viewMode',
|
|
1289
|
+
type: "'chart' | 'table'",
|
|
1290
|
+
description: 'Current showcase view mode.',
|
|
1291
|
+
},
|
|
1292
|
+
],
|
|
1293
|
+
outputs: [
|
|
1294
|
+
{
|
|
1295
|
+
name: 'pointClick',
|
|
1296
|
+
type: 'PraxisChartPointEvent',
|
|
1297
|
+
description: 'Forwards point selection from the chart or mapped row selection from the table.',
|
|
1298
|
+
},
|
|
1299
|
+
{
|
|
1300
|
+
name: 'queryRequest',
|
|
1301
|
+
type: 'PraxisChartQueryRequestEvent',
|
|
1302
|
+
description: 'Forwards remote chart data requests.',
|
|
1303
|
+
},
|
|
1304
|
+
{
|
|
1305
|
+
name: 'loadStateChange',
|
|
1306
|
+
type: 'PraxisChartLoadState',
|
|
1307
|
+
description: 'Forwards chart runtime state transitions.',
|
|
1308
|
+
},
|
|
1309
|
+
],
|
|
1310
|
+
};
|
|
1311
|
+
function providePraxisChartShowcaseWidgetMetadata() {
|
|
1312
|
+
return {
|
|
1313
|
+
provide: ENVIRONMENT_INITIALIZER,
|
|
1314
|
+
multi: true,
|
|
1315
|
+
useFactory: (registry) => () => {
|
|
1316
|
+
registry.register(PRAXIS_CHART_SHOWCASE_WIDGET_METADATA);
|
|
1317
|
+
},
|
|
1318
|
+
deps: [ComponentMetadataRegistry],
|
|
1319
|
+
};
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
function providePraxisCharts() {
|
|
1323
|
+
return [
|
|
1324
|
+
EChartsEngineAdapter,
|
|
1325
|
+
{
|
|
1326
|
+
provide: PRAXIS_CHART_ENGINE,
|
|
1327
|
+
useExisting: EChartsEngineAdapter,
|
|
1328
|
+
},
|
|
1329
|
+
providePraxisChartsMetadata(),
|
|
1330
|
+
providePraxisChartDrilldownPanelMetadata(),
|
|
1331
|
+
providePraxisChartStateProbeMetadata(),
|
|
1332
|
+
providePraxisChartShowcaseWidgetMetadata(),
|
|
1333
|
+
];
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
class PraxisChartCanonicalContractMapperService {
|
|
1337
|
+
toPraxisChartConfig(contract) {
|
|
1338
|
+
this.assertSupportedContract(contract);
|
|
1339
|
+
return {
|
|
1340
|
+
id: contract.chartId,
|
|
1341
|
+
type: contract.kind,
|
|
1342
|
+
title: this.mapTextValue(contract.title),
|
|
1343
|
+
subtitle: this.mapTextValue(contract.subtitle),
|
|
1344
|
+
height: contract.height,
|
|
1345
|
+
axes: this.buildAxes(contract),
|
|
1346
|
+
series: this.buildSeries(contract),
|
|
1347
|
+
dataSource: this.buildDataSource(contract),
|
|
1348
|
+
interactions: this.buildInteractions(contract),
|
|
1349
|
+
theme: this.buildTheme(contract),
|
|
1350
|
+
emptyState: contract.state?.empty
|
|
1351
|
+
? {
|
|
1352
|
+
title: this.mapTextValue(contract.state.empty.title),
|
|
1353
|
+
description: this.mapTextValue(contract.state.empty.description),
|
|
1354
|
+
}
|
|
1355
|
+
: undefined,
|
|
1356
|
+
state: {
|
|
1357
|
+
loadingLabel: this.mapTextValue(contract.state?.loading?.title),
|
|
1358
|
+
error: contract.state?.error
|
|
1359
|
+
? {
|
|
1360
|
+
title: this.mapTextValue(contract.state.error.title),
|
|
1361
|
+
description: this.mapTextValue(contract.state.error.description),
|
|
1362
|
+
}
|
|
1363
|
+
: undefined,
|
|
1364
|
+
},
|
|
1365
|
+
};
|
|
1366
|
+
}
|
|
1367
|
+
assertSupportedContract(contract) {
|
|
1368
|
+
if (contract.source.kind === 'derived') {
|
|
1369
|
+
throw new Error('x-ui.chart source.kind="derived" is not yet implemented in @praxisui/charts.');
|
|
1370
|
+
}
|
|
1371
|
+
if (contract.source.kind !== 'praxis.stats') {
|
|
1372
|
+
throw new Error(`x-ui.chart source.kind="${contract.source.kind}" is not supported in @praxisui/charts.`);
|
|
1373
|
+
}
|
|
1374
|
+
if (!contract.source.resource?.trim()) {
|
|
1375
|
+
throw new Error('x-ui.chart source.resource is required for source.kind="praxis.stats".');
|
|
1376
|
+
}
|
|
1377
|
+
if (!contract.source.operation) {
|
|
1378
|
+
throw new Error('x-ui.chart source.operation is required for source.kind="praxis.stats".');
|
|
1379
|
+
}
|
|
1380
|
+
if (contract.theme?.palette && typeof contract.theme.palette === 'string') {
|
|
1381
|
+
throw new Error('x-ui.chart theme.palette as palette token reference is not yet implemented in @praxisui/charts.');
|
|
1382
|
+
}
|
|
1383
|
+
if (contract.theme?.variant) {
|
|
1384
|
+
throw new Error('x-ui.chart theme.variant is not yet implemented in @praxisui/charts.');
|
|
1385
|
+
}
|
|
1386
|
+
const aggregations = [
|
|
1387
|
+
...(contract.metrics?.map((metric) => metric.aggregation).filter(Boolean) ?? []),
|
|
1388
|
+
...(contract.aggregations?.map((aggregation) => aggregation.operation) ?? []),
|
|
1389
|
+
];
|
|
1390
|
+
if (aggregations.includes('distinct-count')) {
|
|
1391
|
+
throw new Error('x-ui.chart aggregation "distinct-count" is not yet implemented in @praxisui/charts.');
|
|
1392
|
+
}
|
|
1393
|
+
if (!contract.metrics?.length) {
|
|
1394
|
+
throw new Error('x-ui.chart requires at least one metric for the current @praxisui/charts runtime.');
|
|
1395
|
+
}
|
|
1396
|
+
if (contract.kind !== 'pie' && contract.kind !== 'donut' && !contract.dimensions?.length) {
|
|
1397
|
+
throw new Error('x-ui.chart cartesian charts require at least one dimension in the current @praxisui/charts runtime.');
|
|
1398
|
+
}
|
|
1399
|
+
if ((contract.kind === 'pie' || contract.kind === 'donut') && !contract.dimensions?.[0]?.field) {
|
|
1400
|
+
throw new Error('x-ui.chart pie/donut charts require a first dimension for category mapping.');
|
|
1401
|
+
}
|
|
1402
|
+
if (contract.metrics.length > 1 && (contract.kind === 'pie' || contract.kind === 'donut')) {
|
|
1403
|
+
throw new Error('x-ui.chart pie/donut charts with multiple metrics are not yet implemented in @praxisui/charts.');
|
|
1404
|
+
}
|
|
1405
|
+
if (contract.source.kind === 'praxis.stats' && contract.metrics.length > 1) {
|
|
1406
|
+
throw new Error('x-ui.chart praxis.stats currently supports a single metric per chart in @praxisui/charts.');
|
|
1407
|
+
}
|
|
1408
|
+
if (contract.events?.selectionChange || contract.events?.crossFilter) {
|
|
1409
|
+
throw new Error('x-ui.chart selectionChange/crossFilter declarative runtime actions are not yet implemented in @praxisui/charts.');
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
buildAxes(contract) {
|
|
1413
|
+
const firstDimension = contract.dimensions?.[0];
|
|
1414
|
+
const firstMetric = contract.metrics?.[0];
|
|
1415
|
+
if (contract.kind === 'pie' || contract.kind === 'donut') {
|
|
1416
|
+
return {
|
|
1417
|
+
x: {
|
|
1418
|
+
field: firstDimension?.field,
|
|
1419
|
+
label: this.toLabel(firstDimension?.label),
|
|
1420
|
+
},
|
|
1421
|
+
};
|
|
1422
|
+
}
|
|
1423
|
+
return {
|
|
1424
|
+
x: {
|
|
1425
|
+
field: firstDimension?.field,
|
|
1426
|
+
label: this.toLabel(firstDimension?.label),
|
|
1427
|
+
type: firstDimension?.role === 'time' ? 'time' : 'category',
|
|
1428
|
+
},
|
|
1429
|
+
y: {
|
|
1430
|
+
label: this.toLabel(firstMetric?.label),
|
|
1431
|
+
type: 'value',
|
|
1432
|
+
},
|
|
1433
|
+
};
|
|
1434
|
+
}
|
|
1435
|
+
buildSeries(contract) {
|
|
1436
|
+
const firstDimension = contract.dimensions?.[0];
|
|
1437
|
+
const labelsVisible = this.resolveToggle(contract.labels);
|
|
1438
|
+
return (contract.metrics ?? []).map((metric, index) => ({
|
|
1439
|
+
id: `${metric.field}-${index + 1}`,
|
|
1440
|
+
name: this.toLabel(metric.label) ?? metric.field,
|
|
1441
|
+
type: contract.kind === 'stacked-bar' ? 'bar' : contract.kind,
|
|
1442
|
+
categoryField: contract.kind === 'pie' || contract.kind === 'donut' ? firstDimension?.field : undefined,
|
|
1443
|
+
metric: {
|
|
1444
|
+
field: metric.field,
|
|
1445
|
+
aggregation: this.mapAggregation(metric.aggregation),
|
|
1446
|
+
label: this.toLabel(metric.label),
|
|
1447
|
+
},
|
|
1448
|
+
stackId: contract.kind === 'stacked-bar' ? 'stack-1' : undefined,
|
|
1449
|
+
labels: labelsVisible ? { visible: true } : undefined,
|
|
1450
|
+
smooth: contract.kind === 'line' || contract.kind === 'area' ? true : undefined,
|
|
1451
|
+
}));
|
|
1452
|
+
}
|
|
1453
|
+
buildDataSource(contract) {
|
|
1454
|
+
return {
|
|
1455
|
+
kind: 'remote',
|
|
1456
|
+
resourcePath: contract.source.resource,
|
|
1457
|
+
schemaId: contract.source.resource,
|
|
1458
|
+
query: this.buildQuery(contract),
|
|
1459
|
+
};
|
|
1460
|
+
}
|
|
1461
|
+
buildQuery(contract) {
|
|
1462
|
+
const filtersFromContract = this.toQueryFilterMap(contract.filters);
|
|
1463
|
+
const combinedFilters = {
|
|
1464
|
+
...filtersFromContract,
|
|
1465
|
+
};
|
|
1466
|
+
return {
|
|
1467
|
+
sourceKind: 'praxis.stats',
|
|
1468
|
+
statsOperation: contract.source.operation,
|
|
1469
|
+
granularity: contract.source.options?.granularity,
|
|
1470
|
+
fillGaps: contract.source.options?.fillGaps,
|
|
1471
|
+
distributionMode: contract.source.options?.mode,
|
|
1472
|
+
bucketSize: contract.source.options?.bucketSize,
|
|
1473
|
+
bucketCount: contract.source.options?.bucketCount,
|
|
1474
|
+
statsOrderBy: this.mapStatsOrderBy(contract.source.options?.orderBy),
|
|
1475
|
+
statsPath: this.buildStatsPath(contract),
|
|
1476
|
+
statsRequest: this.buildStatsRequest(contract, combinedFilters),
|
|
1477
|
+
dimensions: contract.dimensions?.map((dimension) => dimension.field),
|
|
1478
|
+
metrics: contract.metrics?.map((metric) => ({
|
|
1479
|
+
field: metric.field,
|
|
1480
|
+
aggregation: this.mapAggregation(metric.aggregation),
|
|
1481
|
+
alias: metric.field,
|
|
1482
|
+
})),
|
|
1483
|
+
filters: Object.keys(combinedFilters).length ? combinedFilters : undefined,
|
|
1484
|
+
sort: contract.sort?.map((item) => `${item.field}:${item.direction}`),
|
|
1485
|
+
limit: contract.limit ?? contract.source.options?.limit,
|
|
1486
|
+
};
|
|
1487
|
+
}
|
|
1488
|
+
buildInteractions(contract) {
|
|
1489
|
+
return {
|
|
1490
|
+
pointClick: Boolean(contract.events?.pointClick || contract.events?.drillDown),
|
|
1491
|
+
selection: Boolean(contract.events?.selectionChange),
|
|
1492
|
+
drillDown: Boolean(contract.events?.drillDown),
|
|
1493
|
+
};
|
|
1494
|
+
}
|
|
1495
|
+
buildTheme(contract) {
|
|
1496
|
+
return {
|
|
1497
|
+
palette: Array.isArray(contract.theme?.palette) ? contract.theme.palette : undefined,
|
|
1498
|
+
legend: { visible: this.resolveToggle(contract.legend, true) },
|
|
1499
|
+
tooltip: {
|
|
1500
|
+
enabled: this.resolveToggle(contract.tooltip, true),
|
|
1501
|
+
trigger: contract.kind === 'pie' || contract.kind === 'donut' ? 'item' : 'axis',
|
|
1502
|
+
},
|
|
1503
|
+
};
|
|
1504
|
+
}
|
|
1505
|
+
mapTextValue(value) {
|
|
1506
|
+
if (!value)
|
|
1507
|
+
return undefined;
|
|
1508
|
+
if (typeof value === 'string')
|
|
1509
|
+
return value;
|
|
1510
|
+
return {
|
|
1511
|
+
key: value.key,
|
|
1512
|
+
text: value.fallback,
|
|
1513
|
+
};
|
|
1514
|
+
}
|
|
1515
|
+
toLabel(value) {
|
|
1516
|
+
if (!value)
|
|
1517
|
+
return undefined;
|
|
1518
|
+
if (typeof value === 'string')
|
|
1519
|
+
return value;
|
|
1520
|
+
return value.fallback || value.key;
|
|
1521
|
+
}
|
|
1522
|
+
resolveToggle(value, defaultValue = false) {
|
|
1523
|
+
if (typeof value === 'boolean')
|
|
1524
|
+
return value;
|
|
1525
|
+
if (typeof value === 'object' && value)
|
|
1526
|
+
return value.enabled;
|
|
1527
|
+
return defaultValue;
|
|
1528
|
+
}
|
|
1529
|
+
mapAggregation(aggregation) {
|
|
1530
|
+
switch (aggregation) {
|
|
1531
|
+
case 'avg':
|
|
1532
|
+
case 'min':
|
|
1533
|
+
case 'max':
|
|
1534
|
+
case 'count':
|
|
1535
|
+
case 'sum':
|
|
1536
|
+
return aggregation;
|
|
1537
|
+
case undefined:
|
|
1538
|
+
return undefined;
|
|
1539
|
+
default:
|
|
1540
|
+
throw new Error(`x-ui.chart aggregation "${aggregation}" is not yet implemented in @praxisui/charts.`);
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
toQueryFilterMap(filters) {
|
|
1544
|
+
if (!filters?.length)
|
|
1545
|
+
return undefined;
|
|
1546
|
+
return filters.reduce((acc, filter) => {
|
|
1547
|
+
if (filter.value !== undefined) {
|
|
1548
|
+
acc[filter.field] = filter.value;
|
|
1549
|
+
return acc;
|
|
1550
|
+
}
|
|
1551
|
+
if (filter.values !== undefined) {
|
|
1552
|
+
acc[filter.field] = filter.values;
|
|
1553
|
+
}
|
|
1554
|
+
return acc;
|
|
1555
|
+
}, {});
|
|
1556
|
+
}
|
|
1557
|
+
buildStatsPath(contract) {
|
|
1558
|
+
return `${contract.source.resource.replace(/\/+$/, '')}/stats/${contract.source.operation}`;
|
|
1559
|
+
}
|
|
1560
|
+
buildStatsRequest(contract, filters) {
|
|
1561
|
+
const field = this.resolveStatsField(contract);
|
|
1562
|
+
const metric = this.buildStatsMetricRequest(contract);
|
|
1563
|
+
const limit = contract.limit ?? contract.source.options?.limit;
|
|
1564
|
+
const orderBy = this.mapStatsOrderByToBackend(contract.source.options?.orderBy);
|
|
1565
|
+
switch (contract.source.operation) {
|
|
1566
|
+
case 'group-by':
|
|
1567
|
+
return {
|
|
1568
|
+
filter: filters,
|
|
1569
|
+
field,
|
|
1570
|
+
metric,
|
|
1571
|
+
limit,
|
|
1572
|
+
orderBy,
|
|
1573
|
+
};
|
|
1574
|
+
case 'timeseries':
|
|
1575
|
+
return {
|
|
1576
|
+
filter: filters,
|
|
1577
|
+
field,
|
|
1578
|
+
granularity: this.mapStatsGranularityToBackend(contract.source.options?.granularity),
|
|
1579
|
+
metric,
|
|
1580
|
+
fillGaps: contract.source.options?.fillGaps,
|
|
1581
|
+
};
|
|
1582
|
+
case 'distribution':
|
|
1583
|
+
return {
|
|
1584
|
+
filter: filters,
|
|
1585
|
+
field,
|
|
1586
|
+
mode: this.mapDistributionModeToBackend(contract.source.options?.mode),
|
|
1587
|
+
metric,
|
|
1588
|
+
bucketSize: contract.source.options?.bucketSize,
|
|
1589
|
+
bucketCount: contract.source.options?.bucketCount,
|
|
1590
|
+
limit,
|
|
1591
|
+
orderBy,
|
|
1592
|
+
};
|
|
1593
|
+
default:
|
|
1594
|
+
throw new Error(`x-ui.chart source.operation "${contract.source.operation}" is not supported in @praxisui/charts.`);
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
resolveStatsField(contract) {
|
|
1598
|
+
const dimensionField = contract.dimensions?.[0]?.field?.trim();
|
|
1599
|
+
if (dimensionField) {
|
|
1600
|
+
return dimensionField;
|
|
1601
|
+
}
|
|
1602
|
+
throw new Error('x-ui.chart requires dimensions[0].field to derive the canonical praxis.stats request.');
|
|
1603
|
+
}
|
|
1604
|
+
buildStatsMetricRequest(contract) {
|
|
1605
|
+
const metric = contract.metrics?.[0];
|
|
1606
|
+
if (!metric) {
|
|
1607
|
+
throw new Error('x-ui.chart requires metrics[0] to derive the canonical praxis.stats request.');
|
|
1608
|
+
}
|
|
1609
|
+
const operation = this.mapStatsMetricOperation(metric.aggregation);
|
|
1610
|
+
return {
|
|
1611
|
+
operation,
|
|
1612
|
+
field: operation === 'COUNT' ? undefined : metric.field,
|
|
1613
|
+
};
|
|
1614
|
+
}
|
|
1615
|
+
mapStatsOrderBy(value) {
|
|
1616
|
+
if (!value)
|
|
1617
|
+
return undefined;
|
|
1618
|
+
switch (value) {
|
|
1619
|
+
case 'key-asc':
|
|
1620
|
+
case 'key-desc':
|
|
1621
|
+
case 'value-asc':
|
|
1622
|
+
case 'value-desc':
|
|
1623
|
+
return value;
|
|
1624
|
+
default:
|
|
1625
|
+
throw new Error(`x-ui.chart source.options.orderBy "${value}" is not supported in @praxisui/charts.`);
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
mapStatsOrderByToBackend(value) {
|
|
1629
|
+
switch (value) {
|
|
1630
|
+
case 'key-asc':
|
|
1631
|
+
return 'KEY_ASC';
|
|
1632
|
+
case 'key-desc':
|
|
1633
|
+
return 'KEY_DESC';
|
|
1634
|
+
case 'value-asc':
|
|
1635
|
+
return 'VALUE_ASC';
|
|
1636
|
+
case 'value-desc':
|
|
1637
|
+
return 'VALUE_DESC';
|
|
1638
|
+
case undefined:
|
|
1639
|
+
return undefined;
|
|
1640
|
+
default:
|
|
1641
|
+
throw new Error(`x-ui.chart source.options.orderBy "${value}" is not supported in @praxisui/charts.`);
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
mapStatsGranularityToBackend(value) {
|
|
1645
|
+
switch (value) {
|
|
1646
|
+
case 'hour':
|
|
1647
|
+
return 'HOUR';
|
|
1648
|
+
case 'day':
|
|
1649
|
+
return 'DAY';
|
|
1650
|
+
case 'week':
|
|
1651
|
+
return 'WEEK';
|
|
1652
|
+
case 'month':
|
|
1653
|
+
case undefined:
|
|
1654
|
+
return 'MONTH';
|
|
1655
|
+
case 'quarter':
|
|
1656
|
+
return 'QUARTER';
|
|
1657
|
+
case 'year':
|
|
1658
|
+
return 'YEAR';
|
|
1659
|
+
default:
|
|
1660
|
+
throw new Error(`x-ui.chart source.options.granularity "${value}" is not supported in @praxisui/charts.`);
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
mapDistributionModeToBackend(value) {
|
|
1664
|
+
switch (value) {
|
|
1665
|
+
case 'histogram':
|
|
1666
|
+
return 'HISTOGRAM';
|
|
1667
|
+
case 'terms':
|
|
1668
|
+
case undefined:
|
|
1669
|
+
return 'TERMS';
|
|
1670
|
+
default:
|
|
1671
|
+
throw new Error(`x-ui.chart source.options.mode "${value}" is not supported in @praxisui/charts.`);
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
mapStatsMetricOperation(aggregation) {
|
|
1675
|
+
switch (aggregation) {
|
|
1676
|
+
case undefined:
|
|
1677
|
+
case 'count':
|
|
1678
|
+
return 'COUNT';
|
|
1679
|
+
case 'sum':
|
|
1680
|
+
return 'SUM';
|
|
1681
|
+
case 'avg':
|
|
1682
|
+
return 'AVG';
|
|
1683
|
+
case 'min':
|
|
1684
|
+
return 'MIN';
|
|
1685
|
+
case 'max':
|
|
1686
|
+
return 'MAX';
|
|
1687
|
+
default:
|
|
1688
|
+
throw new Error(`x-ui.chart aggregation "${aggregation}" is not yet implemented in @praxisui/charts.`);
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisChartCanonicalContractMapperService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1692
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisChartCanonicalContractMapperService, providedIn: 'root' });
|
|
1693
|
+
}
|
|
1694
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisChartCanonicalContractMapperService, decorators: [{
|
|
1695
|
+
type: Injectable,
|
|
1696
|
+
args: [{ providedIn: 'root' }]
|
|
1697
|
+
}] });
|
|
1698
|
+
|
|
1699
|
+
class PraxisChartMetadataRegistrationService {
|
|
1700
|
+
registry;
|
|
1701
|
+
registered = false;
|
|
1702
|
+
constructor(registry) {
|
|
1703
|
+
this.registry = registry;
|
|
1704
|
+
this.ensureRegistered();
|
|
1705
|
+
}
|
|
1706
|
+
ensureRegistered() {
|
|
1707
|
+
if (this.registered) {
|
|
1708
|
+
return;
|
|
1709
|
+
}
|
|
1710
|
+
this.registry.register(PRAXIS_CHART_COMPONENT_METADATA);
|
|
1711
|
+
this.registry.register(PRAXIS_CHART_DRILLDOWN_PANEL_METADATA);
|
|
1712
|
+
this.registry.register(PRAXIS_CHART_STATE_PROBE_COMPONENT_METADATA);
|
|
1713
|
+
this.registry.register(PRAXIS_CHART_SHOWCASE_WIDGET_METADATA);
|
|
1714
|
+
this.registered = true;
|
|
1715
|
+
}
|
|
1716
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisChartMetadataRegistrationService, deps: [{ token: i1$1.ComponentMetadataRegistry }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1717
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisChartMetadataRegistrationService, providedIn: 'root' });
|
|
1718
|
+
}
|
|
1719
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisChartMetadataRegistrationService, decorators: [{
|
|
1720
|
+
type: Injectable,
|
|
1721
|
+
args: [{ providedIn: 'root' }]
|
|
1722
|
+
}], ctorParameters: () => [{ type: i1$1.ComponentMetadataRegistry }] });
|
|
1723
|
+
|
|
1724
|
+
class PraxisChartSchemaMapperService {
|
|
1725
|
+
metadataRegistration;
|
|
1726
|
+
canonicalContractMapper;
|
|
1727
|
+
constructor(metadataRegistration, canonicalContractMapper) {
|
|
1728
|
+
this.metadataRegistration = metadataRegistration;
|
|
1729
|
+
this.canonicalContractMapper = canonicalContractMapper;
|
|
1730
|
+
this.metadataRegistration.ensureRegistered();
|
|
1731
|
+
}
|
|
1732
|
+
resolve(input, defaults) {
|
|
1733
|
+
const schema = this.normalize(input);
|
|
1734
|
+
const config = this.toConfig(schema);
|
|
1735
|
+
const key = schema.key?.trim()
|
|
1736
|
+
|| defaults?.key?.trim()
|
|
1737
|
+
|| schema.chart?.chartId?.trim()
|
|
1738
|
+
|| config.id?.trim()
|
|
1739
|
+
|| 'praxis-chart';
|
|
1740
|
+
return {
|
|
1741
|
+
componentId: 'praxis-chart',
|
|
1742
|
+
key,
|
|
1743
|
+
className: schema.className,
|
|
1744
|
+
shell: schema.shell,
|
|
1745
|
+
layout: schema.layout ?? defaults?.layout,
|
|
1746
|
+
definition: this.toWidgetDefinition(schema),
|
|
1747
|
+
};
|
|
1748
|
+
}
|
|
1749
|
+
toWidgetDefinition(input) {
|
|
1750
|
+
const schema = this.normalize(input);
|
|
1751
|
+
return {
|
|
1752
|
+
id: 'praxis-chart',
|
|
1753
|
+
inputs: {
|
|
1754
|
+
config: this.toConfig(schema),
|
|
1755
|
+
data: schema.data ?? null,
|
|
1756
|
+
},
|
|
1757
|
+
outputs: this.toOutputs(schema),
|
|
1758
|
+
bindingOrder: schema.bindingOrder ?? ['config', 'data'],
|
|
1759
|
+
};
|
|
1760
|
+
}
|
|
1761
|
+
toWidgetInstance(input, defaults) {
|
|
1762
|
+
const resolved = this.resolve(input, defaults);
|
|
1763
|
+
return {
|
|
1764
|
+
key: resolved.key,
|
|
1765
|
+
definition: resolved.definition,
|
|
1766
|
+
className: resolved.className,
|
|
1767
|
+
shell: resolved.shell,
|
|
1768
|
+
};
|
|
1769
|
+
}
|
|
1770
|
+
toGridWidgetInstance(input, defaults) {
|
|
1771
|
+
const resolved = this.resolve(input, defaults);
|
|
1772
|
+
return {
|
|
1773
|
+
key: resolved.key,
|
|
1774
|
+
definition: resolved.definition,
|
|
1775
|
+
layout: resolved.layout ?? defaults.layout,
|
|
1776
|
+
className: resolved.className,
|
|
1777
|
+
shell: resolved.shell,
|
|
1778
|
+
};
|
|
1779
|
+
}
|
|
1780
|
+
normalize(input) {
|
|
1781
|
+
if (this.isCanonicalChartContract(input)) {
|
|
1782
|
+
return {
|
|
1783
|
+
kind: 'x-ui.chart',
|
|
1784
|
+
componentId: 'praxis-chart',
|
|
1785
|
+
chart: input,
|
|
1786
|
+
};
|
|
1787
|
+
}
|
|
1788
|
+
if (this.isWidgetSchema(input)) {
|
|
1789
|
+
return {
|
|
1790
|
+
kind: input.kind ?? 'x-ui.chart',
|
|
1791
|
+
componentId: 'praxis-chart',
|
|
1792
|
+
...input,
|
|
1793
|
+
};
|
|
1794
|
+
}
|
|
1795
|
+
throw new Error('PraxisChartSchemaMapperService requires x-ui.chart as the primary public input.');
|
|
1796
|
+
}
|
|
1797
|
+
isWidgetSchema(input) {
|
|
1798
|
+
return typeof input === 'object'
|
|
1799
|
+
&& input !== null
|
|
1800
|
+
&& ('chart' in input
|
|
1801
|
+
|| 'componentId' in input
|
|
1802
|
+
|| 'key' in input
|
|
1803
|
+
|| 'className' in input
|
|
1804
|
+
|| 'outputs' in input
|
|
1805
|
+
|| 'bindingOrder' in input
|
|
1806
|
+
|| 'shell' in input
|
|
1807
|
+
|| 'layout' in input
|
|
1808
|
+
|| 'data' in input);
|
|
1809
|
+
}
|
|
1810
|
+
isCanonicalChartContract(input) {
|
|
1811
|
+
return typeof input === 'object'
|
|
1812
|
+
&& input !== null
|
|
1813
|
+
&& 'version' in input
|
|
1814
|
+
&& 'kind' in input
|
|
1815
|
+
&& 'source' in input;
|
|
1816
|
+
}
|
|
1817
|
+
toConfig(schema) {
|
|
1818
|
+
if (!schema.chart) {
|
|
1819
|
+
throw new Error('PraxisChartWidgetSchema requires widget.chart as the canonical public input.');
|
|
1820
|
+
}
|
|
1821
|
+
return this.canonicalContractMapper.toPraxisChartConfig(schema.chart);
|
|
1822
|
+
}
|
|
1823
|
+
toOutputs(schema) {
|
|
1824
|
+
if (schema.outputs) {
|
|
1825
|
+
return schema.outputs;
|
|
1826
|
+
}
|
|
1827
|
+
if (schema.chart?.events?.pointClick || schema.chart?.events?.drillDown) {
|
|
1828
|
+
return {
|
|
1829
|
+
pointClick: 'emit',
|
|
1830
|
+
};
|
|
1831
|
+
}
|
|
1832
|
+
return undefined;
|
|
1833
|
+
}
|
|
1834
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisChartSchemaMapperService, deps: [{ token: PraxisChartMetadataRegistrationService }, { token: PraxisChartCanonicalContractMapperService }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1835
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisChartSchemaMapperService, providedIn: 'root' });
|
|
1836
|
+
}
|
|
1837
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisChartSchemaMapperService, decorators: [{
|
|
1838
|
+
type: Injectable,
|
|
1839
|
+
args: [{ providedIn: 'root' }]
|
|
1840
|
+
}], ctorParameters: () => [{ type: PraxisChartMetadataRegistrationService }, { type: PraxisChartCanonicalContractMapperService }] });
|
|
1841
|
+
|
|
1842
|
+
class PraxisChartBackendPayloadAdapterService {
|
|
1843
|
+
chartSchemaMapper;
|
|
1844
|
+
canonicalContractMapper;
|
|
1845
|
+
constructor(chartSchemaMapper, canonicalContractMapper) {
|
|
1846
|
+
this.chartSchemaMapper = chartSchemaMapper;
|
|
1847
|
+
this.canonicalContractMapper = canonicalContractMapper;
|
|
1848
|
+
}
|
|
1849
|
+
toChartConfig(payload) {
|
|
1850
|
+
if (!payload.widget.chart) {
|
|
1851
|
+
throw new Error('PraxisChartBackendPayload requires widget.chart as the canonical public input.');
|
|
1852
|
+
}
|
|
1853
|
+
const baseConfig = this.canonicalContractMapper.toPraxisChartConfig(payload.widget.chart);
|
|
1854
|
+
return {
|
|
1855
|
+
...baseConfig,
|
|
1856
|
+
preferredLoadState: payload.widget.runtime?.preferredLoadState ?? baseConfig.preferredLoadState,
|
|
1857
|
+
};
|
|
1858
|
+
}
|
|
1859
|
+
toWidgetInstance(payload) {
|
|
1860
|
+
const widget = this.chartSchemaMapper.toWidgetInstance({
|
|
1861
|
+
kind: payload.widget.kind ?? 'x-ui.chart',
|
|
1862
|
+
key: payload.widget.key,
|
|
1863
|
+
className: payload.widget.className,
|
|
1864
|
+
shell: payload.widget.shell,
|
|
1865
|
+
bindingOrder: payload.widget.bindingOrder,
|
|
1866
|
+
outputs: payload.widget.outputs,
|
|
1867
|
+
chart: payload.widget.chart,
|
|
1868
|
+
data: payload.data,
|
|
1869
|
+
});
|
|
1870
|
+
return {
|
|
1871
|
+
...widget,
|
|
1872
|
+
definition: {
|
|
1873
|
+
...widget.definition,
|
|
1874
|
+
inputs: {
|
|
1875
|
+
...(widget.definition.inputs || {}),
|
|
1876
|
+
config: this.toChartConfig(payload),
|
|
1877
|
+
},
|
|
1878
|
+
},
|
|
1879
|
+
};
|
|
1880
|
+
}
|
|
1881
|
+
toGridWidgetInstance(payload) {
|
|
1882
|
+
const layout = payload.widget.layout ?? { col: 1, row: 1, colSpan: 6, rowSpan: 4 };
|
|
1883
|
+
const widget = this.chartSchemaMapper.toGridWidgetInstance({
|
|
1884
|
+
kind: payload.widget.kind ?? 'x-ui.chart',
|
|
1885
|
+
key: payload.widget.key,
|
|
1886
|
+
className: payload.widget.className,
|
|
1887
|
+
shell: payload.widget.shell,
|
|
1888
|
+
bindingOrder: payload.widget.bindingOrder,
|
|
1889
|
+
outputs: payload.widget.outputs,
|
|
1890
|
+
layout,
|
|
1891
|
+
chart: payload.widget.chart,
|
|
1892
|
+
data: payload.data,
|
|
1893
|
+
}, {
|
|
1894
|
+
key: payload.widget.key,
|
|
1895
|
+
layout,
|
|
1896
|
+
});
|
|
1897
|
+
return {
|
|
1898
|
+
...widget,
|
|
1899
|
+
definition: {
|
|
1900
|
+
...widget.definition,
|
|
1901
|
+
inputs: {
|
|
1902
|
+
...(widget.definition.inputs || {}),
|
|
1903
|
+
config: this.toChartConfig(payload),
|
|
1904
|
+
},
|
|
1905
|
+
},
|
|
1906
|
+
};
|
|
1907
|
+
}
|
|
1908
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisChartBackendPayloadAdapterService, deps: [{ token: PraxisChartSchemaMapperService }, { token: PraxisChartCanonicalContractMapperService }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1909
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisChartBackendPayloadAdapterService, providedIn: 'root' });
|
|
1910
|
+
}
|
|
1911
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisChartBackendPayloadAdapterService, decorators: [{
|
|
1912
|
+
type: Injectable,
|
|
1913
|
+
args: [{ providedIn: 'root' }]
|
|
1914
|
+
}], ctorParameters: () => [{ type: PraxisChartSchemaMapperService }, { type: PraxisChartCanonicalContractMapperService }] });
|
|
1915
|
+
|
|
1916
|
+
const PRAXIS_CHART_BACKEND_MOCK_BAR = {
|
|
1917
|
+
schemaMeta: {
|
|
1918
|
+
schemaId: 'api/human-resources/vw-perfil-heroi|post|response|tenant:demo|locale:pt-BR',
|
|
1919
|
+
schemaHash: 'published-hash-hero-universe-v1',
|
|
1920
|
+
resourcePath: 'api/human-resources/vw-perfil-heroi',
|
|
1921
|
+
operation: 'post',
|
|
1922
|
+
schemaType: 'response',
|
|
1923
|
+
source: 'backend',
|
|
1924
|
+
},
|
|
1925
|
+
widget: {
|
|
1926
|
+
kind: 'x-ui.chart',
|
|
1927
|
+
key: 'monthly-revenue-chart',
|
|
1928
|
+
className: 'analytics-widget analytics-widget-revenue',
|
|
1929
|
+
layout: { col: 1, row: 1, colSpan: 6, rowSpan: 4 },
|
|
1930
|
+
shell: {
|
|
1931
|
+
title: 'Heroes por universo',
|
|
1932
|
+
subtitle: 'Group-by real consumindo praxis.stats publicado',
|
|
1933
|
+
},
|
|
1934
|
+
outputs: {
|
|
1935
|
+
pointClick: 'emit',
|
|
1936
|
+
loadStateChange: 'emit',
|
|
1937
|
+
},
|
|
1938
|
+
chart: {
|
|
1939
|
+
version: '0.1.0',
|
|
1940
|
+
kind: 'bar',
|
|
1941
|
+
preset: 'ranking',
|
|
1942
|
+
chartId: 'monthly-revenue-chart',
|
|
1943
|
+
title: { key: 'charts.heroes.universe.title', fallback: 'Heroes por universo' },
|
|
1944
|
+
subtitle: { key: 'charts.heroes.universe.subtitle', fallback: 'Group-by em /vw-perfil-heroi' },
|
|
1945
|
+
height: 320,
|
|
1946
|
+
source: {
|
|
1947
|
+
kind: 'praxis.stats',
|
|
1948
|
+
resource: 'api/human-resources/vw-perfil-heroi',
|
|
1949
|
+
operation: 'group-by',
|
|
1950
|
+
options: {
|
|
1951
|
+
orderBy: 'value-desc',
|
|
1952
|
+
limit: 10,
|
|
1953
|
+
},
|
|
1954
|
+
},
|
|
1955
|
+
dimensions: [
|
|
1956
|
+
{
|
|
1957
|
+
field: 'universo',
|
|
1958
|
+
label: 'Universo',
|
|
1959
|
+
role: 'category',
|
|
1960
|
+
},
|
|
1961
|
+
],
|
|
1962
|
+
metrics: [
|
|
1963
|
+
{
|
|
1964
|
+
field: 'count',
|
|
1965
|
+
label: 'Total de heroes',
|
|
1966
|
+
aggregation: 'count',
|
|
1967
|
+
},
|
|
1968
|
+
],
|
|
1969
|
+
groupBy: ['universo'],
|
|
1970
|
+
sort: [{ field: 'universo', direction: 'asc' }],
|
|
1971
|
+
legend: { enabled: true },
|
|
1972
|
+
tooltip: { enabled: true },
|
|
1973
|
+
labels: false,
|
|
1974
|
+
theme: {
|
|
1975
|
+
palette: ['#1263b4'],
|
|
1976
|
+
},
|
|
1977
|
+
state: {
|
|
1978
|
+
empty: {
|
|
1979
|
+
title: { key: 'charts.empty.title', fallback: 'Sem dados analiticos' },
|
|
1980
|
+
description: { key: 'charts.empty.description', fallback: 'O backend retornou zero linhas para este recorte.' },
|
|
1981
|
+
},
|
|
1982
|
+
loading: {
|
|
1983
|
+
title: { key: 'charts.loading.title', fallback: 'Carregando dados publicados...' },
|
|
1984
|
+
},
|
|
1985
|
+
error: {
|
|
1986
|
+
title: { key: 'charts.error.title', fallback: 'Falha no contrato analitico' },
|
|
1987
|
+
description: { key: 'charts.error.description', fallback: 'Use este cenario para validar falha controlada do runtime.' },
|
|
1988
|
+
},
|
|
1989
|
+
},
|
|
1990
|
+
events: {
|
|
1991
|
+
pointClick: {
|
|
1992
|
+
action: 'emit',
|
|
1993
|
+
},
|
|
1994
|
+
},
|
|
1995
|
+
},
|
|
1996
|
+
},
|
|
1997
|
+
};
|
|
1998
|
+
const PRAXIS_CHART_BACKEND_MOCK_TIMESERIES = {
|
|
1999
|
+
schemaMeta: {
|
|
2000
|
+
schemaId: 'api/human-resources/vw-indicadores-incidentes|post|response|tenant:demo|locale:pt-BR',
|
|
2001
|
+
schemaHash: 'published-hash-incident-timeseries-v1',
|
|
2002
|
+
resourcePath: 'api/human-resources/vw-indicadores-incidentes',
|
|
2003
|
+
operation: 'post',
|
|
2004
|
+
schemaType: 'response',
|
|
2005
|
+
source: 'backend',
|
|
2006
|
+
},
|
|
2007
|
+
widget: {
|
|
2008
|
+
kind: 'x-ui.chart',
|
|
2009
|
+
key: 'incident-timeseries-chart',
|
|
2010
|
+
layout: { col: 7, row: 1, colSpan: 5, rowSpan: 4 },
|
|
2011
|
+
shell: {
|
|
2012
|
+
title: 'Incidentes por mes',
|
|
2013
|
+
subtitle: 'Time series real consumindo praxis.stats publicado',
|
|
2014
|
+
},
|
|
2015
|
+
chart: {
|
|
2016
|
+
version: '0.1.0',
|
|
2017
|
+
kind: 'line',
|
|
2018
|
+
preset: 'kpi-trend',
|
|
2019
|
+
chartId: 'incident-timeseries-chart',
|
|
2020
|
+
title: { key: 'charts.incidents.timeline.title', fallback: 'Incidentes por mes' },
|
|
2021
|
+
subtitle: { key: 'charts.incidents.timeline.subtitle', fallback: 'Timeseries em /vw-indicadores-incidentes' },
|
|
2022
|
+
source: {
|
|
2023
|
+
kind: 'praxis.stats',
|
|
2024
|
+
resource: 'api/human-resources/vw-indicadores-incidentes',
|
|
2025
|
+
operation: 'timeseries',
|
|
2026
|
+
options: {
|
|
2027
|
+
granularity: 'month',
|
|
2028
|
+
fillGaps: false,
|
|
2029
|
+
},
|
|
2030
|
+
},
|
|
2031
|
+
dimensions: [
|
|
2032
|
+
{
|
|
2033
|
+
field: 'ocorridoEm',
|
|
2034
|
+
label: 'Ocorrido em',
|
|
2035
|
+
role: 'time',
|
|
2036
|
+
},
|
|
2037
|
+
],
|
|
2038
|
+
metrics: [
|
|
2039
|
+
{
|
|
2040
|
+
field: 'count',
|
|
2041
|
+
label: 'Total de incidentes',
|
|
2042
|
+
aggregation: 'count',
|
|
2043
|
+
},
|
|
2044
|
+
],
|
|
2045
|
+
legend: { enabled: true },
|
|
2046
|
+
tooltip: { enabled: true },
|
|
2047
|
+
labels: false,
|
|
2048
|
+
theme: {
|
|
2049
|
+
palette: ['#2b8a3e'],
|
|
2050
|
+
},
|
|
2051
|
+
},
|
|
2052
|
+
},
|
|
2053
|
+
};
|
|
2054
|
+
const PRAXIS_CHART_BACKEND_MOCK_DONUT = {
|
|
2055
|
+
schemaMeta: {
|
|
2056
|
+
schemaId: 'api/human-resources/vw-indicadores-incidentes|post|response|tenant:demo|locale:pt-BR',
|
|
2057
|
+
schemaHash: 'published-hash-incident-severity-v1',
|
|
2058
|
+
resourcePath: 'api/human-resources/vw-indicadores-incidentes',
|
|
2059
|
+
operation: 'post',
|
|
2060
|
+
schemaType: 'response',
|
|
2061
|
+
source: 'backend',
|
|
2062
|
+
},
|
|
2063
|
+
widget: {
|
|
2064
|
+
kind: 'x-ui.chart',
|
|
2065
|
+
key: 'ticket-status-chart',
|
|
2066
|
+
layout: { col: 7, row: 1, colSpan: 5, rowSpan: 4 },
|
|
2067
|
+
shell: {
|
|
2068
|
+
title: 'Severidade de incidentes',
|
|
2069
|
+
subtitle: 'Distribution real consumindo praxis.stats publicado',
|
|
2070
|
+
},
|
|
2071
|
+
chart: {
|
|
2072
|
+
version: '0.1.0',
|
|
2073
|
+
kind: 'donut',
|
|
2074
|
+
preset: 'distribution',
|
|
2075
|
+
chartId: 'ticket-status-chart',
|
|
2076
|
+
title: { key: 'charts.incidents.severity.title', fallback: 'Severidade de incidentes' },
|
|
2077
|
+
source: {
|
|
2078
|
+
kind: 'praxis.stats',
|
|
2079
|
+
resource: 'api/human-resources/vw-indicadores-incidentes',
|
|
2080
|
+
operation: 'distribution',
|
|
2081
|
+
options: {
|
|
2082
|
+
mode: 'terms',
|
|
2083
|
+
orderBy: 'value-desc',
|
|
2084
|
+
limit: 10,
|
|
2085
|
+
},
|
|
2086
|
+
},
|
|
2087
|
+
dimensions: [
|
|
2088
|
+
{
|
|
2089
|
+
field: 'severidade',
|
|
2090
|
+
label: 'Severidade',
|
|
2091
|
+
role: 'segment',
|
|
2092
|
+
},
|
|
2093
|
+
],
|
|
2094
|
+
metrics: [
|
|
2095
|
+
{
|
|
2096
|
+
field: 'count',
|
|
2097
|
+
label: 'Total',
|
|
2098
|
+
aggregation: 'count',
|
|
2099
|
+
},
|
|
2100
|
+
],
|
|
2101
|
+
labels: { enabled: true },
|
|
2102
|
+
legend: { enabled: true },
|
|
2103
|
+
tooltip: { enabled: true },
|
|
2104
|
+
theme: {
|
|
2105
|
+
palette: ['#1263b4', '#2b8a3e', '#f08c00', '#c92a2a'],
|
|
2106
|
+
},
|
|
2107
|
+
},
|
|
2108
|
+
},
|
|
2109
|
+
};
|
|
2110
|
+
|
|
2111
|
+
function buildPraxisChartMockWidgetPage(adapter) {
|
|
2112
|
+
return {
|
|
2113
|
+
widgets: [
|
|
2114
|
+
withShowcaseViewToggle(adapter.toWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_BAR)),
|
|
2115
|
+
withShowcaseViewToggle(adapter.toWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_TIMESERIES)),
|
|
2116
|
+
withShowcaseViewToggle(adapter.toWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_DONUT)),
|
|
2117
|
+
],
|
|
2118
|
+
layout: {
|
|
2119
|
+
orientation: 'columns',
|
|
2120
|
+
columns: 2,
|
|
2121
|
+
gap: '16px',
|
|
2122
|
+
},
|
|
2123
|
+
};
|
|
2124
|
+
}
|
|
2125
|
+
function buildPraxisChartMockGridPage(adapter) {
|
|
2126
|
+
return {
|
|
2127
|
+
widgets: [
|
|
2128
|
+
withShowcaseViewToggle(adapter.toGridWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_BAR)),
|
|
2129
|
+
withShowcaseViewToggle(adapter.toGridWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_TIMESERIES)),
|
|
2130
|
+
withShowcaseViewToggle(adapter.toGridWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_DONUT)),
|
|
2131
|
+
],
|
|
2132
|
+
options: {
|
|
2133
|
+
cols: 12,
|
|
2134
|
+
rowHeight: 88,
|
|
2135
|
+
gap: 16,
|
|
2136
|
+
},
|
|
2137
|
+
};
|
|
2138
|
+
}
|
|
2139
|
+
function buildPraxisChartInteractiveWidgetPage(adapter) {
|
|
2140
|
+
return {
|
|
2141
|
+
widgets: [
|
|
2142
|
+
withShowcaseViewToggle(adapter.toWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_BAR)),
|
|
2143
|
+
withShowcaseViewToggle(adapter.toWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_TIMESERIES)),
|
|
2144
|
+
{
|
|
2145
|
+
key: 'chart-drilldown-panel',
|
|
2146
|
+
definition: {
|
|
2147
|
+
id: 'praxis-chart-drilldown-panel',
|
|
2148
|
+
inputs: {
|
|
2149
|
+
title: 'Drill-down por segmento',
|
|
2150
|
+
selection: null,
|
|
2151
|
+
},
|
|
2152
|
+
},
|
|
2153
|
+
shell: {
|
|
2154
|
+
title: 'Local Drill-down',
|
|
2155
|
+
subtitle: 'Filtrado por pointClick do chart principal',
|
|
2156
|
+
},
|
|
2157
|
+
},
|
|
2158
|
+
{
|
|
2159
|
+
key: 'chart-event-probe',
|
|
2160
|
+
definition: {
|
|
2161
|
+
id: 'praxis-chart-state-probe',
|
|
2162
|
+
inputs: {
|
|
2163
|
+
title: 'Eventos do chart principal',
|
|
2164
|
+
value: 'Clique em uma barra para inspecionar o payload emitido.',
|
|
2165
|
+
},
|
|
2166
|
+
},
|
|
2167
|
+
shell: {
|
|
2168
|
+
title: 'Runtime Probe',
|
|
2169
|
+
subtitle: 'Conectado via WidgetDefinition.connections',
|
|
2170
|
+
},
|
|
2171
|
+
},
|
|
2172
|
+
withShowcaseViewToggle(adapter.toWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_DONUT)),
|
|
2173
|
+
],
|
|
2174
|
+
connections: [
|
|
2175
|
+
{
|
|
2176
|
+
from: { widget: 'monthly-revenue-chart', output: 'pointClick' },
|
|
2177
|
+
to: { widget: 'chart-event-probe', input: 'value' },
|
|
2178
|
+
},
|
|
2179
|
+
{
|
|
2180
|
+
from: { widget: 'monthly-revenue-chart', output: 'pointClick' },
|
|
2181
|
+
to: { widget: 'chart-drilldown-panel', input: 'selection' },
|
|
2182
|
+
},
|
|
2183
|
+
],
|
|
2184
|
+
layout: {
|
|
2185
|
+
orientation: 'columns',
|
|
2186
|
+
columns: 2,
|
|
2187
|
+
gap: '16px',
|
|
2188
|
+
},
|
|
2189
|
+
};
|
|
2190
|
+
}
|
|
2191
|
+
function buildPraxisChartInteractiveGridPage(adapter) {
|
|
2192
|
+
return {
|
|
2193
|
+
widgets: [
|
|
2194
|
+
withShowcaseViewToggle(adapter.toGridWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_BAR)),
|
|
2195
|
+
buildDrilldownGridWidget(),
|
|
2196
|
+
withShowcaseViewToggle(adapter.toGridWidgetInstance({
|
|
2197
|
+
...PRAXIS_CHART_BACKEND_MOCK_TIMESERIES,
|
|
2198
|
+
widget: {
|
|
2199
|
+
...PRAXIS_CHART_BACKEND_MOCK_TIMESERIES.widget,
|
|
2200
|
+
layout: { col: 1, row: 5, colSpan: 6, rowSpan: 4 },
|
|
2201
|
+
},
|
|
2202
|
+
})),
|
|
2203
|
+
buildProbeGridWidget(),
|
|
2204
|
+
withShowcaseViewToggle(adapter.toGridWidgetInstance({
|
|
2205
|
+
...PRAXIS_CHART_BACKEND_MOCK_DONUT,
|
|
2206
|
+
widget: {
|
|
2207
|
+
...PRAXIS_CHART_BACKEND_MOCK_DONUT.widget,
|
|
2208
|
+
layout: { col: 7, row: 5, colSpan: 6, rowSpan: 4 },
|
|
2209
|
+
},
|
|
2210
|
+
})),
|
|
2211
|
+
],
|
|
2212
|
+
connections: [
|
|
2213
|
+
{
|
|
2214
|
+
from: { widget: 'monthly-revenue-chart', output: 'pointClick' },
|
|
2215
|
+
to: { widget: 'chart-event-probe', input: 'value' },
|
|
2216
|
+
},
|
|
2217
|
+
{
|
|
2218
|
+
from: { widget: 'monthly-revenue-chart', output: 'pointClick' },
|
|
2219
|
+
to: { widget: 'chart-drilldown-panel', input: 'selection' },
|
|
2220
|
+
},
|
|
2221
|
+
],
|
|
2222
|
+
options: {
|
|
2223
|
+
cols: 12,
|
|
2224
|
+
rowHeight: 88,
|
|
2225
|
+
gap: 16,
|
|
2226
|
+
},
|
|
2227
|
+
};
|
|
2228
|
+
}
|
|
2229
|
+
function buildDrilldownGridWidget() {
|
|
2230
|
+
return {
|
|
2231
|
+
key: 'chart-drilldown-panel',
|
|
2232
|
+
definition: {
|
|
2233
|
+
id: 'praxis-chart-drilldown-panel',
|
|
2234
|
+
inputs: {
|
|
2235
|
+
title: 'Drill-down por segmento',
|
|
2236
|
+
selection: null,
|
|
2237
|
+
},
|
|
2238
|
+
},
|
|
2239
|
+
layout: { col: 7, row: 1, colSpan: 6, rowSpan: 4 },
|
|
2240
|
+
shell: {
|
|
2241
|
+
title: 'Local Drill-down',
|
|
2242
|
+
subtitle: 'Filtrado por pointClick do chart principal',
|
|
2243
|
+
},
|
|
2244
|
+
};
|
|
2245
|
+
}
|
|
2246
|
+
function buildProbeGridWidget() {
|
|
2247
|
+
return {
|
|
2248
|
+
key: 'chart-event-probe',
|
|
2249
|
+
definition: {
|
|
2250
|
+
id: 'praxis-chart-state-probe',
|
|
2251
|
+
inputs: {
|
|
2252
|
+
title: 'Eventos do chart principal',
|
|
2253
|
+
value: 'Clique em uma barra para inspecionar o payload emitido.',
|
|
2254
|
+
},
|
|
2255
|
+
},
|
|
2256
|
+
layout: { col: 7, row: 9, colSpan: 6, rowSpan: 3 },
|
|
2257
|
+
shell: {
|
|
2258
|
+
title: 'Runtime Probe',
|
|
2259
|
+
subtitle: 'Conectado via WidgetDefinition.connections',
|
|
2260
|
+
},
|
|
2261
|
+
};
|
|
2262
|
+
}
|
|
2263
|
+
function withShowcaseViewToggle(widget, initialViewMode = 'chart') {
|
|
2264
|
+
return {
|
|
2265
|
+
...widget,
|
|
2266
|
+
definition: {
|
|
2267
|
+
...widget.definition,
|
|
2268
|
+
id: 'praxis-chart-showcase-widget',
|
|
2269
|
+
inputs: {
|
|
2270
|
+
...(widget.definition.inputs || {}),
|
|
2271
|
+
viewMode: initialViewMode,
|
|
2272
|
+
},
|
|
2273
|
+
bindingOrder: ['config', 'data', 'viewMode'],
|
|
2274
|
+
},
|
|
2275
|
+
shell: {
|
|
2276
|
+
...(widget.shell || {}),
|
|
2277
|
+
actions: [...(widget.shell?.actions || []), ...buildShowcaseToggleActions()],
|
|
2278
|
+
},
|
|
2279
|
+
};
|
|
2280
|
+
}
|
|
2281
|
+
function buildShowcaseToggleActions() {
|
|
2282
|
+
return [
|
|
2283
|
+
{
|
|
2284
|
+
id: 'show-chart',
|
|
2285
|
+
icon: 'bar_chart',
|
|
2286
|
+
tooltip: 'Ver grafico',
|
|
2287
|
+
variant: 'icon',
|
|
2288
|
+
command: 'pdx:set-input',
|
|
2289
|
+
payload: {
|
|
2290
|
+
input: 'viewMode',
|
|
2291
|
+
value: 'chart',
|
|
2292
|
+
},
|
|
2293
|
+
},
|
|
2294
|
+
{
|
|
2295
|
+
id: 'show-table',
|
|
2296
|
+
icon: 'table_view',
|
|
2297
|
+
tooltip: 'Ver tabela',
|
|
2298
|
+
variant: 'icon',
|
|
2299
|
+
command: 'pdx:set-input',
|
|
2300
|
+
payload: {
|
|
2301
|
+
input: 'viewMode',
|
|
2302
|
+
value: 'table',
|
|
2303
|
+
},
|
|
2304
|
+
},
|
|
2305
|
+
];
|
|
2306
|
+
}
|
|
2307
|
+
|
|
2308
|
+
class PraxisChartCompositionShowcaseComponent {
|
|
2309
|
+
layoutMode = signal('widget', ...(ngDevMode ? [{ debugName: "layoutMode" }] : []));
|
|
2310
|
+
payloadMode = signal('group-by', ...(ngDevMode ? [{ debugName: "payloadMode" }] : []));
|
|
2311
|
+
scenarioMode = signal('baseline', ...(ngDevMode ? [{ debugName: "scenarioMode" }] : []));
|
|
2312
|
+
runtimeContext = {
|
|
2313
|
+
tenantId: 'demo-enterprise',
|
|
2314
|
+
locale: 'pt-BR',
|
|
2315
|
+
environment: 'published-api',
|
|
2316
|
+
};
|
|
2317
|
+
backendPayloadAdapter = inject(PraxisChartBackendPayloadAdapterService);
|
|
2318
|
+
widgetPage = computed(() => {
|
|
2319
|
+
switch (this.scenarioMode()) {
|
|
2320
|
+
case 'interactive':
|
|
2321
|
+
return buildPraxisChartInteractiveWidgetPage(this.backendPayloadAdapter);
|
|
2322
|
+
case 'empty':
|
|
2323
|
+
return {
|
|
2324
|
+
...buildPraxisChartMockWidgetPage(this.backendPayloadAdapter),
|
|
2325
|
+
widgets: [
|
|
2326
|
+
this.backendPayloadAdapter.toWidgetInstance({
|
|
2327
|
+
...PRAXIS_CHART_BACKEND_MOCK_BAR,
|
|
2328
|
+
data: [],
|
|
2329
|
+
}),
|
|
2330
|
+
],
|
|
2331
|
+
};
|
|
2332
|
+
case 'loading':
|
|
2333
|
+
return {
|
|
2334
|
+
...buildPraxisChartMockWidgetPage(this.backendPayloadAdapter),
|
|
2335
|
+
widgets: [
|
|
2336
|
+
this.backendPayloadAdapter.toWidgetInstance({
|
|
2337
|
+
...PRAXIS_CHART_BACKEND_MOCK_BAR,
|
|
2338
|
+
data: [],
|
|
2339
|
+
widget: {
|
|
2340
|
+
...PRAXIS_CHART_BACKEND_MOCK_BAR.widget,
|
|
2341
|
+
runtime: {
|
|
2342
|
+
preferredLoadState: 'loading',
|
|
2343
|
+
},
|
|
2344
|
+
},
|
|
2345
|
+
}),
|
|
2346
|
+
],
|
|
2347
|
+
};
|
|
2348
|
+
case 'error':
|
|
2349
|
+
return {
|
|
2350
|
+
...buildPraxisChartMockWidgetPage(this.backendPayloadAdapter),
|
|
2351
|
+
widgets: [
|
|
2352
|
+
this.backendPayloadAdapter.toWidgetInstance({
|
|
2353
|
+
...PRAXIS_CHART_BACKEND_MOCK_BAR,
|
|
2354
|
+
data: [],
|
|
2355
|
+
widget: {
|
|
2356
|
+
...PRAXIS_CHART_BACKEND_MOCK_BAR.widget,
|
|
2357
|
+
runtime: {
|
|
2358
|
+
preferredLoadState: 'error',
|
|
2359
|
+
},
|
|
2360
|
+
},
|
|
2361
|
+
}),
|
|
2362
|
+
],
|
|
2363
|
+
};
|
|
2364
|
+
case 'baseline':
|
|
2365
|
+
default:
|
|
2366
|
+
return buildPraxisChartMockWidgetPage(this.backendPayloadAdapter);
|
|
2367
|
+
}
|
|
2368
|
+
}, ...(ngDevMode ? [{ debugName: "widgetPage" }] : []));
|
|
2369
|
+
gridPage = computed(() => {
|
|
2370
|
+
switch (this.scenarioMode()) {
|
|
2371
|
+
case 'interactive':
|
|
2372
|
+
return buildPraxisChartInteractiveGridPage(this.backendPayloadAdapter);
|
|
2373
|
+
case 'empty':
|
|
2374
|
+
return {
|
|
2375
|
+
...buildPraxisChartMockGridPage(this.backendPayloadAdapter),
|
|
2376
|
+
widgets: [
|
|
2377
|
+
this.backendPayloadAdapter.toGridWidgetInstance({
|
|
2378
|
+
...PRAXIS_CHART_BACKEND_MOCK_BAR,
|
|
2379
|
+
data: [],
|
|
2380
|
+
}),
|
|
2381
|
+
this.backendPayloadAdapter.toGridWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_TIMESERIES),
|
|
2382
|
+
this.backendPayloadAdapter.toGridWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_DONUT),
|
|
2383
|
+
],
|
|
2384
|
+
};
|
|
2385
|
+
case 'loading':
|
|
2386
|
+
return {
|
|
2387
|
+
...buildPraxisChartMockGridPage(this.backendPayloadAdapter),
|
|
2388
|
+
widgets: [
|
|
2389
|
+
this.backendPayloadAdapter.toGridWidgetInstance({
|
|
2390
|
+
...PRAXIS_CHART_BACKEND_MOCK_BAR,
|
|
2391
|
+
data: [],
|
|
2392
|
+
widget: {
|
|
2393
|
+
...PRAXIS_CHART_BACKEND_MOCK_BAR.widget,
|
|
2394
|
+
runtime: {
|
|
2395
|
+
preferredLoadState: 'loading',
|
|
2396
|
+
},
|
|
2397
|
+
},
|
|
2398
|
+
}),
|
|
2399
|
+
this.backendPayloadAdapter.toGridWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_TIMESERIES),
|
|
2400
|
+
this.backendPayloadAdapter.toGridWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_DONUT),
|
|
2401
|
+
],
|
|
2402
|
+
};
|
|
2403
|
+
case 'error':
|
|
2404
|
+
return {
|
|
2405
|
+
...buildPraxisChartMockGridPage(this.backendPayloadAdapter),
|
|
2406
|
+
widgets: [
|
|
2407
|
+
this.backendPayloadAdapter.toGridWidgetInstance({
|
|
2408
|
+
...PRAXIS_CHART_BACKEND_MOCK_BAR,
|
|
2409
|
+
data: [],
|
|
2410
|
+
widget: {
|
|
2411
|
+
...PRAXIS_CHART_BACKEND_MOCK_BAR.widget,
|
|
2412
|
+
runtime: {
|
|
2413
|
+
preferredLoadState: 'error',
|
|
2414
|
+
},
|
|
2415
|
+
},
|
|
2416
|
+
}),
|
|
2417
|
+
this.backendPayloadAdapter.toGridWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_TIMESERIES),
|
|
2418
|
+
this.backendPayloadAdapter.toGridWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_DONUT),
|
|
2419
|
+
],
|
|
2420
|
+
};
|
|
2421
|
+
case 'baseline':
|
|
2422
|
+
default:
|
|
2423
|
+
return buildPraxisChartMockGridPage(this.backendPayloadAdapter);
|
|
2424
|
+
}
|
|
2425
|
+
}, ...(ngDevMode ? [{ debugName: "gridPage" }] : []));
|
|
2426
|
+
payloadTitle = computed(() => {
|
|
2427
|
+
switch (this.payloadMode()) {
|
|
2428
|
+
case 'timeseries':
|
|
2429
|
+
return 'Envelope timeseries';
|
|
2430
|
+
case 'distribution':
|
|
2431
|
+
return 'Envelope distribution';
|
|
2432
|
+
case 'group-by':
|
|
2433
|
+
default:
|
|
2434
|
+
return 'Envelope group-by';
|
|
2435
|
+
}
|
|
2436
|
+
}, ...(ngDevMode ? [{ debugName: "payloadTitle" }] : []));
|
|
2437
|
+
selectedPayloadJson = computed(() => JSON.stringify(this.selectedPayload(), null, 2), ...(ngDevMode ? [{ debugName: "selectedPayloadJson" }] : []));
|
|
2438
|
+
selectedPayload = computed(() => {
|
|
2439
|
+
switch (this.payloadMode()) {
|
|
2440
|
+
case 'timeseries':
|
|
2441
|
+
return PRAXIS_CHART_BACKEND_MOCK_TIMESERIES;
|
|
2442
|
+
case 'distribution':
|
|
2443
|
+
return PRAXIS_CHART_BACKEND_MOCK_DONUT;
|
|
2444
|
+
case 'group-by':
|
|
2445
|
+
default:
|
|
2446
|
+
return PRAXIS_CHART_BACKEND_MOCK_BAR;
|
|
2447
|
+
}
|
|
2448
|
+
}, ...(ngDevMode ? [{ debugName: "selectedPayload" }] : []));
|
|
2449
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisChartCompositionShowcaseComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2450
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: PraxisChartCompositionShowcaseComponent, isStandalone: true, selector: "praxis-chart-composition-showcase", providers: [providePraxisCharts()], ngImport: i0, template: `
|
|
2451
|
+
<section class="showcase-shell">
|
|
2452
|
+
<header class="showcase-hero">
|
|
2453
|
+
<div class="showcase-copy">
|
|
2454
|
+
<p class="showcase-eyebrow">Praxis Charts</p>
|
|
2455
|
+
<h2>Composicao local com contrato canonico e API publicada</h2>
|
|
2456
|
+
<p>
|
|
2457
|
+
O showcase valida o fluxo <code>widget.chart</code>,
|
|
2458
|
+
<code>praxis.stats</code>, renderizacao dinamica, probe de eventos
|
|
2459
|
+
e o toggle de chart/tabela sobre o mesmo dataset.
|
|
2460
|
+
</p>
|
|
2461
|
+
</div>
|
|
2462
|
+
|
|
2463
|
+
<div class="showcase-controls">
|
|
2464
|
+
<div class="control-group">
|
|
2465
|
+
<span>Layout</span>
|
|
2466
|
+
<div class="control-buttons">
|
|
2467
|
+
<button type="button" [class.active]="layoutMode() === 'widget'" (click)="layoutMode.set('widget')">
|
|
2468
|
+
Widget Page
|
|
2469
|
+
</button>
|
|
2470
|
+
<button type="button" [class.active]="layoutMode() === 'grid'" (click)="layoutMode.set('grid')">
|
|
2471
|
+
Grid Page
|
|
2472
|
+
</button>
|
|
2473
|
+
</div>
|
|
2474
|
+
</div>
|
|
2475
|
+
|
|
2476
|
+
<div class="control-group">
|
|
2477
|
+
<span>Envelope inspecionado</span>
|
|
2478
|
+
<div class="control-buttons">
|
|
2479
|
+
<button type="button" [class.active]="payloadMode() === 'group-by'" (click)="payloadMode.set('group-by')">
|
|
2480
|
+
Group-by
|
|
2481
|
+
</button>
|
|
2482
|
+
<button type="button" [class.active]="payloadMode() === 'timeseries'" (click)="payloadMode.set('timeseries')">
|
|
2483
|
+
Time series
|
|
2484
|
+
</button>
|
|
2485
|
+
<button type="button" [class.active]="payloadMode() === 'distribution'" (click)="payloadMode.set('distribution')">
|
|
2486
|
+
Distribution
|
|
2487
|
+
</button>
|
|
2488
|
+
</div>
|
|
2489
|
+
</div>
|
|
2490
|
+
|
|
2491
|
+
<div class="control-group">
|
|
2492
|
+
<span>Cenario</span>
|
|
2493
|
+
<div class="control-buttons">
|
|
2494
|
+
<button type="button" [class.active]="scenarioMode() === 'baseline'" (click)="scenarioMode.set('baseline')">
|
|
2495
|
+
Baseline
|
|
2496
|
+
</button>
|
|
2497
|
+
<button type="button" [class.active]="scenarioMode() === 'interactive'" (click)="scenarioMode.set('interactive')">
|
|
2498
|
+
Interativo
|
|
2499
|
+
</button>
|
|
2500
|
+
<button type="button" [class.active]="scenarioMode() === 'empty'" (click)="scenarioMode.set('empty')">
|
|
2501
|
+
Empty
|
|
2502
|
+
</button>
|
|
2503
|
+
<button type="button" [class.active]="scenarioMode() === 'loading'" (click)="scenarioMode.set('loading')">
|
|
2504
|
+
Loading
|
|
2505
|
+
</button>
|
|
2506
|
+
<button type="button" [class.active]="scenarioMode() === 'error'" (click)="scenarioMode.set('error')">
|
|
2507
|
+
Error
|
|
2508
|
+
</button>
|
|
2509
|
+
</div>
|
|
2510
|
+
</div>
|
|
2511
|
+
</div>
|
|
2512
|
+
</header>
|
|
2513
|
+
|
|
2514
|
+
<div class="showcase-grid">
|
|
2515
|
+
<article class="runtime-panel">
|
|
2516
|
+
<div class="panel-header">
|
|
2517
|
+
<div>
|
|
2518
|
+
<p class="panel-kicker">Runtime</p>
|
|
2519
|
+
<h3>{{ layoutMode() === 'widget' ? 'DynamicWidgetPage' : 'DynamicGridPage' }}</h3>
|
|
2520
|
+
</div>
|
|
2521
|
+
<span class="panel-chip">{{ layoutMode() }} · {{ scenarioMode() }}</span>
|
|
2522
|
+
</div>
|
|
2523
|
+
|
|
2524
|
+
@if (layoutMode() === 'widget') {
|
|
2525
|
+
<praxis-dynamic-page
|
|
2526
|
+
[page]="widgetPage()"
|
|
2527
|
+
[context]="runtimeContext"
|
|
2528
|
+
[autoPersist]="false"
|
|
2529
|
+
></praxis-dynamic-page>
|
|
2530
|
+
} @else {
|
|
2531
|
+
<praxis-dynamic-grid-page [page]="gridPage()" [context]="runtimeContext"></praxis-dynamic-grid-page>
|
|
2532
|
+
}
|
|
2533
|
+
</article>
|
|
2534
|
+
|
|
2535
|
+
<article class="payload-panel">
|
|
2536
|
+
<div class="panel-header">
|
|
2537
|
+
<div>
|
|
2538
|
+
<p class="panel-kicker">Payload</p>
|
|
2539
|
+
<h3>{{ payloadTitle() }}</h3>
|
|
2540
|
+
</div>
|
|
2541
|
+
<span class="panel-chip">widget.chart</span>
|
|
2542
|
+
</div>
|
|
2543
|
+
|
|
2544
|
+
<pre>{{ selectedPayloadJson() }}</pre>
|
|
2545
|
+
</article>
|
|
2546
|
+
</div>
|
|
2547
|
+
</section>
|
|
2548
|
+
`, isInline: true, styles: [":host{display:block}.showcase-shell{display:grid;gap:24px}.showcase-hero{display:grid;gap:20px;padding:28px;border-radius:28px;background:radial-gradient(circle at top right,rgba(18,99,180,.18),transparent 30%),linear-gradient(135deg,#071836f2,#1263b4c7);color:#f7fbff}.showcase-copy h2{margin:0 0 10px;font-size:clamp(1.8rem,3vw,2.6rem);line-height:1.05}.showcase-copy p{margin:0;max-width:52rem;color:#f7fbffe0}.showcase-eyebrow,.panel-kicker{margin:0 0 8px;font-size:.78rem;letter-spacing:.14em;text-transform:uppercase;color:#f7fbffb8}.showcase-controls{display:flex;flex-wrap:wrap;gap:16px}.control-group{display:grid;gap:8px}.control-group span{font-size:.84rem;color:#f7fbffc2}.control-buttons{display:flex;flex-wrap:wrap;gap:8px}.control-buttons button{border:1px solid rgba(247,251,255,.22);background:#f7fbff14;color:#f7fbff;border-radius:999px;padding:10px 14px;cursor:pointer;transition:background .16s ease,transform .16s ease}.control-buttons button.active{background:#f7fbff;color:#0d2d5f}.showcase-grid{display:grid;gap:20px;grid-template-columns:minmax(0,1.3fr) minmax(320px,.9fr)}.runtime-panel,.payload-panel{display:grid;gap:16px;padding:20px;border-radius:24px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline, #c5c7ce) 76%,transparent);background:linear-gradient(180deg,#fffffff0,#f4f7fbf5)}.panel-header{display:flex;align-items:start;justify-content:space-between;gap:12px}.panel-header h3{margin:0;color:#142033}.panel-chip{border-radius:999px;padding:6px 10px;font-size:.76rem;background:#1263b41f;color:#1263b4}.payload-panel pre{margin:0;padding:16px;border-radius:18px;background:#09111f;color:#d7e6ff;overflow:auto;font-size:.84rem;line-height:1.5}@media(max-width:1080px){.showcase-grid{grid-template-columns:1fr}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DynamicWidgetPageComponent, selector: "praxis-dynamic-page", inputs: ["page", "context", "strictValidation", "enableCustomization", "showPageSettingsButton", "shellEditorComponent", "pageEditorComponent", "autoPersist", "pageIdentity", "componentInstanceId"], outputs: ["pageChange"] }, { kind: "component", type: DynamicGridPageComponent, selector: "praxis-dynamic-grid-page", inputs: ["page", "context", "strictValidation", "gridOptions", "enableCustomization"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2549
|
+
}
|
|
2550
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisChartCompositionShowcaseComponent, decorators: [{
|
|
2551
|
+
type: Component,
|
|
2552
|
+
args: [{ selector: 'praxis-chart-composition-showcase', standalone: true, imports: [CommonModule, DynamicWidgetPageComponent, DynamicGridPageComponent], providers: [providePraxisCharts()], changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
2553
|
+
<section class="showcase-shell">
|
|
2554
|
+
<header class="showcase-hero">
|
|
2555
|
+
<div class="showcase-copy">
|
|
2556
|
+
<p class="showcase-eyebrow">Praxis Charts</p>
|
|
2557
|
+
<h2>Composicao local com contrato canonico e API publicada</h2>
|
|
2558
|
+
<p>
|
|
2559
|
+
O showcase valida o fluxo <code>widget.chart</code>,
|
|
2560
|
+
<code>praxis.stats</code>, renderizacao dinamica, probe de eventos
|
|
2561
|
+
e o toggle de chart/tabela sobre o mesmo dataset.
|
|
2562
|
+
</p>
|
|
2563
|
+
</div>
|
|
2564
|
+
|
|
2565
|
+
<div class="showcase-controls">
|
|
2566
|
+
<div class="control-group">
|
|
2567
|
+
<span>Layout</span>
|
|
2568
|
+
<div class="control-buttons">
|
|
2569
|
+
<button type="button" [class.active]="layoutMode() === 'widget'" (click)="layoutMode.set('widget')">
|
|
2570
|
+
Widget Page
|
|
2571
|
+
</button>
|
|
2572
|
+
<button type="button" [class.active]="layoutMode() === 'grid'" (click)="layoutMode.set('grid')">
|
|
2573
|
+
Grid Page
|
|
2574
|
+
</button>
|
|
2575
|
+
</div>
|
|
2576
|
+
</div>
|
|
2577
|
+
|
|
2578
|
+
<div class="control-group">
|
|
2579
|
+
<span>Envelope inspecionado</span>
|
|
2580
|
+
<div class="control-buttons">
|
|
2581
|
+
<button type="button" [class.active]="payloadMode() === 'group-by'" (click)="payloadMode.set('group-by')">
|
|
2582
|
+
Group-by
|
|
2583
|
+
</button>
|
|
2584
|
+
<button type="button" [class.active]="payloadMode() === 'timeseries'" (click)="payloadMode.set('timeseries')">
|
|
2585
|
+
Time series
|
|
2586
|
+
</button>
|
|
2587
|
+
<button type="button" [class.active]="payloadMode() === 'distribution'" (click)="payloadMode.set('distribution')">
|
|
2588
|
+
Distribution
|
|
2589
|
+
</button>
|
|
2590
|
+
</div>
|
|
2591
|
+
</div>
|
|
2592
|
+
|
|
2593
|
+
<div class="control-group">
|
|
2594
|
+
<span>Cenario</span>
|
|
2595
|
+
<div class="control-buttons">
|
|
2596
|
+
<button type="button" [class.active]="scenarioMode() === 'baseline'" (click)="scenarioMode.set('baseline')">
|
|
2597
|
+
Baseline
|
|
2598
|
+
</button>
|
|
2599
|
+
<button type="button" [class.active]="scenarioMode() === 'interactive'" (click)="scenarioMode.set('interactive')">
|
|
2600
|
+
Interativo
|
|
2601
|
+
</button>
|
|
2602
|
+
<button type="button" [class.active]="scenarioMode() === 'empty'" (click)="scenarioMode.set('empty')">
|
|
2603
|
+
Empty
|
|
2604
|
+
</button>
|
|
2605
|
+
<button type="button" [class.active]="scenarioMode() === 'loading'" (click)="scenarioMode.set('loading')">
|
|
2606
|
+
Loading
|
|
2607
|
+
</button>
|
|
2608
|
+
<button type="button" [class.active]="scenarioMode() === 'error'" (click)="scenarioMode.set('error')">
|
|
2609
|
+
Error
|
|
2610
|
+
</button>
|
|
2611
|
+
</div>
|
|
2612
|
+
</div>
|
|
2613
|
+
</div>
|
|
2614
|
+
</header>
|
|
2615
|
+
|
|
2616
|
+
<div class="showcase-grid">
|
|
2617
|
+
<article class="runtime-panel">
|
|
2618
|
+
<div class="panel-header">
|
|
2619
|
+
<div>
|
|
2620
|
+
<p class="panel-kicker">Runtime</p>
|
|
2621
|
+
<h3>{{ layoutMode() === 'widget' ? 'DynamicWidgetPage' : 'DynamicGridPage' }}</h3>
|
|
2622
|
+
</div>
|
|
2623
|
+
<span class="panel-chip">{{ layoutMode() }} · {{ scenarioMode() }}</span>
|
|
2624
|
+
</div>
|
|
2625
|
+
|
|
2626
|
+
@if (layoutMode() === 'widget') {
|
|
2627
|
+
<praxis-dynamic-page
|
|
2628
|
+
[page]="widgetPage()"
|
|
2629
|
+
[context]="runtimeContext"
|
|
2630
|
+
[autoPersist]="false"
|
|
2631
|
+
></praxis-dynamic-page>
|
|
2632
|
+
} @else {
|
|
2633
|
+
<praxis-dynamic-grid-page [page]="gridPage()" [context]="runtimeContext"></praxis-dynamic-grid-page>
|
|
2634
|
+
}
|
|
2635
|
+
</article>
|
|
2636
|
+
|
|
2637
|
+
<article class="payload-panel">
|
|
2638
|
+
<div class="panel-header">
|
|
2639
|
+
<div>
|
|
2640
|
+
<p class="panel-kicker">Payload</p>
|
|
2641
|
+
<h3>{{ payloadTitle() }}</h3>
|
|
2642
|
+
</div>
|
|
2643
|
+
<span class="panel-chip">widget.chart</span>
|
|
2644
|
+
</div>
|
|
2645
|
+
|
|
2646
|
+
<pre>{{ selectedPayloadJson() }}</pre>
|
|
2647
|
+
</article>
|
|
2648
|
+
</div>
|
|
2649
|
+
</section>
|
|
2650
|
+
`, styles: [":host{display:block}.showcase-shell{display:grid;gap:24px}.showcase-hero{display:grid;gap:20px;padding:28px;border-radius:28px;background:radial-gradient(circle at top right,rgba(18,99,180,.18),transparent 30%),linear-gradient(135deg,#071836f2,#1263b4c7);color:#f7fbff}.showcase-copy h2{margin:0 0 10px;font-size:clamp(1.8rem,3vw,2.6rem);line-height:1.05}.showcase-copy p{margin:0;max-width:52rem;color:#f7fbffe0}.showcase-eyebrow,.panel-kicker{margin:0 0 8px;font-size:.78rem;letter-spacing:.14em;text-transform:uppercase;color:#f7fbffb8}.showcase-controls{display:flex;flex-wrap:wrap;gap:16px}.control-group{display:grid;gap:8px}.control-group span{font-size:.84rem;color:#f7fbffc2}.control-buttons{display:flex;flex-wrap:wrap;gap:8px}.control-buttons button{border:1px solid rgba(247,251,255,.22);background:#f7fbff14;color:#f7fbff;border-radius:999px;padding:10px 14px;cursor:pointer;transition:background .16s ease,transform .16s ease}.control-buttons button.active{background:#f7fbff;color:#0d2d5f}.showcase-grid{display:grid;gap:20px;grid-template-columns:minmax(0,1.3fr) minmax(320px,.9fr)}.runtime-panel,.payload-panel{display:grid;gap:16px;padding:20px;border-radius:24px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline, #c5c7ce) 76%,transparent);background:linear-gradient(180deg,#fffffff0,#f4f7fbf5)}.panel-header{display:flex;align-items:start;justify-content:space-between;gap:12px}.panel-header h3{margin:0;color:#142033}.panel-chip{border-radius:999px;padding:6px 10px;font-size:.76rem;background:#1263b41f;color:#1263b4}.payload-panel pre{margin:0;padding:16px;border-radius:18px;background:#09111f;color:#d7e6ff;overflow:auto;font-size:.84rem;line-height:1.5}@media(max-width:1080px){.showcase-grid{grid-template-columns:1fr}}\n"] }]
|
|
2651
|
+
}] });
|
|
2652
|
+
|
|
2653
|
+
/*
|
|
2654
|
+
* Public API Surface of praxis-charts
|
|
2655
|
+
*/
|
|
2656
|
+
|
|
2657
|
+
/**
|
|
2658
|
+
* Generated bundle index. Do not edit.
|
|
2659
|
+
*/
|
|
2660
|
+
|
|
2661
|
+
export { PRAXIS_CHART_BACKEND_MOCK_BAR, PRAXIS_CHART_BACKEND_MOCK_DONUT, PRAXIS_CHART_BACKEND_MOCK_TIMESERIES, PRAXIS_CHART_COMPONENT_METADATA, PRAXIS_CHART_DRILLDOWN_DATA_BY_MONTH, PRAXIS_CHART_DRILLDOWN_PANEL_METADATA, PRAXIS_CHART_ENGINE, PRAXIS_CHART_STATE_PROBE_COMPONENT_METADATA, PraxisChartBackendPayloadAdapterService, PraxisChartCanonicalContractMapperService, PraxisChartComponent, PraxisChartCompositionShowcaseComponent, PraxisChartDataTransformerService, PraxisChartDrilldownPanelComponent, PraxisChartOptionBuilderService, PraxisChartSchemaMapperService, PraxisChartStateProbeComponent, PraxisChartStatsApiService, buildPraxisChartInteractiveGridPage, buildPraxisChartInteractiveWidgetPage, buildPraxisChartMockGridPage, buildPraxisChartMockWidgetPage, providePraxisChartDrilldownPanelMetadata, providePraxisChartStateProbeMetadata, providePraxisCharts, providePraxisChartsMetadata };
|
|
2662
|
+
//# sourceMappingURL=praxisui-charts.mjs.map
|