@quicdata/analytics 0.0.2 → 0.0.4

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 (81) hide show
  1. package/core/auth.d.ts +19 -0
  2. package/core/auth.d.ts.map +1 -0
  3. package/core/auth.js +34 -0
  4. package/core/fetch-data.d.ts +115 -0
  5. package/core/fetch-data.d.ts.map +1 -0
  6. package/core/fetch-data.js +210 -0
  7. package/core/index.d.ts +9 -0
  8. package/core/index.d.ts.map +1 -0
  9. package/core/index.js +4 -0
  10. package/core/types.d.ts +250 -0
  11. package/core/types.d.ts.map +1 -0
  12. package/core/viewport-observer.d.ts +26 -0
  13. package/core/viewport-observer.d.ts.map +1 -0
  14. package/core/viewport-observer.js +38 -0
  15. package/core/widget-transform-runner.d.ts +22 -0
  16. package/core/widget-transform-runner.d.ts.map +1 -0
  17. package/core/widget-transform-runner.js +102 -0
  18. package/dashboard/dashboard-container.d.ts +100 -0
  19. package/dashboard/dashboard-container.d.ts.map +1 -0
  20. package/dashboard/dashboard-container.js +315 -0
  21. package/dashboard/index.d.ts +4 -0
  22. package/dashboard/index.d.ts.map +1 -0
  23. package/dashboard/index.js +2 -0
  24. package/designer/analytics-designer.d.ts +40 -0
  25. package/designer/analytics-designer.d.ts.map +1 -0
  26. package/designer/analytics-designer.js +267 -0
  27. package/designer/index.d.ts +5 -0
  28. package/designer/index.d.ts.map +1 -0
  29. package/designer/index.js +3 -0
  30. package/filters/filter-bar.d.ts +34 -0
  31. package/filters/filter-bar.d.ts.map +1 -0
  32. package/filters/filter-bar.js +233 -0
  33. package/filters/filter-button.d.ts +22 -0
  34. package/filters/filter-button.d.ts.map +1 -0
  35. package/filters/filter-button.js +86 -0
  36. package/filters/index.d.ts +7 -0
  37. package/filters/index.d.ts.map +1 -0
  38. package/filters/index.js +6 -0
  39. package/filters/widget-toolbar.d.ts +24 -0
  40. package/filters/widget-toolbar.d.ts.map +1 -0
  41. package/filters/widget-toolbar.js +216 -0
  42. package/index.d.ts +10 -0
  43. package/index.d.ts.map +1 -0
  44. package/index.js +6 -0
  45. package/package.json +27 -26
  46. package/widgets/analytics-report.d.ts +49 -0
  47. package/widgets/analytics-report.d.ts.map +1 -0
  48. package/widgets/analytics-report.js +306 -0
  49. package/widgets/analytics-widget.d.ts +39 -0
  50. package/widgets/analytics-widget.d.ts.map +1 -0
  51. package/widgets/analytics-widget.js +230 -0
  52. package/widgets/bar-chart.d.ts +13 -0
  53. package/widgets/bar-chart.d.ts.map +1 -0
  54. package/widgets/bar-chart.js +77 -0
  55. package/widgets/base-chart.d.ts +94 -0
  56. package/widgets/base-chart.d.ts.map +1 -0
  57. package/widgets/base-chart.js +535 -0
  58. package/widgets/index.d.ts +14 -0
  59. package/widgets/index.d.ts.map +1 -0
  60. package/widgets/index.js +14 -0
  61. package/widgets/line-chart.d.ts +13 -0
  62. package/widgets/line-chart.d.ts.map +1 -0
  63. package/widgets/line-chart.js +71 -0
  64. package/widgets/pie-chart.d.ts +13 -0
  65. package/widgets/pie-chart.d.ts.map +1 -0
  66. package/widgets/pie-chart.js +55 -0
  67. package/widgets/table.d.ts +101 -0
  68. package/widgets/table.d.ts.map +1 -0
  69. package/widgets/table.js +740 -0
  70. package/workers/widget-transform.worker.d.ts +21 -0
  71. package/workers/widget-transform.worker.d.ts.map +1 -0
  72. package/workers/widget-transform.worker.js +30 -0
  73. package/src/core/index.d.ts +0 -2
  74. package/src/core/index.d.ts.map +0 -1
  75. package/src/core/index.js +0 -3
  76. package/src/index.d.ts +0 -2
  77. package/src/index.d.ts.map +0 -1
  78. package/src/widgets/index.d.ts +0 -2
  79. package/src/widgets/index.d.ts.map +0 -1
  80. package/src/widgets/index.js +0 -3
  81. /package/{src/index.js → core/types.js} +0 -0
