@quicdata/analytics 0.0.12 → 0.0.14

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quicdata/analytics",
3
- "version": "0.0.12",
3
+ "version": "0.0.14",
4
4
  "type": "module",
5
5
  "main": "index.js",
6
6
  "module": "index.js",
@@ -1,6 +1,7 @@
1
1
  import { LitElement } from 'lit';
2
2
  import type { WidgetDefinitionSubset } from '../core/fetch-data.js';
3
3
  import type { DashboardContainer } from '../dashboard/dashboard-container.js';
4
+ import '../filters/index.js';
4
5
  /**
5
6
  * Number KPI widget. Displays one or more large formatted numbers with labels.
6
7
  * Data shape from backend transformNumber(): { chartType: 'Number', values, labels, options }.
@@ -21,10 +22,14 @@ export declare class NumberKpiWidget extends LitElement {
21
22
  private _loading;
22
23
  private _error;
23
24
  private _dashboardParams;
25
+ private _widgetFilters;
26
+ private _widgetFilterParams;
27
+ private _hideFilter;
24
28
  private _viewportVisible;
25
29
  private _definitionTitle;
26
30
  private _loadGeneration;
27
31
  private _hasLoadedOnce;
32
+ private _ignoreNextFilterChange;
28
33
  private _dashboardEl;
29
34
  private _viewportUnobserve;
30
35
  private _loadDataInFlight;
@@ -45,6 +50,7 @@ export declare class NumberKpiWidget extends LitElement {
45
50
  private _defer;
46
51
  private _loadData;
47
52
  private _onRefresh;
53
+ private _onWidgetFilterChange;
48
54
  /** Format a number: apply decimal places, optionally shorten (1234 → 1.2K). */
49
55
  private _formatValue;
50
56
  render(): import("lit-html").TemplateResult<1>;
