myio-js-library 0.1.197 → 0.1.199

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
@@ -577,6 +577,7 @@ __export(index_exports, {
577
577
  CONSUMPTION_CHART_DEFAULTS: () => DEFAULT_CONFIG,
578
578
  CONSUMPTION_THEME_COLORS: () => THEME_COLORS,
579
579
  ConnectionStatusType: () => ConnectionStatusType,
580
+ ContractSummaryTooltip: () => ContractSummaryTooltip,
580
581
  DEFAULT_CLAMP_RANGE: () => DEFAULT_CLAMP_RANGE,
581
582
  DEFAULT_ENERGY_GROUP_COLORS: () => DEFAULT_ENERGY_GROUP_COLORS,
582
583
  DEFAULT_GAS_GROUP_COLORS: () => DEFAULT_GAS_GROUP_COLORS,
@@ -9044,8 +9045,8 @@ function getStatusDotClass(deviceStatus) {
9044
9045
  return "dot--offline";
9045
9046
  }
9046
9047
  }
9047
- function buildDOM(state5) {
9048
- const { entityObject, i18n, enableSelection, enableDragDrop } = state5;
9048
+ function buildDOM(state6) {
9049
+ const { entityObject, i18n, enableSelection, enableDragDrop } = state6;
9049
9050
  const root = document.createElement("div");
9050
9051
  root.className = "myio-ho-card";
9051
9052
  root.setAttribute("role", "group");
@@ -9201,7 +9202,7 @@ function buildDOM(state5) {
9201
9202
  return root;
9202
9203
  }
9203
9204
  function buildDebugTooltipInfo(entityObject, statusInfo, stateClass, statusDecisionSource, delayTimeConnectionInMins) {
9204
- const formatTimestamp5 = (ts) => {
9205
+ const formatTimestamp6 = (ts) => {
9205
9206
  if (!ts) return "N/A";
9206
9207
  const d = new Date(ts);
9207
9208
  return d.toLocaleString("pt-BR", {
@@ -9228,8 +9229,8 @@ function buildDebugTooltipInfo(entityObject, statusInfo, stateClass, statusDecis
9228
9229
  chipClass: statusInfo.chipClass,
9229
9230
  chipLabel: statusInfo.label,
9230
9231
  // Connection timestamps
9231
- lastConnectTime: formatTimestamp5(entityObject.lastConnectTime),
9232
- lastDisconnectTime: formatTimestamp5(entityObject.lastDisconnectTime),
9232
+ lastConnectTime: formatTimestamp6(entityObject.lastConnectTime),
9233
+ lastDisconnectTime: formatTimestamp6(entityObject.lastDisconnectTime),
9233
9234
  delayTimeConnectionInMins,
9234
9235
  // Raw values
9235
9236
  val: entityObject.val,
@@ -9371,8 +9372,8 @@ function verifyOfflineStatus(entityObject, delayTimeInMins = 15, LogHelper2) {
9371
9372
  }
9372
9373
  return isOffline;
9373
9374
  }
9374
- function paint(root, state5) {
9375
- const { entityObject, i18n, delayTimeConnectionInMins, isSelected, LogHelper: LogHelper2, activeTooltipDebug } = state5;
9375
+ function paint(root, state6) {
9376
+ const { entityObject, i18n, delayTimeConnectionInMins, isSelected, LogHelper: LogHelper2, activeTooltipDebug } = state6;
9376
9377
  let statusDecisionSource = "unknown";
9377
9378
  if (entityObject.connectionStatus) {
9378
9379
  if (entityObject.connectionStatus === "offline") {
@@ -9424,7 +9425,7 @@ function paint(root, state5) {
9424
9425
  numSpan.textContent = primaryValue;
9425
9426
  const barContainer = root.querySelector(".bar");
9426
9427
  const effContainer = root.querySelector(".myio-ho-card__eff");
9427
- if (state5.enableSelection) {
9428
+ if (state6.enableSelection) {
9428
9429
  const checkbox = root.querySelector('.myio-ho-card__select input[type="checkbox"]');
9429
9430
  if (checkbox) {
9430
9431
  checkbox.checked = !!isSelected;
@@ -9472,8 +9473,8 @@ function paint(root, state5) {
9472
9473
  statusDot.className = `status-dot ${dotClass}`;
9473
9474
  }
9474
9475
  }
9475
- function bindEvents(root, state5, callbacks) {
9476
- const { entityObject } = state5;
9476
+ function bindEvents(root, state6, callbacks) {
9477
+ const { entityObject } = state6;
9477
9478
  const kebabBtn = root.querySelector(".myio-ho-card__kebab");
9478
9479
  const menu = root.querySelector(".myio-ho-card__menu");
9479
9480
  function toggleMenu() {
@@ -9529,9 +9530,9 @@ function bindEvents(root, state5, callbacks) {
9529
9530
  const onSelectionChange = () => {
9530
9531
  const selectedIds = MyIOSelectionStore2.getSelectedIds();
9531
9532
  const isSelected = selectedIds.includes(entityObject.entityId);
9532
- if (state5.isSelected !== isSelected) {
9533
- state5.isSelected = isSelected;
9534
- paint(root, state5);
9533
+ if (state6.isSelected !== isSelected) {
9534
+ state6.isSelected = isSelected;
9535
+ paint(root, state6);
9535
9536
  }
9536
9537
  };
9537
9538
  MyIOSelectionStore2.on("selection:change", onSelectionChange);
@@ -9544,7 +9545,7 @@ function bindEvents(root, state5, callbacks) {
9544
9545
  clearTimeout(TempRangeTooltip._hideTimer);
9545
9546
  TempRangeTooltip._hideTimer = null;
9546
9547
  }
9547
- TempRangeTooltip.show(root, state5.entityObject, e);
9548
+ TempRangeTooltip.show(root, state6.entityObject, e);
9548
9549
  };
9549
9550
  const hideTooltip = () => {
9550
9551
  TempRangeTooltip._startDelayedHide();
@@ -9562,7 +9563,7 @@ function bindEvents(root, state5, callbacks) {
9562
9563
  clearTimeout(EnergyRangeTooltip._hideTimer);
9563
9564
  EnergyRangeTooltip._hideTimer = null;
9564
9565
  }
9565
- EnergyRangeTooltip.show(root, state5.entityObject, e);
9566
+ EnergyRangeTooltip.show(root, state6.entityObject, e);
9566
9567
  };
9567
9568
  const hideEnergyTooltip = () => {
9568
9569
  EnergyRangeTooltip._startDelayedHide();
@@ -9614,7 +9615,7 @@ function bindEvents(root, state5, callbacks) {
9614
9615
  }
9615
9616
  });
9616
9617
  }
9617
- if (state5.enableDragDrop) {
9618
+ if (state6.enableDragDrop) {
9618
9619
  root.addEventListener("dragstart", (e) => {
9619
9620
  root.classList.add("is-dragging");
9620
9621
  e.dataTransfer.setData("text/plain", entityObject.entityId);
@@ -9656,17 +9657,17 @@ function renderCardComponentHeadOffice(containerEl, params) {
9656
9657
  throw new Error("renderCardComponentHeadOffice: containerEl is required");
9657
9658
  }
9658
9659
  ensureCss();
9659
- const state5 = normalizeParams(params);
9660
- const root = buildDOM(state5);
9661
- state5.isSelected = params.isSelected || false;
9660
+ const state6 = normalizeParams(params);
9661
+ const root = buildDOM(state6);
9662
+ state6.isSelected = params.isSelected || false;
9662
9663
  containerEl.appendChild(root);
9663
- bindEvents(root, state5, state5.callbacks);
9664
- paint(root, state5);
9664
+ bindEvents(root, state6, state6.callbacks);
9665
+ paint(root, state6);
9665
9666
  return {
9666
9667
  update(next) {
9667
9668
  if (next) {
9668
- Object.assign(state5.entityObject, next);
9669
- paint(root, state5);
9669
+ Object.assign(state6.entityObject, next);
9670
+ paint(root, state6);
9670
9671
  }
9671
9672
  },
9672
9673
  destroy() {
@@ -11396,6 +11397,15 @@ function calculateTempStatus(currentTemp, avgTemp) {
11396
11397
  return { deviation: absDeviation, sign: "-", status: "below", statusText: "Abaixo da media", statusIcon: "\u{1F53B}" };
11397
11398
  }
11398
11399
  }
11400
+ function calculateRangeStatus(currentTemp, minTemp, maxTemp) {
11401
+ if (currentTemp >= minTemp && currentTemp <= maxTemp) {
11402
+ return { status: "ok", statusText: "Dentro da faixa", statusIcon: "\u2713" };
11403
+ } else if (currentTemp > maxTemp) {
11404
+ return { status: "above", statusText: "Acima da faixa", statusIcon: "\u{1F53A}" };
11405
+ } else {
11406
+ return { status: "below", statusText: "Abaixo da faixa", statusIcon: "\u{1F53B}" };
11407
+ }
11408
+ }
11399
11409
  function calcTempBarPosition(temp, minRange, maxRange) {
11400
11410
  const rangeSize = maxRange - minRange;
11401
11411
  const extendedMin = minRange - rangeSize * 0.3;
@@ -11438,8 +11448,10 @@ function generateBodyHTML2(data) {
11438
11448
  const { device, average, lastUpdated } = data;
11439
11449
  const timestamp = formatTimestamp2(lastUpdated);
11440
11450
  const tempStatus = calculateTempStatus(device.currentTemp, average.value);
11451
+ const rangeStatus = calculateRangeStatus(device.currentTemp, device.minTemp, device.maxTemp);
11441
11452
  const deviceBarPos = calcTempBarPosition(device.currentTemp, device.minTemp, device.maxTemp);
11442
11453
  const avgBarPos = calcTempBarPosition(average.value, device.minTemp, device.maxTemp);
11454
+ const rangeStatusClass = rangeStatus.status === "ok" ? "normal" : rangeStatus.status;
11443
11455
  return `
11444
11456
  <div class="myio-temp-comparison-tooltip__body">
11445
11457
  <!-- Main Stats -->
@@ -11454,12 +11466,18 @@ function generateBodyHTML2(data) {
11454
11466
  </div>
11455
11467
  </div>
11456
11468
 
11457
- <!-- Configured Range -->
11469
+ <!-- Configured Range with Status -->
11458
11470
  <div class="myio-temp-comparison-tooltip__range">
11459
11471
  <span class="myio-temp-comparison-tooltip__range-label">Faixa Ideal:</span>
11460
11472
  <span class="myio-temp-comparison-tooltip__range-value">${formatTemp(device.minTemp)} - ${formatTemp(device.maxTemp)}</span>
11461
11473
  </div>
11462
11474
 
11475
+ <!-- Range Status Indicator -->
11476
+ <div class="myio-temp-comparison-tooltip__status ${rangeStatusClass}">
11477
+ <span class="myio-temp-comparison-tooltip__status-icon">${rangeStatus.statusIcon}</span>
11478
+ <span>${rangeStatus.statusText}</span>
11479
+ </div>
11480
+
11463
11481
  <!-- Section: Average Comparison -->
11464
11482
  <div class="myio-temp-comparison-tooltip__section-title">
11465
11483
  <span class="myio-temp-comparison-tooltip__section-icon">\u{1F4CA}</span>
@@ -30969,7 +30987,7 @@ async function openTemperatureModal(params) {
30969
30987
  const defaultDateRange = getTodaySoFar();
30970
30988
  const startTs = params.startDate ? new Date(params.startDate).getTime() : defaultDateRange.startTs;
30971
30989
  const endTs = params.endDate ? new Date(params.endDate).getTime() : defaultDateRange.endTs;
30972
- const state5 = {
30990
+ const state6 = {
30973
30991
  token: params.token,
30974
30992
  deviceId: params.deviceId,
30975
30993
  label: params.label || "Sensor de Temperatura",
@@ -30992,58 +31010,58 @@ async function openTemperatureModal(params) {
30992
31010
  };
30993
31011
  const savedGranularity = localStorage.getItem("myio-temp-modal-granularity");
30994
31012
  const savedTheme = localStorage.getItem("myio-temp-modal-theme");
30995
- if (savedGranularity) state5.granularity = savedGranularity;
30996
- if (savedTheme) state5.theme = savedTheme;
31013
+ if (savedGranularity) state6.granularity = savedGranularity;
31014
+ if (savedTheme) state6.theme = savedTheme;
30997
31015
  const modalContainer = document.createElement("div");
30998
31016
  modalContainer.id = modalId;
30999
31017
  document.body.appendChild(modalContainer);
31000
- renderModal(modalContainer, state5, modalId);
31018
+ renderModal(modalContainer, state6, modalId);
31001
31019
  try {
31002
- state5.data = await fetchTemperatureData(state5.token, state5.deviceId, state5.startTs, state5.endTs);
31003
- state5.stats = calculateStats(state5.data, state5.clampRange);
31004
- state5.isLoading = false;
31005
- renderModal(modalContainer, state5, modalId);
31006
- drawChart(modalId, state5);
31020
+ state6.data = await fetchTemperatureData(state6.token, state6.deviceId, state6.startTs, state6.endTs);
31021
+ state6.stats = calculateStats(state6.data, state6.clampRange);
31022
+ state6.isLoading = false;
31023
+ renderModal(modalContainer, state6, modalId);
31024
+ drawChart(modalId, state6);
31007
31025
  } catch (error) {
31008
31026
  console.error("[TemperatureModal] Error fetching data:", error);
31009
- state5.isLoading = false;
31010
- renderModal(modalContainer, state5, modalId, error);
31027
+ state6.isLoading = false;
31028
+ renderModal(modalContainer, state6, modalId, error);
31011
31029
  }
31012
- await setupEventListeners(modalContainer, state5, modalId, params.onClose);
31030
+ await setupEventListeners(modalContainer, state6, modalId, params.onClose);
31013
31031
  return {
31014
31032
  destroy: () => {
31015
31033
  modalContainer.remove();
31016
31034
  params.onClose?.();
31017
31035
  },
31018
31036
  updateData: async (startDate, endDate, granularity) => {
31019
- state5.startTs = new Date(startDate).getTime();
31020
- state5.endTs = new Date(endDate).getTime();
31021
- if (granularity) state5.granularity = granularity;
31022
- state5.isLoading = true;
31023
- renderModal(modalContainer, state5, modalId);
31037
+ state6.startTs = new Date(startDate).getTime();
31038
+ state6.endTs = new Date(endDate).getTime();
31039
+ if (granularity) state6.granularity = granularity;
31040
+ state6.isLoading = true;
31041
+ renderModal(modalContainer, state6, modalId);
31024
31042
  try {
31025
- state5.data = await fetchTemperatureData(state5.token, state5.deviceId, state5.startTs, state5.endTs);
31026
- state5.stats = calculateStats(state5.data, state5.clampRange);
31027
- state5.isLoading = false;
31028
- renderModal(modalContainer, state5, modalId);
31029
- drawChart(modalId, state5);
31043
+ state6.data = await fetchTemperatureData(state6.token, state6.deviceId, state6.startTs, state6.endTs);
31044
+ state6.stats = calculateStats(state6.data, state6.clampRange);
31045
+ state6.isLoading = false;
31046
+ renderModal(modalContainer, state6, modalId);
31047
+ drawChart(modalId, state6);
31030
31048
  } catch (error) {
31031
31049
  console.error("[TemperatureModal] Error updating data:", error);
31032
- state5.isLoading = false;
31033
- renderModal(modalContainer, state5, modalId, error);
31050
+ state6.isLoading = false;
31051
+ renderModal(modalContainer, state6, modalId, error);
31034
31052
  }
31035
31053
  }
31036
31054
  };
31037
31055
  }
31038
- function renderModal(container, state5, modalId, error) {
31039
- const colors = getThemeColors(state5.theme);
31040
- const startDateStr = new Date(state5.startTs).toLocaleDateString(state5.locale);
31041
- const endDateStr = new Date(state5.endTs).toLocaleDateString(state5.locale);
31042
- const statusText = state5.temperatureStatus === "ok" ? "Dentro da faixa" : state5.temperatureStatus === "above" ? "Acima do limite" : state5.temperatureStatus === "below" ? "Abaixo do limite" : "N/A";
31043
- const statusColor = state5.temperatureStatus === "ok" ? colors.success : state5.temperatureStatus === "above" ? colors.danger : state5.temperatureStatus === "below" ? colors.primary : colors.textMuted;
31044
- const rangeText = state5.temperatureMin !== null && state5.temperatureMax !== null ? `${state5.temperatureMin}\xB0C - ${state5.temperatureMax}\xB0C` : "N\xE3o definida";
31045
- const startDateInput = new Date(state5.startTs).toISOString().slice(0, 16);
31046
- const endDateInput = new Date(state5.endTs).toISOString().slice(0, 16);
31056
+ function renderModal(container, state6, modalId, error) {
31057
+ const colors = getThemeColors(state6.theme);
31058
+ const startDateStr = new Date(state6.startTs).toLocaleDateString(state6.locale);
31059
+ const endDateStr = new Date(state6.endTs).toLocaleDateString(state6.locale);
31060
+ const statusText = state6.temperatureStatus === "ok" ? "Dentro da faixa" : state6.temperatureStatus === "above" ? "Acima do limite" : state6.temperatureStatus === "below" ? "Abaixo do limite" : "N/A";
31061
+ const statusColor = state6.temperatureStatus === "ok" ? colors.success : state6.temperatureStatus === "above" ? colors.danger : state6.temperatureStatus === "below" ? colors.primary : colors.textMuted;
31062
+ const rangeText = state6.temperatureMin !== null && state6.temperatureMax !== null ? `${state6.temperatureMin}\xB0C - ${state6.temperatureMax}\xB0C` : "N\xE3o definida";
31063
+ const startDateInput = new Date(state6.startTs).toISOString().slice(0, 16);
31064
+ const endDateInput = new Date(state6.endTs).toISOString().slice(0, 16);
31047
31065
  const isMaximized = container.__isMaximized || false;
31048
31066
  const contentMaxWidth = isMaximized ? "100%" : "900px";
31049
31067
  const contentMaxHeight = isMaximized ? "100vh" : "95vh";
@@ -31070,7 +31088,7 @@ function renderModal(container, state5, modalId, error) {
31070
31088
  min-height: 20px;
31071
31089
  ">
31072
31090
  <h2 style="margin: 6px; font-size: 18px; font-weight: 600; color: white; line-height: 2;">
31073
- \u{1F321}\uFE0F ${state5.label} - Hist\xF3rico de Temperatura
31091
+ \u{1F321}\uFE0F ${state6.label} - Hist\xF3rico de Temperatura
31074
31092
  </h2>
31075
31093
  <div style="display: flex; gap: 4px; align-items: center;">
31076
31094
  <!-- Theme Toggle -->
@@ -31078,7 +31096,7 @@ function renderModal(container, state5, modalId, error) {
31078
31096
  background: none; border: none; font-size: 16px; cursor: pointer;
31079
31097
  padding: 4px 8px; border-radius: 6px; color: rgba(255,255,255,0.8);
31080
31098
  transition: background-color 0.2s;
31081
- ">${state5.theme === "dark" ? "\u2600\uFE0F" : "\u{1F319}"}</button>
31099
+ ">${state6.theme === "dark" ? "\u2600\uFE0F" : "\u{1F319}"}</button>
31082
31100
  <!-- Maximize Button -->
31083
31101
  <button id="${modalId}-maximize" title="${isMaximized ? "Restaurar" : "Maximizar"}" style="
31084
31102
  background: none; border: none; font-size: 16px; cursor: pointer;
@@ -31100,7 +31118,7 @@ function renderModal(container, state5, modalId, error) {
31100
31118
  <!-- Controls Row -->
31101
31119
  <div style="
31102
31120
  display: flex; gap: 16px; flex-wrap: wrap; align-items: flex-end;
31103
- margin-bottom: 16px; padding: 16px; background: ${state5.theme === "dark" ? "rgba(255,255,255,0.05)" : "#f7f7f7"};
31121
+ margin-bottom: 16px; padding: 16px; background: ${state6.theme === "dark" ? "rgba(255,255,255,0.05)" : "#f7f7f7"};
31104
31122
  border-radius: 6px; border: 1px solid ${colors.border};
31105
31123
  ">
31106
31124
  <!-- Granularity Select -->
@@ -31113,8 +31131,8 @@ function renderModal(container, state5, modalId, error) {
31113
31131
  font-size: 14px; color: ${colors.text}; background: ${colors.surface};
31114
31132
  cursor: pointer; min-width: 130px;
31115
31133
  ">
31116
- <option value="hour" ${state5.granularity === "hour" ? "selected" : ""}>Hora (30 min)</option>
31117
- <option value="day" ${state5.granularity === "day" ? "selected" : ""}>Dia (m\xE9dia)</option>
31134
+ <option value="hour" ${state6.granularity === "hour" ? "selected" : ""}>Hora (30 min)</option>
31135
+ <option value="day" ${state6.granularity === "day" ? "selected" : ""}>Dia (m\xE9dia)</option>
31118
31136
  </select>
31119
31137
  </div>
31120
31138
  <!-- Day Period Filter (Multiselect) -->
@@ -31128,7 +31146,7 @@ function renderModal(container, state5, modalId, error) {
31128
31146
  cursor: pointer; min-width: 180px; text-align: left;
31129
31147
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
31130
31148
  ">
31131
- <span>${getSelectedPeriodsLabel(state5.selectedPeriods)}</span>
31149
+ <span>${getSelectedPeriodsLabel(state6.selectedPeriods)}</span>
31132
31150
  <span style="font-size: 10px;">\u25BC</span>
31133
31151
  </button>
31134
31152
  <div id="${modalId}-period-dropdown" style="
@@ -31141,12 +31159,12 @@ function renderModal(container, state5, modalId, error) {
31141
31159
  <label style="
31142
31160
  display: flex; align-items: center; gap: 8px; padding: 8px 12px;
31143
31161
  cursor: pointer; font-size: 13px; color: ${colors.text};
31144
- " onmouseover="this.style.background='${state5.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"}'"
31162
+ " onmouseover="this.style.background='${state6.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"}'"
31145
31163
  onmouseout="this.style.background='transparent'">
31146
31164
  <input type="checkbox"
31147
31165
  name="${modalId}-period"
31148
31166
  value="${period.id}"
31149
- ${state5.selectedPeriods.includes(period.id) ? "checked" : ""}
31167
+ ${state6.selectedPeriods.includes(period.id) ? "checked" : ""}
31150
31168
  style="width: 16px; height: 16px; cursor: pointer; accent-color: #3e1a7d;">
31151
31169
  ${period.label}
31152
31170
  </label>
@@ -31154,13 +31172,13 @@ function renderModal(container, state5, modalId, error) {
31154
31172
  <div style="border-top: 1px solid ${colors.border}; margin-top: 8px; padding-top: 8px;">
31155
31173
  <button id="${modalId}-period-select-all" type="button" style="
31156
31174
  width: calc(100% - 16px); margin: 0 8px 4px; padding: 6px;
31157
- background: ${state5.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"};
31175
+ background: ${state6.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"};
31158
31176
  border: none; border-radius: 4px; cursor: pointer;
31159
31177
  font-size: 12px; color: ${colors.text};
31160
31178
  ">Selecionar Todos</button>
31161
31179
  <button id="${modalId}-period-clear" type="button" style="
31162
31180
  width: calc(100% - 16px); margin: 0 8px; padding: 6px;
31163
- background: ${state5.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"};
31181
+ background: ${state6.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"};
31164
31182
  border: none; border-radius: 4px; cursor: pointer;
31165
31183
  font-size: 12px; color: ${colors.text};
31166
31184
  ">Limpar Sele\xE7\xE3o</button>
@@ -31185,8 +31203,8 @@ function renderModal(container, state5, modalId, error) {
31185
31203
  font-size: 14px; font-weight: 500; height: 38px;
31186
31204
  display: flex; align-items: center; gap: 8px;
31187
31205
  font-family: 'Roboto', Arial, sans-serif;
31188
- " ${state5.isLoading ? "disabled" : ""}>
31189
- ${state5.isLoading ? '<span style="animation: spin 1s linear infinite; display: inline-block;">\u21BB</span> Carregando...' : "Carregar"}
31206
+ " ${state6.isLoading ? "disabled" : ""}>
31207
+ ${state6.isLoading ? '<span style="animation: spin 1s linear infinite; display: inline-block;">\u21BB</span> Carregando...' : "Carregar"}
31190
31208
  </button>
31191
31209
  </div>
31192
31210
 
@@ -31197,40 +31215,40 @@ function renderModal(container, state5, modalId, error) {
31197
31215
  ">
31198
31216
  <!-- Current Temperature -->
31199
31217
  <div style="
31200
- padding: 16px; background: ${state5.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
31218
+ padding: 16px; background: ${state6.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
31201
31219
  border-radius: 12px; border: 1px solid ${colors.border};
31202
31220
  ">
31203
31221
  <span style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500;">Temperatura Atual</span>
31204
31222
  <div style="font-weight: 700; font-size: 24px; color: ${statusColor}; margin-top: 4px;">
31205
- ${state5.currentTemperature !== null ? formatTemperature(state5.currentTemperature) : "N/A"}
31223
+ ${state6.currentTemperature !== null ? formatTemperature(state6.currentTemperature) : "N/A"}
31206
31224
  </div>
31207
31225
  <div style="font-size: 11px; color: ${statusColor}; margin-top: 2px;">${statusText}</div>
31208
31226
  </div>
31209
31227
  <!-- Average -->
31210
31228
  <div style="
31211
- padding: 16px; background: ${state5.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
31229
+ padding: 16px; background: ${state6.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
31212
31230
  border-radius: 12px; border: 1px solid ${colors.border};
31213
31231
  ">
31214
31232
  <span style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500;">M\xE9dia do Per\xEDodo</span>
31215
31233
  <div id="${modalId}-avg" style="font-weight: 600; font-size: 20px; color: ${colors.text}; margin-top: 4px;">
31216
- ${state5.stats.count > 0 ? formatTemperature(state5.stats.avg) : "N/A"}
31234
+ ${state6.stats.count > 0 ? formatTemperature(state6.stats.avg) : "N/A"}
31217
31235
  </div>
31218
31236
  <div style="font-size: 11px; color: ${colors.textMuted}; margin-top: 2px;">${startDateStr} - ${endDateStr}</div>
31219
31237
  </div>
31220
31238
  <!-- Min/Max -->
31221
31239
  <div style="
31222
- padding: 16px; background: ${state5.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
31240
+ padding: 16px; background: ${state6.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
31223
31241
  border-radius: 12px; border: 1px solid ${colors.border};
31224
31242
  ">
31225
31243
  <span style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500;">Min / Max</span>
31226
31244
  <div id="${modalId}-minmax" style="font-weight: 600; font-size: 20px; color: ${colors.text}; margin-top: 4px;">
31227
- ${state5.stats.count > 0 ? `${formatTemperature(state5.stats.min)} / ${formatTemperature(state5.stats.max)}` : "N/A"}
31245
+ ${state6.stats.count > 0 ? `${formatTemperature(state6.stats.min)} / ${formatTemperature(state6.stats.max)}` : "N/A"}
31228
31246
  </div>
31229
- <div style="font-size: 11px; color: ${colors.textMuted}; margin-top: 2px;">${state5.stats.count} leituras</div>
31247
+ <div style="font-size: 11px; color: ${colors.textMuted}; margin-top: 2px;">${state6.stats.count} leituras</div>
31230
31248
  </div>
31231
31249
  <!-- Ideal Range -->
31232
31250
  <div style="
31233
- padding: 16px; background: ${state5.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
31251
+ padding: 16px; background: ${state6.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
31234
31252
  border-radius: 12px; border: 1px solid ${colors.border};
31235
31253
  ">
31236
31254
  <span style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500;">Faixa Ideal</span>
@@ -31246,18 +31264,18 @@ function renderModal(container, state5, modalId, error) {
31246
31264
  Hist\xF3rico de Temperatura
31247
31265
  </h3>
31248
31266
  <div id="${modalId}-chart" style="
31249
- height: 320px; background: ${state5.theme === "dark" ? "rgba(255,255,255,0.03)" : "#fafafa"};
31267
+ height: 320px; background: ${state6.theme === "dark" ? "rgba(255,255,255,0.03)" : "#fafafa"};
31250
31268
  border-radius: 12px; display: flex; justify-content: center; align-items: center;
31251
31269
  border: 1px solid ${colors.border}; position: relative;
31252
31270
  ">
31253
- ${state5.isLoading ? `<div style="text-align: center; color: ${colors.textMuted};">
31271
+ ${state6.isLoading ? `<div style="text-align: center; color: ${colors.textMuted};">
31254
31272
  <div style="animation: spin 1s linear infinite; font-size: 32px; margin-bottom: 8px;">\u21BB</div>
31255
31273
  <div>Carregando dados...</div>
31256
31274
  </div>` : error ? `<div style="text-align: center; color: ${colors.danger};">
31257
31275
  <div style="font-size: 32px; margin-bottom: 8px;">\u26A0\uFE0F</div>
31258
31276
  <div>Erro ao carregar dados</div>
31259
31277
  <div style="font-size: 12px; margin-top: 4px;">${error.message}</div>
31260
- </div>` : state5.data.length === 0 ? `<div style="text-align: center; color: ${colors.textMuted};">
31278
+ </div>` : state6.data.length === 0 ? `<div style="text-align: center; color: ${colors.textMuted};">
31261
31279
  <div style="font-size: 32px; margin-bottom: 8px;">\u{1F4ED}</div>
31262
31280
  <div>Sem dados para o per\xEDodo selecionado</div>
31263
31281
  </div>` : `<canvas id="${modalId}-canvas" style="width: 100%; height: 100%;"></canvas>`}
@@ -31267,12 +31285,12 @@ function renderModal(container, state5, modalId, error) {
31267
31285
  <!-- Actions -->
31268
31286
  <div style="display: flex; justify-content: flex-end; gap: 12px;">
31269
31287
  <button id="${modalId}-export" style="
31270
- background: ${state5.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f7f7f7"};
31288
+ background: ${state6.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f7f7f7"};
31271
31289
  color: ${colors.text}; border: 1px solid ${colors.border};
31272
31290
  padding: 8px 16px; border-radius: 6px; cursor: pointer;
31273
31291
  font-size: 14px; display: flex; align-items: center; gap: 8px;
31274
31292
  font-family: 'Roboto', Arial, sans-serif;
31275
- " ${state5.data.length === 0 ? "disabled" : ""}>
31293
+ " ${state6.data.length === 0 ? "disabled" : ""}>
31276
31294
  \u{1F4E5} Exportar CSV
31277
31295
  </button>
31278
31296
  <button id="${modalId}-close-btn" style="
@@ -31323,14 +31341,14 @@ function renderModal(container, state5, modalId, error) {
31323
31341
  </style>
31324
31342
  `;
31325
31343
  }
31326
- function drawChart(modalId, state5) {
31344
+ function drawChart(modalId, state6) {
31327
31345
  const chartContainer = document.getElementById(`${modalId}-chart`);
31328
31346
  const canvas = document.getElementById(`${modalId}-canvas`);
31329
- if (!chartContainer || !canvas || state5.data.length === 0) return;
31347
+ if (!chartContainer || !canvas || state6.data.length === 0) return;
31330
31348
  const ctx = canvas.getContext("2d");
31331
31349
  if (!ctx) return;
31332
- const colors = getThemeColors(state5.theme);
31333
- const filteredData = filterByDayPeriods(state5.data, state5.selectedPeriods);
31350
+ const colors = getThemeColors(state6.theme);
31351
+ const filteredData = filterByDayPeriods(state6.data, state6.selectedPeriods);
31334
31352
  if (filteredData.length === 0) {
31335
31353
  canvas.width = chartContainer.clientWidth;
31336
31354
  canvas.height = chartContainer.clientHeight;
@@ -31341,14 +31359,14 @@ function drawChart(modalId, state5) {
31341
31359
  return;
31342
31360
  }
31343
31361
  let chartData;
31344
- if (state5.granularity === "hour") {
31362
+ if (state6.granularity === "hour") {
31345
31363
  const interpolated = interpolateTemperature(filteredData, {
31346
31364
  intervalMinutes: 30,
31347
- startTs: state5.startTs,
31348
- endTs: state5.endTs,
31349
- clampRange: state5.clampRange
31365
+ startTs: state6.startTs,
31366
+ endTs: state6.endTs,
31367
+ clampRange: state6.clampRange
31350
31368
  });
31351
- const filteredInterpolated = filterByDayPeriods(interpolated, state5.selectedPeriods);
31369
+ const filteredInterpolated = filterByDayPeriods(interpolated, state6.selectedPeriods);
31352
31370
  chartData = filteredInterpolated.map((item) => ({
31353
31371
  x: item.ts,
31354
31372
  y: Number(item.value),
@@ -31356,7 +31374,7 @@ function drawChart(modalId, state5) {
31356
31374
  screenY: 0
31357
31375
  }));
31358
31376
  } else {
31359
- const daily = aggregateByDay(filteredData, state5.clampRange);
31377
+ const daily = aggregateByDay(filteredData, state6.clampRange);
31360
31378
  chartData = daily.map((item) => ({
31361
31379
  x: item.dateTs,
31362
31380
  y: item.avg,
@@ -31374,12 +31392,12 @@ function drawChart(modalId, state5) {
31374
31392
  const paddingRight = 20;
31375
31393
  const paddingTop = 20;
31376
31394
  const paddingBottom = 55;
31377
- const isPeriodsFiltered = state5.selectedPeriods.length < 4 && state5.selectedPeriods.length > 0;
31395
+ const isPeriodsFiltered = state6.selectedPeriods.length < 4 && state6.selectedPeriods.length > 0;
31378
31396
  const values = chartData.map((d) => d.y);
31379
31397
  const dataMin = Math.min(...values);
31380
31398
  const dataMax = Math.max(...values);
31381
- const thresholdMin = state5.temperatureMin !== null ? state5.temperatureMin : dataMin;
31382
- const thresholdMax = state5.temperatureMax !== null ? state5.temperatureMax : dataMax;
31399
+ const thresholdMin = state6.temperatureMin !== null ? state6.temperatureMin : dataMin;
31400
+ const thresholdMax = state6.temperatureMax !== null ? state6.temperatureMax : dataMax;
31383
31401
  const minY = Math.min(dataMin, thresholdMin) - 1;
31384
31402
  const maxY = Math.max(dataMax, thresholdMax) + 1;
31385
31403
  const chartWidth = width - paddingLeft - paddingRight;
@@ -31411,9 +31429,9 @@ function drawChart(modalId, state5) {
31411
31429
  ctx.lineTo(width - paddingRight, y);
31412
31430
  ctx.stroke();
31413
31431
  }
31414
- if (state5.temperatureMin !== null && state5.temperatureMax !== null) {
31415
- const rangeMinY = height - paddingBottom - (state5.temperatureMin - minY) * scaleY;
31416
- const rangeMaxY = height - paddingBottom - (state5.temperatureMax - minY) * scaleY;
31432
+ if (state6.temperatureMin !== null && state6.temperatureMax !== null) {
31433
+ const rangeMinY = height - paddingBottom - (state6.temperatureMin - minY) * scaleY;
31434
+ const rangeMaxY = height - paddingBottom - (state6.temperatureMax - minY) * scaleY;
31417
31435
  ctx.fillStyle = "rgba(76, 175, 80, 0.1)";
31418
31436
  ctx.fillRect(paddingLeft, rangeMaxY, chartWidth, rangeMinY - rangeMaxY);
31419
31437
  ctx.strokeStyle = colors.success;
@@ -31455,10 +31473,10 @@ function drawChart(modalId, state5) {
31455
31473
  const point = chartData[i];
31456
31474
  const date = new Date(point.x);
31457
31475
  let label;
31458
- if (state5.granularity === "hour") {
31459
- label = date.toLocaleTimeString(state5.locale, { hour: "2-digit", minute: "2-digit" });
31476
+ if (state6.granularity === "hour") {
31477
+ label = date.toLocaleTimeString(state6.locale, { hour: "2-digit", minute: "2-digit" });
31460
31478
  } else {
31461
- label = date.toLocaleDateString(state5.locale, { day: "2-digit", month: "2-digit" });
31479
+ label = date.toLocaleDateString(state6.locale, { day: "2-digit", month: "2-digit" });
31462
31480
  }
31463
31481
  ctx.strokeStyle = colors.chartGrid;
31464
31482
  ctx.lineWidth = 1;
@@ -31476,16 +31494,16 @@ function drawChart(modalId, state5) {
31476
31494
  ctx.lineTo(paddingLeft, height - paddingBottom);
31477
31495
  ctx.lineTo(width - paddingRight, height - paddingBottom);
31478
31496
  ctx.stroke();
31479
- setupChartTooltip(canvas, chartContainer, chartData, state5, colors);
31497
+ setupChartTooltip(canvas, chartContainer, chartData, state6, colors);
31480
31498
  }
31481
- function setupChartTooltip(canvas, container, chartData, state5, colors) {
31499
+ function setupChartTooltip(canvas, container, chartData, state6, colors) {
31482
31500
  const existingTooltip = container.querySelector(".myio-chart-tooltip");
31483
31501
  if (existingTooltip) existingTooltip.remove();
31484
31502
  const tooltip = document.createElement("div");
31485
31503
  tooltip.className = "myio-chart-tooltip";
31486
31504
  tooltip.style.cssText = `
31487
31505
  position: absolute;
31488
- background: ${state5.theme === "dark" ? "rgba(30, 30, 40, 0.95)" : "rgba(255, 255, 255, 0.98)"};
31506
+ background: ${state6.theme === "dark" ? "rgba(30, 30, 40, 0.95)" : "rgba(255, 255, 255, 0.98)"};
31489
31507
  border: 1px solid ${colors.border};
31490
31508
  border-radius: 8px;
31491
31509
  padding: 10px 14px;
@@ -31522,17 +31540,17 @@ function setupChartTooltip(canvas, container, chartData, state5, colors) {
31522
31540
  if (point) {
31523
31541
  const date = new Date(point.x);
31524
31542
  let dateStr;
31525
- if (state5.granularity === "hour") {
31526
- dateStr = date.toLocaleDateString(state5.locale, {
31543
+ if (state6.granularity === "hour") {
31544
+ dateStr = date.toLocaleDateString(state6.locale, {
31527
31545
  day: "2-digit",
31528
31546
  month: "2-digit",
31529
31547
  year: "numeric"
31530
- }) + " " + date.toLocaleTimeString(state5.locale, {
31548
+ }) + " " + date.toLocaleTimeString(state6.locale, {
31531
31549
  hour: "2-digit",
31532
31550
  minute: "2-digit"
31533
31551
  });
31534
31552
  } else {
31535
- dateStr = date.toLocaleDateString(state5.locale, {
31553
+ dateStr = date.toLocaleDateString(state6.locale, {
31536
31554
  day: "2-digit",
31537
31555
  month: "2-digit",
31538
31556
  year: "numeric"
@@ -31570,7 +31588,7 @@ function setupChartTooltip(canvas, container, chartData, state5, colors) {
31570
31588
  canvas.style.cursor = "default";
31571
31589
  });
31572
31590
  }
31573
- async function setupEventListeners(container, state5, modalId, onClose) {
31591
+ async function setupEventListeners(container, state6, modalId, onClose) {
31574
31592
  const closeModal = () => {
31575
31593
  container.remove();
31576
31594
  onClose?.();
@@ -31581,19 +31599,19 @@ async function setupEventListeners(container, state5, modalId, onClose) {
31581
31599
  document.getElementById(`${modalId}-close`)?.addEventListener("click", closeModal);
31582
31600
  document.getElementById(`${modalId}-close-btn`)?.addEventListener("click", closeModal);
31583
31601
  const dateRangeInput = document.getElementById(`${modalId}-date-range`);
31584
- if (dateRangeInput && !state5.dateRangePicker) {
31602
+ if (dateRangeInput && !state6.dateRangePicker) {
31585
31603
  try {
31586
- state5.dateRangePicker = await createDateRangePicker2(dateRangeInput, {
31587
- presetStart: new Date(state5.startTs).toISOString(),
31588
- presetEnd: new Date(state5.endTs).toISOString(),
31604
+ state6.dateRangePicker = await createDateRangePicker2(dateRangeInput, {
31605
+ presetStart: new Date(state6.startTs).toISOString(),
31606
+ presetEnd: new Date(state6.endTs).toISOString(),
31589
31607
  includeTime: true,
31590
31608
  timePrecision: "minute",
31591
31609
  maxRangeDays: 90,
31592
- locale: state5.locale,
31610
+ locale: state6.locale,
31593
31611
  parentEl: container.querySelector(".myio-temp-modal-content"),
31594
31612
  onApply: (result) => {
31595
- state5.startTs = new Date(result.startISO).getTime();
31596
- state5.endTs = new Date(result.endISO).getTime();
31613
+ state6.startTs = new Date(result.startISO).getTime();
31614
+ state6.endTs = new Date(result.endISO).getTime();
31597
31615
  console.log("[TemperatureModal] Date range applied:", result);
31598
31616
  }
31599
31617
  });
@@ -31602,19 +31620,19 @@ async function setupEventListeners(container, state5, modalId, onClose) {
31602
31620
  }
31603
31621
  }
31604
31622
  document.getElementById(`${modalId}-theme-toggle`)?.addEventListener("click", async () => {
31605
- state5.theme = state5.theme === "dark" ? "light" : "dark";
31606
- localStorage.setItem("myio-temp-modal-theme", state5.theme);
31607
- state5.dateRangePicker = null;
31608
- renderModal(container, state5, modalId);
31609
- if (state5.data.length > 0) drawChart(modalId, state5);
31610
- await setupEventListeners(container, state5, modalId, onClose);
31623
+ state6.theme = state6.theme === "dark" ? "light" : "dark";
31624
+ localStorage.setItem("myio-temp-modal-theme", state6.theme);
31625
+ state6.dateRangePicker = null;
31626
+ renderModal(container, state6, modalId);
31627
+ if (state6.data.length > 0) drawChart(modalId, state6);
31628
+ await setupEventListeners(container, state6, modalId, onClose);
31611
31629
  });
31612
31630
  document.getElementById(`${modalId}-maximize`)?.addEventListener("click", async () => {
31613
31631
  container.__isMaximized = !container.__isMaximized;
31614
- state5.dateRangePicker = null;
31615
- renderModal(container, state5, modalId);
31616
- if (state5.data.length > 0) drawChart(modalId, state5);
31617
- await setupEventListeners(container, state5, modalId, onClose);
31632
+ state6.dateRangePicker = null;
31633
+ renderModal(container, state6, modalId);
31634
+ if (state6.data.length > 0) drawChart(modalId, state6);
31635
+ await setupEventListeners(container, state6, modalId, onClose);
31618
31636
  });
31619
31637
  const periodBtn = document.getElementById(`${modalId}-period-btn`);
31620
31638
  const periodDropdown = document.getElementById(`${modalId}-period-dropdown`);
@@ -31633,71 +31651,71 @@ async function setupEventListeners(container, state5, modalId, onClose) {
31633
31651
  periodCheckboxes.forEach((checkbox) => {
31634
31652
  checkbox.addEventListener("change", () => {
31635
31653
  const checked = Array.from(periodCheckboxes).filter((cb) => cb.checked).map((cb) => cb.value);
31636
- state5.selectedPeriods = checked;
31654
+ state6.selectedPeriods = checked;
31637
31655
  const btnLabel = periodBtn?.querySelector("span:first-child");
31638
31656
  if (btnLabel) {
31639
- btnLabel.textContent = getSelectedPeriodsLabel(state5.selectedPeriods);
31657
+ btnLabel.textContent = getSelectedPeriodsLabel(state6.selectedPeriods);
31640
31658
  }
31641
- if (state5.data.length > 0) drawChart(modalId, state5);
31659
+ if (state6.data.length > 0) drawChart(modalId, state6);
31642
31660
  });
31643
31661
  });
31644
31662
  document.getElementById(`${modalId}-period-select-all`)?.addEventListener("click", () => {
31645
31663
  periodCheckboxes.forEach((cb) => {
31646
31664
  cb.checked = true;
31647
31665
  });
31648
- state5.selectedPeriods = ["madrugada", "manha", "tarde", "noite"];
31666
+ state6.selectedPeriods = ["madrugada", "manha", "tarde", "noite"];
31649
31667
  const btnLabel = periodBtn?.querySelector("span:first-child");
31650
31668
  if (btnLabel) {
31651
- btnLabel.textContent = getSelectedPeriodsLabel(state5.selectedPeriods);
31669
+ btnLabel.textContent = getSelectedPeriodsLabel(state6.selectedPeriods);
31652
31670
  }
31653
- if (state5.data.length > 0) drawChart(modalId, state5);
31671
+ if (state6.data.length > 0) drawChart(modalId, state6);
31654
31672
  });
31655
31673
  document.getElementById(`${modalId}-period-clear`)?.addEventListener("click", () => {
31656
31674
  periodCheckboxes.forEach((cb) => {
31657
31675
  cb.checked = false;
31658
31676
  });
31659
- state5.selectedPeriods = [];
31677
+ state6.selectedPeriods = [];
31660
31678
  const btnLabel = periodBtn?.querySelector("span:first-child");
31661
31679
  if (btnLabel) {
31662
- btnLabel.textContent = getSelectedPeriodsLabel(state5.selectedPeriods);
31680
+ btnLabel.textContent = getSelectedPeriodsLabel(state6.selectedPeriods);
31663
31681
  }
31664
- if (state5.data.length > 0) drawChart(modalId, state5);
31682
+ if (state6.data.length > 0) drawChart(modalId, state6);
31665
31683
  });
31666
31684
  document.getElementById(`${modalId}-granularity`)?.addEventListener("change", (e) => {
31667
- state5.granularity = e.target.value;
31668
- localStorage.setItem("myio-temp-modal-granularity", state5.granularity);
31669
- if (state5.data.length > 0) drawChart(modalId, state5);
31685
+ state6.granularity = e.target.value;
31686
+ localStorage.setItem("myio-temp-modal-granularity", state6.granularity);
31687
+ if (state6.data.length > 0) drawChart(modalId, state6);
31670
31688
  });
31671
31689
  document.getElementById(`${modalId}-query`)?.addEventListener("click", async () => {
31672
- if (state5.startTs >= state5.endTs) {
31690
+ if (state6.startTs >= state6.endTs) {
31673
31691
  alert("Por favor, selecione um per\xEDodo v\xE1lido");
31674
31692
  return;
31675
31693
  }
31676
- state5.isLoading = true;
31677
- state5.dateRangePicker = null;
31678
- renderModal(container, state5, modalId);
31694
+ state6.isLoading = true;
31695
+ state6.dateRangePicker = null;
31696
+ renderModal(container, state6, modalId);
31679
31697
  try {
31680
- state5.data = await fetchTemperatureData(state5.token, state5.deviceId, state5.startTs, state5.endTs);
31681
- state5.stats = calculateStats(state5.data, state5.clampRange);
31682
- state5.isLoading = false;
31683
- renderModal(container, state5, modalId);
31684
- drawChart(modalId, state5);
31685
- await setupEventListeners(container, state5, modalId, onClose);
31698
+ state6.data = await fetchTemperatureData(state6.token, state6.deviceId, state6.startTs, state6.endTs);
31699
+ state6.stats = calculateStats(state6.data, state6.clampRange);
31700
+ state6.isLoading = false;
31701
+ renderModal(container, state6, modalId);
31702
+ drawChart(modalId, state6);
31703
+ await setupEventListeners(container, state6, modalId, onClose);
31686
31704
  } catch (error) {
31687
31705
  console.error("[TemperatureModal] Error fetching data:", error);
31688
- state5.isLoading = false;
31689
- renderModal(container, state5, modalId, error);
31690
- await setupEventListeners(container, state5, modalId, onClose);
31706
+ state6.isLoading = false;
31707
+ renderModal(container, state6, modalId, error);
31708
+ await setupEventListeners(container, state6, modalId, onClose);
31691
31709
  }
31692
31710
  });
31693
31711
  document.getElementById(`${modalId}-export`)?.addEventListener("click", () => {
31694
- if (state5.data.length === 0) return;
31695
- const startDateStr = new Date(state5.startTs).toLocaleDateString(state5.locale).replace(/\//g, "-");
31696
- const endDateStr = new Date(state5.endTs).toLocaleDateString(state5.locale).replace(/\//g, "-");
31712
+ if (state6.data.length === 0) return;
31713
+ const startDateStr = new Date(state6.startTs).toLocaleDateString(state6.locale).replace(/\//g, "-");
31714
+ const endDateStr = new Date(state6.endTs).toLocaleDateString(state6.locale).replace(/\//g, "-");
31697
31715
  exportTemperatureCSV(
31698
- state5.data,
31699
- state5.label,
31700
- state5.stats,
31716
+ state6.data,
31717
+ state6.label,
31718
+ state6.stats,
31701
31719
  startDateStr,
31702
31720
  endDateStr
31703
31721
  );
@@ -31710,7 +31728,7 @@ async function openTemperatureComparisonModal(params) {
31710
31728
  const defaultDateRange = getTodaySoFar();
31711
31729
  const startTs = params.startDate ? new Date(params.startDate).getTime() : defaultDateRange.startTs;
31712
31730
  const endTs = params.endDate ? new Date(params.endDate).getTime() : defaultDateRange.endTs;
31713
- const state5 = {
31731
+ const state6 = {
31714
31732
  token: params.token,
31715
31733
  devices: params.devices,
31716
31734
  startTs,
@@ -31729,44 +31747,44 @@ async function openTemperatureComparisonModal(params) {
31729
31747
  };
31730
31748
  const savedGranularity = localStorage.getItem("myio-temp-comparison-granularity");
31731
31749
  const savedTheme = localStorage.getItem("myio-temp-comparison-theme");
31732
- if (savedGranularity) state5.granularity = savedGranularity;
31733
- if (savedTheme) state5.theme = savedTheme;
31750
+ if (savedGranularity) state6.granularity = savedGranularity;
31751
+ if (savedTheme) state6.theme = savedTheme;
31734
31752
  const modalContainer = document.createElement("div");
31735
31753
  modalContainer.id = modalId;
31736
31754
  document.body.appendChild(modalContainer);
31737
- renderModal2(modalContainer, state5, modalId);
31738
- await fetchAllDevicesData(state5);
31739
- renderModal2(modalContainer, state5, modalId);
31740
- drawComparisonChart(modalId, state5);
31741
- await setupEventListeners2(modalContainer, state5, modalId, params.onClose);
31755
+ renderModal2(modalContainer, state6, modalId);
31756
+ await fetchAllDevicesData(state6);
31757
+ renderModal2(modalContainer, state6, modalId);
31758
+ drawComparisonChart(modalId, state6);
31759
+ await setupEventListeners2(modalContainer, state6, modalId, params.onClose);
31742
31760
  return {
31743
31761
  destroy: () => {
31744
31762
  modalContainer.remove();
31745
31763
  params.onClose?.();
31746
31764
  },
31747
31765
  updateData: async (startDate, endDate, granularity) => {
31748
- state5.startTs = new Date(startDate).getTime();
31749
- state5.endTs = new Date(endDate).getTime();
31750
- if (granularity) state5.granularity = granularity;
31751
- state5.isLoading = true;
31752
- renderModal2(modalContainer, state5, modalId);
31753
- await fetchAllDevicesData(state5);
31754
- renderModal2(modalContainer, state5, modalId);
31755
- drawComparisonChart(modalId, state5);
31756
- setupEventListeners2(modalContainer, state5, modalId, params.onClose);
31766
+ state6.startTs = new Date(startDate).getTime();
31767
+ state6.endTs = new Date(endDate).getTime();
31768
+ if (granularity) state6.granularity = granularity;
31769
+ state6.isLoading = true;
31770
+ renderModal2(modalContainer, state6, modalId);
31771
+ await fetchAllDevicesData(state6);
31772
+ renderModal2(modalContainer, state6, modalId);
31773
+ drawComparisonChart(modalId, state6);
31774
+ setupEventListeners2(modalContainer, state6, modalId, params.onClose);
31757
31775
  }
31758
31776
  };
31759
31777
  }
31760
- async function fetchAllDevicesData(state5) {
31761
- state5.isLoading = true;
31762
- state5.deviceData = [];
31778
+ async function fetchAllDevicesData(state6) {
31779
+ state6.isLoading = true;
31780
+ state6.deviceData = [];
31763
31781
  try {
31764
31782
  const results = await Promise.all(
31765
- state5.devices.map(async (device, index) => {
31783
+ state6.devices.map(async (device, index) => {
31766
31784
  const deviceId = device.tbId || device.id;
31767
31785
  try {
31768
- const data = await fetchTemperatureData(state5.token, deviceId, state5.startTs, state5.endTs);
31769
- const stats = calculateStats(data, state5.clampRange);
31786
+ const data = await fetchTemperatureData(state6.token, deviceId, state6.startTs, state6.endTs);
31787
+ const stats = calculateStats(data, state6.clampRange);
31770
31788
  return {
31771
31789
  device,
31772
31790
  data,
@@ -31784,21 +31802,21 @@ async function fetchAllDevicesData(state5) {
31784
31802
  }
31785
31803
  })
31786
31804
  );
31787
- state5.deviceData = results;
31805
+ state6.deviceData = results;
31788
31806
  } catch (error) {
31789
31807
  console.error("[TemperatureComparisonModal] Error fetching data:", error);
31790
31808
  }
31791
- state5.isLoading = false;
31809
+ state6.isLoading = false;
31792
31810
  }
31793
- function renderModal2(container, state5, modalId) {
31794
- const colors = getThemeColors(state5.theme);
31795
- const startDateStr = new Date(state5.startTs).toLocaleDateString(state5.locale);
31796
- const endDateStr = new Date(state5.endTs).toLocaleDateString(state5.locale);
31797
- const startDateInput = new Date(state5.startTs).toISOString().slice(0, 16);
31798
- const endDateInput = new Date(state5.endTs).toISOString().slice(0, 16);
31799
- const legendHTML = state5.deviceData.map((dd) => `
31811
+ function renderModal2(container, state6, modalId) {
31812
+ const colors = getThemeColors(state6.theme);
31813
+ const startDateStr = new Date(state6.startTs).toLocaleDateString(state6.locale);
31814
+ const endDateStr = new Date(state6.endTs).toLocaleDateString(state6.locale);
31815
+ const startDateInput = new Date(state6.startTs).toISOString().slice(0, 16);
31816
+ const endDateInput = new Date(state6.endTs).toISOString().slice(0, 16);
31817
+ const legendHTML = state6.deviceData.map((dd) => `
31800
31818
  <div style="display: flex; align-items: center; gap: 8px; padding: 8px 12px;
31801
- background: ${state5.theme === "dark" ? "rgba(255,255,255,0.05)" : "rgba(0,0,0,0.03)"};
31819
+ background: ${state6.theme === "dark" ? "rgba(255,255,255,0.05)" : "rgba(0,0,0,0.03)"};
31802
31820
  border-radius: 8px;">
31803
31821
  <span style="width: 12px; height: 12px; border-radius: 50%; background: ${dd.color};"></span>
31804
31822
  <span style="color: ${colors.text}; font-size: 13px;">${dd.device.label}</span>
@@ -31807,9 +31825,9 @@ function renderModal2(container, state5, modalId) {
31807
31825
  </span>
31808
31826
  </div>
31809
31827
  `).join("");
31810
- const statsHTML = state5.deviceData.map((dd) => `
31828
+ const statsHTML = state6.deviceData.map((dd) => `
31811
31829
  <div style="
31812
- padding: 12px; background: ${state5.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
31830
+ padding: 12px; background: ${state6.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
31813
31831
  border-radius: 10px; border-left: 4px solid ${dd.color};
31814
31832
  min-width: 150px;
31815
31833
  ">
@@ -31860,7 +31878,7 @@ function renderModal2(container, state5, modalId) {
31860
31878
  min-height: 20px;
31861
31879
  ">
31862
31880
  <h2 style="margin: 6px; font-size: 18px; font-weight: 600; color: white; line-height: 2;">
31863
- \u{1F321}\uFE0F Compara\xE7\xE3o de Temperatura - ${state5.devices.length} sensores
31881
+ \u{1F321}\uFE0F Compara\xE7\xE3o de Temperatura - ${state6.devices.length} sensores
31864
31882
  </h2>
31865
31883
  <div style="display: flex; gap: 4px; align-items: center;">
31866
31884
  <!-- Theme Toggle -->
@@ -31868,7 +31886,7 @@ function renderModal2(container, state5, modalId) {
31868
31886
  background: none; border: none; font-size: 16px; cursor: pointer;
31869
31887
  padding: 4px 8px; border-radius: 6px; color: rgba(255,255,255,0.8);
31870
31888
  transition: background-color 0.2s;
31871
- ">${state5.theme === "dark" ? "\u2600\uFE0F" : "\u{1F319}"}</button>
31889
+ ">${state6.theme === "dark" ? "\u2600\uFE0F" : "\u{1F319}"}</button>
31872
31890
  <!-- Maximize Button -->
31873
31891
  <button id="${modalId}-maximize" title="${isMaximized ? "Restaurar" : "Maximizar"}" style="
31874
31892
  background: none; border: none; font-size: 16px; cursor: pointer;
@@ -31891,7 +31909,7 @@ function renderModal2(container, state5, modalId) {
31891
31909
  <div style="
31892
31910
  display: flex; gap: 16px; flex-wrap: wrap; align-items: flex-end;
31893
31911
  margin-bottom: 16px; padding: 16px;
31894
- background: ${state5.theme === "dark" ? "rgba(255,255,255,0.05)" : "#f7f7f7"};
31912
+ background: ${state6.theme === "dark" ? "rgba(255,255,255,0.05)" : "#f7f7f7"};
31895
31913
  border-radius: 6px; border: 1px solid ${colors.border};
31896
31914
  ">
31897
31915
  <!-- Granularity Select -->
@@ -31904,8 +31922,8 @@ function renderModal2(container, state5, modalId) {
31904
31922
  font-size: 14px; color: ${colors.text}; background: ${colors.surface};
31905
31923
  cursor: pointer; min-width: 130px;
31906
31924
  ">
31907
- <option value="hour" ${state5.granularity === "hour" ? "selected" : ""}>Hora (30 min)</option>
31908
- <option value="day" ${state5.granularity === "day" ? "selected" : ""}>Dia (m\xE9dia)</option>
31925
+ <option value="hour" ${state6.granularity === "hour" ? "selected" : ""}>Hora (30 min)</option>
31926
+ <option value="day" ${state6.granularity === "day" ? "selected" : ""}>Dia (m\xE9dia)</option>
31909
31927
  </select>
31910
31928
  </div>
31911
31929
  <!-- Day Period Filter (Multiselect) -->
@@ -31919,7 +31937,7 @@ function renderModal2(container, state5, modalId) {
31919
31937
  cursor: pointer; min-width: 180px; text-align: left;
31920
31938
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
31921
31939
  ">
31922
- <span>${getSelectedPeriodsLabel(state5.selectedPeriods)}</span>
31940
+ <span>${getSelectedPeriodsLabel(state6.selectedPeriods)}</span>
31923
31941
  <span style="font-size: 10px;">\u25BC</span>
31924
31942
  </button>
31925
31943
  <div id="${modalId}-period-dropdown" style="
@@ -31932,12 +31950,12 @@ function renderModal2(container, state5, modalId) {
31932
31950
  <label style="
31933
31951
  display: flex; align-items: center; gap: 8px; padding: 8px 12px;
31934
31952
  cursor: pointer; font-size: 13px; color: ${colors.text};
31935
- " onmouseover="this.style.background='${state5.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"}'"
31953
+ " onmouseover="this.style.background='${state6.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"}'"
31936
31954
  onmouseout="this.style.background='transparent'">
31937
31955
  <input type="checkbox"
31938
31956
  name="${modalId}-period"
31939
31957
  value="${period.id}"
31940
- ${state5.selectedPeriods.includes(period.id) ? "checked" : ""}
31958
+ ${state6.selectedPeriods.includes(period.id) ? "checked" : ""}
31941
31959
  style="width: 16px; height: 16px; cursor: pointer; accent-color: #3e1a7d;">
31942
31960
  ${period.label}
31943
31961
  </label>
@@ -31945,13 +31963,13 @@ function renderModal2(container, state5, modalId) {
31945
31963
  <div style="border-top: 1px solid ${colors.border}; margin-top: 8px; padding-top: 8px;">
31946
31964
  <button id="${modalId}-period-select-all" type="button" style="
31947
31965
  width: calc(100% - 16px); margin: 0 8px 4px; padding: 6px;
31948
- background: ${state5.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"};
31966
+ background: ${state6.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"};
31949
31967
  border: none; border-radius: 4px; cursor: pointer;
31950
31968
  font-size: 12px; color: ${colors.text};
31951
31969
  ">Selecionar Todos</button>
31952
31970
  <button id="${modalId}-period-clear" type="button" style="
31953
31971
  width: calc(100% - 16px); margin: 0 8px; padding: 6px;
31954
- background: ${state5.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"};
31972
+ background: ${state6.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"};
31955
31973
  border: none; border-radius: 4px; cursor: pointer;
31956
31974
  font-size: 12px; color: ${colors.text};
31957
31975
  ">Limpar Sele\xE7\xE3o</button>
@@ -31976,8 +31994,8 @@ function renderModal2(container, state5, modalId) {
31976
31994
  font-size: 14px; font-weight: 500; height: 38px;
31977
31995
  display: flex; align-items: center; gap: 8px;
31978
31996
  font-family: 'Roboto', Arial, sans-serif;
31979
- " ${state5.isLoading ? "disabled" : ""}>
31980
- ${state5.isLoading ? '<span style="animation: spin 1s linear infinite; display: inline-block;">\u21BB</span> Carregando...' : "Carregar"}
31997
+ " ${state6.isLoading ? "disabled" : ""}>
31998
+ ${state6.isLoading ? '<span style="animation: spin 1s linear infinite; display: inline-block;">\u21BB</span> Carregando...' : "Carregar"}
31981
31999
  </button>
31982
32000
  </div>
31983
32001
 
@@ -31993,14 +32011,14 @@ function renderModal2(container, state5, modalId) {
31993
32011
  <div style="margin-bottom: 24px;">
31994
32012
  <div id="${modalId}-chart" style="
31995
32013
  height: 380px;
31996
- background: ${state5.theme === "dark" ? "rgba(255,255,255,0.03)" : "#fafafa"};
32014
+ background: ${state6.theme === "dark" ? "rgba(255,255,255,0.03)" : "#fafafa"};
31997
32015
  border-radius: 14px; display: flex; justify-content: center; align-items: center;
31998
32016
  border: 1px solid ${colors.border}; position: relative;
31999
32017
  ">
32000
- ${state5.isLoading ? `<div style="text-align: center; color: ${colors.textMuted};">
32018
+ ${state6.isLoading ? `<div style="text-align: center; color: ${colors.textMuted};">
32001
32019
  <div style="animation: spin 1s linear infinite; font-size: 36px; margin-bottom: 12px;">\u21BB</div>
32002
- <div style="font-size: 15px;">Carregando dados de ${state5.devices.length} sensores...</div>
32003
- </div>` : state5.deviceData.every((dd) => dd.data.length === 0) ? `<div style="text-align: center; color: ${colors.textMuted};">
32020
+ <div style="font-size: 15px;">Carregando dados de ${state6.devices.length} sensores...</div>
32021
+ </div>` : state6.deviceData.every((dd) => dd.data.length === 0) ? `<div style="text-align: center; color: ${colors.textMuted};">
32004
32022
  <div style="font-size: 48px; margin-bottom: 12px;">\u{1F4ED}</div>
32005
32023
  <div style="font-size: 16px;">Sem dados para o per\xEDodo selecionado</div>
32006
32024
  </div>` : `<canvas id="${modalId}-canvas" style="width: 100%; height: 100%;"></canvas>`}
@@ -32018,12 +32036,12 @@ function renderModal2(container, state5, modalId) {
32018
32036
  <!-- Actions -->
32019
32037
  <div style="display: flex; justify-content: flex-end; gap: 12px;">
32020
32038
  <button id="${modalId}-export" style="
32021
- background: ${state5.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f7f7f7"};
32039
+ background: ${state6.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f7f7f7"};
32022
32040
  color: ${colors.text}; border: 1px solid ${colors.border};
32023
32041
  padding: 8px 16px; border-radius: 6px; cursor: pointer;
32024
32042
  font-size: 14px; display: flex; align-items: center; gap: 8px;
32025
32043
  font-family: 'Roboto', Arial, sans-serif;
32026
- " ${state5.deviceData.every((dd) => dd.data.length === 0) ? "disabled" : ""}>
32044
+ " ${state6.deviceData.every((dd) => dd.data.length === 0) ? "disabled" : ""}>
32027
32045
  \u{1F4E5} Exportar CSV
32028
32046
  </button>
32029
32047
  <button id="${modalId}-close-btn" style="
@@ -32058,15 +32076,15 @@ function renderModal2(container, state5, modalId) {
32058
32076
  </style>
32059
32077
  `;
32060
32078
  }
32061
- function drawComparisonChart(modalId, state5) {
32079
+ function drawComparisonChart(modalId, state6) {
32062
32080
  const chartContainer = document.getElementById(`${modalId}-chart`);
32063
32081
  const canvas = document.getElementById(`${modalId}-canvas`);
32064
32082
  if (!chartContainer || !canvas) return;
32065
- const hasData = state5.deviceData.some((dd) => dd.data.length > 0);
32083
+ const hasData = state6.deviceData.some((dd) => dd.data.length > 0);
32066
32084
  if (!hasData) return;
32067
32085
  const ctx = canvas.getContext("2d");
32068
32086
  if (!ctx) return;
32069
- const colors = getThemeColors(state5.theme);
32087
+ const colors = getThemeColors(state6.theme);
32070
32088
  const width = chartContainer.clientWidth - 2;
32071
32089
  const height = 380;
32072
32090
  canvas.width = width;
@@ -32077,19 +32095,19 @@ function drawComparisonChart(modalId, state5) {
32077
32095
  const paddingBottom = 55;
32078
32096
  ctx.clearRect(0, 0, width, height);
32079
32097
  const processedData = [];
32080
- state5.deviceData.forEach((dd) => {
32098
+ state6.deviceData.forEach((dd) => {
32081
32099
  if (dd.data.length === 0) return;
32082
- const filteredData = filterByDayPeriods(dd.data, state5.selectedPeriods);
32100
+ const filteredData = filterByDayPeriods(dd.data, state6.selectedPeriods);
32083
32101
  if (filteredData.length === 0) return;
32084
32102
  let points;
32085
- if (state5.granularity === "hour") {
32103
+ if (state6.granularity === "hour") {
32086
32104
  const interpolated = interpolateTemperature(filteredData, {
32087
32105
  intervalMinutes: 30,
32088
- startTs: state5.startTs,
32089
- endTs: state5.endTs,
32090
- clampRange: state5.clampRange
32106
+ startTs: state6.startTs,
32107
+ endTs: state6.endTs,
32108
+ clampRange: state6.clampRange
32091
32109
  });
32092
- const filteredInterpolated = filterByDayPeriods(interpolated, state5.selectedPeriods);
32110
+ const filteredInterpolated = filterByDayPeriods(interpolated, state6.selectedPeriods);
32093
32111
  points = filteredInterpolated.map((item) => ({
32094
32112
  x: item.ts,
32095
32113
  y: Number(item.value),
@@ -32099,7 +32117,7 @@ function drawComparisonChart(modalId, state5) {
32099
32117
  deviceColor: dd.color
32100
32118
  }));
32101
32119
  } else {
32102
- const daily = aggregateByDay(filteredData, state5.clampRange);
32120
+ const daily = aggregateByDay(filteredData, state6.clampRange);
32103
32121
  points = daily.map((item) => ({
32104
32122
  x: item.dateTs,
32105
32123
  y: item.avg,
@@ -32120,7 +32138,7 @@ function drawComparisonChart(modalId, state5) {
32120
32138
  ctx.fillText("Nenhum dado para os per\xEDodos selecionados", width / 2, height / 2);
32121
32139
  return;
32122
32140
  }
32123
- const isPeriodsFiltered = state5.selectedPeriods.length < 4 && state5.selectedPeriods.length > 0;
32141
+ const isPeriodsFiltered = state6.selectedPeriods.length < 4 && state6.selectedPeriods.length > 0;
32124
32142
  let dataMinY = Infinity;
32125
32143
  let dataMaxY = -Infinity;
32126
32144
  processedData.forEach(({ points }) => {
@@ -32130,7 +32148,7 @@ function drawComparisonChart(modalId, state5) {
32130
32148
  });
32131
32149
  });
32132
32150
  const rangeMap = /* @__PURE__ */ new Map();
32133
- state5.deviceData.forEach((dd, index) => {
32151
+ state6.deviceData.forEach((dd, index) => {
32134
32152
  const device = dd.device;
32135
32153
  const min = device.temperatureMin;
32136
32154
  const max = device.temperatureMax;
@@ -32149,10 +32167,10 @@ function drawComparisonChart(modalId, state5) {
32149
32167
  }
32150
32168
  }
32151
32169
  });
32152
- if (rangeMap.size === 0 && state5.temperatureMin !== null && state5.temperatureMax !== null) {
32170
+ if (rangeMap.size === 0 && state6.temperatureMin !== null && state6.temperatureMax !== null) {
32153
32171
  rangeMap.set("global", {
32154
- min: state5.temperatureMin,
32155
- max: state5.temperatureMax,
32172
+ min: state6.temperatureMin,
32173
+ max: state6.temperatureMax,
32156
32174
  customerName: "Global",
32157
32175
  color: colors.success,
32158
32176
  deviceLabels: []
@@ -32273,10 +32291,10 @@ function drawComparisonChart(modalId, state5) {
32273
32291
  const point = xAxisPoints[i];
32274
32292
  const date = new Date(point.x);
32275
32293
  let label;
32276
- if (state5.granularity === "hour") {
32277
- label = date.toLocaleTimeString(state5.locale, { hour: "2-digit", minute: "2-digit" });
32294
+ if (state6.granularity === "hour") {
32295
+ label = date.toLocaleTimeString(state6.locale, { hour: "2-digit", minute: "2-digit" });
32278
32296
  } else {
32279
- label = date.toLocaleDateString(state5.locale, { day: "2-digit", month: "2-digit" });
32297
+ label = date.toLocaleDateString(state6.locale, { day: "2-digit", month: "2-digit" });
32280
32298
  }
32281
32299
  ctx.strokeStyle = colors.chartGrid;
32282
32300
  ctx.lineWidth = 1;
@@ -32295,16 +32313,16 @@ function drawComparisonChart(modalId, state5) {
32295
32313
  ctx.lineTo(width - paddingRight, height - paddingBottom);
32296
32314
  ctx.stroke();
32297
32315
  const allChartPoints = processedData.flatMap((pd) => pd.points);
32298
- setupComparisonChartTooltip(canvas, chartContainer, allChartPoints, state5, colors);
32316
+ setupComparisonChartTooltip(canvas, chartContainer, allChartPoints, state6, colors);
32299
32317
  }
32300
- function setupComparisonChartTooltip(canvas, container, chartData, state5, colors) {
32318
+ function setupComparisonChartTooltip(canvas, container, chartData, state6, colors) {
32301
32319
  const existingTooltip = container.querySelector(".myio-chart-tooltip");
32302
32320
  if (existingTooltip) existingTooltip.remove();
32303
32321
  const tooltip = document.createElement("div");
32304
32322
  tooltip.className = "myio-chart-tooltip";
32305
32323
  tooltip.style.cssText = `
32306
32324
  position: absolute;
32307
- background: ${state5.theme === "dark" ? "rgba(30, 30, 40, 0.95)" : "rgba(255, 255, 255, 0.98)"};
32325
+ background: ${state6.theme === "dark" ? "rgba(30, 30, 40, 0.95)" : "rgba(255, 255, 255, 0.98)"};
32308
32326
  border: 1px solid ${colors.border};
32309
32327
  border-radius: 8px;
32310
32328
  padding: 10px 14px;
@@ -32341,17 +32359,17 @@ function setupComparisonChartTooltip(canvas, container, chartData, state5, color
32341
32359
  if (point) {
32342
32360
  const date = new Date(point.x);
32343
32361
  let dateStr;
32344
- if (state5.granularity === "hour") {
32345
- dateStr = date.toLocaleDateString(state5.locale, {
32362
+ if (state6.granularity === "hour") {
32363
+ dateStr = date.toLocaleDateString(state6.locale, {
32346
32364
  day: "2-digit",
32347
32365
  month: "2-digit",
32348
32366
  year: "numeric"
32349
- }) + " " + date.toLocaleTimeString(state5.locale, {
32367
+ }) + " " + date.toLocaleTimeString(state6.locale, {
32350
32368
  hour: "2-digit",
32351
32369
  minute: "2-digit"
32352
32370
  });
32353
32371
  } else {
32354
- dateStr = date.toLocaleDateString(state5.locale, {
32372
+ dateStr = date.toLocaleDateString(state6.locale, {
32355
32373
  day: "2-digit",
32356
32374
  month: "2-digit",
32357
32375
  year: "numeric"
@@ -32393,7 +32411,7 @@ function setupComparisonChartTooltip(canvas, container, chartData, state5, color
32393
32411
  canvas.style.cursor = "default";
32394
32412
  });
32395
32413
  }
32396
- async function setupEventListeners2(container, state5, modalId, onClose) {
32414
+ async function setupEventListeners2(container, state6, modalId, onClose) {
32397
32415
  const closeModal = () => {
32398
32416
  container.remove();
32399
32417
  onClose?.();
@@ -32404,19 +32422,19 @@ async function setupEventListeners2(container, state5, modalId, onClose) {
32404
32422
  document.getElementById(`${modalId}-close`)?.addEventListener("click", closeModal);
32405
32423
  document.getElementById(`${modalId}-close-btn`)?.addEventListener("click", closeModal);
32406
32424
  const dateRangeInput = document.getElementById(`${modalId}-date-range`);
32407
- if (dateRangeInput && !state5.dateRangePicker) {
32425
+ if (dateRangeInput && !state6.dateRangePicker) {
32408
32426
  try {
32409
- state5.dateRangePicker = await createDateRangePicker2(dateRangeInput, {
32410
- presetStart: new Date(state5.startTs).toISOString(),
32411
- presetEnd: new Date(state5.endTs).toISOString(),
32427
+ state6.dateRangePicker = await createDateRangePicker2(dateRangeInput, {
32428
+ presetStart: new Date(state6.startTs).toISOString(),
32429
+ presetEnd: new Date(state6.endTs).toISOString(),
32412
32430
  includeTime: true,
32413
32431
  timePrecision: "minute",
32414
32432
  maxRangeDays: 90,
32415
- locale: state5.locale,
32433
+ locale: state6.locale,
32416
32434
  parentEl: container.querySelector(".myio-temp-comparison-content"),
32417
32435
  onApply: (result) => {
32418
- state5.startTs = new Date(result.startISO).getTime();
32419
- state5.endTs = new Date(result.endISO).getTime();
32436
+ state6.startTs = new Date(result.startISO).getTime();
32437
+ state6.endTs = new Date(result.endISO).getTime();
32420
32438
  console.log("[TemperatureComparisonModal] Date range applied:", result);
32421
32439
  }
32422
32440
  });
@@ -32425,23 +32443,23 @@ async function setupEventListeners2(container, state5, modalId, onClose) {
32425
32443
  }
32426
32444
  }
32427
32445
  document.getElementById(`${modalId}-theme-toggle`)?.addEventListener("click", async () => {
32428
- state5.theme = state5.theme === "dark" ? "light" : "dark";
32429
- localStorage.setItem("myio-temp-comparison-theme", state5.theme);
32430
- state5.dateRangePicker = null;
32431
- renderModal2(container, state5, modalId);
32432
- if (state5.deviceData.some((dd) => dd.data.length > 0)) {
32433
- drawComparisonChart(modalId, state5);
32434
- }
32435
- await setupEventListeners2(container, state5, modalId, onClose);
32446
+ state6.theme = state6.theme === "dark" ? "light" : "dark";
32447
+ localStorage.setItem("myio-temp-comparison-theme", state6.theme);
32448
+ state6.dateRangePicker = null;
32449
+ renderModal2(container, state6, modalId);
32450
+ if (state6.deviceData.some((dd) => dd.data.length > 0)) {
32451
+ drawComparisonChart(modalId, state6);
32452
+ }
32453
+ await setupEventListeners2(container, state6, modalId, onClose);
32436
32454
  });
32437
32455
  document.getElementById(`${modalId}-maximize`)?.addEventListener("click", async () => {
32438
32456
  container.__isMaximized = !container.__isMaximized;
32439
- state5.dateRangePicker = null;
32440
- renderModal2(container, state5, modalId);
32441
- if (state5.deviceData.some((dd) => dd.data.length > 0)) {
32442
- drawComparisonChart(modalId, state5);
32457
+ state6.dateRangePicker = null;
32458
+ renderModal2(container, state6, modalId);
32459
+ if (state6.deviceData.some((dd) => dd.data.length > 0)) {
32460
+ drawComparisonChart(modalId, state6);
32443
32461
  }
32444
- await setupEventListeners2(container, state5, modalId, onClose);
32462
+ await setupEventListeners2(container, state6, modalId, onClose);
32445
32463
  });
32446
32464
  const periodBtn = document.getElementById(`${modalId}-period-btn`);
32447
32465
  const periodDropdown = document.getElementById(`${modalId}-period-dropdown`);
@@ -32460,13 +32478,13 @@ async function setupEventListeners2(container, state5, modalId, onClose) {
32460
32478
  periodCheckboxes.forEach((checkbox) => {
32461
32479
  checkbox.addEventListener("change", () => {
32462
32480
  const checked = Array.from(periodCheckboxes).filter((cb) => cb.checked).map((cb) => cb.value);
32463
- state5.selectedPeriods = checked;
32481
+ state6.selectedPeriods = checked;
32464
32482
  const btnLabel = periodBtn?.querySelector("span:first-child");
32465
32483
  if (btnLabel) {
32466
- btnLabel.textContent = getSelectedPeriodsLabel(state5.selectedPeriods);
32484
+ btnLabel.textContent = getSelectedPeriodsLabel(state6.selectedPeriods);
32467
32485
  }
32468
- if (state5.deviceData.some((dd) => dd.data.length > 0)) {
32469
- drawComparisonChart(modalId, state5);
32486
+ if (state6.deviceData.some((dd) => dd.data.length > 0)) {
32487
+ drawComparisonChart(modalId, state6);
32470
32488
  }
32471
32489
  });
32472
32490
  });
@@ -32474,77 +32492,77 @@ async function setupEventListeners2(container, state5, modalId, onClose) {
32474
32492
  periodCheckboxes.forEach((cb) => {
32475
32493
  cb.checked = true;
32476
32494
  });
32477
- state5.selectedPeriods = ["madrugada", "manha", "tarde", "noite"];
32495
+ state6.selectedPeriods = ["madrugada", "manha", "tarde", "noite"];
32478
32496
  const btnLabel = periodBtn?.querySelector("span:first-child");
32479
32497
  if (btnLabel) {
32480
- btnLabel.textContent = getSelectedPeriodsLabel(state5.selectedPeriods);
32498
+ btnLabel.textContent = getSelectedPeriodsLabel(state6.selectedPeriods);
32481
32499
  }
32482
- if (state5.deviceData.some((dd) => dd.data.length > 0)) {
32483
- drawComparisonChart(modalId, state5);
32500
+ if (state6.deviceData.some((dd) => dd.data.length > 0)) {
32501
+ drawComparisonChart(modalId, state6);
32484
32502
  }
32485
32503
  });
32486
32504
  document.getElementById(`${modalId}-period-clear`)?.addEventListener("click", () => {
32487
32505
  periodCheckboxes.forEach((cb) => {
32488
32506
  cb.checked = false;
32489
32507
  });
32490
- state5.selectedPeriods = [];
32508
+ state6.selectedPeriods = [];
32491
32509
  const btnLabel = periodBtn?.querySelector("span:first-child");
32492
32510
  if (btnLabel) {
32493
- btnLabel.textContent = getSelectedPeriodsLabel(state5.selectedPeriods);
32511
+ btnLabel.textContent = getSelectedPeriodsLabel(state6.selectedPeriods);
32494
32512
  }
32495
- if (state5.deviceData.some((dd) => dd.data.length > 0)) {
32496
- drawComparisonChart(modalId, state5);
32513
+ if (state6.deviceData.some((dd) => dd.data.length > 0)) {
32514
+ drawComparisonChart(modalId, state6);
32497
32515
  }
32498
32516
  });
32499
32517
  document.getElementById(`${modalId}-granularity`)?.addEventListener("change", (e) => {
32500
- state5.granularity = e.target.value;
32501
- localStorage.setItem("myio-temp-comparison-granularity", state5.granularity);
32502
- if (state5.deviceData.some((dd) => dd.data.length > 0)) {
32503
- drawComparisonChart(modalId, state5);
32518
+ state6.granularity = e.target.value;
32519
+ localStorage.setItem("myio-temp-comparison-granularity", state6.granularity);
32520
+ if (state6.deviceData.some((dd) => dd.data.length > 0)) {
32521
+ drawComparisonChart(modalId, state6);
32504
32522
  }
32505
32523
  });
32506
32524
  document.getElementById(`${modalId}-query`)?.addEventListener("click", async () => {
32507
- if (state5.startTs >= state5.endTs) {
32525
+ if (state6.startTs >= state6.endTs) {
32508
32526
  alert("Por favor, selecione um per\xEDodo v\xE1lido");
32509
32527
  return;
32510
32528
  }
32511
- state5.isLoading = true;
32512
- state5.dateRangePicker = null;
32513
- renderModal2(container, state5, modalId);
32514
- await fetchAllDevicesData(state5);
32515
- renderModal2(container, state5, modalId);
32516
- drawComparisonChart(modalId, state5);
32517
- await setupEventListeners2(container, state5, modalId, onClose);
32529
+ state6.isLoading = true;
32530
+ state6.dateRangePicker = null;
32531
+ renderModal2(container, state6, modalId);
32532
+ await fetchAllDevicesData(state6);
32533
+ renderModal2(container, state6, modalId);
32534
+ drawComparisonChart(modalId, state6);
32535
+ await setupEventListeners2(container, state6, modalId, onClose);
32518
32536
  });
32519
32537
  document.getElementById(`${modalId}-export`)?.addEventListener("click", () => {
32520
- if (state5.deviceData.every((dd) => dd.data.length === 0)) return;
32521
- exportComparisonCSV(state5);
32538
+ if (state6.deviceData.every((dd) => dd.data.length === 0)) return;
32539
+ exportComparisonCSV(state6);
32522
32540
  });
32523
32541
  }
32524
- function exportComparisonCSV(state5) {
32525
- const startDateStr = new Date(state5.startTs).toLocaleDateString(state5.locale).replace(/\//g, "-");
32526
- const endDateStr = new Date(state5.endTs).toLocaleDateString(state5.locale).replace(/\//g, "-");
32542
+ function exportComparisonCSV(state6) {
32543
+ const startDateStr = new Date(state6.startTs).toLocaleDateString(state6.locale).replace(/\//g, "-");
32544
+ const endDateStr = new Date(state6.endTs).toLocaleDateString(state6.locale).replace(/\//g, "-");
32527
32545
  const BOM = "\uFEFF";
32528
32546
  let csvContent = BOM;
32529
32547
  csvContent += `Compara\xE7\xE3o de Temperatura
32530
32548
  `;
32531
32549
  csvContent += `Per\xEDodo: ${startDateStr} at\xE9 ${endDateStr}
32532
32550
  `;
32533
- csvContent += `Sensores: ${state5.devices.map((d) => d.label).join(", ")}
32551
+ csvContent += `Sensores: ${state6.devices.map((d) => d.label).join(", ")}
32534
32552
  `;
32535
32553
  csvContent += "\n";
32536
32554
  csvContent += "Estat\xEDsticas por Sensor:\n";
32537
32555
  csvContent += "Sensor,M\xE9dia (\xB0C),Min (\xB0C),Max (\xB0C),Leituras\n";
32538
- state5.deviceData.forEach((dd) => {
32556
+ state6.deviceData.forEach((dd) => {
32539
32557
  csvContent += `"${dd.device.label}",${dd.stats.avg.toFixed(2)},${dd.stats.min.toFixed(2)},${dd.stats.max.toFixed(2)},${dd.stats.count}
32540
32558
  `;
32541
32559
  });
32542
32560
  csvContent += "\n";
32543
32561
  csvContent += "Dados Detalhados:\n";
32544
32562
  csvContent += "Data/Hora,Sensor,Temperatura (\xB0C)\n";
32545
- state5.deviceData.forEach((dd) => {
32563
+ state6.deviceData.forEach((dd) => {
32546
32564
  dd.data.forEach((item) => {
32547
- const date = new Date(item.ts).toLocaleString(state5.locale);
32565
+ const date = new Date(item.ts).toLocaleString(state6.locale);
32548
32566
  const temp = Number(item.value).toFixed(2);
32549
32567
  csvContent += `"${date}","${dd.device.label}",${temp}
32550
32568
  `;
@@ -32652,10 +32670,10 @@ async function saveCustomerAttributes(customerId, token, minTemperature, maxTemp
32652
32670
  throw new Error(`Failed to save attributes: ${response.status}`);
32653
32671
  }
32654
32672
  }
32655
- function renderModal3(container, state5, modalId, onClose, onSave) {
32656
- const colors = getColors(state5.theme);
32657
- const minValue = state5.minTemperature !== null ? state5.minTemperature : "";
32658
- const maxValue = state5.maxTemperature !== null ? state5.maxTemperature : "";
32673
+ function renderModal3(container, state6, modalId, onClose, onSave) {
32674
+ const colors = getColors(state6.theme);
32675
+ const minValue = state6.minTemperature !== null ? state6.minTemperature : "";
32676
+ const maxValue = state6.maxTemperature !== null ? state6.maxTemperature : "";
32659
32677
  container.innerHTML = `
32660
32678
  <style>
32661
32679
  @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
@@ -32920,23 +32938,23 @@ function renderModal3(container, state5, modalId, onClose, onSave) {
32920
32938
  </div>
32921
32939
 
32922
32940
  <div class="modal-body">
32923
- ${state5.isLoading ? `
32941
+ ${state6.isLoading ? `
32924
32942
  <div class="loading-overlay">
32925
32943
  <div class="loading-spinner"></div>
32926
32944
  <div>Carregando configura\xE7\xF5es...</div>
32927
32945
  </div>
32928
32946
  ` : `
32929
- ${state5.error ? `
32930
- <div class="message message-error">${state5.error}</div>
32947
+ ${state6.error ? `
32948
+ <div class="message message-error">${state6.error}</div>
32931
32949
  ` : ""}
32932
32950
 
32933
- ${state5.successMessage ? `
32934
- <div class="message message-success">${state5.successMessage}</div>
32951
+ ${state6.successMessage ? `
32952
+ <div class="message message-success">${state6.successMessage}</div>
32935
32953
  ` : ""}
32936
32954
 
32937
32955
  <div class="customer-info">
32938
32956
  <div class="customer-label">Shopping / Cliente</div>
32939
- <div class="customer-name">${state5.customerName || "N\xE3o identificado"}</div>
32957
+ <div class="customer-name">${state6.customerName || "N\xE3o identificado"}</div>
32940
32958
  </div>
32941
32959
 
32942
32960
  <div class="form-group">
@@ -32980,11 +32998,11 @@ function renderModal3(container, state5, modalId, onClose, onSave) {
32980
32998
  `}
32981
32999
  </div>
32982
33000
 
32983
- ${!state5.isLoading ? `
33001
+ ${!state6.isLoading ? `
32984
33002
  <div class="modal-footer">
32985
33003
  <button class="btn btn-secondary" id="${modalId}-cancel">Cancelar</button>
32986
- <button class="btn btn-primary" id="${modalId}-save" ${state5.isSaving ? "disabled" : ""}>
32987
- ${state5.isSaving ? '<div class="spinner"></div> Salvando...' : "Salvar"}
33004
+ <button class="btn btn-primary" id="${modalId}-save" ${state6.isSaving ? "disabled" : ""}>
33005
+ ${state6.isSaving ? '<div class="spinner"></div> Salvando...' : "Salvar"}
32988
33006
  </button>
32989
33007
  </div>
32990
33008
  ` : ""}
@@ -33021,18 +33039,18 @@ function renderModal3(container, state5, modalId, onClose, onSave) {
33021
33039
  const min = parseFloat(minInput.value);
33022
33040
  const max = parseFloat(maxInput.value);
33023
33041
  if (isNaN(min) || isNaN(max)) {
33024
- state5.error = "Por favor, preencha ambos os valores.";
33025
- renderModal3(container, state5, modalId, onClose, onSave);
33042
+ state6.error = "Por favor, preencha ambos os valores.";
33043
+ renderModal3(container, state6, modalId, onClose, onSave);
33026
33044
  return;
33027
33045
  }
33028
33046
  if (min >= max) {
33029
- state5.error = "A temperatura m\xEDnima deve ser menor que a m\xE1xima.";
33030
- renderModal3(container, state5, modalId, onClose, onSave);
33047
+ state6.error = "A temperatura m\xEDnima deve ser menor que a m\xE1xima.";
33048
+ renderModal3(container, state6, modalId, onClose, onSave);
33031
33049
  return;
33032
33050
  }
33033
33051
  if (min < 0 || max > 50) {
33034
- state5.error = "Os valores devem estar entre 0\xB0C e 50\xB0C.";
33035
- renderModal3(container, state5, modalId, onClose, onSave);
33052
+ state6.error = "Os valores devem estar entre 0\xB0C e 50\xB0C.";
33053
+ renderModal3(container, state6, modalId, onClose, onSave);
33036
33054
  return;
33037
33055
  }
33038
33056
  await onSave(min, max);
@@ -33040,7 +33058,7 @@ function renderModal3(container, state5, modalId, onClose, onSave) {
33040
33058
  }
33041
33059
  function openTemperatureSettingsModal(params) {
33042
33060
  const modalId = `myio-temp-settings-${Date.now()}`;
33043
- const state5 = {
33061
+ const state6 = {
33044
33062
  customerId: params.customerId,
33045
33063
  customerName: params.customerName || "",
33046
33064
  token: params.token,
@@ -33060,37 +33078,37 @@ function openTemperatureSettingsModal(params) {
33060
33078
  params.onClose?.();
33061
33079
  };
33062
33080
  const handleSave = async (min, max) => {
33063
- state5.isSaving = true;
33064
- state5.error = null;
33065
- state5.successMessage = null;
33066
- renderModal3(container, state5, modalId, destroy, handleSave);
33081
+ state6.isSaving = true;
33082
+ state6.error = null;
33083
+ state6.successMessage = null;
33084
+ renderModal3(container, state6, modalId, destroy, handleSave);
33067
33085
  try {
33068
- await saveCustomerAttributes(state5.customerId, state5.token, min, max, params.onError);
33069
- state5.minTemperature = min;
33070
- state5.maxTemperature = max;
33071
- state5.isSaving = false;
33072
- state5.successMessage = "Configura\xE7\xF5es salvas com sucesso!";
33073
- renderModal3(container, state5, modalId, destroy, handleSave);
33086
+ await saveCustomerAttributes(state6.customerId, state6.token, min, max, params.onError);
33087
+ state6.minTemperature = min;
33088
+ state6.maxTemperature = max;
33089
+ state6.isSaving = false;
33090
+ state6.successMessage = "Configura\xE7\xF5es salvas com sucesso!";
33091
+ renderModal3(container, state6, modalId, destroy, handleSave);
33074
33092
  params.onSave?.({ minTemperature: min, maxTemperature: max });
33075
33093
  setTimeout(() => {
33076
33094
  destroy();
33077
33095
  }, 1500);
33078
33096
  } catch (error) {
33079
- state5.isSaving = false;
33080
- state5.error = `Erro ao salvar: ${error.message}`;
33081
- renderModal3(container, state5, modalId, destroy, handleSave);
33097
+ state6.isSaving = false;
33098
+ state6.error = `Erro ao salvar: ${error.message}`;
33099
+ renderModal3(container, state6, modalId, destroy, handleSave);
33082
33100
  }
33083
33101
  };
33084
- renderModal3(container, state5, modalId, destroy, handleSave);
33085
- fetchCustomerAttributes(state5.customerId, state5.token, params.onError).then(({ minTemperature, maxTemperature }) => {
33086
- state5.minTemperature = minTemperature;
33087
- state5.maxTemperature = maxTemperature;
33088
- state5.isLoading = false;
33089
- renderModal3(container, state5, modalId, destroy, handleSave);
33102
+ renderModal3(container, state6, modalId, destroy, handleSave);
33103
+ fetchCustomerAttributes(state6.customerId, state6.token, params.onError).then(({ minTemperature, maxTemperature }) => {
33104
+ state6.minTemperature = minTemperature;
33105
+ state6.maxTemperature = maxTemperature;
33106
+ state6.isLoading = false;
33107
+ renderModal3(container, state6, modalId, destroy, handleSave);
33090
33108
  }).catch((error) => {
33091
- state5.isLoading = false;
33092
- state5.error = `Erro ao carregar: ${error.message}`;
33093
- renderModal3(container, state5, modalId, destroy, handleSave);
33109
+ state6.isLoading = false;
33110
+ state6.error = `Erro ao carregar: ${error.message}`;
33111
+ renderModal3(container, state6, modalId, destroy, handleSave);
33094
33112
  });
33095
33113
  return { destroy };
33096
33114
  }
@@ -34360,7 +34378,7 @@ var EnergySummaryTooltip = {
34360
34378
  * RFC-0105 Enhancement: Now fetches device lists from MyIOOrchestratorData
34361
34379
  * to populate device lists for status popup display
34362
34380
  */
34363
- buildSummaryFromState(state5, receivedData, domain = "energy") {
34381
+ buildSummaryFromState(state6, receivedData, domain = "energy") {
34364
34382
  const summary = {
34365
34383
  totalDevices: 0,
34366
34384
  totalConsumption: 0,
@@ -34383,22 +34401,22 @@ var EnergySummaryTooltip = {
34383
34401
  },
34384
34402
  lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
34385
34403
  };
34386
- if (!state5) return summary;
34404
+ if (!state6) return summary;
34387
34405
  const entrada = {
34388
34406
  id: "entrada",
34389
34407
  name: "Entrada",
34390
34408
  icon: CATEGORY_ICONS.entrada,
34391
- deviceCount: state5.entrada?.devices?.length || (receivedData?.entrada_total?.device_count || 0),
34392
- consumption: state5.entrada?.total || 0,
34409
+ deviceCount: state6.entrada?.devices?.length || (receivedData?.entrada_total?.device_count || 0),
34410
+ consumption: state6.entrada?.total || 0,
34393
34411
  percentage: 100
34394
34412
  };
34395
34413
  const lojas = {
34396
34414
  id: "lojas",
34397
34415
  name: "Lojas",
34398
34416
  icon: CATEGORY_ICONS.lojas,
34399
- deviceCount: state5.consumidores?.lojas?.devices?.length || (receivedData?.lojas_total?.device_count || 0),
34400
- consumption: state5.consumidores?.lojas?.total || 0,
34401
- percentage: state5.consumidores?.lojas?.perc || 0
34417
+ deviceCount: state6.consumidores?.lojas?.devices?.length || (receivedData?.lojas_total?.device_count || 0),
34418
+ consumption: state6.consumidores?.lojas?.total || 0,
34419
+ percentage: state6.consumidores?.lojas?.perc || 0
34402
34420
  };
34403
34421
  const climatizacaoData = receivedData?.climatizacao || {};
34404
34422
  const elevadoresData = receivedData?.elevadores || {};
@@ -34409,9 +34427,9 @@ var EnergySummaryTooltip = {
34409
34427
  id: "climatizacao",
34410
34428
  name: "Climatizacao",
34411
34429
  icon: CATEGORY_ICONS.climatizacao,
34412
- deviceCount: climatizacaoData.count || state5.consumidores?.climatizacao?.devices?.length || 0,
34413
- consumption: state5.consumidores?.climatizacao?.total || 0,
34414
- percentage: state5.consumidores?.climatizacao?.perc || 0
34430
+ deviceCount: climatizacaoData.count || state6.consumidores?.climatizacao?.devices?.length || 0,
34431
+ consumption: state6.consumidores?.climatizacao?.total || 0,
34432
+ percentage: state6.consumidores?.climatizacao?.perc || 0
34415
34433
  };
34416
34434
  if (climatizacaoData.subcategories) {
34417
34435
  climatizacao.children = [];
@@ -34462,40 +34480,40 @@ var EnergySummaryTooltip = {
34462
34480
  id: "elevadores",
34463
34481
  name: "Elevadores",
34464
34482
  icon: CATEGORY_ICONS.elevadores,
34465
- deviceCount: elevadoresData.count || state5.consumidores?.elevadores?.devices?.length || 0,
34466
- consumption: state5.consumidores?.elevadores?.total || 0,
34467
- percentage: state5.consumidores?.elevadores?.perc || 0
34483
+ deviceCount: elevadoresData.count || state6.consumidores?.elevadores?.devices?.length || 0,
34484
+ consumption: state6.consumidores?.elevadores?.total || 0,
34485
+ percentage: state6.consumidores?.elevadores?.perc || 0
34468
34486
  });
34469
34487
  areaComumChildren.push({
34470
34488
  id: "escadasRolantes",
34471
34489
  name: "Esc. Rolantes",
34472
34490
  icon: CATEGORY_ICONS.escadas,
34473
- deviceCount: escadasData.count || state5.consumidores?.escadasRolantes?.devices?.length || 0,
34474
- consumption: state5.consumidores?.escadasRolantes?.total || 0,
34475
- percentage: state5.consumidores?.escadasRolantes?.perc || 0
34491
+ deviceCount: escadasData.count || state6.consumidores?.escadasRolantes?.devices?.length || 0,
34492
+ consumption: state6.consumidores?.escadasRolantes?.total || 0,
34493
+ percentage: state6.consumidores?.escadasRolantes?.perc || 0
34476
34494
  });
34477
34495
  areaComumChildren.push({
34478
34496
  id: "outros",
34479
34497
  name: "Outros",
34480
34498
  icon: CATEGORY_ICONS.outros,
34481
- deviceCount: outrosData.count || state5.consumidores?.outros?.devices?.length || 0,
34482
- consumption: state5.consumidores?.outros?.total || 0,
34483
- percentage: state5.consumidores?.outros?.perc || 0
34499
+ deviceCount: outrosData.count || state6.consumidores?.outros?.devices?.length || 0,
34500
+ consumption: state6.consumidores?.outros?.total || 0,
34501
+ percentage: state6.consumidores?.outros?.perc || 0
34484
34502
  });
34485
34503
  const areaComumDeviceCount = areaComumChildren.reduce((sum, c) => sum + c.deviceCount, 0);
34486
- const areaComumConsumption = state5.consumidores?.areaComum?.total || areaComumChildren.reduce((sum, c) => sum + c.consumption, 0);
34504
+ const areaComumConsumption = state6.consumidores?.areaComum?.total || areaComumChildren.reduce((sum, c) => sum + c.consumption, 0);
34487
34505
  const areaComum = {
34488
34506
  id: "areaComum",
34489
34507
  name: "Area Comum",
34490
34508
  icon: CATEGORY_ICONS.areaComum,
34491
34509
  deviceCount: areaComumDeviceCount,
34492
34510
  consumption: areaComumConsumption,
34493
- percentage: state5.consumidores?.areaComum?.perc || 0,
34511
+ percentage: state6.consumidores?.areaComum?.perc || 0,
34494
34512
  children: areaComumChildren
34495
34513
  };
34496
34514
  summary.byCategory = [entrada, lojas, areaComum];
34497
34515
  summary.totalDevices = entrada.deviceCount + lojas.deviceCount + areaComumDeviceCount;
34498
- summary.totalConsumption = state5.grandTotal || entrada.consumption;
34516
+ summary.totalConsumption = state6.grandTotal || entrada.consumption;
34499
34517
  const widgetAggregation = receivedData?.deviceStatusAggregation;
34500
34518
  if (widgetAggregation && widgetAggregation.hasData) {
34501
34519
  summary.byStatus = {
@@ -35783,7 +35801,7 @@ var WaterSummaryTooltip = {
35783
35801
  * RFC-0105 Enhancement: Now fetches device lists from MyIOOrchestratorData
35784
35802
  * to populate device lists for status popup display
35785
35803
  */
35786
- buildSummaryFromState(state5, receivedData, includeBathrooms = false, domain = "water") {
35804
+ buildSummaryFromState(state6, receivedData, includeBathrooms = false, domain = "water") {
35787
35805
  const summary = {
35788
35806
  totalDevices: 0,
35789
35807
  totalConsumption: 0,
@@ -35807,22 +35825,22 @@ var WaterSummaryTooltip = {
35807
35825
  lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
35808
35826
  includeBathrooms
35809
35827
  };
35810
- if (!state5) return summary;
35828
+ if (!state6) return summary;
35811
35829
  const entrada = {
35812
35830
  id: "entrada",
35813
35831
  name: "Entrada",
35814
35832
  icon: WATER_CATEGORY_ICONS.entrada,
35815
- deviceCount: state5.entrada?.devices?.length || (receivedData?.entrada_total?.device_count || 0),
35816
- consumption: state5.entrada?.total || 0,
35833
+ deviceCount: state6.entrada?.devices?.length || (receivedData?.entrada_total?.device_count || 0),
35834
+ consumption: state6.entrada?.total || 0,
35817
35835
  percentage: 100
35818
35836
  };
35819
35837
  const lojas = {
35820
35838
  id: "lojas",
35821
35839
  name: "Lojas",
35822
35840
  icon: WATER_CATEGORY_ICONS.lojas,
35823
- deviceCount: state5.lojas?.devices?.length || (receivedData?.lojas_total?.device_count || 0),
35824
- consumption: state5.lojas?.total || 0,
35825
- percentage: state5.lojas?.perc || 0
35841
+ deviceCount: state6.lojas?.devices?.length || (receivedData?.lojas_total?.device_count || 0),
35842
+ consumption: state6.lojas?.total || 0,
35843
+ percentage: state6.lojas?.perc || 0
35826
35844
  };
35827
35845
  summary.byCategory = [entrada, lojas];
35828
35846
  if (includeBathrooms) {
@@ -35830,9 +35848,9 @@ var WaterSummaryTooltip = {
35830
35848
  id: "banheiros",
35831
35849
  name: "Banheiros",
35832
35850
  icon: WATER_CATEGORY_ICONS.banheiros,
35833
- deviceCount: state5.banheiros?.devices?.length || (receivedData?.banheiros_total?.device_count || 0),
35834
- consumption: state5.banheiros?.total || 0,
35835
- percentage: state5.banheiros?.perc || 0
35851
+ deviceCount: state6.banheiros?.devices?.length || (receivedData?.banheiros_total?.device_count || 0),
35852
+ consumption: state6.banheiros?.total || 0,
35853
+ percentage: state6.banheiros?.perc || 0
35836
35854
  };
35837
35855
  summary.byCategory.push(banheiros);
35838
35856
  }
@@ -35840,24 +35858,24 @@ var WaterSummaryTooltip = {
35840
35858
  id: "areaComum",
35841
35859
  name: "\xC1rea Comum",
35842
35860
  icon: WATER_CATEGORY_ICONS.areaComum,
35843
- deviceCount: state5.areaComum?.devices?.length || (receivedData?.area_comum_total?.device_count || 0),
35844
- consumption: state5.areaComum?.total || 0,
35845
- percentage: state5.areaComum?.perc || 0
35861
+ deviceCount: state6.areaComum?.devices?.length || (receivedData?.area_comum_total?.device_count || 0),
35862
+ consumption: state6.areaComum?.total || 0,
35863
+ percentage: state6.areaComum?.perc || 0
35846
35864
  };
35847
35865
  summary.byCategory.push(areaComum);
35848
- if (state5.pontosNaoMapeados && state5.pontosNaoMapeados.total > 0) {
35866
+ if (state6.pontosNaoMapeados && state6.pontosNaoMapeados.total > 0) {
35849
35867
  const pontosNaoMapeados = {
35850
35868
  id: "pontosNaoMapeados",
35851
35869
  name: "Pontos N\xE3o Mapeados",
35852
35870
  icon: WATER_CATEGORY_ICONS.pontosNaoMapeados,
35853
- deviceCount: state5.pontosNaoMapeados?.devices?.length || 0,
35854
- consumption: state5.pontosNaoMapeados?.total || 0,
35855
- percentage: state5.pontosNaoMapeados?.perc || 0
35871
+ deviceCount: state6.pontosNaoMapeados?.devices?.length || 0,
35872
+ consumption: state6.pontosNaoMapeados?.total || 0,
35873
+ percentage: state6.pontosNaoMapeados?.perc || 0
35856
35874
  };
35857
35875
  summary.byCategory.push(pontosNaoMapeados);
35858
35876
  }
35859
35877
  summary.totalDevices = summary.byCategory.reduce((sum, cat) => sum + cat.deviceCount, 0);
35860
- summary.totalConsumption = state5.entrada?.total || 0;
35878
+ summary.totalConsumption = state6.entrada?.total || 0;
35861
35879
  const widgetAggregation = receivedData?.deviceStatusAggregation;
35862
35880
  if (widgetAggregation && widgetAggregation.hasData) {
35863
35881
  summary.byStatus = {
@@ -37394,6 +37412,996 @@ var TempSensorSummaryTooltip = {
37394
37412
  }
37395
37413
  };
37396
37414
 
37415
+ // src/utils/ContractSummaryTooltip.ts
37416
+ var CONTRACT_SUMMARY_TOOLTIP_CSS = `
37417
+ /* ============================================
37418
+ Contract Summary Tooltip (RFC-0107)
37419
+ Premium draggable tooltip with dark theme
37420
+ ============================================ */
37421
+
37422
+ .myio-contract-summary-tooltip {
37423
+ position: fixed;
37424
+ z-index: 99999;
37425
+ pointer-events: none;
37426
+ opacity: 0;
37427
+ transition: opacity 0.25s ease, transform 0.25s ease;
37428
+ transform: translateY(5px);
37429
+ }
37430
+
37431
+ .myio-contract-summary-tooltip.visible {
37432
+ opacity: 1;
37433
+ pointer-events: auto;
37434
+ transform: translateY(0);
37435
+ }
37436
+
37437
+ .myio-contract-summary-tooltip.closing {
37438
+ opacity: 0;
37439
+ transform: translateY(8px);
37440
+ transition: opacity 0.4s ease, transform 0.4s ease;
37441
+ }
37442
+
37443
+ .myio-contract-summary-tooltip.pinned {
37444
+ box-shadow: 0 0 0 2px #9684B5, 0 10px 40px rgba(0, 0, 0, 0.3);
37445
+ border-radius: 16px;
37446
+ }
37447
+
37448
+ .myio-contract-summary-tooltip.dragging {
37449
+ transition: none !important;
37450
+ cursor: move;
37451
+ }
37452
+
37453
+ .myio-contract-summary-tooltip.maximized {
37454
+ top: 20px !important;
37455
+ left: 20px !important;
37456
+ right: 20px !important;
37457
+ bottom: 20px !important;
37458
+ width: auto !important;
37459
+ max-width: none !important;
37460
+ }
37461
+
37462
+ .myio-contract-summary-tooltip.maximized .myio-contract-summary-tooltip__panel {
37463
+ width: 100%;
37464
+ height: 100%;
37465
+ max-width: none;
37466
+ display: flex;
37467
+ flex-direction: column;
37468
+ }
37469
+
37470
+ .myio-contract-summary-tooltip.maximized .myio-contract-summary-tooltip__body {
37471
+ flex: 1;
37472
+ overflow-y: auto;
37473
+ }
37474
+
37475
+ .myio-contract-summary-tooltip__panel {
37476
+ background: #2d1458;
37477
+ border: 1px solid rgba(255, 255, 255, 0.1);
37478
+ border-radius: 16px;
37479
+ box-shadow:
37480
+ 0 20px 60px rgba(0, 0, 0, 0.4),
37481
+ 0 8px 20px rgba(0, 0, 0, 0.25),
37482
+ 0 0 0 1px rgba(255, 255, 255, 0.05);
37483
+ min-width: 320px;
37484
+ max-width: 380px;
37485
+ font-family: Inter, system-ui, -apple-system, sans-serif;
37486
+ font-size: 12px;
37487
+ color: #ffffff;
37488
+ overflow: hidden;
37489
+ }
37490
+
37491
+ /* Header */
37492
+ .myio-contract-summary-tooltip__header {
37493
+ display: flex;
37494
+ align-items: center;
37495
+ gap: 10px;
37496
+ padding: 14px 16px;
37497
+ background: linear-gradient(135deg, #9684B5 0%, #2d1458 100%);
37498
+ border-radius: 16px 16px 0 0;
37499
+ position: relative;
37500
+ overflow: hidden;
37501
+ cursor: move;
37502
+ user-select: none;
37503
+ }
37504
+
37505
+ .myio-contract-summary-tooltip__header::before {
37506
+ content: '';
37507
+ position: absolute;
37508
+ top: 0;
37509
+ left: 0;
37510
+ right: 0;
37511
+ bottom: 0;
37512
+ background: url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%23ffffff' fill-opacity='0.05'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
37513
+ opacity: 0.3;
37514
+ }
37515
+
37516
+ .myio-contract-summary-tooltip__icon {
37517
+ width: 40px;
37518
+ height: 40px;
37519
+ background: rgba(255, 255, 255, 0.15);
37520
+ border-radius: 12px;
37521
+ display: flex;
37522
+ align-items: center;
37523
+ justify-content: center;
37524
+ font-size: 20px;
37525
+ backdrop-filter: blur(10px);
37526
+ position: relative;
37527
+ z-index: 1;
37528
+ }
37529
+
37530
+ .myio-contract-summary-tooltip__icon.valid {
37531
+ background: rgba(76, 175, 80, 0.3);
37532
+ }
37533
+
37534
+ .myio-contract-summary-tooltip__icon.invalid {
37535
+ background: rgba(244, 67, 54, 0.3);
37536
+ }
37537
+
37538
+ .myio-contract-summary-tooltip__header-info {
37539
+ flex: 1;
37540
+ position: relative;
37541
+ z-index: 1;
37542
+ }
37543
+
37544
+ .myio-contract-summary-tooltip__title {
37545
+ font-weight: 700;
37546
+ font-size: 15px;
37547
+ color: #ffffff;
37548
+ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
37549
+ margin-bottom: 2px;
37550
+ }
37551
+
37552
+ .myio-contract-summary-tooltip__subtitle {
37553
+ font-size: 11px;
37554
+ color: rgba(255, 255, 255, 0.7);
37555
+ }
37556
+
37557
+ .myio-contract-summary-tooltip__header-actions {
37558
+ display: flex;
37559
+ align-items: center;
37560
+ gap: 4px;
37561
+ position: relative;
37562
+ z-index: 1;
37563
+ }
37564
+
37565
+ .myio-contract-summary-tooltip__header-btn {
37566
+ width: 28px;
37567
+ height: 28px;
37568
+ border: none;
37569
+ background: rgba(255, 255, 255, 0.15);
37570
+ border-radius: 8px;
37571
+ cursor: pointer;
37572
+ display: flex;
37573
+ align-items: center;
37574
+ justify-content: center;
37575
+ transition: all 0.2s ease;
37576
+ color: rgba(255, 255, 255, 0.8);
37577
+ }
37578
+
37579
+ .myio-contract-summary-tooltip__header-btn:hover {
37580
+ background: rgba(255, 255, 255, 0.25);
37581
+ color: #ffffff;
37582
+ transform: scale(1.05);
37583
+ }
37584
+
37585
+ .myio-contract-summary-tooltip__header-btn.pinned {
37586
+ background: rgba(255, 255, 255, 0.9);
37587
+ color: #9684B5;
37588
+ }
37589
+
37590
+ .myio-contract-summary-tooltip__header-btn.pinned:hover {
37591
+ background: #ffffff;
37592
+ color: #2d1458;
37593
+ }
37594
+
37595
+ .myio-contract-summary-tooltip__header-btn svg {
37596
+ width: 14px;
37597
+ height: 14px;
37598
+ }
37599
+
37600
+ /* Body */
37601
+ .myio-contract-summary-tooltip__body {
37602
+ padding: 16px;
37603
+ }
37604
+
37605
+ /* Domain Section */
37606
+ .myio-contract-summary-tooltip__domain {
37607
+ margin-bottom: 14px;
37608
+ background: rgba(255, 255, 255, 0.05);
37609
+ border-radius: 12px;
37610
+ overflow: hidden;
37611
+ border: 1px solid rgba(255, 255, 255, 0.08);
37612
+ }
37613
+
37614
+ .myio-contract-summary-tooltip__domain:last-child {
37615
+ margin-bottom: 0;
37616
+ }
37617
+
37618
+ .myio-contract-summary-tooltip__domain-header {
37619
+ display: flex;
37620
+ align-items: center;
37621
+ justify-content: space-between;
37622
+ padding: 10px 14px;
37623
+ cursor: pointer;
37624
+ transition: background 0.2s ease;
37625
+ }
37626
+
37627
+ .myio-contract-summary-tooltip__domain-header:hover {
37628
+ background: rgba(255, 255, 255, 0.05);
37629
+ }
37630
+
37631
+ .myio-contract-summary-tooltip__domain-info {
37632
+ display: flex;
37633
+ align-items: center;
37634
+ gap: 8px;
37635
+ }
37636
+
37637
+ .myio-contract-summary-tooltip__domain-icon {
37638
+ font-size: 18px;
37639
+ }
37640
+
37641
+ .myio-contract-summary-tooltip__domain-name {
37642
+ font-weight: 600;
37643
+ font-size: 13px;
37644
+ }
37645
+
37646
+ .myio-contract-summary-tooltip__domain-count {
37647
+ font-size: 12px;
37648
+ color: #81c784;
37649
+ font-weight: 600;
37650
+ }
37651
+
37652
+ .myio-contract-summary-tooltip__expand-icon {
37653
+ font-size: 10px;
37654
+ opacity: 0.6;
37655
+ transition: transform 0.3s ease;
37656
+ }
37657
+
37658
+ .myio-contract-summary-tooltip__domain.expanded .myio-contract-summary-tooltip__expand-icon {
37659
+ transform: rotate(180deg);
37660
+ }
37661
+
37662
+ /* Domain Details */
37663
+ .myio-contract-summary-tooltip__domain-details {
37664
+ max-height: 0;
37665
+ overflow: hidden;
37666
+ transition: max-height 0.3s ease;
37667
+ background: rgba(0, 0, 0, 0.15);
37668
+ }
37669
+
37670
+ .myio-contract-summary-tooltip__domain.expanded .myio-contract-summary-tooltip__domain-details {
37671
+ max-height: 150px;
37672
+ }
37673
+
37674
+ .myio-contract-summary-tooltip__detail-row {
37675
+ display: flex;
37676
+ align-items: center;
37677
+ justify-content: space-between;
37678
+ padding: 6px 14px 6px 40px;
37679
+ font-size: 12px;
37680
+ border-top: 1px solid rgba(255, 255, 255, 0.05);
37681
+ }
37682
+
37683
+ .myio-contract-summary-tooltip__detail-row:first-child {
37684
+ border-top: none;
37685
+ }
37686
+
37687
+ .myio-contract-summary-tooltip__detail-label {
37688
+ opacity: 0.7;
37689
+ display: flex;
37690
+ align-items: center;
37691
+ gap: 6px;
37692
+ }
37693
+
37694
+ .myio-contract-summary-tooltip__detail-label::before {
37695
+ content: '';
37696
+ width: 4px;
37697
+ height: 4px;
37698
+ border-radius: 50%;
37699
+ background: currentColor;
37700
+ opacity: 0.5;
37701
+ }
37702
+
37703
+ .myio-contract-summary-tooltip__detail-count {
37704
+ font-weight: 500;
37705
+ color: #81c784;
37706
+ }
37707
+
37708
+ /* Status Banner */
37709
+ .myio-contract-summary-tooltip__status {
37710
+ display: flex;
37711
+ align-items: center;
37712
+ justify-content: center;
37713
+ gap: 8px;
37714
+ padding: 10px 14px;
37715
+ border-radius: 10px;
37716
+ margin-bottom: 14px;
37717
+ font-size: 12px;
37718
+ font-weight: 600;
37719
+ }
37720
+
37721
+ .myio-contract-summary-tooltip__status.valid {
37722
+ background: rgba(76, 175, 80, 0.2);
37723
+ color: #81c784;
37724
+ border: 1px solid rgba(76, 175, 80, 0.3);
37725
+ }
37726
+
37727
+ .myio-contract-summary-tooltip__status.invalid {
37728
+ background: rgba(244, 67, 54, 0.2);
37729
+ color: #ef5350;
37730
+ border: 1px solid rgba(244, 67, 54, 0.3);
37731
+ }
37732
+
37733
+ .myio-contract-summary-tooltip__status-icon {
37734
+ font-size: 14px;
37735
+ }
37736
+
37737
+ /* Discrepancies */
37738
+ .myio-contract-summary-tooltip__discrepancies {
37739
+ background: rgba(244, 67, 54, 0.15);
37740
+ border: 1px solid rgba(244, 67, 54, 0.3);
37741
+ border-radius: 10px;
37742
+ padding: 10px 14px;
37743
+ margin-bottom: 14px;
37744
+ }
37745
+
37746
+ .myio-contract-summary-tooltip__discrepancies-title {
37747
+ font-size: 11px;
37748
+ font-weight: 600;
37749
+ color: #ef5350;
37750
+ margin-bottom: 6px;
37751
+ text-transform: uppercase;
37752
+ letter-spacing: 0.5px;
37753
+ }
37754
+
37755
+ .myio-contract-summary-tooltip__discrepancy-item {
37756
+ font-size: 11px;
37757
+ color: rgba(255, 255, 255, 0.8);
37758
+ padding: 3px 0;
37759
+ }
37760
+
37761
+ /* Footer */
37762
+ .myio-contract-summary-tooltip__footer {
37763
+ display: flex;
37764
+ justify-content: space-between;
37765
+ align-items: center;
37766
+ padding: 12px 16px;
37767
+ background: rgba(0, 0, 0, 0.2);
37768
+ border-top: 1px solid rgba(255, 255, 255, 0.05);
37769
+ border-radius: 0 0 16px 16px;
37770
+ }
37771
+
37772
+ .myio-contract-summary-tooltip__footer-label {
37773
+ font-size: 10px;
37774
+ color: rgba(255, 255, 255, 0.5);
37775
+ }
37776
+
37777
+ .myio-contract-summary-tooltip__footer-value {
37778
+ font-size: 11px;
37779
+ font-weight: 600;
37780
+ color: rgba(255, 255, 255, 0.8);
37781
+ }
37782
+
37783
+ /* Total Devices Badge */
37784
+ .myio-contract-summary-tooltip__total {
37785
+ display: flex;
37786
+ align-items: center;
37787
+ justify-content: center;
37788
+ gap: 8px;
37789
+ padding: 12px;
37790
+ background: rgba(255, 255, 255, 0.08);
37791
+ border-radius: 10px;
37792
+ margin-bottom: 14px;
37793
+ }
37794
+
37795
+ .myio-contract-summary-tooltip__total-label {
37796
+ font-size: 12px;
37797
+ opacity: 0.8;
37798
+ }
37799
+
37800
+ .myio-contract-summary-tooltip__total-value {
37801
+ font-size: 18px;
37802
+ font-weight: 700;
37803
+ color: #81c784;
37804
+ }
37805
+
37806
+ /* Responsive */
37807
+ @media (max-width: 400px) {
37808
+ .myio-contract-summary-tooltip__panel {
37809
+ min-width: 280px;
37810
+ max-width: 95vw;
37811
+ }
37812
+ }
37813
+ `;
37814
+ var cssInjected9 = false;
37815
+ function injectCSS9() {
37816
+ if (cssInjected9) return;
37817
+ if (typeof document === "undefined") return;
37818
+ const styleId = "myio-contract-summary-tooltip-styles";
37819
+ if (document.getElementById(styleId)) {
37820
+ cssInjected9 = true;
37821
+ return;
37822
+ }
37823
+ const style = document.createElement("style");
37824
+ style.id = styleId;
37825
+ style.textContent = CONTRACT_SUMMARY_TOOLTIP_CSS;
37826
+ document.head.appendChild(style);
37827
+ cssInjected9 = true;
37828
+ }
37829
+ var state5 = {
37830
+ hideTimer: null,
37831
+ isMouseOverTooltip: false,
37832
+ isMaximized: false,
37833
+ isDragging: false,
37834
+ dragOffset: { x: 0, y: 0 },
37835
+ savedPosition: null,
37836
+ pinnedCounter: 0,
37837
+ expandedDomains: /* @__PURE__ */ new Set(["energy", "water", "temperature"])
37838
+ };
37839
+ function formatTimestamp5(isoString) {
37840
+ if (!isoString) return "Agora";
37841
+ try {
37842
+ const date = new Date(isoString);
37843
+ return date.toLocaleString("pt-BR", {
37844
+ hour: "2-digit",
37845
+ minute: "2-digit",
37846
+ day: "2-digit",
37847
+ month: "2-digit"
37848
+ });
37849
+ } catch {
37850
+ return "Agora";
37851
+ }
37852
+ }
37853
+ function calculateTotalDevices(data) {
37854
+ return data.energy.total + data.water.total + data.temperature.total;
37855
+ }
37856
+ function generateHeaderHTML5(data) {
37857
+ const iconClass = data.isValid ? "valid" : "invalid";
37858
+ const iconSymbol = data.isValid ? "\u2713" : "!";
37859
+ const totalDevices = calculateTotalDevices(data);
37860
+ return `
37861
+ <div class="myio-contract-summary-tooltip__header" data-drag-handle>
37862
+ <div class="myio-contract-summary-tooltip__icon ${iconClass}">${iconSymbol}</div>
37863
+ <div class="myio-contract-summary-tooltip__header-info">
37864
+ <div class="myio-contract-summary-tooltip__title">Contract Summary</div>
37865
+ <div class="myio-contract-summary-tooltip__subtitle">${totalDevices} devices loaded</div>
37866
+ </div>
37867
+ <div class="myio-contract-summary-tooltip__header-actions">
37868
+ <button class="myio-contract-summary-tooltip__header-btn" data-action="pin" title="Pin to screen">
37869
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
37870
+ <path d="M9 4v6l-2 4v2h10v-2l-2-4V4"/>
37871
+ <line x1="12" y1="16" x2="12" y2="21"/>
37872
+ <line x1="8" y1="4" x2="16" y2="4"/>
37873
+ </svg>
37874
+ </button>
37875
+ <button class="myio-contract-summary-tooltip__header-btn" data-action="maximize" title="Maximize">
37876
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
37877
+ <rect x="3" y="3" width="18" height="18" rx="2"/>
37878
+ </svg>
37879
+ </button>
37880
+ <button class="myio-contract-summary-tooltip__header-btn" data-action="close" title="Close">
37881
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
37882
+ <path d="M18 6L6 18M6 6l12 12"/>
37883
+ </svg>
37884
+ </button>
37885
+ </div>
37886
+ </div>
37887
+ `;
37888
+ }
37889
+ function generateDomainHTML(domain, icon, name, counts, isTemperature = false) {
37890
+ const isExpanded = state5.expandedDomains.has(domain);
37891
+ const expandedClass = isExpanded ? "expanded" : "";
37892
+ let detailsHTML = "";
37893
+ if (isTemperature) {
37894
+ const tempCounts = counts;
37895
+ detailsHTML = `
37896
+ <div class="myio-contract-summary-tooltip__detail-row">
37897
+ <span class="myio-contract-summary-tooltip__detail-label">Internal (Climate)</span>
37898
+ <span class="myio-contract-summary-tooltip__detail-count">${tempCounts.internal}</span>
37899
+ </div>
37900
+ <div class="myio-contract-summary-tooltip__detail-row">
37901
+ <span class="myio-contract-summary-tooltip__detail-label">Stores (Non-Climate)</span>
37902
+ <span class="myio-contract-summary-tooltip__detail-count">${tempCounts.stores}</span>
37903
+ </div>
37904
+ `;
37905
+ } else {
37906
+ const domainCounts = counts;
37907
+ detailsHTML = `
37908
+ <div class="myio-contract-summary-tooltip__detail-row">
37909
+ <span class="myio-contract-summary-tooltip__detail-label">Entries</span>
37910
+ <span class="myio-contract-summary-tooltip__detail-count">${domainCounts.entries}</span>
37911
+ </div>
37912
+ <div class="myio-contract-summary-tooltip__detail-row">
37913
+ <span class="myio-contract-summary-tooltip__detail-label">Common Area</span>
37914
+ <span class="myio-contract-summary-tooltip__detail-count">${domainCounts.commonArea}</span>
37915
+ </div>
37916
+ <div class="myio-contract-summary-tooltip__detail-row">
37917
+ <span class="myio-contract-summary-tooltip__detail-label">Stores</span>
37918
+ <span class="myio-contract-summary-tooltip__detail-count">${domainCounts.stores}</span>
37919
+ </div>
37920
+ `;
37921
+ }
37922
+ return `
37923
+ <div class="myio-contract-summary-tooltip__domain ${expandedClass}" data-domain="${domain}">
37924
+ <div class="myio-contract-summary-tooltip__domain-header" data-toggle-domain="${domain}">
37925
+ <div class="myio-contract-summary-tooltip__domain-info">
37926
+ <span class="myio-contract-summary-tooltip__domain-icon">${icon}</span>
37927
+ <span class="myio-contract-summary-tooltip__domain-name">${name}</span>
37928
+ </div>
37929
+ <div style="display: flex; align-items: center; gap: 8px;">
37930
+ <span class="myio-contract-summary-tooltip__domain-count">${counts.total} devices</span>
37931
+ <span class="myio-contract-summary-tooltip__expand-icon">\u25BC</span>
37932
+ </div>
37933
+ </div>
37934
+ <div class="myio-contract-summary-tooltip__domain-details">
37935
+ ${detailsHTML}
37936
+ </div>
37937
+ </div>
37938
+ `;
37939
+ }
37940
+ function generateBodyHTML3(data) {
37941
+ const totalDevices = calculateTotalDevices(data);
37942
+ const timestamp = formatTimestamp5(data.timestamp);
37943
+ const statusClass = data.isValid ? "valid" : "invalid";
37944
+ const statusIcon = data.isValid ? "\u2713" : "\u26A0";
37945
+ const statusText = data.isValid ? "Contract validated successfully" : "Validation issues detected";
37946
+ let discrepanciesHTML = "";
37947
+ if (data.discrepancies && data.discrepancies.length > 0) {
37948
+ const items = data.discrepancies.map((d) => `<div class="myio-contract-summary-tooltip__discrepancy-item">${d.domain}: expected ${d.expected}, found ${d.actual}</div>`).join("");
37949
+ discrepanciesHTML = `
37950
+ <div class="myio-contract-summary-tooltip__discrepancies">
37951
+ <div class="myio-contract-summary-tooltip__discrepancies-title">Discrepancies</div>
37952
+ ${items}
37953
+ </div>
37954
+ `;
37955
+ }
37956
+ return `
37957
+ <div class="myio-contract-summary-tooltip__body">
37958
+ <!-- Status Banner -->
37959
+ <div class="myio-contract-summary-tooltip__status ${statusClass}">
37960
+ <span class="myio-contract-summary-tooltip__status-icon">${statusIcon}</span>
37961
+ <span>${statusText}</span>
37962
+ </div>
37963
+
37964
+ ${discrepanciesHTML}
37965
+
37966
+ <!-- Total Devices -->
37967
+ <div class="myio-contract-summary-tooltip__total">
37968
+ <span class="myio-contract-summary-tooltip__total-label">Total Devices:</span>
37969
+ <span class="myio-contract-summary-tooltip__total-value">${totalDevices}</span>
37970
+ </div>
37971
+
37972
+ <!-- Energy -->
37973
+ ${generateDomainHTML("energy", "\u26A1", "Energy", data.energy)}
37974
+
37975
+ <!-- Water -->
37976
+ ${generateDomainHTML("water", "\u{1F4A7}", "Water", data.water)}
37977
+
37978
+ <!-- Temperature -->
37979
+ ${generateDomainHTML("temperature", "\u{1F321}\uFE0F", "Temperature", data.temperature, true)}
37980
+ </div>
37981
+
37982
+ <!-- Footer -->
37983
+ <div class="myio-contract-summary-tooltip__footer">
37984
+ <span class="myio-contract-summary-tooltip__footer-label">Loaded at</span>
37985
+ <span class="myio-contract-summary-tooltip__footer-value">${timestamp}</span>
37986
+ </div>
37987
+ `;
37988
+ }
37989
+ function setupHoverListeners5(container) {
37990
+ container.onmouseenter = () => {
37991
+ state5.isMouseOverTooltip = true;
37992
+ if (state5.hideTimer) {
37993
+ clearTimeout(state5.hideTimer);
37994
+ state5.hideTimer = null;
37995
+ }
37996
+ };
37997
+ container.onmouseleave = () => {
37998
+ state5.isMouseOverTooltip = false;
37999
+ startDelayedHide5();
38000
+ };
38001
+ }
38002
+ function setupDomainToggleListeners(container) {
38003
+ const toggles = container.querySelectorAll("[data-toggle-domain]");
38004
+ toggles.forEach((toggle) => {
38005
+ toggle.onclick = (e) => {
38006
+ e.stopPropagation();
38007
+ const domain = toggle.dataset.toggleDomain;
38008
+ if (!domain) return;
38009
+ const domainEl = container.querySelector(`[data-domain="${domain}"]`);
38010
+ if (!domainEl) return;
38011
+ if (state5.expandedDomains.has(domain)) {
38012
+ state5.expandedDomains.delete(domain);
38013
+ domainEl.classList.remove("expanded");
38014
+ } else {
38015
+ state5.expandedDomains.add(domain);
38016
+ domainEl.classList.add("expanded");
38017
+ }
38018
+ };
38019
+ });
38020
+ }
38021
+ function setupButtonListeners5(container) {
38022
+ const buttons = container.querySelectorAll("[data-action]");
38023
+ buttons.forEach((btn) => {
38024
+ btn.onclick = (e) => {
38025
+ e.stopPropagation();
38026
+ const action = btn.dataset.action;
38027
+ switch (action) {
38028
+ case "pin":
38029
+ createPinnedClone5(container);
38030
+ break;
38031
+ case "maximize":
38032
+ toggleMaximize5(container);
38033
+ break;
38034
+ case "close":
38035
+ ContractSummaryTooltip.close();
38036
+ break;
38037
+ }
38038
+ };
38039
+ });
38040
+ }
38041
+ function setupDragListeners5(container) {
38042
+ const header = container.querySelector("[data-drag-handle]");
38043
+ if (!header) return;
38044
+ header.onmousedown = (e) => {
38045
+ if (e.target.closest("[data-action]")) return;
38046
+ if (e.target.closest("[data-toggle-domain]")) return;
38047
+ if (state5.isMaximized) return;
38048
+ state5.isDragging = true;
38049
+ container.classList.add("dragging");
38050
+ const rect = container.getBoundingClientRect();
38051
+ state5.dragOffset = {
38052
+ x: e.clientX - rect.left,
38053
+ y: e.clientY - rect.top
38054
+ };
38055
+ const onMouseMove = (e2) => {
38056
+ if (!state5.isDragging) return;
38057
+ const newLeft = e2.clientX - state5.dragOffset.x;
38058
+ const newTop = e2.clientY - state5.dragOffset.y;
38059
+ const maxLeft = window.innerWidth - container.offsetWidth;
38060
+ const maxTop = window.innerHeight - container.offsetHeight;
38061
+ container.style.left = Math.max(0, Math.min(newLeft, maxLeft)) + "px";
38062
+ container.style.top = Math.max(0, Math.min(newTop, maxTop)) + "px";
38063
+ };
38064
+ const onMouseUp = () => {
38065
+ state5.isDragging = false;
38066
+ container.classList.remove("dragging");
38067
+ document.removeEventListener("mousemove", onMouseMove);
38068
+ document.removeEventListener("mouseup", onMouseUp);
38069
+ };
38070
+ document.addEventListener("mousemove", onMouseMove);
38071
+ document.addEventListener("mouseup", onMouseUp);
38072
+ };
38073
+ }
38074
+ function createPinnedClone5(container) {
38075
+ state5.pinnedCounter++;
38076
+ const pinnedId = `myio-contract-summary-tooltip-pinned-${state5.pinnedCounter}`;
38077
+ const clone = container.cloneNode(true);
38078
+ clone.id = pinnedId;
38079
+ clone.classList.add("pinned");
38080
+ clone.classList.remove("closing");
38081
+ const pinBtn = clone.querySelector('[data-action="pin"]');
38082
+ if (pinBtn) {
38083
+ pinBtn.classList.add("pinned");
38084
+ pinBtn.setAttribute("title", "Unpin");
38085
+ pinBtn.innerHTML = `
38086
+ <svg viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="1">
38087
+ <path d="M9 4v6l-2 4v2h10v-2l-2-4V4"/>
38088
+ <line x1="12" y1="16" x2="12" y2="21"/>
38089
+ <line x1="8" y1="4" x2="16" y2="4"/>
38090
+ </svg>
38091
+ `;
38092
+ }
38093
+ document.body.appendChild(clone);
38094
+ setupPinnedCloneListeners5(clone, pinnedId);
38095
+ ContractSummaryTooltip.hide();
38096
+ }
38097
+ function setupPinnedCloneListeners5(clone, cloneId) {
38098
+ let isMaximized = false;
38099
+ let savedPosition = null;
38100
+ const cloneExpandedDomains = new Set(state5.expandedDomains);
38101
+ const toggles = clone.querySelectorAll("[data-toggle-domain]");
38102
+ toggles.forEach((toggle) => {
38103
+ toggle.onclick = (e) => {
38104
+ e.stopPropagation();
38105
+ const domain = toggle.dataset.toggleDomain;
38106
+ if (!domain) return;
38107
+ const domainEl = clone.querySelector(`[data-domain="${domain}"]`);
38108
+ if (!domainEl) return;
38109
+ if (cloneExpandedDomains.has(domain)) {
38110
+ cloneExpandedDomains.delete(domain);
38111
+ domainEl.classList.remove("expanded");
38112
+ } else {
38113
+ cloneExpandedDomains.add(domain);
38114
+ domainEl.classList.add("expanded");
38115
+ }
38116
+ };
38117
+ });
38118
+ const pinBtn = clone.querySelector('[data-action="pin"]');
38119
+ if (pinBtn) {
38120
+ pinBtn.onclick = (e) => {
38121
+ e.stopPropagation();
38122
+ closePinnedClone5(cloneId);
38123
+ };
38124
+ }
38125
+ const closeBtn = clone.querySelector('[data-action="close"]');
38126
+ if (closeBtn) {
38127
+ closeBtn.onclick = (e) => {
38128
+ e.stopPropagation();
38129
+ closePinnedClone5(cloneId);
38130
+ };
38131
+ }
38132
+ const maxBtn = clone.querySelector('[data-action="maximize"]');
38133
+ if (maxBtn) {
38134
+ maxBtn.onclick = (e) => {
38135
+ e.stopPropagation();
38136
+ isMaximized = !isMaximized;
38137
+ if (isMaximized) {
38138
+ savedPosition = { left: clone.style.left, top: clone.style.top };
38139
+ maxBtn.innerHTML = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="5" y="5" width="14" height="14" rx="2"/><path d="M9 5V3h12v12h-2"/></svg>`;
38140
+ maxBtn.setAttribute("title", "Restore");
38141
+ } else {
38142
+ maxBtn.innerHTML = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/></svg>`;
38143
+ maxBtn.setAttribute("title", "Maximize");
38144
+ if (savedPosition) {
38145
+ clone.style.left = savedPosition.left;
38146
+ clone.style.top = savedPosition.top;
38147
+ }
38148
+ }
38149
+ clone.classList.toggle("maximized", isMaximized);
38150
+ };
38151
+ }
38152
+ const header = clone.querySelector("[data-drag-handle]");
38153
+ if (header) {
38154
+ let isDragging = false;
38155
+ let dragOffset = { x: 0, y: 0 };
38156
+ header.onmousedown = (e) => {
38157
+ if (e.target.closest("[data-action]")) return;
38158
+ if (e.target.closest("[data-toggle-domain]")) return;
38159
+ if (isMaximized) return;
38160
+ isDragging = true;
38161
+ clone.classList.add("dragging");
38162
+ const rect = clone.getBoundingClientRect();
38163
+ dragOffset = { x: e.clientX - rect.left, y: e.clientY - rect.top };
38164
+ const onMouseMove = (e2) => {
38165
+ if (!isDragging) return;
38166
+ const newLeft = e2.clientX - dragOffset.x;
38167
+ const newTop = e2.clientY - dragOffset.y;
38168
+ const maxLeft = window.innerWidth - clone.offsetWidth;
38169
+ const maxTop = window.innerHeight - clone.offsetHeight;
38170
+ clone.style.left = Math.max(0, Math.min(newLeft, maxLeft)) + "px";
38171
+ clone.style.top = Math.max(0, Math.min(newTop, maxTop)) + "px";
38172
+ };
38173
+ const onMouseUp = () => {
38174
+ isDragging = false;
38175
+ clone.classList.remove("dragging");
38176
+ document.removeEventListener("mousemove", onMouseMove);
38177
+ document.removeEventListener("mouseup", onMouseUp);
38178
+ };
38179
+ document.addEventListener("mousemove", onMouseMove);
38180
+ document.addEventListener("mouseup", onMouseUp);
38181
+ };
38182
+ }
38183
+ }
38184
+ function closePinnedClone5(cloneId) {
38185
+ const clone = document.getElementById(cloneId);
38186
+ if (clone) {
38187
+ clone.classList.add("closing");
38188
+ setTimeout(() => clone.remove(), 400);
38189
+ }
38190
+ }
38191
+ function toggleMaximize5(container) {
38192
+ state5.isMaximized = !state5.isMaximized;
38193
+ if (state5.isMaximized) {
38194
+ state5.savedPosition = {
38195
+ left: container.style.left,
38196
+ top: container.style.top
38197
+ };
38198
+ }
38199
+ container.classList.toggle("maximized", state5.isMaximized);
38200
+ const maxBtn = container.querySelector('[data-action="maximize"]');
38201
+ if (maxBtn) {
38202
+ if (state5.isMaximized) {
38203
+ maxBtn.innerHTML = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="5" y="5" width="14" height="14" rx="2"/><path d="M9 5V3h12v12h-2"/></svg>`;
38204
+ maxBtn.setAttribute("title", "Restore");
38205
+ } else {
38206
+ maxBtn.innerHTML = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/></svg>`;
38207
+ maxBtn.setAttribute("title", "Maximize");
38208
+ if (state5.savedPosition) {
38209
+ container.style.left = state5.savedPosition.left;
38210
+ container.style.top = state5.savedPosition.top;
38211
+ }
38212
+ }
38213
+ }
38214
+ }
38215
+ function startDelayedHide5() {
38216
+ if (state5.isMouseOverTooltip) return;
38217
+ if (state5.hideTimer) {
38218
+ clearTimeout(state5.hideTimer);
38219
+ }
38220
+ state5.hideTimer = setTimeout(() => {
38221
+ hideWithAnimation5();
38222
+ }, 1500);
38223
+ }
38224
+ function hideWithAnimation5() {
38225
+ const container = document.getElementById("myio-contract-summary-tooltip");
38226
+ if (container && container.classList.contains("visible")) {
38227
+ container.classList.add("closing");
38228
+ setTimeout(() => {
38229
+ container.classList.remove("visible", "closing");
38230
+ }, 400);
38231
+ }
38232
+ }
38233
+ function positionTooltip5(container, triggerElement) {
38234
+ const rect = triggerElement.getBoundingClientRect();
38235
+ let left = rect.left;
38236
+ let top = rect.bottom + 8;
38237
+ const tooltipWidth = 360;
38238
+ if (left + tooltipWidth > window.innerWidth - 20) {
38239
+ left = window.innerWidth - tooltipWidth - 20;
38240
+ }
38241
+ if (left < 10) left = 10;
38242
+ if (top + 500 > window.innerHeight) {
38243
+ top = rect.top - 8 - 500;
38244
+ if (top < 10) top = 10;
38245
+ }
38246
+ container.style.left = left + "px";
38247
+ container.style.top = top + "px";
38248
+ }
38249
+ var ContractSummaryTooltip = {
38250
+ containerId: "myio-contract-summary-tooltip",
38251
+ /**
38252
+ * Get or create container
38253
+ */
38254
+ getContainer() {
38255
+ injectCSS9();
38256
+ let container = document.getElementById(this.containerId);
38257
+ if (!container) {
38258
+ container = document.createElement("div");
38259
+ container.id = this.containerId;
38260
+ container.className = "myio-contract-summary-tooltip";
38261
+ document.body.appendChild(container);
38262
+ }
38263
+ return container;
38264
+ },
38265
+ /**
38266
+ * Show tooltip
38267
+ */
38268
+ show(triggerElement, data) {
38269
+ if (state5.hideTimer) {
38270
+ clearTimeout(state5.hideTimer);
38271
+ state5.hideTimer = null;
38272
+ }
38273
+ const container = this.getContainer();
38274
+ container.classList.remove("closing");
38275
+ container.innerHTML = `
38276
+ <div class="myio-contract-summary-tooltip__panel">
38277
+ ${generateHeaderHTML5(data)}
38278
+ ${generateBodyHTML3(data)}
38279
+ </div>
38280
+ `;
38281
+ positionTooltip5(container, triggerElement);
38282
+ container.classList.add("visible");
38283
+ setupHoverListeners5(container);
38284
+ setupButtonListeners5(container);
38285
+ setupDragListeners5(container);
38286
+ setupDomainToggleListeners(container);
38287
+ },
38288
+ /**
38289
+ * Start delayed hide
38290
+ */
38291
+ startDelayedHide() {
38292
+ startDelayedHide5();
38293
+ },
38294
+ /**
38295
+ * Hide immediately
38296
+ */
38297
+ hide() {
38298
+ if (state5.hideTimer) {
38299
+ clearTimeout(state5.hideTimer);
38300
+ state5.hideTimer = null;
38301
+ }
38302
+ state5.isMouseOverTooltip = false;
38303
+ const container = document.getElementById(this.containerId);
38304
+ if (container) {
38305
+ container.classList.remove("visible", "closing");
38306
+ }
38307
+ },
38308
+ /**
38309
+ * Close and reset all states
38310
+ */
38311
+ close() {
38312
+ state5.isMaximized = false;
38313
+ state5.isDragging = false;
38314
+ state5.savedPosition = null;
38315
+ if (state5.hideTimer) {
38316
+ clearTimeout(state5.hideTimer);
38317
+ state5.hideTimer = null;
38318
+ }
38319
+ state5.isMouseOverTooltip = false;
38320
+ const container = document.getElementById(this.containerId);
38321
+ if (container) {
38322
+ container.classList.remove("visible", "pinned", "maximized", "dragging", "closing");
38323
+ }
38324
+ },
38325
+ /**
38326
+ * Attach tooltip to trigger element with click behavior
38327
+ */
38328
+ attach(triggerElement, getDataFn) {
38329
+ const self = this;
38330
+ const handleClick = () => {
38331
+ if (state5.hideTimer) {
38332
+ clearTimeout(state5.hideTimer);
38333
+ state5.hideTimer = null;
38334
+ }
38335
+ const data = getDataFn();
38336
+ if (data) {
38337
+ self.show(triggerElement, data);
38338
+ }
38339
+ };
38340
+ triggerElement.addEventListener("click", handleClick);
38341
+ return () => {
38342
+ triggerElement.removeEventListener("click", handleClick);
38343
+ self.hide();
38344
+ };
38345
+ },
38346
+ /**
38347
+ * Attach tooltip with hover behavior
38348
+ */
38349
+ attachHover(triggerElement, getDataFn) {
38350
+ const self = this;
38351
+ const handleMouseEnter = () => {
38352
+ if (state5.hideTimer) {
38353
+ clearTimeout(state5.hideTimer);
38354
+ state5.hideTimer = null;
38355
+ }
38356
+ const data = getDataFn();
38357
+ if (data) {
38358
+ self.show(triggerElement, data);
38359
+ }
38360
+ };
38361
+ const handleMouseLeave = () => {
38362
+ startDelayedHide5();
38363
+ };
38364
+ triggerElement.addEventListener("mouseenter", handleMouseEnter);
38365
+ triggerElement.addEventListener("mouseleave", handleMouseLeave);
38366
+ return () => {
38367
+ triggerElement.removeEventListener("mouseenter", handleMouseEnter);
38368
+ triggerElement.removeEventListener("mouseleave", handleMouseLeave);
38369
+ self.hide();
38370
+ };
38371
+ },
38372
+ /**
38373
+ * Build contract data from window.CONTRACT_STATE
38374
+ * Helper method to build the data structure from global state
38375
+ */
38376
+ buildFromGlobalState() {
38377
+ const globalState = window.CONTRACT_STATE;
38378
+ if (!globalState) return null;
38379
+ return {
38380
+ isLoaded: globalState.isLoaded ?? false,
38381
+ isValid: globalState.isValid ?? false,
38382
+ timestamp: globalState.timestamp ?? null,
38383
+ energy: {
38384
+ total: globalState.energy?.total ?? 0,
38385
+ entries: globalState.energy?.entries ?? 0,
38386
+ commonArea: globalState.energy?.commonArea ?? 0,
38387
+ stores: globalState.energy?.stores ?? 0
38388
+ },
38389
+ water: {
38390
+ total: globalState.water?.total ?? 0,
38391
+ entries: globalState.water?.entries ?? 0,
38392
+ commonArea: globalState.water?.commonArea ?? 0,
38393
+ stores: globalState.water?.stores ?? 0
38394
+ },
38395
+ temperature: {
38396
+ total: globalState.temperature?.total ?? 0,
38397
+ internal: globalState.temperature?.internal ?? 0,
38398
+ stores: globalState.temperature?.stores ?? 0
38399
+ },
38400
+ discrepancies: globalState.discrepancies
38401
+ };
38402
+ }
38403
+ };
38404
+
37397
38405
  // src/components/ModalHeader/index.ts
37398
38406
  var DEFAULT_BG_COLOR = "#3e1a7d";
37399
38407
  var DEFAULT_TEXT_COLOR = "white";
@@ -41158,6 +42166,7 @@ function createDistributionChartWidget(config) {
41158
42166
  CONSUMPTION_CHART_DEFAULTS,
41159
42167
  CONSUMPTION_THEME_COLORS,
41160
42168
  ConnectionStatusType,
42169
+ ContractSummaryTooltip,
41161
42170
  DEFAULT_CLAMP_RANGE,
41162
42171
  DEFAULT_ENERGY_GROUP_COLORS,
41163
42172
  DEFAULT_GAS_GROUP_COLORS,