myio-js-library 0.1.557 → 0.1.558

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/dist/index.cjs CHANGED
@@ -908,7 +908,7 @@ module.exports = __toCommonJS(src_exports);
908
908
  // package.json
909
909
  var package_default = {
910
910
  name: "myio-js-library",
911
- version: "0.1.557",
911
+ version: "0.1.558",
912
912
  description: "A clean, standalone JS SDK for MYIO projects",
913
913
  license: "MIT",
914
914
  repository: "github:gh-myio/myio-js-library",
@@ -12855,6 +12855,26 @@ var CSS_STRING = `
12855
12855
  margin-left: 4px;
12856
12856
  }
12857
12857
 
12858
+ /* Consumo ainda carregando (spinner no lugar do valor) */
12859
+ .myio-ho-card__value.is-loading .num {
12860
+ display: inline-flex;
12861
+ align-items: center;
12862
+ gap: 8px;
12863
+ font-size: 14px;
12864
+ font-weight: 600;
12865
+ color: var(--myio-muted, #9ca3af);
12866
+ }
12867
+ .myio-ho-card__value-spinner {
12868
+ width: 14px;
12869
+ height: 14px;
12870
+ border: 2px solid rgba(124, 58, 237, 0.2);
12871
+ border-top-color: var(--myio-brand-700, #7c3aed);
12872
+ border-radius: 50%;
12873
+ animation: myio-consumption-spin 0.8s linear infinite;
12874
+ flex-shrink: 0;
12875
+ }
12876
+ @keyframes myio-consumption-spin { to { transform: rotate(360deg); } }
12877
+
12858
12878
  /* Efficiency bar */
12859
12879
  .myio-ho-card__eff {
12860
12880
  display: flex;
@@ -16527,7 +16547,20 @@ function paint(root, state6) {
16527
16547
  const primaryValue = formatValueByDomain(entityObject.val, entityObject.domain);
16528
16548
  const numSpan = root.querySelector(".myio-ho-card__value .num");
16529
16549
  const unitSpan = root.querySelector(".myio-ho-card__value .unit");
16530
- numSpan.textContent = primaryValue;
16550
+ const valueBox = root.querySelector(".myio-ho-card__value");
16551
+ const dataLoading = !!entityObject.dataLoading && entityObject.domain !== "temperature";
16552
+ if (valueBox) {
16553
+ valueBox.classList.toggle("is-loading", dataLoading);
16554
+ if (dataLoading) valueBox.setAttribute("aria-busy", "true");
16555
+ else valueBox.removeAttribute("aria-busy");
16556
+ }
16557
+ if (dataLoading) {
16558
+ numSpan.innerHTML = '<span class="myio-ho-card__value-spinner" aria-hidden="true"></span>Carregando\u2026';
16559
+ numSpan.title = "Carregando consumo do per\xEDodo";
16560
+ } else {
16561
+ numSpan.textContent = primaryValue;
16562
+ numSpan.removeAttribute("title");
16563
+ }
16531
16564
  const barContainer = root.querySelector(".bar");
16532
16565
  const effContainer = root.querySelector(".myio-ho-card__eff");
16533
16566
  if (state6.enableSelection) {
@@ -71709,6 +71742,25 @@ var HeaderView = class {
71709
71742
  transition: color 0.2s ease;
71710
71743
  }
71711
71744
 
71745
+ /* Consumo ainda carregando */
71746
+ .${HEADER_CSS_PREFIX}-kpi-loading {
71747
+ display: inline-flex;
71748
+ align-items: center;
71749
+ gap: 10px;
71750
+ font-size: 1rem;
71751
+ font-weight: 600;
71752
+ opacity: 0.85;
71753
+ }
71754
+ .${HEADER_CSS_PREFIX}-kpi-spinner {
71755
+ width: 18px;
71756
+ height: 18px;
71757
+ border: 2.5px solid rgba(255, 255, 255, 0.3);
71758
+ border-top-color: currentColor;
71759
+ border-radius: 50%;
71760
+ animation: myio-consumption-spin 0.8s linear infinite;
71761
+ }
71762
+ @keyframes myio-consumption-spin { to { transform: rotate(360deg); } }
71763
+
71712
71764
  .${HEADER_CSS_PREFIX}-card__subrow {
71713
71765
  display: flex;
71714
71766
  align-items: center;
@@ -72147,7 +72199,28 @@ var HeaderView = class {
72147
72199
  /**
72148
72200
  * Update energy card
72149
72201
  */
72202
+ /** Spinner nos KPIs de consumo (energia e água) enquanto o período carrega */
72203
+ setConsumptionLoading(loading) {
72204
+ if (!loading) {
72205
+ [this.energyKpiEl, this.waterKpiEl].forEach((el2) => {
72206
+ if (el2?.querySelector(`.${HEADER_CSS_PREFIX}-kpi-loading`)) el2.textContent = "-";
72207
+ });
72208
+ return;
72209
+ }
72210
+ const html = `<span class="${HEADER_CSS_PREFIX}-kpi-loading" aria-busy="true"><span class="${HEADER_CSS_PREFIX}-kpi-spinner" aria-hidden="true"></span>Carregando\u2026</span>`;
72211
+ if (this.energyKpiEl) this.energyKpiEl.innerHTML = html;
72212
+ if (this.waterKpiEl) this.waterKpiEl.innerHTML = html;
72213
+ if (this.energyTrendEl) this.energyTrendEl.textContent = "";
72214
+ if (this.waterTrendEl) this.waterTrendEl.textContent = "";
72215
+ }
72150
72216
  updateEnergyCard(data) {
72217
+ if (data.loading) {
72218
+ if (this.energyKpiEl) {
72219
+ this.energyKpiEl.innerHTML = `<span class="${HEADER_CSS_PREFIX}-kpi-loading" aria-busy="true"><span class="${HEADER_CSS_PREFIX}-kpi-spinner" aria-hidden="true"></span>Carregando\u2026</span>`;
72220
+ }
72221
+ if (this.energyTrendEl) this.energyTrendEl.textContent = "";
72222
+ return;
72223
+ }
72151
72224
  if (this.energyKpiEl) {
72152
72225
  if (data.isFiltered && data.unfilteredTotal > 0) {
72153
72226
  const filteredFormatted = this.formatEnergy(data.customerTotal);
@@ -72196,6 +72269,13 @@ var HeaderView = class {
72196
72269
  * Update water card
72197
72270
  */
72198
72271
  updateWaterCard(data) {
72272
+ if (data.loading) {
72273
+ if (this.waterKpiEl) {
72274
+ this.waterKpiEl.innerHTML = `<span class="${HEADER_CSS_PREFIX}-kpi-loading" aria-busy="true"><span class="${HEADER_CSS_PREFIX}-kpi-spinner" aria-hidden="true"></span>Carregando\u2026</span>`;
72275
+ }
72276
+ if (this.waterTrendEl) this.waterTrendEl.textContent = "";
72277
+ return;
72278
+ }
72199
72279
  if (this.waterKpiEl) {
72200
72280
  if (data.isFiltered && data.unfilteredTotal > 0) {
72201
72281
  const filteredFormatted = this.formatWater(data.filteredTotal);
@@ -73181,6 +73261,9 @@ function createHeaderComponent(params) {
73181
73261
  tooltipData.temperature = data;
73182
73262
  view.updateTemperatureCard(data);
73183
73263
  }
73264
+ function setConsumptionLoading(loading) {
73265
+ view.setConsumptionLoading(loading);
73266
+ }
73184
73267
  function updateWaterKPI(data) {
73185
73268
  tooltipData.water = data;
73186
73269
  view.updateWaterCard(data);
@@ -73236,6 +73319,7 @@ function createHeaderComponent(params) {
73236
73319
  updateEnergyKPI,
73237
73320
  updateTemperatureKPI,
73238
73321
  updateWaterKPI,
73322
+ setConsumptionLoading,
73239
73323
  updateTooltipData,
73240
73324
  // Shopping/Filter
73241
73325
  updateShoppings,
@@ -123722,6 +123806,20 @@ var HEADER_DEVICES_GRID_STYLES = `
123722
123806
  body.light-mode .telemetry-grid-wrap.maximized .telemetry-grid-header {
123723
123807
  background: #f8fafc;
123724
123808
  }
123809
+
123810
+ /* Consumo ainda carregando */
123811
+ .hdg-value-spinner {
123812
+ display: inline-block;
123813
+ width: 13px;
123814
+ height: 13px;
123815
+ margin-right: 6px;
123816
+ vertical-align: -2px;
123817
+ border: 2px solid rgba(124, 58, 237, 0.2);
123818
+ border-top-color: var(--myio-brand-700, #7c3aed);
123819
+ border-radius: 50%;
123820
+ animation: myio-consumption-spin 0.8s linear infinite;
123821
+ }
123822
+ @keyframes myio-consumption-spin { to { transform: rotate(360deg); } }
123725
123823
  `;
123726
123824
  function injectHeaderDevicesGridStyles() {
123727
123825
  const styleId = "hdg-styles";
@@ -123739,14 +123837,15 @@ var DOMAIN_CONFIG7 = {
123739
123837
  consumptionLabel: "Consumo Total",
123740
123838
  zeroLabel: "Sem Consumo",
123741
123839
  unit: "kWh",
123742
- formatValue: (value) => formatEnergy(value, void 0, 2)
123840
+ // 3 casas: mesmo padrão do dashboard de shopping (RFC-0108, measurementSettings default)
123841
+ formatValue: (value) => formatEnergy(value, void 0, 3)
123743
123842
  },
123744
123843
  water: {
123745
123844
  totalLabel: "Total Hidr\xF4metros",
123746
123845
  consumptionLabel: "Consumo Total",
123747
123846
  zeroLabel: "Sem Consumo",
123748
123847
  unit: "m\xB3",
123749
- formatValue: (value) => formatWater(value)
123848
+ formatValue: (value) => `${(Number(value) || 0).toLocaleString("pt-BR", { minimumFractionDigits: 3, maximumFractionDigits: 3 })} m\xB3`
123750
123849
  },
123751
123850
  temperature: {
123752
123851
  totalLabel: "Total Sensores",
@@ -123914,6 +124013,16 @@ var HeaderDevicesGridView = class {
123914
124013
  const percentage = total > 0 ? (online / total * 100).toFixed(1) : "0.0";
123915
124014
  connectivityEl.textContent = `${online}/${total} (${percentage}%)`;
123916
124015
  totalEl.textContent = total.toString();
124016
+ if (stats.loading) {
124017
+ const spin = '<span class="hdg-value-spinner" aria-hidden="true"></span>';
124018
+ consumptionEl.innerHTML = `${spin}Carregando\u2026`;
124019
+ zeroEl.innerHTML = spin;
124020
+ consumptionEl.setAttribute("aria-busy", "true");
124021
+ zeroEl.setAttribute("aria-busy", "true");
124022
+ return;
124023
+ }
124024
+ consumptionEl.removeAttribute("aria-busy");
124025
+ zeroEl.removeAttribute("aria-busy");
123917
124026
  consumptionEl.textContent = this.domainConfig.formatValue(consumption);
123918
124027
  zeroEl.textContent = zeroCount.toString();
123919
124028
  }
@@ -124020,7 +124129,8 @@ var HeaderDevicesGridController = class {
124020
124129
  online,
124021
124130
  total: devices.length,
124022
124131
  consumption: displayValue,
124023
- zeroCount
124132
+ zeroCount,
124133
+ loading: this.domain !== "temperature" && devices.some((d) => d.dataLoading)
124024
124134
  });
124025
124135
  }
124026
124136
  setThemeMode(mode) {
package/dist/index.d.cts CHANGED
@@ -7688,6 +7688,8 @@ interface ShoppingEnergy {
7688
7688
  * Energy KPI data
7689
7689
  */
7690
7690
  interface EnergyKPI {
7691
+ /** Consumo do período ainda carregando → spinner no KPI */
7692
+ loading?: boolean;
7691
7693
  /** Total consumption for selected customers */
7692
7694
  customerTotal: number;
7693
7695
  /** Total consumption before filtering */
@@ -7758,6 +7760,8 @@ interface ShoppingWater {
7758
7760
  * Water KPI data
7759
7761
  */
7760
7762
  interface WaterKPI {
7763
+ /** Consumo do período ainda carregando → spinner no KPI */
7764
+ loading?: boolean;
7761
7765
  /** Total consumption for selected customers */
7762
7766
  filteredTotal: number;
7763
7767
  /** Total consumption before filtering */
@@ -7939,6 +7943,8 @@ interface HeaderComponentInstance {
7939
7943
  updateTemperatureKPI: (data: TemperatureKPI) => void;
7940
7944
  /** Update water KPI */
7941
7945
  updateWaterKPI: (data: WaterKPI) => void;
7946
+ /** Spinner nos KPIs de consumo (energia/água) enquanto o período carrega */
7947
+ setConsumptionLoading: (loading: boolean) => void;
7942
7948
  /** Update tooltip data for a specific card */
7943
7949
  updateTooltipData: (cardType: CardType, data: unknown) => void;
7944
7950
  /** Update available shoppings */
@@ -8227,6 +8233,8 @@ declare class HeaderView {
8227
8233
  /**
8228
8234
  * Update energy card
8229
8235
  */
8236
+ /** Spinner nos KPIs de consumo (energia e água) enquanto o período carrega */
8237
+ setConsumptionLoading(loading: boolean): void;
8230
8238
  updateEnergyCard(data: EnergyKPI): void;
8231
8239
  /**
8232
8240
  * Update temperature card
@@ -17475,6 +17483,8 @@ interface HeaderStats {
17475
17483
  total: number;
17476
17484
  consumption: number;
17477
17485
  zeroCount: number;
17486
+ /** Consumo ainda carregando → spinner em Consumo Total / Sem Consumo */
17487
+ loading?: boolean;
17478
17488
  }
17479
17489
  interface HeaderLabels {
17480
17490
  connectivity?: string;
@@ -17509,6 +17519,8 @@ interface HeaderDevicesGridInstance {
17509
17519
  destroy: () => void;
17510
17520
  }
17511
17521
  interface HeaderDevice {
17522
+ /** Consumo do período ainda não chegou (HO: /summary em andamento) */
17523
+ dataLoading?: boolean;
17512
17524
  entityId?: string;
17513
17525
  ingestionId?: string;
17514
17526
  deviceStatus?: string;
@@ -17586,7 +17598,7 @@ declare class HeaderDevicesGridController implements HeaderDevicesGridInstance {
17586
17598
  * CSS-in-JS styles for the stats header
17587
17599
  * FIEL ao showcase/telemetry-grid/index.html
17588
17600
  */
17589
- declare const HEADER_DEVICES_GRID_STYLES = "\n /* RFC-0125: HeaderDevicesGrid Component - FIEL ao TELEMETRY widget */\n .hdg-header {\n display: flex !important;\n flex-direction: row !important;\n align-items: stretch;\n gap: 0;\n padding: 0;\n background: var(--card-bg, #1e293b);\n border-radius: 8px;\n margin-bottom: 12px;\n overflow: hidden;\n }\n\n .hdg-header .stat-item {\n display: flex !important;\n flex-direction: column !important;\n gap: 2px;\n flex: 1;\n min-width: 0;\n padding: 12px 16px;\n border-right: 1px solid var(--card-bd, #334155);\n }\n\n .hdg-header .stat-item:last-of-type {\n border-right: none;\n }\n\n .hdg-header .stat-label {\n font-size: 11px;\n color: var(--ink-2, #94a3b8);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .hdg-header .stat-value {\n font-size: 18px;\n font-weight: 700;\n color: var(--ink-1, #f1f5f9);\n }\n\n /* Highlight com background de destaque e barras em ambos os lados */\n .hdg-header .stat-item.highlight {\n background: linear-gradient(135deg, rgba(245, 158, 11, 0.15) 0%, rgba(245, 158, 11, 0.25) 100%);\n border-left: 3px solid var(--telemetry-primary, #f59e0b);\n border-right: 3px solid var(--telemetry-primary, #f59e0b);\n position: relative;\n }\n\n .hdg-header .stat-item.highlight .stat-value {\n color: var(--telemetry-primary, #f59e0b);\n font-size: 20px;\n }\n\n /* Ajuste para o item apos o highlight nao ter borda dupla */\n .hdg-header .stat-item.highlight + .stat-item {\n border-left: none;\n }\n\n .hdg-header .filter-actions {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n border-left: 1px solid var(--card-bd, #334155);\n }\n\n .hdg-header .search-wrap {\n display: none;\n overflow: hidden;\n transition: all 0.2s ease;\n }\n\n .hdg-header .search-wrap.active {\n display: block;\n }\n\n .hdg-header .search-wrap input {\n padding: 6px 12px;\n border: 1px solid var(--card-bd, #334155);\n border-radius: 6px;\n background: transparent;\n color: var(--ink-1, #f1f5f9);\n font-size: 13px;\n width: 180px;\n }\n\n .hdg-header .search-wrap input:focus {\n outline: none;\n border-color: var(--telemetry-primary, #f59e0b);\n }\n\n .hdg-header .icon-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n border: 1px solid var(--card-bd, #334155);\n border-radius: 6px;\n background: transparent;\n color: var(--ink-2, #94a3b8);\n cursor: pointer;\n transition: all 0.15s ease;\n }\n\n .hdg-header .icon-btn:hover {\n background: var(--telemetry-primary, #f59e0b);\n border-color: var(--telemetry-primary, #f59e0b);\n color: white;\n }\n\n .hdg-header .icon-btn svg {\n width: 18px;\n height: 18px;\n fill: currentColor;\n display: block;\n }\n\n .hdg-header .icon-btn .icon-minimize {\n display: none;\n }\n\n .hdg-header .icon-btn .icon-maximize {\n display: block;\n }\n\n /* Light mode */\n .hdg-header--light {\n background: #ffffff;\n border: 1px solid #e2e8f0;\n }\n\n .hdg-header--light .stat-item {\n border-color: #e2e8f0;\n }\n\n .hdg-header--light .stat-item.highlight {\n background: linear-gradient(135deg, rgba(245, 158, 11, 0.1) 0%, rgba(245, 158, 11, 0.2) 100%);\n border-left-color: var(--telemetry-primary, #f59e0b);\n border-right-color: var(--telemetry-primary, #f59e0b);\n }\n\n .hdg-header--light .stat-label {\n color: #64748b;\n }\n\n .hdg-header--light .stat-value {\n color: #1e293b;\n }\n\n .hdg-header--light .filter-actions {\n border-color: #e2e8f0;\n }\n\n .hdg-header--light .search-wrap input {\n background: #f1f5f9;\n border-color: #cbd5e1;\n color: #1e293b;\n }\n\n .hdg-header--light .icon-btn {\n border-color: #cbd5e1;\n color: #64748b;\n }\n\n /* Maximized state for parent container */\n .telemetry-grid-wrap.maximized {\n position: fixed !important;\n inset: 0 !important;\n z-index: 99999 !important;\n width: 100vw !important;\n height: 100vh !important;\n background: var(--card-bg, #0f172a) !important;\n border-radius: 0 !important;\n overflow: auto !important;\n padding: 16px !important;\n }\n\n .telemetry-grid-wrap.maximized .telemetry-grid-header {\n position: sticky;\n top: 0;\n z-index: 10;\n background: var(--card-bg, #0f172a);\n padding-bottom: 12px;\n }\n\n .telemetry-grid-wrap.maximized .telemetry-cards-grid {\n max-height: none !important;\n overflow: visible !important;\n }\n\n .telemetry-grid-wrap.maximized .hdg-header .icon-btn .icon-maximize {\n display: none;\n }\n\n .telemetry-grid-wrap.maximized .hdg-header .icon-btn .icon-minimize {\n display: block;\n }\n\n /* Light mode maximized */\n body.light-mode .telemetry-grid-wrap.maximized {\n background: #f8fafc !important;\n }\n\n body.light-mode .telemetry-grid-wrap.maximized .telemetry-grid-header {\n background: #f8fafc;\n }\n";
17601
+ declare const HEADER_DEVICES_GRID_STYLES = "\n /* RFC-0125: HeaderDevicesGrid Component - FIEL ao TELEMETRY widget */\n .hdg-header {\n display: flex !important;\n flex-direction: row !important;\n align-items: stretch;\n gap: 0;\n padding: 0;\n background: var(--card-bg, #1e293b);\n border-radius: 8px;\n margin-bottom: 12px;\n overflow: hidden;\n }\n\n .hdg-header .stat-item {\n display: flex !important;\n flex-direction: column !important;\n gap: 2px;\n flex: 1;\n min-width: 0;\n padding: 12px 16px;\n border-right: 1px solid var(--card-bd, #334155);\n }\n\n .hdg-header .stat-item:last-of-type {\n border-right: none;\n }\n\n .hdg-header .stat-label {\n font-size: 11px;\n color: var(--ink-2, #94a3b8);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .hdg-header .stat-value {\n font-size: 18px;\n font-weight: 700;\n color: var(--ink-1, #f1f5f9);\n }\n\n /* Highlight com background de destaque e barras em ambos os lados */\n .hdg-header .stat-item.highlight {\n background: linear-gradient(135deg, rgba(245, 158, 11, 0.15) 0%, rgba(245, 158, 11, 0.25) 100%);\n border-left: 3px solid var(--telemetry-primary, #f59e0b);\n border-right: 3px solid var(--telemetry-primary, #f59e0b);\n position: relative;\n }\n\n .hdg-header .stat-item.highlight .stat-value {\n color: var(--telemetry-primary, #f59e0b);\n font-size: 20px;\n }\n\n /* Ajuste para o item apos o highlight nao ter borda dupla */\n .hdg-header .stat-item.highlight + .stat-item {\n border-left: none;\n }\n\n .hdg-header .filter-actions {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n border-left: 1px solid var(--card-bd, #334155);\n }\n\n .hdg-header .search-wrap {\n display: none;\n overflow: hidden;\n transition: all 0.2s ease;\n }\n\n .hdg-header .search-wrap.active {\n display: block;\n }\n\n .hdg-header .search-wrap input {\n padding: 6px 12px;\n border: 1px solid var(--card-bd, #334155);\n border-radius: 6px;\n background: transparent;\n color: var(--ink-1, #f1f5f9);\n font-size: 13px;\n width: 180px;\n }\n\n .hdg-header .search-wrap input:focus {\n outline: none;\n border-color: var(--telemetry-primary, #f59e0b);\n }\n\n .hdg-header .icon-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n border: 1px solid var(--card-bd, #334155);\n border-radius: 6px;\n background: transparent;\n color: var(--ink-2, #94a3b8);\n cursor: pointer;\n transition: all 0.15s ease;\n }\n\n .hdg-header .icon-btn:hover {\n background: var(--telemetry-primary, #f59e0b);\n border-color: var(--telemetry-primary, #f59e0b);\n color: white;\n }\n\n .hdg-header .icon-btn svg {\n width: 18px;\n height: 18px;\n fill: currentColor;\n display: block;\n }\n\n .hdg-header .icon-btn .icon-minimize {\n display: none;\n }\n\n .hdg-header .icon-btn .icon-maximize {\n display: block;\n }\n\n /* Light mode */\n .hdg-header--light {\n background: #ffffff;\n border: 1px solid #e2e8f0;\n }\n\n .hdg-header--light .stat-item {\n border-color: #e2e8f0;\n }\n\n .hdg-header--light .stat-item.highlight {\n background: linear-gradient(135deg, rgba(245, 158, 11, 0.1) 0%, rgba(245, 158, 11, 0.2) 100%);\n border-left-color: var(--telemetry-primary, #f59e0b);\n border-right-color: var(--telemetry-primary, #f59e0b);\n }\n\n .hdg-header--light .stat-label {\n color: #64748b;\n }\n\n .hdg-header--light .stat-value {\n color: #1e293b;\n }\n\n .hdg-header--light .filter-actions {\n border-color: #e2e8f0;\n }\n\n .hdg-header--light .search-wrap input {\n background: #f1f5f9;\n border-color: #cbd5e1;\n color: #1e293b;\n }\n\n .hdg-header--light .icon-btn {\n border-color: #cbd5e1;\n color: #64748b;\n }\n\n /* Maximized state for parent container */\n .telemetry-grid-wrap.maximized {\n position: fixed !important;\n inset: 0 !important;\n z-index: 99999 !important;\n width: 100vw !important;\n height: 100vh !important;\n background: var(--card-bg, #0f172a) !important;\n border-radius: 0 !important;\n overflow: auto !important;\n padding: 16px !important;\n }\n\n .telemetry-grid-wrap.maximized .telemetry-grid-header {\n position: sticky;\n top: 0;\n z-index: 10;\n background: var(--card-bg, #0f172a);\n padding-bottom: 12px;\n }\n\n .telemetry-grid-wrap.maximized .telemetry-cards-grid {\n max-height: none !important;\n overflow: visible !important;\n }\n\n .telemetry-grid-wrap.maximized .hdg-header .icon-btn .icon-maximize {\n display: none;\n }\n\n .telemetry-grid-wrap.maximized .hdg-header .icon-btn .icon-minimize {\n display: block;\n }\n\n /* Light mode maximized */\n body.light-mode .telemetry-grid-wrap.maximized {\n background: #f8fafc !important;\n }\n\n body.light-mode .telemetry-grid-wrap.maximized .telemetry-grid-header {\n background: #f8fafc;\n }\n\n/* Consumo ainda carregando */\n.hdg-value-spinner {\n display: inline-block;\n width: 13px;\n height: 13px;\n margin-right: 6px;\n vertical-align: -2px;\n border: 2px solid rgba(124, 58, 237, 0.2);\n border-top-color: var(--myio-brand-700, #7c3aed);\n border-radius: 50%;\n animation: myio-consumption-spin 0.8s linear infinite;\n}\n@keyframes myio-consumption-spin { to { transform: rotate(360deg); } }\n";
17590
17602
  declare function injectHeaderDevicesGridStyles(): void;
17591
17603
 
17592
17604
  /**
package/dist/index.d.ts CHANGED
@@ -7688,6 +7688,8 @@ interface ShoppingEnergy {
7688
7688
  * Energy KPI data
7689
7689
  */
7690
7690
  interface EnergyKPI {
7691
+ /** Consumo do período ainda carregando → spinner no KPI */
7692
+ loading?: boolean;
7691
7693
  /** Total consumption for selected customers */
7692
7694
  customerTotal: number;
7693
7695
  /** Total consumption before filtering */
@@ -7758,6 +7760,8 @@ interface ShoppingWater {
7758
7760
  * Water KPI data
7759
7761
  */
7760
7762
  interface WaterKPI {
7763
+ /** Consumo do período ainda carregando → spinner no KPI */
7764
+ loading?: boolean;
7761
7765
  /** Total consumption for selected customers */
7762
7766
  filteredTotal: number;
7763
7767
  /** Total consumption before filtering */
@@ -7939,6 +7943,8 @@ interface HeaderComponentInstance {
7939
7943
  updateTemperatureKPI: (data: TemperatureKPI) => void;
7940
7944
  /** Update water KPI */
7941
7945
  updateWaterKPI: (data: WaterKPI) => void;
7946
+ /** Spinner nos KPIs de consumo (energia/água) enquanto o período carrega */
7947
+ setConsumptionLoading: (loading: boolean) => void;
7942
7948
  /** Update tooltip data for a specific card */
7943
7949
  updateTooltipData: (cardType: CardType, data: unknown) => void;
7944
7950
  /** Update available shoppings */
@@ -8227,6 +8233,8 @@ declare class HeaderView {
8227
8233
  /**
8228
8234
  * Update energy card
8229
8235
  */
8236
+ /** Spinner nos KPIs de consumo (energia e água) enquanto o período carrega */
8237
+ setConsumptionLoading(loading: boolean): void;
8230
8238
  updateEnergyCard(data: EnergyKPI): void;
8231
8239
  /**
8232
8240
  * Update temperature card
@@ -17475,6 +17483,8 @@ interface HeaderStats {
17475
17483
  total: number;
17476
17484
  consumption: number;
17477
17485
  zeroCount: number;
17486
+ /** Consumo ainda carregando → spinner em Consumo Total / Sem Consumo */
17487
+ loading?: boolean;
17478
17488
  }
17479
17489
  interface HeaderLabels {
17480
17490
  connectivity?: string;
@@ -17509,6 +17519,8 @@ interface HeaderDevicesGridInstance {
17509
17519
  destroy: () => void;
17510
17520
  }
17511
17521
  interface HeaderDevice {
17522
+ /** Consumo do período ainda não chegou (HO: /summary em andamento) */
17523
+ dataLoading?: boolean;
17512
17524
  entityId?: string;
17513
17525
  ingestionId?: string;
17514
17526
  deviceStatus?: string;
@@ -17586,7 +17598,7 @@ declare class HeaderDevicesGridController implements HeaderDevicesGridInstance {
17586
17598
  * CSS-in-JS styles for the stats header
17587
17599
  * FIEL ao showcase/telemetry-grid/index.html
17588
17600
  */
17589
- declare const HEADER_DEVICES_GRID_STYLES = "\n /* RFC-0125: HeaderDevicesGrid Component - FIEL ao TELEMETRY widget */\n .hdg-header {\n display: flex !important;\n flex-direction: row !important;\n align-items: stretch;\n gap: 0;\n padding: 0;\n background: var(--card-bg, #1e293b);\n border-radius: 8px;\n margin-bottom: 12px;\n overflow: hidden;\n }\n\n .hdg-header .stat-item {\n display: flex !important;\n flex-direction: column !important;\n gap: 2px;\n flex: 1;\n min-width: 0;\n padding: 12px 16px;\n border-right: 1px solid var(--card-bd, #334155);\n }\n\n .hdg-header .stat-item:last-of-type {\n border-right: none;\n }\n\n .hdg-header .stat-label {\n font-size: 11px;\n color: var(--ink-2, #94a3b8);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .hdg-header .stat-value {\n font-size: 18px;\n font-weight: 700;\n color: var(--ink-1, #f1f5f9);\n }\n\n /* Highlight com background de destaque e barras em ambos os lados */\n .hdg-header .stat-item.highlight {\n background: linear-gradient(135deg, rgba(245, 158, 11, 0.15) 0%, rgba(245, 158, 11, 0.25) 100%);\n border-left: 3px solid var(--telemetry-primary, #f59e0b);\n border-right: 3px solid var(--telemetry-primary, #f59e0b);\n position: relative;\n }\n\n .hdg-header .stat-item.highlight .stat-value {\n color: var(--telemetry-primary, #f59e0b);\n font-size: 20px;\n }\n\n /* Ajuste para o item apos o highlight nao ter borda dupla */\n .hdg-header .stat-item.highlight + .stat-item {\n border-left: none;\n }\n\n .hdg-header .filter-actions {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n border-left: 1px solid var(--card-bd, #334155);\n }\n\n .hdg-header .search-wrap {\n display: none;\n overflow: hidden;\n transition: all 0.2s ease;\n }\n\n .hdg-header .search-wrap.active {\n display: block;\n }\n\n .hdg-header .search-wrap input {\n padding: 6px 12px;\n border: 1px solid var(--card-bd, #334155);\n border-radius: 6px;\n background: transparent;\n color: var(--ink-1, #f1f5f9);\n font-size: 13px;\n width: 180px;\n }\n\n .hdg-header .search-wrap input:focus {\n outline: none;\n border-color: var(--telemetry-primary, #f59e0b);\n }\n\n .hdg-header .icon-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n border: 1px solid var(--card-bd, #334155);\n border-radius: 6px;\n background: transparent;\n color: var(--ink-2, #94a3b8);\n cursor: pointer;\n transition: all 0.15s ease;\n }\n\n .hdg-header .icon-btn:hover {\n background: var(--telemetry-primary, #f59e0b);\n border-color: var(--telemetry-primary, #f59e0b);\n color: white;\n }\n\n .hdg-header .icon-btn svg {\n width: 18px;\n height: 18px;\n fill: currentColor;\n display: block;\n }\n\n .hdg-header .icon-btn .icon-minimize {\n display: none;\n }\n\n .hdg-header .icon-btn .icon-maximize {\n display: block;\n }\n\n /* Light mode */\n .hdg-header--light {\n background: #ffffff;\n border: 1px solid #e2e8f0;\n }\n\n .hdg-header--light .stat-item {\n border-color: #e2e8f0;\n }\n\n .hdg-header--light .stat-item.highlight {\n background: linear-gradient(135deg, rgba(245, 158, 11, 0.1) 0%, rgba(245, 158, 11, 0.2) 100%);\n border-left-color: var(--telemetry-primary, #f59e0b);\n border-right-color: var(--telemetry-primary, #f59e0b);\n }\n\n .hdg-header--light .stat-label {\n color: #64748b;\n }\n\n .hdg-header--light .stat-value {\n color: #1e293b;\n }\n\n .hdg-header--light .filter-actions {\n border-color: #e2e8f0;\n }\n\n .hdg-header--light .search-wrap input {\n background: #f1f5f9;\n border-color: #cbd5e1;\n color: #1e293b;\n }\n\n .hdg-header--light .icon-btn {\n border-color: #cbd5e1;\n color: #64748b;\n }\n\n /* Maximized state for parent container */\n .telemetry-grid-wrap.maximized {\n position: fixed !important;\n inset: 0 !important;\n z-index: 99999 !important;\n width: 100vw !important;\n height: 100vh !important;\n background: var(--card-bg, #0f172a) !important;\n border-radius: 0 !important;\n overflow: auto !important;\n padding: 16px !important;\n }\n\n .telemetry-grid-wrap.maximized .telemetry-grid-header {\n position: sticky;\n top: 0;\n z-index: 10;\n background: var(--card-bg, #0f172a);\n padding-bottom: 12px;\n }\n\n .telemetry-grid-wrap.maximized .telemetry-cards-grid {\n max-height: none !important;\n overflow: visible !important;\n }\n\n .telemetry-grid-wrap.maximized .hdg-header .icon-btn .icon-maximize {\n display: none;\n }\n\n .telemetry-grid-wrap.maximized .hdg-header .icon-btn .icon-minimize {\n display: block;\n }\n\n /* Light mode maximized */\n body.light-mode .telemetry-grid-wrap.maximized {\n background: #f8fafc !important;\n }\n\n body.light-mode .telemetry-grid-wrap.maximized .telemetry-grid-header {\n background: #f8fafc;\n }\n";
17601
+ declare const HEADER_DEVICES_GRID_STYLES = "\n /* RFC-0125: HeaderDevicesGrid Component - FIEL ao TELEMETRY widget */\n .hdg-header {\n display: flex !important;\n flex-direction: row !important;\n align-items: stretch;\n gap: 0;\n padding: 0;\n background: var(--card-bg, #1e293b);\n border-radius: 8px;\n margin-bottom: 12px;\n overflow: hidden;\n }\n\n .hdg-header .stat-item {\n display: flex !important;\n flex-direction: column !important;\n gap: 2px;\n flex: 1;\n min-width: 0;\n padding: 12px 16px;\n border-right: 1px solid var(--card-bd, #334155);\n }\n\n .hdg-header .stat-item:last-of-type {\n border-right: none;\n }\n\n .hdg-header .stat-label {\n font-size: 11px;\n color: var(--ink-2, #94a3b8);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .hdg-header .stat-value {\n font-size: 18px;\n font-weight: 700;\n color: var(--ink-1, #f1f5f9);\n }\n\n /* Highlight com background de destaque e barras em ambos os lados */\n .hdg-header .stat-item.highlight {\n background: linear-gradient(135deg, rgba(245, 158, 11, 0.15) 0%, rgba(245, 158, 11, 0.25) 100%);\n border-left: 3px solid var(--telemetry-primary, #f59e0b);\n border-right: 3px solid var(--telemetry-primary, #f59e0b);\n position: relative;\n }\n\n .hdg-header .stat-item.highlight .stat-value {\n color: var(--telemetry-primary, #f59e0b);\n font-size: 20px;\n }\n\n /* Ajuste para o item apos o highlight nao ter borda dupla */\n .hdg-header .stat-item.highlight + .stat-item {\n border-left: none;\n }\n\n .hdg-header .filter-actions {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n border-left: 1px solid var(--card-bd, #334155);\n }\n\n .hdg-header .search-wrap {\n display: none;\n overflow: hidden;\n transition: all 0.2s ease;\n }\n\n .hdg-header .search-wrap.active {\n display: block;\n }\n\n .hdg-header .search-wrap input {\n padding: 6px 12px;\n border: 1px solid var(--card-bd, #334155);\n border-radius: 6px;\n background: transparent;\n color: var(--ink-1, #f1f5f9);\n font-size: 13px;\n width: 180px;\n }\n\n .hdg-header .search-wrap input:focus {\n outline: none;\n border-color: var(--telemetry-primary, #f59e0b);\n }\n\n .hdg-header .icon-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n border: 1px solid var(--card-bd, #334155);\n border-radius: 6px;\n background: transparent;\n color: var(--ink-2, #94a3b8);\n cursor: pointer;\n transition: all 0.15s ease;\n }\n\n .hdg-header .icon-btn:hover {\n background: var(--telemetry-primary, #f59e0b);\n border-color: var(--telemetry-primary, #f59e0b);\n color: white;\n }\n\n .hdg-header .icon-btn svg {\n width: 18px;\n height: 18px;\n fill: currentColor;\n display: block;\n }\n\n .hdg-header .icon-btn .icon-minimize {\n display: none;\n }\n\n .hdg-header .icon-btn .icon-maximize {\n display: block;\n }\n\n /* Light mode */\n .hdg-header--light {\n background: #ffffff;\n border: 1px solid #e2e8f0;\n }\n\n .hdg-header--light .stat-item {\n border-color: #e2e8f0;\n }\n\n .hdg-header--light .stat-item.highlight {\n background: linear-gradient(135deg, rgba(245, 158, 11, 0.1) 0%, rgba(245, 158, 11, 0.2) 100%);\n border-left-color: var(--telemetry-primary, #f59e0b);\n border-right-color: var(--telemetry-primary, #f59e0b);\n }\n\n .hdg-header--light .stat-label {\n color: #64748b;\n }\n\n .hdg-header--light .stat-value {\n color: #1e293b;\n }\n\n .hdg-header--light .filter-actions {\n border-color: #e2e8f0;\n }\n\n .hdg-header--light .search-wrap input {\n background: #f1f5f9;\n border-color: #cbd5e1;\n color: #1e293b;\n }\n\n .hdg-header--light .icon-btn {\n border-color: #cbd5e1;\n color: #64748b;\n }\n\n /* Maximized state for parent container */\n .telemetry-grid-wrap.maximized {\n position: fixed !important;\n inset: 0 !important;\n z-index: 99999 !important;\n width: 100vw !important;\n height: 100vh !important;\n background: var(--card-bg, #0f172a) !important;\n border-radius: 0 !important;\n overflow: auto !important;\n padding: 16px !important;\n }\n\n .telemetry-grid-wrap.maximized .telemetry-grid-header {\n position: sticky;\n top: 0;\n z-index: 10;\n background: var(--card-bg, #0f172a);\n padding-bottom: 12px;\n }\n\n .telemetry-grid-wrap.maximized .telemetry-cards-grid {\n max-height: none !important;\n overflow: visible !important;\n }\n\n .telemetry-grid-wrap.maximized .hdg-header .icon-btn .icon-maximize {\n display: none;\n }\n\n .telemetry-grid-wrap.maximized .hdg-header .icon-btn .icon-minimize {\n display: block;\n }\n\n /* Light mode maximized */\n body.light-mode .telemetry-grid-wrap.maximized {\n background: #f8fafc !important;\n }\n\n body.light-mode .telemetry-grid-wrap.maximized .telemetry-grid-header {\n background: #f8fafc;\n }\n\n/* Consumo ainda carregando */\n.hdg-value-spinner {\n display: inline-block;\n width: 13px;\n height: 13px;\n margin-right: 6px;\n vertical-align: -2px;\n border: 2px solid rgba(124, 58, 237, 0.2);\n border-top-color: var(--myio-brand-700, #7c3aed);\n border-radius: 50%;\n animation: myio-consumption-spin 0.8s linear infinite;\n}\n@keyframes myio-consumption-spin { to { transform: rotate(360deg); } }\n";
17590
17602
  declare function injectHeaderDevicesGridStyles(): void;
17591
17603
 
17592
17604
  /**
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  // package.json
11
11
  var package_default = {
12
12
  name: "myio-js-library",
13
- version: "0.1.557",
13
+ version: "0.1.558",
14
14
  description: "A clean, standalone JS SDK for MYIO projects",
15
15
  license: "MIT",
16
16
  repository: "github:gh-myio/myio-js-library",
@@ -11242,6 +11242,26 @@ var CSS_STRING = `
11242
11242
  margin-left: 4px;
11243
11243
  }
11244
11244
 
11245
+ /* Consumo ainda carregando (spinner no lugar do valor) */
11246
+ .myio-ho-card__value.is-loading .num {
11247
+ display: inline-flex;
11248
+ align-items: center;
11249
+ gap: 8px;
11250
+ font-size: 14px;
11251
+ font-weight: 600;
11252
+ color: var(--myio-muted, #9ca3af);
11253
+ }
11254
+ .myio-ho-card__value-spinner {
11255
+ width: 14px;
11256
+ height: 14px;
11257
+ border: 2px solid rgba(124, 58, 237, 0.2);
11258
+ border-top-color: var(--myio-brand-700, #7c3aed);
11259
+ border-radius: 50%;
11260
+ animation: myio-consumption-spin 0.8s linear infinite;
11261
+ flex-shrink: 0;
11262
+ }
11263
+ @keyframes myio-consumption-spin { to { transform: rotate(360deg); } }
11264
+
11245
11265
  /* Efficiency bar */
11246
11266
  .myio-ho-card__eff {
11247
11267
  display: flex;
@@ -14914,7 +14934,20 @@ function paint(root, state5) {
14914
14934
  const primaryValue = formatValueByDomain(entityObject.val, entityObject.domain);
14915
14935
  const numSpan = root.querySelector(".myio-ho-card__value .num");
14916
14936
  const unitSpan = root.querySelector(".myio-ho-card__value .unit");
14917
- numSpan.textContent = primaryValue;
14937
+ const valueBox = root.querySelector(".myio-ho-card__value");
14938
+ const dataLoading = !!entityObject.dataLoading && entityObject.domain !== "temperature";
14939
+ if (valueBox) {
14940
+ valueBox.classList.toggle("is-loading", dataLoading);
14941
+ if (dataLoading) valueBox.setAttribute("aria-busy", "true");
14942
+ else valueBox.removeAttribute("aria-busy");
14943
+ }
14944
+ if (dataLoading) {
14945
+ numSpan.innerHTML = '<span class="myio-ho-card__value-spinner" aria-hidden="true"></span>Carregando\u2026';
14946
+ numSpan.title = "Carregando consumo do per\xEDodo";
14947
+ } else {
14948
+ numSpan.textContent = primaryValue;
14949
+ numSpan.removeAttribute("title");
14950
+ }
14918
14951
  const barContainer = root.querySelector(".bar");
14919
14952
  const effContainer = root.querySelector(".myio-ho-card__eff");
14920
14953
  if (state5.enableSelection) {
@@ -70086,6 +70119,25 @@ var HeaderView = class {
70086
70119
  transition: color 0.2s ease;
70087
70120
  }
70088
70121
 
70122
+ /* Consumo ainda carregando */
70123
+ .${HEADER_CSS_PREFIX}-kpi-loading {
70124
+ display: inline-flex;
70125
+ align-items: center;
70126
+ gap: 10px;
70127
+ font-size: 1rem;
70128
+ font-weight: 600;
70129
+ opacity: 0.85;
70130
+ }
70131
+ .${HEADER_CSS_PREFIX}-kpi-spinner {
70132
+ width: 18px;
70133
+ height: 18px;
70134
+ border: 2.5px solid rgba(255, 255, 255, 0.3);
70135
+ border-top-color: currentColor;
70136
+ border-radius: 50%;
70137
+ animation: myio-consumption-spin 0.8s linear infinite;
70138
+ }
70139
+ @keyframes myio-consumption-spin { to { transform: rotate(360deg); } }
70140
+
70089
70141
  .${HEADER_CSS_PREFIX}-card__subrow {
70090
70142
  display: flex;
70091
70143
  align-items: center;
@@ -70524,7 +70576,28 @@ var HeaderView = class {
70524
70576
  /**
70525
70577
  * Update energy card
70526
70578
  */
70579
+ /** Spinner nos KPIs de consumo (energia e água) enquanto o período carrega */
70580
+ setConsumptionLoading(loading) {
70581
+ if (!loading) {
70582
+ [this.energyKpiEl, this.waterKpiEl].forEach((el2) => {
70583
+ if (el2?.querySelector(`.${HEADER_CSS_PREFIX}-kpi-loading`)) el2.textContent = "-";
70584
+ });
70585
+ return;
70586
+ }
70587
+ const html = `<span class="${HEADER_CSS_PREFIX}-kpi-loading" aria-busy="true"><span class="${HEADER_CSS_PREFIX}-kpi-spinner" aria-hidden="true"></span>Carregando\u2026</span>`;
70588
+ if (this.energyKpiEl) this.energyKpiEl.innerHTML = html;
70589
+ if (this.waterKpiEl) this.waterKpiEl.innerHTML = html;
70590
+ if (this.energyTrendEl) this.energyTrendEl.textContent = "";
70591
+ if (this.waterTrendEl) this.waterTrendEl.textContent = "";
70592
+ }
70527
70593
  updateEnergyCard(data) {
70594
+ if (data.loading) {
70595
+ if (this.energyKpiEl) {
70596
+ this.energyKpiEl.innerHTML = `<span class="${HEADER_CSS_PREFIX}-kpi-loading" aria-busy="true"><span class="${HEADER_CSS_PREFIX}-kpi-spinner" aria-hidden="true"></span>Carregando\u2026</span>`;
70597
+ }
70598
+ if (this.energyTrendEl) this.energyTrendEl.textContent = "";
70599
+ return;
70600
+ }
70528
70601
  if (this.energyKpiEl) {
70529
70602
  if (data.isFiltered && data.unfilteredTotal > 0) {
70530
70603
  const filteredFormatted = this.formatEnergy(data.customerTotal);
@@ -70573,6 +70646,13 @@ var HeaderView = class {
70573
70646
  * Update water card
70574
70647
  */
70575
70648
  updateWaterCard(data) {
70649
+ if (data.loading) {
70650
+ if (this.waterKpiEl) {
70651
+ this.waterKpiEl.innerHTML = `<span class="${HEADER_CSS_PREFIX}-kpi-loading" aria-busy="true"><span class="${HEADER_CSS_PREFIX}-kpi-spinner" aria-hidden="true"></span>Carregando\u2026</span>`;
70652
+ }
70653
+ if (this.waterTrendEl) this.waterTrendEl.textContent = "";
70654
+ return;
70655
+ }
70576
70656
  if (this.waterKpiEl) {
70577
70657
  if (data.isFiltered && data.unfilteredTotal > 0) {
70578
70658
  const filteredFormatted = this.formatWater(data.filteredTotal);
@@ -71558,6 +71638,9 @@ function createHeaderComponent(params) {
71558
71638
  tooltipData.temperature = data;
71559
71639
  view.updateTemperatureCard(data);
71560
71640
  }
71641
+ function setConsumptionLoading(loading) {
71642
+ view.setConsumptionLoading(loading);
71643
+ }
71561
71644
  function updateWaterKPI(data) {
71562
71645
  tooltipData.water = data;
71563
71646
  view.updateWaterCard(data);
@@ -71613,6 +71696,7 @@ function createHeaderComponent(params) {
71613
71696
  updateEnergyKPI,
71614
71697
  updateTemperatureKPI,
71615
71698
  updateWaterKPI,
71699
+ setConsumptionLoading,
71616
71700
  updateTooltipData,
71617
71701
  // Shopping/Filter
71618
71702
  updateShoppings,
@@ -120831,6 +120915,20 @@ var HEADER_DEVICES_GRID_STYLES = `
120831
120915
  body.light-mode .telemetry-grid-wrap.maximized .telemetry-grid-header {
120832
120916
  background: #f8fafc;
120833
120917
  }
120918
+
120919
+ /* Consumo ainda carregando */
120920
+ .hdg-value-spinner {
120921
+ display: inline-block;
120922
+ width: 13px;
120923
+ height: 13px;
120924
+ margin-right: 6px;
120925
+ vertical-align: -2px;
120926
+ border: 2px solid rgba(124, 58, 237, 0.2);
120927
+ border-top-color: var(--myio-brand-700, #7c3aed);
120928
+ border-radius: 50%;
120929
+ animation: myio-consumption-spin 0.8s linear infinite;
120930
+ }
120931
+ @keyframes myio-consumption-spin { to { transform: rotate(360deg); } }
120834
120932
  `;
120835
120933
  function injectHeaderDevicesGridStyles() {
120836
120934
  const styleId = "hdg-styles";
@@ -120848,14 +120946,15 @@ var DOMAIN_CONFIG7 = {
120848
120946
  consumptionLabel: "Consumo Total",
120849
120947
  zeroLabel: "Sem Consumo",
120850
120948
  unit: "kWh",
120851
- formatValue: (value) => formatEnergy(value, void 0, 2)
120949
+ // 3 casas: mesmo padrão do dashboard de shopping (RFC-0108, measurementSettings default)
120950
+ formatValue: (value) => formatEnergy(value, void 0, 3)
120852
120951
  },
120853
120952
  water: {
120854
120953
  totalLabel: "Total Hidr\xF4metros",
120855
120954
  consumptionLabel: "Consumo Total",
120856
120955
  zeroLabel: "Sem Consumo",
120857
120956
  unit: "m\xB3",
120858
- formatValue: (value) => formatWater(value)
120957
+ formatValue: (value) => `${(Number(value) || 0).toLocaleString("pt-BR", { minimumFractionDigits: 3, maximumFractionDigits: 3 })} m\xB3`
120859
120958
  },
120860
120959
  temperature: {
120861
120960
  totalLabel: "Total Sensores",
@@ -121023,6 +121122,16 @@ var HeaderDevicesGridView = class {
121023
121122
  const percentage = total > 0 ? (online / total * 100).toFixed(1) : "0.0";
121024
121123
  connectivityEl.textContent = `${online}/${total} (${percentage}%)`;
121025
121124
  totalEl.textContent = total.toString();
121125
+ if (stats.loading) {
121126
+ const spin = '<span class="hdg-value-spinner" aria-hidden="true"></span>';
121127
+ consumptionEl.innerHTML = `${spin}Carregando\u2026`;
121128
+ zeroEl.innerHTML = spin;
121129
+ consumptionEl.setAttribute("aria-busy", "true");
121130
+ zeroEl.setAttribute("aria-busy", "true");
121131
+ return;
121132
+ }
121133
+ consumptionEl.removeAttribute("aria-busy");
121134
+ zeroEl.removeAttribute("aria-busy");
121026
121135
  consumptionEl.textContent = this.domainConfig.formatValue(consumption);
121027
121136
  zeroEl.textContent = zeroCount.toString();
121028
121137
  }
@@ -121129,7 +121238,8 @@ var HeaderDevicesGridController = class {
121129
121238
  online,
121130
121239
  total: devices.length,
121131
121240
  consumption: displayValue,
121132
- zeroCount
121241
+ zeroCount,
121242
+ loading: this.domain !== "temperature" && devices.some((d) => d.dataLoading)
121133
121243
  });
121134
121244
  }
121135
121245
  setThemeMode(mode) {