@@ -1 +1 @@
1
- {"version":3,"file":"number-kpi.d.ts","sourceRoot":"","sources":["../../../../libs/analytics/src/widgets/number-kpi.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAsB,MAAM,KAAK,CAAC;AAGrD,OAAO,KAAK,EAA0B,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAM5F,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AAO9E;;;GAGG;AACH,qBACa,eAAgB,SAAQ,UAAU;IAC7C,OAAgB,MAAM,0BAsMpB;IAEkC,KAAK,EAAE,MAAM,CAAC;IACqB,iBAAiB,EAAE,OAAO,CAAC;IACvC,OAAO,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC;IACrC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;IACtD,SAAS,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACpC,IAAI,EAAE,OAAO,CAAC;IACe,cAAc,EAAE,MAAM,CAAC;IACrD,iBAAiB,EAAE,sBAAsB,GAAG,SAAS,CAAC;IAE1F,QAAyB,QAAQ,CAA6B;IAC9D,QAAyB,QAAQ,CAAU;IAC3C,QAAyB,MAAM,CAAgB;IAC/C,QAAyB,gBAAgB,CAA4C;IACrF,QAAyB,gBAAgB,CAAU;IACnD,QAAyB,gBAAgB,CAAS;IAElD,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,YAAY,CAAmC;IACvD,OAAO,CAAC,kBAAkB,CAA6B;IACvD,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,uBAAuB,CAAS;IACxC,OAAO,CAAC,kBAAkB,CAAS;;IAsBnC,OAAO,CAAC,6BAA6B,CAInC;IAEF,OAAO,CAAC,wBAAwB,CAG9B;IAEO,iBAAiB,IAAI,IAAI;IAKzB,oBAAoB,IAAI,IAAI;IAO5B,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IA2B5C,YAAY,IAAI,IAAI;IAkB7B,OAAO,CAAC,gBAAgB;IASxB,OAAO,CAAC,gBAAgB;IASxB,OAAO,CAAC,oBAAoB;IAO5B,OAAO,CAAC,mBAAmB;IAQ3B,OAAO,KAAK,eAAe,GAE1B;IAED,OAAO,CAAC,MAAM;YAQA,SAAS;IA+DvB,OAAO,CAAC,UAAU;IAKlB,+EAA+E;IAC/E,OAAO,CAAC,YAAY;IAsBX,MAAM;CAqFhB"}
1
+ {"version":3,"file":"number-kpi.d.ts","sourceRoot":"","sources":["../../../../libs/analytics/src/widgets/number-kpi.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAsB,MAAM,KAAK,CAAC;AAGrD,OAAO,KAAK,EAA0B,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAM5F,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AAM9E,OAAO,qBAAqB,CAAC;AAE7B;;;GAGG;AACH,qBACa,eAAgB,SAAQ,UAAU;IAC7C,OAAgB,MAAM,0BA2MpB;IAEkC,KAAK,EAAE,MAAM,CAAC;IACqB,iBAAiB,EAAE,OAAO,CAAC;IACvC,OAAO,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC;IACrC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;IACtD,SAAS,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACpC,IAAI,EAAE,OAAO,CAAC;IACe,cAAc,EAAE,MAAM,CAAC;IACrD,iBAAiB,EAAE,sBAAsB,GAAG,SAAS,CAAC;IAE1F,QAAyB,QAAQ,CAA6B;IAC9D,QAAyB,QAAQ,CAAU;IAC3C,QAAyB,MAAM,CAAgB;IAC/C,QAAyB,gBAAgB,CAA4C;IACrF,QAAyB,cAAc,CAAqB;IAC5D,QAAyB,mBAAmB,CAA4C;IACxF,QAAyB,WAAW,CAAU;IAC9C,QAAyB,gBAAgB,CAAU;IACnD,QAAyB,gBAAgB,CAAS;IAElD,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,uBAAuB,CAAS;IACxC,OAAO,CAAC,YAAY,CAAmC;IACvD,OAAO,CAAC,kBAAkB,CAA6B;IACvD,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,uBAAuB,CAAS;IACxC,OAAO,CAAC,kBAAkB,CAAS;;IAyBnC,OAAO,CAAC,6BAA6B,CAInC;IAEF,OAAO,CAAC,wBAAwB,CAG9B;IAEO,iBAAiB,IAAI,IAAI;IAKzB,oBAAoB,IAAI,IAAI;IAO5B,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAuC5C,YAAY,IAAI,IAAI;IAkB7B,OAAO,CAAC,gBAAgB;IASxB,OAAO,CAAC,gBAAgB;IASxB,OAAO,CAAC,oBAAoB;IAO5B,OAAO,CAAC,mBAAmB;IAyB3B,OAAO,KAAK,eAAe,GAE1B;IAED,OAAO,CAAC,MAAM;YAQA,SAAS;IA6EvB,OAAO,CAAC,UAAU;IAKlB,OAAO,CAAC,qBAAqB;IAI7B,+EAA+E;IAC/E,OAAO,CAAC,YAAY;IAsBX,MAAM;CAmGhB"}
@@ -4,6 +4,7 @@ import { customElement, property, state } from 'lit/decorators.js';
4
4
  import { buildWidgetDataUrl, fetchWidgetData, fetchWidgetDefinition, } from '../core/fetch-data.js';
5
5
  import { EVENT_DASHBOARD_FILTER_CHANGE, EVENT_DASHBOARD_REFRESH, } from '../dashboard/index.js';
6
6
  import { observeViewport } from '../core/viewport-observer.js';
7
+ import '../filters/index.js';
7
8
  /**
8
9
  * Number KPI widget. Displays one or more large formatted numbers with labels.
9
10
  * Data shape from backend transformNumber(): { chartType: 'Number', values, labels, options }.
@@ -24,6 +25,11 @@ let NumberKpiWidget = class NumberKpiWidget extends LitElement {
24
25
  display: flex;
25
26
  flex-direction: column;
26
27
  min-height: 0;
28
+ --analytics-widget-toolbar-top: 2.5rem;
29
+ }
30
+ .widget-wrap:hover analytics-widget-toolbar {
31
+ opacity: 1;
32
+ pointer-events: auto;
27
33
  }
28
34
 
29
35
  .widget-header {
@@ -212,6 +218,7 @@ let NumberKpiWidget = class NumberKpiWidget extends LitElement {
212
218
  super();
213
219
  this._loadGeneration = 0;
214
220
  this._hasLoadedOnce = false;
221
+ this._ignoreNextFilterChange = false;
215
222
  this._dashboardEl = null;
216
223
  this._viewportUnobserve = null;
217
224
  this._loadDataInFlight = false;
@@ -241,6 +248,9 @@ let NumberKpiWidget = class NumberKpiWidget extends LitElement {
241
248
  this._loading = false;
242
249
  this._error = null;
243
250
  this._dashboardParams = {};
251
+ this._widgetFilters = [];
252
+ this._widgetFilterParams = {};
253
+ this._hideFilter = false;
244
254
  this._viewportVisible = true;
245
255
  this._definitionTitle = '';
246
256
  }
@@ -261,11 +271,23 @@ let NumberKpiWidget = class NumberKpiWidget extends LitElement {
261
271
  }
262
272
  if (changed.has('widgetId') || changed.has('apiUrl')) {
263
273
  this._definitionTitle = '';
274
+ this._widgetFilters = [];
275
+ this._widgetFilterParams = {};
276
+ this._ignoreNextFilterChange = true;
277
+ }
278
+ const onlyFilterState = (changed.has('_widgetFilterParams') || changed.has('_widgetFilters')) &&
279
+ !changed.has('dataUrl') &&
280
+ !changed.has('dataParams') &&
281
+ !changed.has('widgetId') &&
282
+ !changed.has('apiUrl');
283
+ if (onlyFilterState && this._ignoreNextFilterChange) {
284
+ this._ignoreNextFilterChange = false;
264
285
  }
265
- if (changed.has('dataUrl') ||
286
+ else if (changed.has('dataUrl') ||
266
287
  changed.has('dataParams') ||
267
288
  changed.has('widgetId') ||
268
- changed.has('apiUrl')) {
289
+ changed.has('apiUrl') ||
290
+ changed.has('_widgetFilterParams')) {
269
291
  if (this._getEffectiveDataUrl() && !this._loadDataScheduled) {
270
292
  this._loadDataScheduled = true;
271
293
  queueMicrotask(() => {
@@ -321,7 +343,26 @@ let NumberKpiWidget = class NumberKpiWidget extends LitElement {
321
343
  return this.dataUrl;
322
344
  }
323
345
  _getEffectiveParams() {
324
- const out = { ...(this.dataParams ?? {}) };
346
+ let widgetFilterParams = {};
347
+ if (this._hideFilter) {
348
+ for (const f of this._widgetFilters) {
349
+ if (f.default_value !== undefined && f.default_value !== null) {
350
+ widgetFilterParams[f.param_name] = f.default_value;
351
+ }
352
+ }
353
+ }
354
+ else if (this._widgetFilters.length > 0) {
355
+ for (const f of this._widgetFilters) {
356
+ const setByUser = f.param_name in this._widgetFilterParams;
357
+ widgetFilterParams[f.param_name] = setByUser
358
+ ? (this._widgetFilterParams[f.param_name] ?? '')
359
+ : (f.default_value ?? '');
360
+ }
361
+ }
362
+ else {
363
+ widgetFilterParams = { ...this._widgetFilterParams };
364
+ }
365
+ const out = { ...widgetFilterParams, ...(this.dataParams ?? {}) };
325
366
  if (Object.keys(this._dashboardParams).length > 0) {
326
367
  out.dashboard_params = this._dashboardParams;
327
368
  }
@@ -358,17 +399,27 @@ let NumberKpiWidget = class NumberKpiWidget extends LitElement {
358
399
  try {
359
400
  const useWidgetApi = Boolean(this.apiUrl && this.widgetId);
360
401
  let params;
361
- if (useWidgetApi && !this._definitionTitle) {
402
+ if (useWidgetApi && this._widgetFilters.length === 0) {
362
403
  const def = this.initialDefinition ?? (await fetchWidgetDefinition(this.apiUrl, this.widgetId));
363
404
  this._definitionTitle = def.title ?? '';
364
- params = { ...(this.dataParams ?? {}) };
405
+ this._widgetFilters = def.filters ?? [];
406
+ this._hideFilter = def.hide_filter ?? false;
407
+ const defaultParams = {};
408
+ for (const f of (def.filters ?? [])) {
409
+ if (f.default_value !== undefined && f.default_value !== null) {
410
+ defaultParams[f.param_name] = f.default_value;
411
+ }
412
+ }
413
+ this._widgetFilterParams = { ...defaultParams, ...this._widgetFilterParams };
414
+ this._ignoreNextFilterChange = true;
415
+ params = { ...defaultParams, ...(this.dataParams ?? {}) };
416
+ if (Object.keys(this._dashboardParams).length > 0) {
417
+ params.dashboard_params = this._dashboardParams;
418
+ }
365
419
  }
366
420
  else {
367
421
  params = this._getEffectiveParams();
368
422
  }
369
- if (Object.keys(this._dashboardParams).length > 0) {
370
- params.dashboard_params = this._dashboardParams;
371
- }
372
423
  const res = await fetchWidgetData(dataUrl, params);
373
424
  if (gen !== this._loadGeneration)
374
425
  return;
@@ -376,11 +427,17 @@ let NumberKpiWidget = class NumberKpiWidget extends LitElement {
376
427
  const kpiData = wd && wd.chartType === 'Number'
377
428
  ? { chartType: 'Number', values: wd.values ?? [], labels: wd.labels ?? [], options: wd.options, shorten_numbers: wd.shorten_numbers }
378
429
  : null;
430
+ const filters = res.filters;
431
+ const hideFilter = res.hide_filter;
379
432
  this._defer(() => {
380
433
  if (gen !== this._loadGeneration)
381
434
  return;
382
435
  this._loadDataInFlight = false;
383
436
  this._kpiData = kpiData;
437
+ if (filters?.length)
438
+ this._widgetFilters = filters;
439
+ if (hideFilter !== undefined)
440
+ this._hideFilter = hideFilter;
384
441
  this._hasLoadedOnce = true;
385
442
  this._loading = false;
386
443
  if (this._loadDataPendingRefresh) {
@@ -410,6 +467,9 @@ let NumberKpiWidget = class NumberKpiWidget extends LitElement {
410
467
  return;
411
468
  this._loadData();
412
469
  }
470
+ _onWidgetFilterChange(e) {
471
+ this._widgetFilterParams = { ...(e.detail?.params ?? {}) };
472
+ }
413
473
  /** Format a number: apply decimal places, optionally shorten (1234 → 1.2K). */
414
474
  _formatValue(value, decimal, shorten) {
415
475
  const dec = decimal != null && decimal !== '' ? parseInt(decimal, 10) : undefined;
@@ -436,6 +496,8 @@ let NumberKpiWidget = class NumberKpiWidget extends LitElement {
436
496
  }
437
497
  render() {
438
498
  const canRefresh = Boolean(this._getEffectiveDataUrl()) && !this.hideRefreshButton;
499
+ const hasWidgetFilters = this._widgetFilters.length > 0 && !this._hideFilter;
500
+ const widgetActiveCount = Object.keys(this._widgetFilterParams).filter((k) => this._widgetFilterParams[k] !== '' && this._widgetFilterParams[k] != null).length;
439
501
  const showSkeleton = this._loading && !this._hasLoadedOnce;
440
502
  const showSpinnerOverlay = this._loading && this._hasLoadedOnce;
441
503
  const showTitlePlaceholder = !this._effectiveTitle && !this.hideRefreshButton;
@@ -512,6 +574,16 @@ let NumberKpiWidget = class NumberKpiWidget extends LitElement {
512
574
  <div class="loading-spinner" aria-hidden="true"></div>
513
575
  </div>`
514
576
  : nothing}
577
+ ${hasWidgetFilters
578
+ ? html `
579
+ <analytics-widget-toolbar
580
+ .filters=${this._widgetFilters}
581
+ .values=${this._widgetFilterParams}
582
+ .activeCount=${widgetActiveCount}
583
+ @analytics-filter-change=${this._onWidgetFilterChange}
584
+ ></analytics-widget-toolbar>
585
+ `
586
+ : ''}
515
587
  </div>
516
588
  `;
517
589
  }
@@ -558,6 +630,15 @@ __decorate([
558
630
  __decorate([
559
631
  state()
560
632
  ], NumberKpiWidget.prototype, "_dashboardParams", void 0);
633
+ __decorate([
634
+ state()
635
+ ], NumberKpiWidget.prototype, "_widgetFilters", void 0);
636
+ __decorate([
637
+ state()
638
+ ], NumberKpiWidget.prototype, "_widgetFilterParams", void 0);
639
+ __decorate([
640
+ state()
641
+ ], NumberKpiWidget.prototype, "_hideFilter", void 0);
561
642
  __decorate([
562
643
  state()
563
644
  ], NumberKpiWidget.prototype, "_viewportVisible", void 0);