@@ -0,0 +1,306 @@
1
+ import { __decorate } from "tslib";
2
+ import { LitElement, html, css, nothing } from 'lit';
3
+ import { customElement, property, state } from 'lit/decorators.js';
4
+ import { fetchReportDefinition, buildReportDataUrl, buildReportPreviewUrl, buildReportPdfUrl, buildReportExcelUrl, appendParamsToUrl, } from '../core/fetch-data.js';
5
+ import { EVENT_FILTER_CHANGE } from '../filters/filter-bar.js';
6
+ import '../filters/filter-bar.js';
7
+ import './table.js';
8
+ /**
9
+ * Analytics report component: show report by report definition id.
10
+ * Similar to analytics-widget: has filters, print, and export (PDF/Excel) buttons.
11
+ * Renders report data inline with analytics-table (same as table widget); filter bar above; toolbar with Print, Download PDF, Download Excel.
12
+ */
13
+ let AnalyticsReport = class AnalyticsReport extends LitElement {
14
+ static { this.styles = css `
15
+ :host {
16
+ display: block;
17
+ width: 100%;
18
+ height: 100%;
19
+ min-height: 200px;
20
+ }
21
+ .report-root {
22
+ display: flex;
23
+ flex-direction: column;
24
+ width: 100%;
25
+ height: 100%;
26
+ min-height: 200px;
27
+ }
28
+ .report-header {
29
+ flex-shrink: 0;
30
+ display: flex;
31
+ align-items: center;
32
+ justify-content: space-between;
33
+ gap: 1rem;
34
+ padding: 0.5rem 1rem;
35
+ border-bottom: 1px solid var(--analytics-report-border, #e2e8f0);
36
+ background: var(--analytics-report-header-bg, #f8fafc);
37
+ }
38
+ .report-title {
39
+ font-size: 1rem;
40
+ font-weight: 600;
41
+ color: var(--analytics-report-title-color, #1e293b);
42
+ margin: 0;
43
+ }
44
+ .report-actions {
45
+ display: flex;
46
+ align-items: center;
47
+ gap: 0.5rem;
48
+ }
49
+ .report-actions button,
50
+ .report-actions a {
51
+ padding: 0.375rem 0.75rem;
52
+ font-size: 0.875rem;
53
+ border-radius: 6px;
54
+ border: 1px solid var(--analytics-report-btn-border, #cbd5e1);
55
+ background: var(--analytics-report-btn-bg, #fff);
56
+ color: var(--analytics-report-btn-color, #334155);
57
+ cursor: pointer;
58
+ text-decoration: none;
59
+ }
60
+ .report-actions button:hover,
61
+ .report-actions a:hover {
62
+ background: var(--analytics-report-btn-hover-bg, #f1f5f9);
63
+ color: var(--analytics-report-btn-hover-color, #0f172a);
64
+ }
65
+ .report-filters {
66
+ flex-shrink: 0;
67
+ border-bottom: 1px solid var(--analytics-report-border, #e2e8f0);
68
+ }
69
+ .report-table-wrap {
70
+ flex: 1;
71
+ min-height: 0;
72
+ display: flex;
73
+ flex-direction: column;
74
+ background: #fff;
75
+ }
76
+ .report-table-wrap analytics-table {
77
+ flex: 1;
78
+ min-height: 0;
79
+ }
80
+ .loading,
81
+ .error {
82
+ display: flex;
83
+ align-items: center;
84
+ justify-content: center;
85
+ min-height: 200px;
86
+ padding: 1rem;
87
+ color: #64748b;
88
+ }
89
+ .error {
90
+ color: #b91c1c;
91
+ }
92
+ .loading-spinner {
93
+ width: 2rem;
94
+ height: 2rem;
95
+ border: 2px solid #e2e8f0;
96
+ border-top-color: #3b82f6;
97
+ border-radius: 50%;
98
+ animation: analytics-report-spin 0.7s linear infinite;
99
+ }
100
+ @keyframes analytics-report-spin {
101
+ to { transform: rotate(360deg); }
102
+ }
103
+ `; }
104
+ constructor() {
105
+ super();
106
+ this.reportId = '';
107
+ this.apiUrl = '';
108
+ this.customTitle = '';
109
+ this.title = '';
110
+ this.dataParams = {};
111
+ this._name = '';
112
+ this._filters = [];
113
+ this._params = { ...(this.dataParams ?? {}) };
114
+ this._previewUrlBase = '';
115
+ this._pdfUrlBase = '';
116
+ this._excelUrlBase = '';
117
+ this._loading = false;
118
+ this._error = null;
119
+ }
120
+ connectedCallback() {
121
+ super.connectedCallback();
122
+ this.addEventListener(EVENT_FILTER_CHANGE, this._onFilterChange);
123
+ }
124
+ disconnectedCallback() {
125
+ super.disconnectedCallback();
126
+ this.removeEventListener(EVENT_FILTER_CHANGE, this._onFilterChange);
127
+ }
128
+ updated(changed) {
129
+ if (!this.apiUrl || !this.reportId || this._loading)
130
+ return;
131
+ if (changed.has('reportId') || changed.has('apiUrl')) {
132
+ this._name = '';
133
+ this._filters = [];
134
+ this._previewUrlBase = '';
135
+ this._pdfUrlBase = '';
136
+ this._excelUrlBase = '';
137
+ this._params = { ...(this.dataParams ?? {}) };
138
+ this._loadReport();
139
+ }
140
+ else if (this._name === '' && !this._error) {
141
+ this._loadReport();
142
+ }
143
+ }
144
+ async _loadReport() {
145
+ if (!this.apiUrl || !this.reportId || this._loading)
146
+ return;
147
+ this._loading = true;
148
+ this._error = null;
149
+ try {
150
+ const def = await fetchReportDefinition(this.apiUrl, this.reportId);
151
+ this._name = def.name ?? '';
152
+ this._filters = def.filters ?? [];
153
+ this._params = this._paramsFromFiltersAndDataParams(this._filters);
154
+ this._previewUrlBase = def.preview_url ?? '';
155
+ this._pdfUrlBase = def.download_pdf_url ?? '';
156
+ this._excelUrlBase = def.download_excel_url ?? '';
157
+ }
158
+ catch (e) {
159
+ this._error = e instanceof Error ? e.message : String(e);
160
+ this._name = '';
161
+ this._filters = [];
162
+ this._previewUrlBase = '';
163
+ this._pdfUrlBase = '';
164
+ this._excelUrlBase = '';
165
+ }
166
+ finally {
167
+ this._loading = false;
168
+ }
169
+ }
170
+ /** Build initial params: dataParams first, then filter default_value for any param not set. Runs from beginning so first data request uses defaults. */
171
+ _paramsFromFiltersAndDataParams(filters) {
172
+ const out = { ...(this.dataParams ?? {}) };
173
+ for (const f of filters) {
174
+ if (f.param_name && (f.param_name in out))
175
+ continue;
176
+ if (f.default_value !== undefined && f.default_value !== null) {
177
+ out[f.param_name] = f.default_value;
178
+ }
179
+ }
180
+ return out;
181
+ }
182
+ _onFilterChange(e) {
183
+ if (e.detail?.params) {
184
+ this._params = { ...e.detail.params };
185
+ }
186
+ }
187
+ get _dataUrl() {
188
+ if (!this.apiUrl || !this.reportId)
189
+ return '';
190
+ return buildReportDataUrl(this.apiUrl, this.reportId);
191
+ }
192
+ get _previewUrl() {
193
+ if (this._previewUrlBase)
194
+ return appendParamsToUrl(this._previewUrlBase, this._params);
195
+ if (!this.apiUrl || !this.reportId)
196
+ return '';
197
+ return buildReportPreviewUrl(this.apiUrl, this.reportId, this._params);
198
+ }
199
+ get _pdfUrl() {
200
+ if (this._pdfUrlBase)
201
+ return appendParamsToUrl(this._pdfUrlBase, this._params);
202
+ if (!this.apiUrl || !this.reportId)
203
+ return '#';
204
+ return buildReportPdfUrl(this.apiUrl, this.reportId, this._params);
205
+ }
206
+ get _excelUrl() {
207
+ if (this._excelUrlBase)
208
+ return appendParamsToUrl(this._excelUrlBase, this._params);
209
+ if (!this.apiUrl || !this.reportId)
210
+ return '#';
211
+ return buildReportExcelUrl(this.apiUrl, this.reportId, this._params);
212
+ }
213
+ _print() {
214
+ window.open(this._previewUrl, '_blank', 'noopener')?.print();
215
+ }
216
+ render() {
217
+ if (this._loading) {
218
+ return html `
219
+ <div class="loading" aria-busy="true" aria-live="polite">
220
+ <div class="loading-spinner" aria-hidden="true"></div>
221
+ </div>
222
+ `;
223
+ }
224
+ if (this._error) {
225
+ return html `<div class="error" role="alert">${this._error}</div>`;
226
+ }
227
+ if (!this.reportId || !this.apiUrl) {
228
+ return nothing;
229
+ }
230
+ const custom = (this.customTitle ?? '').trim();
231
+ const displayTitle = custom !== '' ? custom : (this._name || 'Report');
232
+ return html `
233
+ <div class="report-root">
234
+ <div class="report-header">
235
+ <h1 class="report-title">${displayTitle}</h1>
236
+ <div class="report-actions">
237
+ <button type="button" @click=${this._print}>Print</button>
238
+ <a href=${this._pdfUrl} target="_blank" rel="noopener" download>Download PDF</a>
239
+ <a href=${this._excelUrl} target="_blank" rel="noopener" download>Download Excel</a>
240
+ </div>
241
+ </div>
242
+ ${this._filters.length > 0
243
+ ? html `
244
+ <div class="report-filters">
245
+ <analytics-filter-bar
246
+ .filters=${this._filters}
247
+ .values=${this._params}
248
+ .showClose=${false}
249
+ ></analytics-filter-bar>
250
+ </div>
251
+ `
252
+ : ''}
253
+ <div class="report-table-wrap">
254
+ <analytics-table
255
+ data-url=${this._dataUrl}
256
+ .dataParams=${this._params}
257
+ .title=${''}
258
+ ></analytics-table>
259
+ </div>
260
+ </div>
261
+ `;
262
+ }
263
+ };
264
+ __decorate([
265
+ property({ type: String, attribute: 'report-id' })
266
+ ], AnalyticsReport.prototype, "reportId", void 0);
267
+ __decorate([
268
+ property({ type: String, attribute: 'api-url' })
269
+ ], AnalyticsReport.prototype, "apiUrl", void 0);
270
+ __decorate([
271
+ property({ type: String, attribute: 'custom-title' })
272
+ ], AnalyticsReport.prototype, "customTitle", void 0);
273
+ __decorate([
274
+ property({ type: String })
275
+ ], AnalyticsReport.prototype, "title", void 0);
276
+ __decorate([
277
+ property({ type: Object })
278
+ ], AnalyticsReport.prototype, "dataParams", void 0);
279
+ __decorate([
280
+ state()
281
+ ], AnalyticsReport.prototype, "_name", void 0);
282
+ __decorate([
283
+ state()
284
+ ], AnalyticsReport.prototype, "_filters", void 0);
285
+ __decorate([
286
+ state()
287
+ ], AnalyticsReport.prototype, "_params", void 0);
288
+ __decorate([
289
+ state()
290
+ ], AnalyticsReport.prototype, "_previewUrlBase", void 0);
291
+ __decorate([
292
+ state()
293
+ ], AnalyticsReport.prototype, "_pdfUrlBase", void 0);
294
+ __decorate([
295
+ state()
296
+ ], AnalyticsReport.prototype, "_excelUrlBase", void 0);
297
+ __decorate([
298
+ state()
299
+ ], AnalyticsReport.prototype, "_loading", void 0);
300
+ __decorate([
301
+ state()
302
+ ], AnalyticsReport.prototype, "_error", void 0);
303
+ AnalyticsReport = __decorate([
304
+ customElement('analytics-report')
305
+ ], AnalyticsReport);
306
+ export { AnalyticsReport };
@@ -0,0 +1,39 @@
1
+ import { LitElement, nothing } from 'lit';
2
+ import type { DashboardContainer } from '../dashboard/dashboard-container.js';
3
+ import './bar-chart.js';
4
+ import './line-chart.js';
5
+ import './pie-chart.js';
6
+ import './table.js';
7
+ /**
8
+ * Generic analytics widget: pass widgetId + apiUrl (and optional config);
9
+ * fetches the widget definition and renders the appropriate chart/table internally.
10
+ * Use this so developers don't need to know the chart type.
11
+ */
12
+ export declare class AnalyticsWidget extends LitElement {
13
+ static styles: import("lit").CSSResult;
14
+ /** Widget id (used with apiUrl to load definition and data). */
15
+ widgetId: string;
16
+ /** API base URL (e.g. /api/analytics). Used with widgetId to fetch definition and data. */
17
+ apiUrl: string;
18
+ /** Optional title override. When unset, the inner widget uses the title from the definition. */
19
+ title: string;
20
+ /** Query params for data requests (e.g. date_from, date_to). */
21
+ dataParams: Record<string, string | number | boolean>;
22
+ /** When set, the inner widget receives dashboard filter/refresh events. */
23
+ dashboard: DashboardContainer | null;
24
+ /** When true, the inner widget only fetches when it enters the viewport. */
25
+ lazy: boolean;
26
+ /** CSS margin for viewport prefetch when lazy is true (e.g. '200px'). */
27
+ prefetchMargin: string;
28
+ private _chartType;
29
+ private _definition;
30
+ private _loading;
31
+ private _error;
32
+ constructor();
33
+ updated(changed: Map<string, unknown>): void;
34
+ private _loadDefinition;
35
+ private get _innerTag();
36
+ private _renderInnerWidget;
37
+ render(): import("lit-html").TemplateResult<1> | typeof nothing;
38
+ }
39
+ //# sourceMappingURL=analytics-widget.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analytics-widget.d.ts","sourceRoot":"","sources":["../../../../libs/analytics/src/widgets/analytics-widget.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,OAAO,EAAE,MAAM,KAAK,CAAC;AAIrD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AAE9E,OAAO,gBAAgB,CAAC;AACxB,OAAO,iBAAiB,CAAC;AACzB,OAAO,gBAAgB,CAAC;AACxB,OAAO,YAAY,CAAC;AAapB;;;;GAIG;AACH,qBACa,eAAgB,SAAQ,UAAU;IAC7C,OAAgB,MAAM,0BAmCpB;IAEF,gEAAgE;IACJ,QAAQ,EAAE,MAAM,CAAC;IAE7E,2FAA2F;IACjC,MAAM,EAAE,MAAM,CAAC;IAEzE,gGAAgG;IAC5D,KAAK,EAAE,MAAM,CAAC;IAElD,gEAAgE;IAC5B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;IAE1F,2EAA2E;IACvC,SAAS,EAAE,kBAAkB,GAAG,IAAI,CAAC;IAEzE,4EAA4E;IACvC,IAAI,EAAE,OAAO,CAAC;IAEnD,yEAAyE;IACP,cAAc,EAAE,MAAM,CAAC;IAEzF,QAAyB,UAAU,CAAuB;IAC1D,QAAyB,WAAW,CAAgC;IACpE,QAAyB,QAAQ,CAAU;IAC3C,QAAyB,MAAM,CAAgB;;IAiBtC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;YAWvC,eAAe;IAqB7B,OAAO,KAAK,SAAS,GAGpB;IAED,OAAO,CAAC,kBAAkB;IA8DjB,MAAM;CAgBhB"}
@@ -0,0 +1,230 @@
1
+ import { __decorate } from "tslib";
2
+ import { LitElement, html, css, nothing } from 'lit';
3
+ import { customElement, property, state } from 'lit/decorators.js';
4
+ import { fetchWidgetDefinition } from '../core/fetch-data.js';
5
+ // Register inner widgets so they are defined when we render them
6
+ import './bar-chart.js';
7
+ import './line-chart.js';
8
+ import './pie-chart.js';
9
+ import './table.js';
10
+ /** Map API chart_type to inner widget type. */
11
+ const CHART_TYPE_TO_TAG = {
12
+ Bar: 'analytics-bar-chart',
13
+ BarHorizontal: 'analytics-bar-chart',
14
+ Line: 'analytics-line-chart',
15
+ Pie: 'analytics-pie-chart',
16
+ Donut: 'analytics-pie-chart',
17
+ Number: 'analytics-table', // KPI: fallback to table for now
18
+ Table: 'analytics-table',
19
+ };
20
+ /**
21
+ * Generic analytics widget: pass widgetId + apiUrl (and optional config);
22
+ * fetches the widget definition and renders the appropriate chart/table internally.
23
+ * Use this so developers don't need to know the chart type.
24
+ */
25
+ let AnalyticsWidget = class AnalyticsWidget extends LitElement {
26
+ static { this.styles = css `
27
+ :host {
28
+ display: block;
29
+ width: 100%;
30
+ height: 100%;
31
+ min-height: 120px;
32
+ }
33
+ .widget-inner {
34
+ width: 100%;
35
+ height: 100%;
36
+ min-height: 120px;
37
+ }
38
+ .loading,
39
+ .error {
40
+ display: flex;
41
+ align-items: center;
42
+ justify-content: center;
43
+ min-height: 120px;
44
+ padding: 1rem;
45
+ color: #6b7280;
46
+ }
47
+ .error {
48
+ color: #b91c1c;
49
+ }
50
+ .loading-spinner {
51
+ width: 2rem;
52
+ height: 2rem;
53
+ border: 2px solid #e5e7eb;
54
+ border-top-color: #3b82f6;
55
+ border-radius: 50%;
56
+ animation: analytics-widget-spin 0.7s linear infinite;
57
+ }
58
+ @keyframes analytics-widget-spin {
59
+ to { transform: rotate(360deg); }
60
+ }
61
+ `; }
62
+ constructor() {
63
+ super();
64
+ this.widgetId = '';
65
+ this.apiUrl = '';
66
+ this.title = '';
67
+ this.dataParams = {};
68
+ this.dashboard = null;
69
+ this.lazy = false;
70
+ this.prefetchMargin = '200px';
71
+ this._chartType = '';
72
+ this._definition = null;
73
+ this._loading = false;
74
+ this._error = null;
75
+ }
76
+ updated(changed) {
77
+ if (!this.apiUrl || !this.widgetId || this._loading)
78
+ return;
79
+ if (changed.has('widgetId') || changed.has('apiUrl')) {
80
+ this._chartType = '';
81
+ this._definition = null;
82
+ this._loadDefinition();
83
+ }
84
+ else if (!this._chartType) {
85
+ this._loadDefinition();
86
+ }
87
+ }
88
+ async _loadDefinition() {
89
+ if (!this.apiUrl || !this.widgetId || this._loading)
90
+ return;
91
+ this._loading = true;
92
+ this._error = null;
93
+ try {
94
+ const def = await fetchWidgetDefinition(this.apiUrl, this.widgetId);
95
+ this._chartType = def.chart_type;
96
+ this._definition = {
97
+ title: def.title,
98
+ filters: def.filters,
99
+ hide_filter: def.hide_filter,
100
+ };
101
+ }
102
+ catch (e) {
103
+ this._error = e instanceof Error ? e.message : String(e);
104
+ this._chartType = '';
105
+ this._definition = null;
106
+ }
107
+ finally {
108
+ this._loading = false;
109
+ }
110
+ }
111
+ get _innerTag() {
112
+ if (!this._chartType)
113
+ return '';
114
+ return CHART_TYPE_TO_TAG[this._chartType] ?? 'analytics-table';
115
+ }
116
+ _renderInnerWidget() {
117
+ const common = {
118
+ widgetId: this.widgetId,
119
+ apiUrl: this.apiUrl,
120
+ title: this.title,
121
+ dataParams: this.dataParams ?? {},
122
+ dashboard: this.dashboard,
123
+ lazy: this.lazy,
124
+ prefetchMargin: this.prefetchMargin || '200px',
125
+ initialDefinition: this._definition ?? undefined,
126
+ };
127
+ const tag = this._innerTag;
128
+ switch (tag) {
129
+ case 'analytics-bar-chart':
130
+ return html `<analytics-bar-chart
131
+ widget-id=${common.widgetId}
132
+ api-url=${common.apiUrl}
133
+ .title=${common.title}
134
+ .dataParams=${common.dataParams}
135
+ .dashboard=${common.dashboard}
136
+ .initialDefinition=${common.initialDefinition}
137
+ ?lazy=${common.lazy}
138
+ prefetch-margin=${common.prefetchMargin}
139
+ ></analytics-bar-chart>`;
140
+ case 'analytics-line-chart':
141
+ return html `<analytics-line-chart
142
+ widget-id=${common.widgetId}
143
+ api-url=${common.apiUrl}
144
+ .title=${common.title}
145
+ .dataParams=${common.dataParams}
146
+ .dashboard=${common.dashboard}
147
+ .initialDefinition=${common.initialDefinition}
148
+ ?lazy=${common.lazy}
149
+ prefetch-margin=${common.prefetchMargin}
150
+ ></analytics-line-chart>`;
151
+ case 'analytics-pie-chart':
152
+ return html `<analytics-pie-chart
153
+ widget-id=${common.widgetId}
154
+ api-url=${common.apiUrl}
155
+ .title=${common.title}
156
+ .dataParams=${common.dataParams}
157
+ .dashboard=${common.dashboard}
158
+ .initialDefinition=${common.initialDefinition}
159
+ ?lazy=${common.lazy}
160
+ prefetch-margin=${common.prefetchMargin}
161
+ ></analytics-pie-chart>`;
162
+ case 'analytics-table':
163
+ return html `<analytics-table
164
+ widget-id=${common.widgetId}
165
+ api-url=${common.apiUrl}
166
+ .title=${common.title}
167
+ .dataParams=${common.dataParams}
168
+ .dashboard=${common.dashboard}
169
+ .initialDefinition=${common.initialDefinition}
170
+ ?lazy=${common.lazy}
171
+ prefetch-margin=${common.prefetchMargin}
172
+ ></analytics-table>`;
173
+ default:
174
+ return nothing;
175
+ }
176
+ }
177
+ render() {
178
+ if (this._loading) {
179
+ return html `
180
+ <div class="loading" aria-busy="true" aria-live="polite">
181
+ <div class="loading-spinner" aria-hidden="true"></div>
182
+ </div>
183
+ `;
184
+ }
185
+ if (this._error) {
186
+ return html `<div class="error" role="alert">${this._error}</div>`;
187
+ }
188
+ if (!this._innerTag) {
189
+ return nothing;
190
+ }
191
+ return html `<div class="widget-inner">${this._renderInnerWidget()}</div>`;
192
+ }
193
+ };
194
+ __decorate([
195
+ property({ type: String, attribute: 'widget-id' })
196
+ ], AnalyticsWidget.prototype, "widgetId", void 0);
197
+ __decorate([
198
+ property({ type: String, attribute: 'api-url' })
199
+ ], AnalyticsWidget.prototype, "apiUrl", void 0);
200
+ __decorate([
201
+ property({ type: String })
202
+ ], AnalyticsWidget.prototype, "title", void 0);
203
+ __decorate([
204
+ property({ type: Object })
205
+ ], AnalyticsWidget.prototype, "dataParams", void 0);
206
+ __decorate([
207
+ property({ type: Object })
208
+ ], AnalyticsWidget.prototype, "dashboard", void 0);
209
+ __decorate([
210
+ property({ type: Boolean })
211
+ ], AnalyticsWidget.prototype, "lazy", void 0);
212
+ __decorate([
213
+ property({ type: String, attribute: 'prefetch-margin' })
214
+ ], AnalyticsWidget.prototype, "prefetchMargin", void 0);
215
+ __decorate([
216
+ state()
217
+ ], AnalyticsWidget.prototype, "_chartType", void 0);
218
+ __decorate([
219
+ state()
220
+ ], AnalyticsWidget.prototype, "_definition", void 0);
221
+ __decorate([
222
+ state()
223
+ ], AnalyticsWidget.prototype, "_loading", void 0);
224
+ __decorate([
225
+ state()
226
+ ], AnalyticsWidget.prototype, "_error", void 0);
227
+ AnalyticsWidget = __decorate([
228
+ customElement('analytics-widget')
229
+ ], AnalyticsWidget);
230
+ export { AnalyticsWidget };
@@ -0,0 +1,13 @@
1
+ import type { EChartsOption } from 'echarts/types/dist/option';
2
+ import { BaseChartWidget } from './base-chart.js';
3
+ /**
4
+ * Bar 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 BarChartWidget extends BaseChartWidget {
9
+ buildOption(data: Record<string, unknown>[], meta: Record<string, {
10
+ type?: string;
11
+ }>, widgetData?: unknown): EChartsOption;
12
+ }
13
+ //# sourceMappingURL=bar-chart.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bar-chart.d.ts","sourceRoot":"","sources":["../../../../libs/analytics/src/widgets/bar-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,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;CAsDjB"}
@@ -0,0 +1,77 @@
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
+ * Bar 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 BarChartWidget = class BarChartWidget 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: 'bar',
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
+ const isHorizontal = wd.chartType === 'BarHorizontal';
35
+ return {
36
+ grid: {
37
+ left: isHorizontal ? 120 : 48,
38
+ right: 24,
39
+ top: 24,
40
+ bottom: multiSeries ? 80 : 48,
41
+ },
42
+ tooltip: { trigger: 'axis' },
43
+ legend: legendConfig(multiSeries),
44
+ xAxis: isHorizontal ? { type: 'value' } : { type: 'category', data: categories },
45
+ yAxis: isHorizontal ? { type: 'category', data: categories } : { type: 'value' },
46
+ series,
47
+ };
48
+ }
49
+ const keys = data.length ? Object.keys(data[0]) : Object.keys(meta);
50
+ if (keys.length === 0) {
51
+ return { xAxis: { type: 'category', data: [] }, yAxis: { type: 'value' }, series: [] };
52
+ }
53
+ const categoryKey = keys[0];
54
+ const valueKeys = keys.slice(1).filter((k) => meta[k]?.type === 'number' || typeof (data[0]?.[k]) === 'number');
55
+ const categories = [...new Set(data.map((r) => String(r[categoryKey] ?? '')))];
56
+ const series = valueKeys.length
57
+ ? valueKeys.map((name) => ({
58
+ name,
59
+ type: 'bar',
60
+ data: data.map((r) => Number(r[name]) || 0),
61
+ }))
62
+ : [{ name: categoryKey, type: 'bar', data: data.map((r) => Number(r[categoryKey]) || 0) }];
63
+ const multiSeries = series.length > 1;
64
+ return {
65
+ grid: { left: 48, right: 24, top: 24, bottom: multiSeries ? 80 : 48 },
66
+ tooltip: { trigger: 'axis' },
67
+ legend: legendConfig(multiSeries),
68
+ xAxis: { type: 'category', data: categories },
69
+ yAxis: { type: 'value' },
70
+ series,
71
+ };
72
+ }
73
+ };
74
+ BarChartWidget = __decorate([
75
+ customElement('analytics-bar-chart')
76
+ ], BarChartWidget);
77
+ export { BarChartWidget };