@quicdata/analytics 0.0.3 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/analytics.css +69 -0
  2. package/core/auth.d.ts +19 -0
  3. package/core/auth.d.ts.map +1 -0
  4. package/core/auth.js +34 -0
  5. package/core/fetch-data.d.ts +115 -0
  6. package/core/fetch-data.d.ts.map +1 -0
  7. package/core/fetch-data.js +210 -0
  8. package/core/index.d.ts +8 -1
  9. package/core/index.d.ts.map +1 -1
  10. package/core/index.js +4 -3
  11. package/core/types.d.ts +250 -0
  12. package/core/types.d.ts.map +1 -0
  13. package/core/types.js +1 -0
  14. package/core/viewport-observer.d.ts +26 -0
  15. package/core/viewport-observer.d.ts.map +1 -0
  16. package/core/viewport-observer.js +38 -0
  17. package/core/widget-transform-runner.d.ts +22 -0
  18. package/core/widget-transform-runner.d.ts.map +1 -0
  19. package/core/widget-transform-runner.js +102 -0
  20. package/dashboard/dashboard-container.d.ts +100 -0
  21. package/dashboard/dashboard-container.d.ts.map +1 -0
  22. package/dashboard/dashboard-container.js +315 -0
  23. package/dashboard/index.d.ts +4 -0
  24. package/dashboard/index.d.ts.map +1 -0
  25. package/dashboard/index.js +2 -0
  26. package/designer/analytics-designer.d.ts +40 -0
  27. package/designer/analytics-designer.d.ts.map +1 -0
  28. package/designer/analytics-designer.js +267 -0
  29. package/designer/index.d.ts +5 -0
  30. package/designer/index.d.ts.map +1 -0
  31. package/designer/index.js +3 -0
  32. package/filters/filter-bar.d.ts +34 -0
  33. package/filters/filter-bar.d.ts.map +1 -0
  34. package/filters/filter-bar.js +233 -0
  35. package/filters/filter-button.d.ts +22 -0
  36. package/filters/filter-button.d.ts.map +1 -0
  37. package/filters/filter-button.js +86 -0
  38. package/filters/index.d.ts +7 -0
  39. package/filters/index.d.ts.map +1 -0
  40. package/filters/index.js +6 -0
  41. package/filters/widget-toolbar.d.ts +24 -0
  42. package/filters/widget-toolbar.d.ts.map +1 -0
  43. package/filters/widget-toolbar.js +219 -0
  44. package/index.d.ts +9 -1
  45. package/index.js +6 -1
  46. package/package.json +34 -9
  47. package/widgets/analytics-report.d.ts +49 -0
  48. package/widgets/analytics-report.d.ts.map +1 -0
  49. package/widgets/analytics-report.js +306 -0
  50. package/widgets/analytics-widget.d.ts +39 -0
  51. package/widgets/analytics-widget.d.ts.map +1 -0
  52. package/widgets/analytics-widget.js +230 -0
  53. package/widgets/bar-chart.d.ts +13 -0
  54. package/widgets/bar-chart.d.ts.map +1 -0
  55. package/widgets/bar-chart.js +77 -0
  56. package/widgets/base-chart.d.ts +92 -0
  57. package/widgets/base-chart.d.ts.map +1 -0
  58. package/widgets/base-chart.js +524 -0
  59. package/widgets/index.d.ts +13 -1
  60. package/widgets/index.d.ts.map +1 -1
  61. package/widgets/index.js +14 -3
  62. package/widgets/line-chart.d.ts +13 -0
  63. package/widgets/line-chart.d.ts.map +1 -0
  64. package/widgets/line-chart.js +71 -0
  65. package/widgets/pie-chart.d.ts +13 -0
  66. package/widgets/pie-chart.d.ts.map +1 -0
  67. package/widgets/pie-chart.js +55 -0
  68. package/widgets/table.d.ts +96 -0
  69. package/widgets/table.d.ts.map +1 -0
  70. package/widgets/table.js +721 -0
  71. package/index.d.ts.map +0 -1
@@ -0,0 +1,524 @@
1
+ import { __decorate } from "tslib";
2
+ import { LitElement, html, css, nothing } from 'lit';
3
+ import { property, state } from 'lit/decorators.js';
4
+ import * as echarts from 'echarts';
5
+ import { buildWidgetDataUrl, fetchAnalyticsData, fetchWidgetData, fetchWidgetDefinition, } from '../core/fetch-data.js';
6
+ import { EVENT_DASHBOARD_FILTER_CHANGE, EVENT_DASHBOARD_REFRESH, } from '../dashboard/index.js';
7
+ import { observeViewport } from '../core/viewport-observer.js';
8
+ import '../filters/index.js';
9
+ /**
10
+ * Base Lit + ECharts widget. Fetches data from data-url (Laravel analytics API)
11
+ * or uses inline data, then renders Apache ECharts.
12
+ * When dashboard is set, merges dashboard params with dataParams and listens for
13
+ * analytics-dashboard-filter-change and analytics-dashboard-refresh.
14
+ */
15
+ export class BaseChartWidget extends LitElement {
16
+ static { this.styles = css `
17
+ :host {
18
+ display: flex;
19
+ flex-direction: column;
20
+ width: 100%;
21
+ height: 100%;
22
+ min-height: 120px;
23
+ }
24
+ .chart {
25
+ width: 100%;
26
+ height: 100%;
27
+ min-height: 120px;
28
+ }
29
+ .error {
30
+ color: #c00;
31
+ padding: 0.5rem;
32
+ font-size: 0.875rem;
33
+ }
34
+ .loading-body {
35
+ display: flex;
36
+ align-items: center;
37
+ justify-content: center;
38
+ min-height: 100px;
39
+ padding: 1rem;
40
+ }
41
+ .loading-overlay {
42
+ position: absolute;
43
+ inset: 0;
44
+ z-index: 2;
45
+ display: flex;
46
+ align-items: center;
47
+ justify-content: center;
48
+ background: rgba(255, 255, 255, 0.65);
49
+ backdrop-filter: blur(4px);
50
+ -webkit-backdrop-filter: blur(4px);
51
+ border-radius: 4px;
52
+ }
53
+ .loading-spinner {
54
+ width: 2rem;
55
+ height: 2rem;
56
+ border: 2px solid #e5e7eb;
57
+ border-top-color: #3b82f6;
58
+ border-radius: 50%;
59
+ animation: analytics-spin 0.7s linear infinite;
60
+ }
61
+ @keyframes analytics-spin {
62
+ to { transform: rotate(360deg); }
63
+ }
64
+ .widget-with-toolbar {
65
+ position: relative;
66
+ width: 100%;
67
+ height: 100%;
68
+ min-height: 120px;
69
+ display: flex;
70
+ flex-direction: column;
71
+ }
72
+ .widget-with-toolbar:hover analytics-widget-toolbar {
73
+ opacity: 1;
74
+ pointer-events: auto;
75
+ }
76
+ .widget-header {
77
+ flex-shrink: 0;
78
+ padding: 0.5rem 0.75rem 0.25rem 0.75rem;
79
+ min-width: 0;
80
+ }
81
+ .widget-title {
82
+ margin: 0;
83
+ font-size: 0.9375rem;
84
+ font-weight: 600;
85
+ color: #374151;
86
+ line-height: 1.3;
87
+ display: -webkit-box;
88
+ -webkit-box-orient: vertical;
89
+ -webkit-line-clamp: 1;
90
+ overflow: hidden;
91
+ text-overflow: ellipsis;
92
+ }
93
+ .widget-body {
94
+ flex: 1;
95
+ min-height: 0;
96
+ position: relative;
97
+ box-sizing: border-box;
98
+ }
99
+ .widget-body .chart {
100
+ height: 100%;
101
+ min-height: 100px;
102
+ }
103
+ `; }
104
+ /** Delay (ms) after last resize event before calling chart.resize(). */
105
+ static { this._RESIZE_DELAY_MS = 300; }
106
+ constructor() {
107
+ super();
108
+ this._loadGeneration = 0;
109
+ this._loadDataScheduled = false;
110
+ this._ignoreNextFilterChange = false;
111
+ this._chart = null;
112
+ this._dashboardEl = null;
113
+ this._resizeObserver = null;
114
+ /** Single debounce: each resize cancels previous, then we wait and call chart.resize() once. */
115
+ this._resizeTimeoutId = null;
116
+ this._hasLoadedOnce = false;
117
+ this._viewportUnobserve = null;
118
+ this._boundOnDashboardFilterChange = (e) => {
119
+ const ce = e;
120
+ this._dashboardParams = { ...(ce.detail?.params ?? {}) };
121
+ this._loadData(); // refetch with new dashboard params
122
+ };
123
+ this._boundOnDashboardRefresh = () => {
124
+ if (this.lazy && !this._viewportVisible)
125
+ return;
126
+ this._loadData(); // no state change; refetch with current params
127
+ };
128
+ this.title = '';
129
+ this.dataUrl = '';
130
+ this.widgetId = '';
131
+ this.apiUrl = '';
132
+ this.dataParams = {};
133
+ this.dashboard = null;
134
+ this.data = null;
135
+ this.meta = { columns: {} };
136
+ this.options = {};
137
+ this.lazy = false;
138
+ this.prefetchMargin = '200px';
139
+ this.initialDefinition = undefined;
140
+ this._loading = false;
141
+ this._error = null;
142
+ this._dashboardParams = {};
143
+ this._widgetFilters = [];
144
+ this._widgetFilterParams = {};
145
+ this._hideFilter = false;
146
+ this._widgetData = null;
147
+ this._viewportVisible = true;
148
+ this._definitionTitle = '';
149
+ }
150
+ connectedCallback() {
151
+ super.connectedCallback();
152
+ this._attachDashboard();
153
+ }
154
+ disconnectedCallback() {
155
+ this._viewportUnobserve?.();
156
+ this._viewportUnobserve = null;
157
+ this._detachDashboard();
158
+ if (this._resizeTimeoutId != null) {
159
+ clearTimeout(this._resizeTimeoutId);
160
+ this._resizeTimeoutId = null;
161
+ }
162
+ this._resizeObserver?.disconnect();
163
+ this._chart?.dispose();
164
+ this._chart = null;
165
+ super.disconnectedCallback();
166
+ }
167
+ updated(changed) {
168
+ if (changed.has('dashboard')) {
169
+ this._detachDashboard();
170
+ this._attachDashboard();
171
+ }
172
+ if (changed.has('widgetId') || changed.has('apiUrl')) {
173
+ this._widgetFilters = [];
174
+ this._widgetFilterParams = {};
175
+ this._definitionTitle = '';
176
+ this._ignoreNextFilterChange = true;
177
+ }
178
+ const onlyFilterState = (changed.has('_widgetFilterParams') || changed.has('_widgetFilters')) &&
179
+ !changed.has('dataUrl') &&
180
+ !changed.has('dataParams') &&
181
+ !changed.has('widgetId') &&
182
+ !changed.has('apiUrl');
183
+ if (onlyFilterState && this._ignoreNextFilterChange) {
184
+ this._ignoreNextFilterChange = false;
185
+ }
186
+ else if (changed.has('dataUrl') ||
187
+ changed.has('dataParams') ||
188
+ changed.has('widgetId') ||
189
+ changed.has('apiUrl') ||
190
+ changed.has('_widgetFilterParams')) {
191
+ if (!this._loadDataScheduled) {
192
+ this._loadDataScheduled = true;
193
+ queueMicrotask(() => {
194
+ this._loadDataScheduled = false;
195
+ this._loadData();
196
+ });
197
+ }
198
+ }
199
+ if (changed.has('lazy')) {
200
+ this._viewportVisible = !this.lazy;
201
+ }
202
+ if (changed.has('data') || changed.has('meta') || changed.has('options') || this._chart) {
203
+ this._renderChart();
204
+ }
205
+ }
206
+ _attachDashboard() {
207
+ const d = this.dashboard;
208
+ if (!d)
209
+ return;
210
+ this._dashboardEl = d;
211
+ this._dashboardParams = typeof d.getDashboardParams === 'function' ? d.getDashboardParams() : {};
212
+ d.addEventListener(EVENT_DASHBOARD_FILTER_CHANGE, this._boundOnDashboardFilterChange);
213
+ d.addEventListener(EVENT_DASHBOARD_REFRESH, this._boundOnDashboardRefresh);
214
+ }
215
+ _detachDashboard() {
216
+ const d = this._dashboardEl;
217
+ if (!d)
218
+ return;
219
+ d.removeEventListener(EVENT_DASHBOARD_FILTER_CHANGE, this._boundOnDashboardFilterChange);
220
+ d.removeEventListener(EVENT_DASHBOARD_REFRESH, this._boundOnDashboardRefresh);
221
+ this._dashboardEl = null;
222
+ this._dashboardParams = {};
223
+ }
224
+ firstUpdated() {
225
+ const container = this.renderRoot.querySelector('.chart');
226
+ if (container) {
227
+ this._resizeObserver = new ResizeObserver(() => {
228
+ if (this._resizeTimeoutId != null) {
229
+ clearTimeout(this._resizeTimeoutId);
230
+ this._resizeTimeoutId = null;
231
+ }
232
+ this._resizeTimeoutId = setTimeout(() => {
233
+ this._resizeTimeoutId = null;
234
+ this._chart?.resize();
235
+ }, BaseChartWidget._RESIZE_DELAY_MS);
236
+ });
237
+ this._resizeObserver.observe(container);
238
+ }
239
+ if (this.lazy) {
240
+ this._viewportVisible = false;
241
+ this._viewportUnobserve = observeViewport(this, { rootMargin: this.prefetchMargin || '200px', threshold: 0 }, {
242
+ onEnter: () => {
243
+ this._viewportVisible = true;
244
+ if (!this._hasLoadedOnce) {
245
+ this._hasLoadedOnce = true;
246
+ this._loadData();
247
+ }
248
+ },
249
+ onLeave: () => {
250
+ this._viewportVisible = false;
251
+ },
252
+ });
253
+ }
254
+ }
255
+ _getEffectiveDataUrl() {
256
+ if (this.apiUrl && this.widgetId) {
257
+ return buildWidgetDataUrl(this.apiUrl, this.widgetId);
258
+ }
259
+ return this.dataUrl;
260
+ }
261
+ /** Display title: optional host `title` overrides definition title from API. */
262
+ get _effectiveTitle() {
263
+ return (this.title != null && this.title !== '') ? this.title : (this._definitionTitle ?? '');
264
+ }
265
+ /** Run a callback after the current update cycle to avoid Lit "change-in-update" warning. */
266
+ _defer(fn) {
267
+ if (typeof requestAnimationFrame !== 'undefined') {
268
+ requestAnimationFrame(fn);
269
+ }
270
+ else {
271
+ setTimeout(fn, 0);
272
+ }
273
+ }
274
+ _getEffectiveParams() {
275
+ const dashboardParams = this._dashboardParams;
276
+ const widgetParams = this.dataParams ?? {};
277
+ let widgetFilterParams = {};
278
+ if (this._hideFilter) {
279
+ for (const f of this._widgetFilters) {
280
+ if (f.default_value !== undefined && f.default_value !== null) {
281
+ widgetFilterParams[f.param_name] = f.default_value;
282
+ }
283
+ }
284
+ }
285
+ else if (this._widgetFilters.length > 0) {
286
+ for (const f of this._widgetFilters) {
287
+ const setByUser = f.param_name in this._widgetFilterParams;
288
+ widgetFilterParams[f.param_name] = setByUser
289
+ ? (this._widgetFilterParams[f.param_name] ?? '')
290
+ : (f.default_value ?? '');
291
+ }
292
+ }
293
+ else {
294
+ widgetFilterParams = { ...this._widgetFilterParams };
295
+ }
296
+ const out = { ...widgetFilterParams, ...widgetParams };
297
+ if (Object.keys(dashboardParams).length > 0) {
298
+ out.dashboard_params = dashboardParams;
299
+ }
300
+ return out;
301
+ }
302
+ async _loadData() {
303
+ if (this.lazy && !this._viewportVisible)
304
+ return;
305
+ const dataUrl = this._getEffectiveDataUrl();
306
+ if (!dataUrl) {
307
+ this._error = null;
308
+ this._loading = false;
309
+ this._renderChart();
310
+ return;
311
+ }
312
+ const gen = ++this._loadGeneration;
313
+ this._loading = true;
314
+ this._error = null;
315
+ try {
316
+ const useWidgetApi = Boolean(this.apiUrl && this.widgetId);
317
+ if (useWidgetApi) {
318
+ let params;
319
+ if (this._widgetFilters.length === 0) {
320
+ const def = this.initialDefinition ?? (await fetchWidgetDefinition(this.apiUrl, this.widgetId));
321
+ this._definitionTitle = def.title ?? '';
322
+ this._widgetFilters = def.filters ?? [];
323
+ this._hideFilter = def.hide_filter ?? false;
324
+ const defaultParams = {};
325
+ for (const f of (def.filters ?? [])) {
326
+ if (f.default_value !== undefined && f.default_value !== null) {
327
+ defaultParams[f.param_name] = f.default_value;
328
+ }
329
+ }
330
+ this._widgetFilterParams = { ...defaultParams, ...this._widgetFilterParams };
331
+ this._ignoreNextFilterChange = true;
332
+ params = { ...defaultParams, ...(this.dataParams ?? {}) };
333
+ if (Object.keys(this._dashboardParams).length > 0) {
334
+ params.dashboard_params = this._dashboardParams;
335
+ }
336
+ }
337
+ else {
338
+ params = this._getEffectiveParams();
339
+ }
340
+ const res = await fetchWidgetData(dataUrl, params);
341
+ if (gen !== this._loadGeneration)
342
+ return;
343
+ const data = res.data ?? [];
344
+ const meta = res.meta ?? { columns: {} };
345
+ const widgetData = res.widgetData ?? null;
346
+ const filters = res.filters;
347
+ const hideFilter = res.hide_filter;
348
+ this._defer(() => {
349
+ if (gen !== this._loadGeneration)
350
+ return;
351
+ this.data = data;
352
+ this.meta = meta;
353
+ this._widgetData = widgetData;
354
+ if (filters?.length)
355
+ this._widgetFilters = filters;
356
+ if (hideFilter !== undefined)
357
+ this._hideFilter = hideFilter;
358
+ this._loading = false;
359
+ this._renderChart();
360
+ });
361
+ }
362
+ else {
363
+ const params = this._getEffectiveParams();
364
+ const res = await fetchAnalyticsData(dataUrl, params);
365
+ if (gen !== this._loadGeneration)
366
+ return;
367
+ const data = res.data ?? [];
368
+ const meta = res.meta ?? { columns: {} };
369
+ this._defer(() => {
370
+ if (gen !== this._loadGeneration)
371
+ return;
372
+ this.data = data;
373
+ this.meta = meta;
374
+ this._widgetData = null;
375
+ this._loading = false;
376
+ this._renderChart();
377
+ });
378
+ }
379
+ }
380
+ catch (e) {
381
+ const errMsg = e instanceof Error ? e.message : String(e);
382
+ this._defer(() => {
383
+ if (gen !== this._loadGeneration)
384
+ return;
385
+ this._error = errMsg;
386
+ this.data = [];
387
+ this._widgetData = null;
388
+ this._loading = false;
389
+ this._renderChart();
390
+ });
391
+ }
392
+ }
393
+ _renderChart() {
394
+ const container = this.renderRoot.querySelector('.chart');
395
+ if (!container)
396
+ return;
397
+ const rows = this.data ?? [];
398
+ const meta = this.meta?.columns ?? {};
399
+ const baseOption = this.buildOption(rows, meta, this._widgetData ?? null);
400
+ const merged = {
401
+ ...baseOption,
402
+ ...this.options,
403
+ };
404
+ // Re-init if container changed (e.g. after loading/error view replaced DOM)
405
+ if (this._chart && this._chart.getDom() !== container) {
406
+ this._chart.dispose();
407
+ this._chart = null;
408
+ }
409
+ if (!this._chart) {
410
+ this._chart = echarts.init(container);
411
+ }
412
+ this._chart.setOption(merged, { notMerge: true });
413
+ }
414
+ /**
415
+ * Build ECharts option from API data. Override in subclasses (bar, line, pie).
416
+ * When widgetData has categories + series (e.g. from split_by), use those for bar/line.
417
+ * Default: generic grid + empty series.
418
+ */
419
+ buildOption(_data, _meta, _widgetData) {
420
+ return {
421
+ grid: { left: 48, right: 24, top: 24, bottom: 48 },
422
+ xAxis: { type: 'category', data: [] },
423
+ yAxis: { type: 'value' },
424
+ series: [],
425
+ };
426
+ }
427
+ _onWidgetFilterChange(e) {
428
+ this._widgetFilterParams = { ...(e.detail?.params ?? {}) };
429
+ }
430
+ render() {
431
+ const hasWidgetFilters = this._widgetFilters.length > 0 && !this._hideFilter;
432
+ const widgetActiveCount = Object.keys(this._widgetFilterParams).filter((k) => this._widgetFilterParams[k] !== '' && this._widgetFilterParams[k] != null).length;
433
+ return html `
434
+ <div class="widget-with-toolbar">
435
+ ${this._effectiveTitle
436
+ ? html `<header class="widget-header"><h2 class="widget-title">${this._effectiveTitle}</h2></header>`
437
+ : ''}
438
+ <div class="widget-body">
439
+ ${this._error
440
+ ? html `<div class="error">${this._error}</div>`
441
+ : html `<div class="chart"></div>`}
442
+ ${this._loading
443
+ ? html `<div class="loading-overlay" aria-busy="true" aria-live="polite">
444
+ <div class="loading-spinner" aria-hidden="true"></div>
445
+ </div>`
446
+ : nothing}
447
+ </div>
448
+ ${hasWidgetFilters
449
+ ? html `
450
+ <analytics-widget-toolbar
451
+ .filters=${this._widgetFilters}
452
+ .values=${this._widgetFilterParams}
453
+ .activeCount=${widgetActiveCount}
454
+ @analytics-filter-change=${this._onWidgetFilterChange}
455
+ ></analytics-widget-toolbar>
456
+ `
457
+ : ''}
458
+ </div>
459
+ `;
460
+ }
461
+ }
462
+ __decorate([
463
+ property({ type: String })
464
+ ], BaseChartWidget.prototype, "title", void 0);
465
+ __decorate([
466
+ property({ type: String, attribute: 'data-url' })
467
+ ], BaseChartWidget.prototype, "dataUrl", void 0);
468
+ __decorate([
469
+ property({ type: String, attribute: 'widget-id' })
470
+ ], BaseChartWidget.prototype, "widgetId", void 0);
471
+ __decorate([
472
+ property({ type: String, attribute: 'api-url' })
473
+ ], BaseChartWidget.prototype, "apiUrl", void 0);
474
+ __decorate([
475
+ property({ type: Object })
476
+ ], BaseChartWidget.prototype, "dataParams", void 0);
477
+ __decorate([
478
+ property({ type: Object })
479
+ ], BaseChartWidget.prototype, "dashboard", void 0);
480
+ __decorate([
481
+ property({ type: Object })
482
+ ], BaseChartWidget.prototype, "data", void 0);
483
+ __decorate([
484
+ property({ type: Object })
485
+ ], BaseChartWidget.prototype, "meta", void 0);
486
+ __decorate([
487
+ property({ type: Object })
488
+ ], BaseChartWidget.prototype, "options", void 0);
489
+ __decorate([
490
+ property({ type: Boolean })
491
+ ], BaseChartWidget.prototype, "lazy", void 0);
492
+ __decorate([
493
+ property({ type: String, attribute: 'prefetch-margin' })
494
+ ], BaseChartWidget.prototype, "prefetchMargin", void 0);
495
+ __decorate([
496
+ property({ type: Object })
497
+ ], BaseChartWidget.prototype, "initialDefinition", void 0);
498
+ __decorate([
499
+ state()
500
+ ], BaseChartWidget.prototype, "_loading", void 0);
501
+ __decorate([
502
+ state()
503
+ ], BaseChartWidget.prototype, "_error", void 0);
504
+ __decorate([
505
+ state()
506
+ ], BaseChartWidget.prototype, "_dashboardParams", void 0);
507
+ __decorate([
508
+ state()
509
+ ], BaseChartWidget.prototype, "_widgetFilters", void 0);
510
+ __decorate([
511
+ state()
512
+ ], BaseChartWidget.prototype, "_widgetFilterParams", void 0);
513
+ __decorate([
514
+ state()
515
+ ], BaseChartWidget.prototype, "_hideFilter", void 0);
516
+ __decorate([
517
+ state()
518
+ ], BaseChartWidget.prototype, "_widgetData", void 0);
519
+ __decorate([
520
+ state()
521
+ ], BaseChartWidget.prototype, "_viewportVisible", void 0);
522
+ __decorate([
523
+ state()
524
+ ], BaseChartWidget.prototype, "_definitionTitle", void 0);
@@ -1,2 +1,14 @@
1
- export declare const widget: () => string;
1
+ export { BaseChartWidget } from './base-chart.js';
2
+ export { BarChartWidget } from './bar-chart.js';
3
+ export { LineChartWidget } from './line-chart.js';
4
+ export { PieChartWidget } from './pie-chart.js';
5
+ export { TableWidget } from './table.js';
6
+ export { AnalyticsWidget } from './analytics-widget.js';
7
+ export { AnalyticsReport } from './analytics-report.js';
8
+ import './bar-chart.js';
9
+ import './line-chart.js';
10
+ import './pie-chart.js';
11
+ import './table.js';
12
+ import './analytics-widget.js';
13
+ import './analytics-report.js';
2
14
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../libs/analytics/src/widgets/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,MAAM,cAElB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../libs/analytics/src/widgets/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAGxD,OAAO,gBAAgB,CAAC;AACxB,OAAO,iBAAiB,CAAC;AACzB,OAAO,gBAAgB,CAAC;AACxB,OAAO,YAAY,CAAC;AACpB,OAAO,uBAAuB,CAAC;AAC/B,OAAO,uBAAuB,CAAC"}
package/widgets/index.js CHANGED
@@ -1,3 +1,14 @@
1
- export const widget = () => {
2
- return 'widget';
3
- };
1
+ export { BaseChartWidget } from './base-chart.js';
2
+ export { BarChartWidget } from './bar-chart.js';
3
+ export { LineChartWidget } from './line-chart.js';
4
+ export { PieChartWidget } from './pie-chart.js';
5
+ export { TableWidget } from './table.js';
6
+ export { AnalyticsWidget } from './analytics-widget.js';
7
+ export { AnalyticsReport } from './analytics-report.js';
8
+ // Register custom elements so they work when imported (e.g. in designer/dashboard)
9
+ import './bar-chart.js';
10
+ import './line-chart.js';
11
+ import './pie-chart.js';
12
+ import './table.js';
13
+ import './analytics-widget.js';
14
+ import './analytics-report.js';
@@ -0,0 +1,13 @@
1
+ import type { EChartsOption } from 'echarts/types/dist/option';
2
+ import { BaseChartWidget } from './base-chart.js';
3
+ /**
4
+ * Line chart widget (Apache ECharts). Use data-url or inline data.
5
+ * When widgetData is present (from API), use its categories + series so x-axis is correct (e.g. split_by).
6
+ * Otherwise first column = categories, remaining = series.
7
+ */
8
+ export declare class LineChartWidget extends BaseChartWidget {
9
+ buildOption(data: Record<string, unknown>[], meta: Record<string, {
10
+ type?: string;
11
+ }>, widgetData?: unknown): EChartsOption;
12
+ }
13
+ //# sourceMappingURL=line-chart.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"line-chart.d.ts","sourceRoot":"","sources":["../../../../libs/analytics/src/widgets/line-chart.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAuBlD;;;;GAIG;AACH,qBACa,eAAgB,SAAQ,eAAe;IACzC,WAAW,CAClB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAC/B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,EACvC,UAAU,CAAC,EAAE,OAAO,GACnB,aAAa;CAgDjB"}
@@ -0,0 +1,71 @@
1
+ import { __decorate } from "tslib";
2
+ import { customElement } from 'lit/decorators.js';
3
+ import { BaseChartWidget } from './base-chart.js';
4
+ const LEGEND_NAME_MAX_LEN = 25;
5
+ function legendConfig(multipleSeries) {
6
+ if (!multipleSeries)
7
+ return undefined;
8
+ return {
9
+ type: 'scroll',
10
+ bottom: 8,
11
+ left: 'center',
12
+ formatter: (name) => name.length > LEGEND_NAME_MAX_LEN ? name.slice(0, LEGEND_NAME_MAX_LEN) + '…' : name,
13
+ };
14
+ }
15
+ /**
16
+ * Line chart widget (Apache ECharts). Use data-url or inline data.
17
+ * When widgetData is present (from API), use its categories + series so x-axis is correct (e.g. split_by).
18
+ * Otherwise first column = categories, remaining = series.
19
+ */
20
+ let LineChartWidget = class LineChartWidget extends BaseChartWidget {
21
+ buildOption(data, meta, widgetData) {
22
+ const wd = widgetData;
23
+ // Prefer widgetData whenever API sent it (categories = x-axis, series = one per split) so x-axis is never wrong.
24
+ if (wd && typeof wd === 'object' && Array.isArray(wd.categories)) {
25
+ const categories = wd.categories.map(String);
26
+ const seriesList = Array.isArray(wd.series) ? wd.series : [];
27
+ const series = seriesList.map((s) => ({
28
+ name: String(s?.name ?? ''),
29
+ type: 'line',
30
+ data: Array.isArray(s?.data) ? s.data.map((v) => Number(v) || 0) : [],
31
+ stack: wd.stack ? 'total' : undefined,
32
+ }));
33
+ const multiSeries = series.length > 1;
34
+ return {
35
+ grid: { left: 48, right: 24, top: 24, bottom: multiSeries ? 80 : 48 },
36
+ tooltip: { trigger: 'axis' },
37
+ legend: legendConfig(multiSeries),
38
+ xAxis: { type: 'category', data: categories },
39
+ yAxis: { type: 'value' },
40
+ series,
41
+ };
42
+ }
43
+ const keys = data.length ? Object.keys(data[0]) : Object.keys(meta);
44
+ if (keys.length === 0) {
45
+ return { xAxis: { type: 'category', data: [] }, yAxis: { type: 'value' }, series: [] };
46
+ }
47
+ const categoryKey = keys[0];
48
+ const valueKeys = keys.slice(1).filter((k) => meta[k]?.type === 'number' || typeof (data[0]?.[k]) === 'number');
49
+ const categories = [...new Set(data.map((r) => String(r[categoryKey] ?? '')))];
50
+ const series = valueKeys.length
51
+ ? valueKeys.map((name) => ({
52
+ name,
53
+ type: 'line',
54
+ data: data.map((r) => Number(r[name]) || 0),
55
+ }))
56
+ : [{ name: categoryKey, type: 'line', data: data.map((r) => Number(r[categoryKey]) || 0) }];
57
+ const multiSeries = series.length > 1;
58
+ return {
59
+ grid: { left: 48, right: 24, top: 24, bottom: multiSeries ? 80 : 48 },
60
+ tooltip: { trigger: 'axis' },
61
+ legend: legendConfig(multiSeries),
62
+ xAxis: { type: 'category', data: categories },
63
+ yAxis: { type: 'value' },
64
+ series,
65
+ };
66
+ }
67
+ };
68
+ LineChartWidget = __decorate([
69
+ customElement('analytics-line-chart')
70
+ ], LineChartWidget);
71
+ export { LineChartWidget };
@@ -0,0 +1,13 @@
1
+ import type { EChartsOption } from 'echarts/types/dist/option';
2
+ import { BaseChartWidget } from './base-chart.js';
3
+ /**
4
+ * Pie/Donut chart widget (Apache ECharts). Use data-url or inline data.
5
+ * When widgetData is present (from API), use its items so slice labels use backend formatters (e.g. status -> Pending, Approved).
6
+ * Otherwise: first column = name, second = value; or single column = count per category.
7
+ */
8
+ export declare class PieChartWidget extends BaseChartWidget {
9
+ buildOption(data: Record<string, unknown>[], meta: Record<string, {
10
+ type?: string;
11
+ }>, widgetData?: unknown): EChartsOption;
12
+ }
13
+ //# sourceMappingURL=pie-chart.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pie-chart.d.ts","sourceRoot":"","sources":["../../../../libs/analytics/src/widgets/pie-chart.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAQlD;;;;GAIG;AACH,qBACa,cAAe,SAAQ,eAAe;IACxC,WAAW,CAClB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAC/B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,EACvC,UAAU,CAAC,EAAE,OAAO,GACnB,aAAa;CA2CjB"}