myio-js-library 0.1.185 → 0.1.186

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
@@ -592,6 +592,7 @@ __export(index_exports, {
592
592
  IMPORTANCE_COLORS: () => IMPORTANCE_COLORS,
593
593
  IMPORTANCE_LABELS: () => IMPORTANCE_LABELS,
594
594
  IMPORTANCE_LABELS_EN: () => IMPORTANCE_LABELS_EN,
595
+ InfoTooltip: () => InfoTooltip,
595
596
  MyIOChartModal: () => MyIOChartModal,
596
597
  MyIODraggableCard: () => MyIODraggableCard,
597
598
  MyIOSelectionStore: () => MyIOSelectionStore,
@@ -9039,8 +9040,8 @@ function getStatusDotClass(deviceStatus) {
9039
9040
  return "dot--offline";
9040
9041
  }
9041
9042
  }
9042
- function buildDOM(state) {
9043
- const { entityObject, i18n, enableSelection, enableDragDrop } = state;
9043
+ function buildDOM(state2) {
9044
+ const { entityObject, i18n, enableSelection, enableDragDrop } = state2;
9044
9045
  const root = document.createElement("div");
9045
9046
  root.className = "myio-ho-card";
9046
9047
  root.setAttribute("role", "group");
@@ -9366,8 +9367,8 @@ function verifyOfflineStatus(entityObject, delayTimeInMins = 15, LogHelper2) {
9366
9367
  }
9367
9368
  return isOffline;
9368
9369
  }
9369
- function paint(root, state) {
9370
- const { entityObject, i18n, delayTimeConnectionInMins, isSelected, LogHelper: LogHelper2, activeTooltipDebug } = state;
9370
+ function paint(root, state2) {
9371
+ const { entityObject, i18n, delayTimeConnectionInMins, isSelected, LogHelper: LogHelper2, activeTooltipDebug } = state2;
9371
9372
  let statusDecisionSource = "unknown";
9372
9373
  if (entityObject.connectionStatus) {
9373
9374
  if (entityObject.connectionStatus === "offline") {
@@ -9419,7 +9420,7 @@ function paint(root, state) {
9419
9420
  numSpan.textContent = primaryValue;
9420
9421
  const barContainer = root.querySelector(".bar");
9421
9422
  const effContainer = root.querySelector(".myio-ho-card__eff");
9422
- if (state.enableSelection) {
9423
+ if (state2.enableSelection) {
9423
9424
  const checkbox = root.querySelector('.myio-ho-card__select input[type="checkbox"]');
9424
9425
  if (checkbox) {
9425
9426
  checkbox.checked = !!isSelected;
@@ -9467,8 +9468,8 @@ function paint(root, state) {
9467
9468
  statusDot.className = `status-dot ${dotClass}`;
9468
9469
  }
9469
9470
  }
9470
- function bindEvents(root, state, callbacks) {
9471
- const { entityObject } = state;
9471
+ function bindEvents(root, state2, callbacks) {
9472
+ const { entityObject } = state2;
9472
9473
  const kebabBtn = root.querySelector(".myio-ho-card__kebab");
9473
9474
  const menu = root.querySelector(".myio-ho-card__menu");
9474
9475
  function toggleMenu() {
@@ -9524,9 +9525,9 @@ function bindEvents(root, state, callbacks) {
9524
9525
  const onSelectionChange = () => {
9525
9526
  const selectedIds = MyIOSelectionStore2.getSelectedIds();
9526
9527
  const isSelected = selectedIds.includes(entityObject.entityId);
9527
- if (state.isSelected !== isSelected) {
9528
- state.isSelected = isSelected;
9529
- paint(root, state);
9528
+ if (state2.isSelected !== isSelected) {
9529
+ state2.isSelected = isSelected;
9530
+ paint(root, state2);
9530
9531
  }
9531
9532
  };
9532
9533
  MyIOSelectionStore2.on("selection:change", onSelectionChange);
@@ -9539,7 +9540,7 @@ function bindEvents(root, state, callbacks) {
9539
9540
  clearTimeout(TempRangeTooltip._hideTimer);
9540
9541
  TempRangeTooltip._hideTimer = null;
9541
9542
  }
9542
- TempRangeTooltip.show(root, state.entityObject, e);
9543
+ TempRangeTooltip.show(root, state2.entityObject, e);
9543
9544
  };
9544
9545
  const hideTooltip = () => {
9545
9546
  TempRangeTooltip._startDelayedHide();
@@ -9557,7 +9558,7 @@ function bindEvents(root, state, callbacks) {
9557
9558
  clearTimeout(EnergyRangeTooltip._hideTimer);
9558
9559
  EnergyRangeTooltip._hideTimer = null;
9559
9560
  }
9560
- EnergyRangeTooltip.show(root, state.entityObject, e);
9561
+ EnergyRangeTooltip.show(root, state2.entityObject, e);
9561
9562
  };
9562
9563
  const hideEnergyTooltip = () => {
9563
9564
  EnergyRangeTooltip._startDelayedHide();
@@ -9609,7 +9610,7 @@ function bindEvents(root, state, callbacks) {
9609
9610
  }
9610
9611
  });
9611
9612
  }
9612
- if (state.enableDragDrop) {
9613
+ if (state2.enableDragDrop) {
9613
9614
  root.addEventListener("dragstart", (e) => {
9614
9615
  root.classList.add("is-dragging");
9615
9616
  e.dataTransfer.setData("text/plain", entityObject.entityId);
@@ -9651,17 +9652,17 @@ function renderCardComponentHeadOffice(containerEl, params) {
9651
9652
  throw new Error("renderCardComponentHeadOffice: containerEl is required");
9652
9653
  }
9653
9654
  ensureCss();
9654
- const state = normalizeParams(params);
9655
- const root = buildDOM(state);
9656
- state.isSelected = params.isSelected || false;
9655
+ const state2 = normalizeParams(params);
9656
+ const root = buildDOM(state2);
9657
+ state2.isSelected = params.isSelected || false;
9657
9658
  containerEl.appendChild(root);
9658
- bindEvents(root, state, state.callbacks);
9659
- paint(root, state);
9659
+ bindEvents(root, state2, state2.callbacks);
9660
+ paint(root, state2);
9660
9661
  return {
9661
9662
  update(next) {
9662
9663
  if (next) {
9663
- Object.assign(state.entityObject, next);
9664
- paint(root, state);
9664
+ Object.assign(state2.entityObject, next);
9665
+ paint(root, state2);
9665
9666
  }
9666
9667
  },
9667
9668
  destroy() {
@@ -21144,15 +21145,45 @@ var PowerLimitsPersister = class {
21144
21145
  }
21145
21146
  }
21146
21147
  /**
21147
- * Save mapInstantaneousPower to customer server_scope attributes
21148
+ * Fetch child customer relations (level 1) from a parent customer
21149
+ * Returns array of child customer IDs
21148
21150
  */
21149
- async saveCustomerPowerLimits(customerId, limits) {
21151
+ async fetchChildCustomerIds(parentCustomerId) {
21152
+ try {
21153
+ const url = `${this.tbBaseUrl}/api/relations/info?fromId=${parentCustomerId}&fromType=CUSTOMER`;
21154
+ const response = await fetch(url, {
21155
+ method: "GET",
21156
+ headers: {
21157
+ "X-Authorization": `Bearer ${this.jwtToken}`,
21158
+ "Content-Type": "application/json"
21159
+ }
21160
+ });
21161
+ if (!response.ok) {
21162
+ console.warn("[PowerLimitsPersister] Failed to fetch relations:", response.status);
21163
+ return [];
21164
+ }
21165
+ const relations = await response.json();
21166
+ if (!Array.isArray(relations) || relations.length === 0) {
21167
+ console.log("[PowerLimitsPersister] No child customer relations found");
21168
+ return [];
21169
+ }
21170
+ const childCustomerIds = relations.filter((rel) => rel.to?.entityType === "CUSTOMER" && rel.to?.id).map((rel) => rel.to.id);
21171
+ console.log(`[PowerLimitsPersister] Found ${childCustomerIds.length} child customer(s)`);
21172
+ return childCustomerIds;
21173
+ } catch (error) {
21174
+ console.error("[PowerLimitsPersister] Error fetching relations:", error);
21175
+ return [];
21176
+ }
21177
+ }
21178
+ /**
21179
+ * Save power limits to a single customer (internal method)
21180
+ */
21181
+ async saveToSingleCustomer(customerId, limits) {
21150
21182
  try {
21151
21183
  const url = `${this.tbBaseUrl}/api/plugins/telemetry/CUSTOMER/${customerId}/attributes/SERVER_SCOPE`;
21152
21184
  const payload = {
21153
21185
  mapInstantaneousPower: limits
21154
21186
  };
21155
- console.log("[PowerLimitsPersister] Saving power limits:", payload);
21156
21187
  const response = await fetch(url, {
21157
21188
  method: "POST",
21158
21189
  headers: {
@@ -21162,10 +21193,41 @@ var PowerLimitsPersister = class {
21162
21193
  body: JSON.stringify(payload)
21163
21194
  });
21164
21195
  if (!response.ok) {
21165
- throw this.createHttpError(response.status, await response.text().catch(() => ""));
21196
+ console.warn(`[PowerLimitsPersister] Failed to save to customer ${customerId}:`, response.status);
21197
+ return false;
21166
21198
  }
21167
- console.log("[PowerLimitsPersister] Successfully saved power limits");
21168
- return { ok: true };
21199
+ return true;
21200
+ } catch (error) {
21201
+ console.error(`[PowerLimitsPersister] Error saving to customer ${customerId}:`, error);
21202
+ return false;
21203
+ }
21204
+ }
21205
+ /**
21206
+ * Save mapInstantaneousPower to customer server_scope attributes
21207
+ * Also propagates to all child customers (level 1 relations)
21208
+ */
21209
+ async saveCustomerPowerLimits(customerId, limits) {
21210
+ try {
21211
+ console.log("[PowerLimitsPersister] Saving power limits:", { customerId, limits });
21212
+ const mainSaveSuccess = await this.saveToSingleCustomer(customerId, limits);
21213
+ if (!mainSaveSuccess) {
21214
+ throw new Error("Failed to save to main customer");
21215
+ }
21216
+ console.log("[PowerLimitsPersister] Successfully saved to main customer");
21217
+ const childCustomerIds = await this.fetchChildCustomerIds(customerId);
21218
+ let successCount = 1;
21219
+ if (childCustomerIds.length > 0) {
21220
+ console.log(`[PowerLimitsPersister] Saving to ${childCustomerIds.length} child customer(s)...`);
21221
+ const savePromises = childCustomerIds.map(
21222
+ (childId) => this.saveToSingleCustomer(childId, limits)
21223
+ );
21224
+ const results = await Promise.all(savePromises);
21225
+ const childSuccessCount = results.filter(Boolean).length;
21226
+ successCount += childSuccessCount;
21227
+ console.log(`[PowerLimitsPersister] Saved to ${childSuccessCount}/${childCustomerIds.length} child customer(s)`);
21228
+ }
21229
+ console.log(`[PowerLimitsPersister] Total: saved to ${successCount} customer(s)`);
21230
+ return { ok: true, savedCount: successCount };
21169
21231
  } catch (error) {
21170
21232
  console.error("[PowerLimitsPersister] Error saving power limits:", error);
21171
21233
  return { ok: false, error: this.mapError(error) };
@@ -22355,6 +22417,136 @@ var AnnotationsTab = class {
22355
22417
  this.annotations = [];
22356
22418
  }
22357
22419
  }
22420
+ /**
22421
+ * Show a confirmation modal and return user's choice
22422
+ * Replaces native confirm() for better UX
22423
+ */
22424
+ showConfirmation(message, title = "Confirmar") {
22425
+ return new Promise((resolve) => {
22426
+ const overlay = document.createElement("div");
22427
+ overlay.className = "annotations-confirm-overlay";
22428
+ overlay.innerHTML = `
22429
+ <div class="annotations-confirm-modal">
22430
+ <div class="annotations-confirm-header">
22431
+ <span class="annotations-confirm-icon">\u26A0\uFE0F</span>
22432
+ <span class="annotations-confirm-title">${title}</span>
22433
+ </div>
22434
+ <div class="annotations-confirm-body">
22435
+ <p>${message}</p>
22436
+ </div>
22437
+ <div class="annotations-confirm-actions">
22438
+ <button class="annotations-confirm-btn annotations-confirm-btn--cancel" data-action="cancel">
22439
+ Cancelar
22440
+ </button>
22441
+ <button class="annotations-confirm-btn annotations-confirm-btn--confirm" data-action="confirm">
22442
+ Confirmar
22443
+ </button>
22444
+ </div>
22445
+ </div>
22446
+ `;
22447
+ if (!document.getElementById("annotations-confirm-styles")) {
22448
+ const style = document.createElement("style");
22449
+ style.id = "annotations-confirm-styles";
22450
+ style.textContent = `
22451
+ .annotations-confirm-overlay {
22452
+ position: fixed;
22453
+ inset: 0;
22454
+ background: rgba(0, 0, 0, 0.5);
22455
+ backdrop-filter: blur(4px);
22456
+ display: flex;
22457
+ align-items: center;
22458
+ justify-content: center;
22459
+ z-index: 100001;
22460
+ animation: confirmFadeIn 0.2s ease;
22461
+ }
22462
+ @keyframes confirmFadeIn {
22463
+ from { opacity: 0; }
22464
+ to { opacity: 1; }
22465
+ }
22466
+ .annotations-confirm-modal {
22467
+ background: white;
22468
+ border-radius: 12px;
22469
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
22470
+ max-width: 400px;
22471
+ width: 90%;
22472
+ overflow: hidden;
22473
+ animation: confirmSlideIn 0.25s ease;
22474
+ }
22475
+ @keyframes confirmSlideIn {
22476
+ from { transform: translateY(-20px) scale(0.95); opacity: 0; }
22477
+ to { transform: translateY(0) scale(1); opacity: 1; }
22478
+ }
22479
+ .annotations-confirm-header {
22480
+ display: flex;
22481
+ align-items: center;
22482
+ gap: 10px;
22483
+ padding: 16px 20px;
22484
+ background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);
22485
+ border-bottom: 1px solid #f59e0b;
22486
+ }
22487
+ .annotations-confirm-icon {
22488
+ font-size: 20px;
22489
+ }
22490
+ .annotations-confirm-title {
22491
+ font-weight: 600;
22492
+ color: #92400e;
22493
+ font-size: 16px;
22494
+ }
22495
+ .annotations-confirm-body {
22496
+ padding: 20px;
22497
+ }
22498
+ .annotations-confirm-body p {
22499
+ margin: 0;
22500
+ color: #374151;
22501
+ font-size: 14px;
22502
+ line-height: 1.5;
22503
+ }
22504
+ .annotations-confirm-actions {
22505
+ display: flex;
22506
+ gap: 12px;
22507
+ padding: 16px 20px;
22508
+ background: #f9fafb;
22509
+ border-top: 1px solid #e5e7eb;
22510
+ justify-content: flex-end;
22511
+ }
22512
+ .annotations-confirm-btn {
22513
+ padding: 10px 20px;
22514
+ border-radius: 8px;
22515
+ font-size: 14px;
22516
+ font-weight: 500;
22517
+ cursor: pointer;
22518
+ transition: all 0.2s ease;
22519
+ border: none;
22520
+ }
22521
+ .annotations-confirm-btn--cancel {
22522
+ background: #e5e7eb;
22523
+ color: #374151;
22524
+ }
22525
+ .annotations-confirm-btn--cancel:hover {
22526
+ background: #d1d5db;
22527
+ }
22528
+ .annotations-confirm-btn--confirm {
22529
+ background: #f59e0b;
22530
+ color: white;
22531
+ }
22532
+ .annotations-confirm-btn--confirm:hover {
22533
+ background: #d97706;
22534
+ }
22535
+ `;
22536
+ document.head.appendChild(style);
22537
+ }
22538
+ document.body.appendChild(overlay);
22539
+ const cleanup = (result) => {
22540
+ overlay.remove();
22541
+ resolve(result);
22542
+ };
22543
+ overlay.querySelector('[data-action="cancel"]')?.addEventListener("click", () => cleanup(false));
22544
+ overlay.querySelector('[data-action="confirm"]')?.addEventListener("click", () => cleanup(true));
22545
+ overlay.addEventListener("click", (e) => {
22546
+ if (e.target === overlay) cleanup(false);
22547
+ });
22548
+ });
22549
+ }
22358
22550
  async saveAnnotations() {
22359
22551
  try {
22360
22552
  const data = {
@@ -22418,7 +22610,7 @@ var AnnotationsTab = class {
22418
22610
  this.render();
22419
22611
  } else {
22420
22612
  this.annotations.shift();
22421
- alert("Erro ao salvar anota\xE7\xE3o. Tente novamente.");
22613
+ MyIOToast.show("Erro ao salvar anota\xE7\xE3o. Tente novamente.", "error");
22422
22614
  }
22423
22615
  }
22424
22616
  async editAnnotation(id, changes) {
@@ -22428,9 +22620,10 @@ var AnnotationsTab = class {
22428
22620
  const now = (/* @__PURE__ */ new Date()).toISOString();
22429
22621
  const changeRecord = {};
22430
22622
  for (const [key, value] of Object.entries(changes)) {
22431
- if (annotation[key] !== value) {
22623
+ const annotationAny = annotation;
22624
+ if (annotationAny[key] !== value) {
22432
22625
  changeRecord[key] = {
22433
- from: annotation[key],
22626
+ from: annotationAny[key],
22434
22627
  to: value
22435
22628
  };
22436
22629
  }
@@ -22455,7 +22648,7 @@ var AnnotationsTab = class {
22455
22648
  const success = await this.saveAnnotations();
22456
22649
  if (!success) {
22457
22650
  this.annotations[index] = annotation;
22458
- alert("Erro ao atualizar anota\xE7\xE3o. Tente novamente.");
22651
+ MyIOToast.show("Erro ao atualizar anota\xE7\xE3o. Tente novamente.", "error");
22459
22652
  }
22460
22653
  }
22461
22654
  async archiveAnnotation(id) {
@@ -22483,7 +22676,7 @@ var AnnotationsTab = class {
22483
22676
  this.render();
22484
22677
  } else {
22485
22678
  this.annotations[index] = annotation;
22486
- alert("Erro ao arquivar anota\xE7\xE3o. Tente novamente.");
22679
+ MyIOToast.show("Erro ao arquivar anota\xE7\xE3o. Tente novamente.", "error");
22487
22680
  }
22488
22681
  }
22489
22682
  async acknowledgeAnnotation(id) {
@@ -22998,8 +23191,8 @@ var AnnotationsTab = class {
22998
23191
  card.querySelector('[data-action="details"]')?.addEventListener("click", () => {
22999
23192
  this.showDetailModal(id);
23000
23193
  });
23001
- card.querySelector('[data-action="archive"]')?.addEventListener("click", () => {
23002
- if (confirm("Tem certeza que deseja arquivar esta anota\xE7\xE3o?")) {
23194
+ card.querySelector('[data-action="archive"]')?.addEventListener("click", async () => {
23195
+ if (await this.showConfirmation("Tem certeza que deseja arquivar esta anota\xE7\xE3o?", "Arquivar Anota\xE7\xE3o")) {
23003
23196
  this.archiveAnnotation(id);
23004
23197
  }
23005
23198
  });
@@ -23016,8 +23209,8 @@ var AnnotationsTab = class {
23016
23209
  row.querySelector('[data-action="details"]')?.addEventListener("click", () => {
23017
23210
  this.showDetailModal(id);
23018
23211
  });
23019
- row.querySelector('[data-action="archive"]')?.addEventListener("click", () => {
23020
- if (confirm("Tem certeza que deseja arquivar esta anota\xE7\xE3o?")) {
23212
+ row.querySelector('[data-action="archive"]')?.addEventListener("click", async () => {
23213
+ if (await this.showConfirmation("Tem certeza que deseja arquivar esta anota\xE7\xE3o?", "Arquivar Anota\xE7\xE3o")) {
23021
23214
  this.archiveAnnotation(id);
23022
23215
  }
23023
23216
  });
@@ -23026,12 +23219,189 @@ var AnnotationsTab = class {
23026
23219
  // ============================================
23027
23220
  // EDIT MODAL
23028
23221
  // ============================================
23029
- showEditModal(id) {
23222
+ /**
23223
+ * Show a styled input modal for editing annotation text
23224
+ * Replaces native prompt() for better UX
23225
+ */
23226
+ showInputModal(title, placeholder, initialValue) {
23227
+ return new Promise((resolve) => {
23228
+ const overlay = document.createElement("div");
23229
+ overlay.className = "annotations-input-overlay";
23230
+ overlay.innerHTML = `
23231
+ <div class="annotations-input-modal">
23232
+ <div class="annotations-input-header">
23233
+ <span class="annotations-input-icon">\u270F\uFE0F</span>
23234
+ <span class="annotations-input-title">${title}</span>
23235
+ </div>
23236
+ <div class="annotations-input-body">
23237
+ <textarea
23238
+ class="annotations-input-textarea"
23239
+ placeholder="${placeholder}"
23240
+ maxlength="255"
23241
+ >${initialValue}</textarea>
23242
+ <div class="annotations-input-char-count">
23243
+ <span id="input-char-count">${initialValue.length}</span> / 255
23244
+ </div>
23245
+ </div>
23246
+ <div class="annotations-input-actions">
23247
+ <button class="annotations-input-btn annotations-input-btn--cancel" data-action="cancel">
23248
+ Cancelar
23249
+ </button>
23250
+ <button class="annotations-input-btn annotations-input-btn--confirm" data-action="confirm">
23251
+ Salvar
23252
+ </button>
23253
+ </div>
23254
+ </div>
23255
+ `;
23256
+ if (!document.getElementById("annotations-input-styles")) {
23257
+ const style = document.createElement("style");
23258
+ style.id = "annotations-input-styles";
23259
+ style.textContent = `
23260
+ .annotations-input-overlay {
23261
+ position: fixed;
23262
+ inset: 0;
23263
+ background: rgba(0, 0, 0, 0.5);
23264
+ backdrop-filter: blur(4px);
23265
+ display: flex;
23266
+ align-items: center;
23267
+ justify-content: center;
23268
+ z-index: 100001;
23269
+ animation: inputFadeIn 0.2s ease;
23270
+ }
23271
+ @keyframes inputFadeIn {
23272
+ from { opacity: 0; }
23273
+ to { opacity: 1; }
23274
+ }
23275
+ .annotations-input-modal {
23276
+ background: white;
23277
+ border-radius: 12px;
23278
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
23279
+ max-width: 500px;
23280
+ width: 90%;
23281
+ overflow: hidden;
23282
+ animation: inputSlideIn 0.25s ease;
23283
+ }
23284
+ @keyframes inputSlideIn {
23285
+ from { transform: translateY(-20px) scale(0.95); opacity: 0; }
23286
+ to { transform: translateY(0) scale(1); opacity: 1; }
23287
+ }
23288
+ .annotations-input-header {
23289
+ display: flex;
23290
+ align-items: center;
23291
+ gap: 10px;
23292
+ padding: 16px 20px;
23293
+ background: linear-gradient(135deg, #6c5ce7 0%, #a29bfe 100%);
23294
+ border-bottom: 1px solid #5b4cdb;
23295
+ }
23296
+ .annotations-input-icon {
23297
+ font-size: 20px;
23298
+ }
23299
+ .annotations-input-title {
23300
+ font-weight: 600;
23301
+ color: white;
23302
+ font-size: 16px;
23303
+ }
23304
+ .annotations-input-body {
23305
+ padding: 20px;
23306
+ }
23307
+ .annotations-input-textarea {
23308
+ width: 100%;
23309
+ min-height: 100px;
23310
+ padding: 12px;
23311
+ border: 1px solid #dee2e6;
23312
+ border-radius: 8px;
23313
+ font-size: 14px;
23314
+ font-family: inherit;
23315
+ resize: vertical;
23316
+ color: #212529;
23317
+ }
23318
+ .annotations-input-textarea:focus {
23319
+ outline: none;
23320
+ border-color: #6c5ce7;
23321
+ box-shadow: 0 0 0 3px rgba(108, 92, 231, 0.15);
23322
+ }
23323
+ .annotations-input-char-count {
23324
+ font-size: 11px;
23325
+ color: #6c757d;
23326
+ text-align: right;
23327
+ margin-top: 6px;
23328
+ }
23329
+ .annotations-input-actions {
23330
+ display: flex;
23331
+ gap: 12px;
23332
+ padding: 16px 20px;
23333
+ background: #f9fafb;
23334
+ border-top: 1px solid #e5e7eb;
23335
+ justify-content: flex-end;
23336
+ }
23337
+ .annotations-input-btn {
23338
+ padding: 10px 20px;
23339
+ border-radius: 8px;
23340
+ font-size: 14px;
23341
+ font-weight: 500;
23342
+ cursor: pointer;
23343
+ transition: all 0.2s ease;
23344
+ border: none;
23345
+ }
23346
+ .annotations-input-btn--cancel {
23347
+ background: #e5e7eb;
23348
+ color: #374151;
23349
+ }
23350
+ .annotations-input-btn--cancel:hover {
23351
+ background: #d1d5db;
23352
+ }
23353
+ .annotations-input-btn--confirm {
23354
+ background: #6c5ce7;
23355
+ color: white;
23356
+ }
23357
+ .annotations-input-btn--confirm:hover {
23358
+ background: #5b4cdb;
23359
+ }
23360
+ `;
23361
+ document.head.appendChild(style);
23362
+ }
23363
+ document.body.appendChild(overlay);
23364
+ const textarea = overlay.querySelector(".annotations-input-textarea");
23365
+ const charCount = overlay.querySelector("#input-char-count");
23366
+ textarea.focus();
23367
+ textarea.select();
23368
+ textarea.addEventListener("input", () => {
23369
+ charCount.textContent = String(textarea.value.length);
23370
+ });
23371
+ const cleanup = (result) => {
23372
+ overlay.remove();
23373
+ resolve(result);
23374
+ };
23375
+ overlay.querySelector('[data-action="cancel"]')?.addEventListener("click", () => cleanup(null));
23376
+ overlay.querySelector('[data-action="confirm"]')?.addEventListener("click", () => {
23377
+ const value = textarea.value.trim();
23378
+ cleanup(value || null);
23379
+ });
23380
+ overlay.addEventListener("click", (e) => {
23381
+ if (e.target === overlay) cleanup(null);
23382
+ });
23383
+ textarea.addEventListener("keydown", (e) => {
23384
+ if (e.key === "Enter" && e.ctrlKey) {
23385
+ const value = textarea.value.trim();
23386
+ cleanup(value || null);
23387
+ }
23388
+ if (e.key === "Escape") {
23389
+ cleanup(null);
23390
+ }
23391
+ });
23392
+ });
23393
+ }
23394
+ async showEditModal(id) {
23030
23395
  const annotation = this.annotations.find((a) => a.id === id);
23031
23396
  if (!annotation) return;
23032
- const newText = prompt("Editar texto da anota\xE7\xE3o:", annotation.text);
23033
- if (newText && newText.trim() !== annotation.text) {
23034
- this.updateAnnotation(id, { text: newText.trim() });
23397
+ const newText = await this.showInputModal(
23398
+ "Editar Anota\xE7\xE3o",
23399
+ "Digite o novo texto da anota\xE7\xE3o...",
23400
+ annotation.text
23401
+ );
23402
+ if (newText && newText !== annotation.text) {
23403
+ await this.editAnnotation(id, { text: newText });
23404
+ this.render();
23035
23405
  }
23036
23406
  }
23037
23407
  // ============================================
@@ -23125,7 +23495,7 @@ var AnnotationsTab = class {
23125
23495
  overlay.querySelector(".annotation-detail__close")?.addEventListener("click", () => overlay.remove());
23126
23496
  overlay.querySelector('[data-action="close"]')?.addEventListener("click", () => overlay.remove());
23127
23497
  overlay.querySelector('[data-action="archive"]')?.addEventListener("click", async () => {
23128
- if (confirm("Tem certeza que deseja arquivar esta anota\xE7\xE3o?")) {
23498
+ if (await this.showConfirmation("Tem certeza que deseja arquivar esta anota\xE7\xE3o?", "Arquivar Anota\xE7\xE3o")) {
23129
23499
  overlay.remove();
23130
23500
  await this.archiveAnnotation(id);
23131
23501
  }
@@ -27690,7 +28060,7 @@ async function openTemperatureModal(params) {
27690
28060
  const defaultDateRange = getTodaySoFar();
27691
28061
  const startTs = params.startDate ? new Date(params.startDate).getTime() : defaultDateRange.startTs;
27692
28062
  const endTs = params.endDate ? new Date(params.endDate).getTime() : defaultDateRange.endTs;
27693
- const state = {
28063
+ const state2 = {
27694
28064
  token: params.token,
27695
28065
  deviceId: params.deviceId,
27696
28066
  label: params.label || "Sensor de Temperatura",
@@ -27713,58 +28083,58 @@ async function openTemperatureModal(params) {
27713
28083
  };
27714
28084
  const savedGranularity = localStorage.getItem("myio-temp-modal-granularity");
27715
28085
  const savedTheme = localStorage.getItem("myio-temp-modal-theme");
27716
- if (savedGranularity) state.granularity = savedGranularity;
27717
- if (savedTheme) state.theme = savedTheme;
28086
+ if (savedGranularity) state2.granularity = savedGranularity;
28087
+ if (savedTheme) state2.theme = savedTheme;
27718
28088
  const modalContainer = document.createElement("div");
27719
28089
  modalContainer.id = modalId;
27720
28090
  document.body.appendChild(modalContainer);
27721
- renderModal(modalContainer, state, modalId);
28091
+ renderModal(modalContainer, state2, modalId);
27722
28092
  try {
27723
- state.data = await fetchTemperatureData(state.token, state.deviceId, state.startTs, state.endTs);
27724
- state.stats = calculateStats(state.data, state.clampRange);
27725
- state.isLoading = false;
27726
- renderModal(modalContainer, state, modalId);
27727
- drawChart(modalId, state);
28093
+ state2.data = await fetchTemperatureData(state2.token, state2.deviceId, state2.startTs, state2.endTs);
28094
+ state2.stats = calculateStats(state2.data, state2.clampRange);
28095
+ state2.isLoading = false;
28096
+ renderModal(modalContainer, state2, modalId);
28097
+ drawChart(modalId, state2);
27728
28098
  } catch (error) {
27729
28099
  console.error("[TemperatureModal] Error fetching data:", error);
27730
- state.isLoading = false;
27731
- renderModal(modalContainer, state, modalId, error);
28100
+ state2.isLoading = false;
28101
+ renderModal(modalContainer, state2, modalId, error);
27732
28102
  }
27733
- await setupEventListeners(modalContainer, state, modalId, params.onClose);
28103
+ await setupEventListeners(modalContainer, state2, modalId, params.onClose);
27734
28104
  return {
27735
28105
  destroy: () => {
27736
28106
  modalContainer.remove();
27737
28107
  params.onClose?.();
27738
28108
  },
27739
28109
  updateData: async (startDate, endDate, granularity) => {
27740
- state.startTs = new Date(startDate).getTime();
27741
- state.endTs = new Date(endDate).getTime();
27742
- if (granularity) state.granularity = granularity;
27743
- state.isLoading = true;
27744
- renderModal(modalContainer, state, modalId);
28110
+ state2.startTs = new Date(startDate).getTime();
28111
+ state2.endTs = new Date(endDate).getTime();
28112
+ if (granularity) state2.granularity = granularity;
28113
+ state2.isLoading = true;
28114
+ renderModal(modalContainer, state2, modalId);
27745
28115
  try {
27746
- state.data = await fetchTemperatureData(state.token, state.deviceId, state.startTs, state.endTs);
27747
- state.stats = calculateStats(state.data, state.clampRange);
27748
- state.isLoading = false;
27749
- renderModal(modalContainer, state, modalId);
27750
- drawChart(modalId, state);
28116
+ state2.data = await fetchTemperatureData(state2.token, state2.deviceId, state2.startTs, state2.endTs);
28117
+ state2.stats = calculateStats(state2.data, state2.clampRange);
28118
+ state2.isLoading = false;
28119
+ renderModal(modalContainer, state2, modalId);
28120
+ drawChart(modalId, state2);
27751
28121
  } catch (error) {
27752
28122
  console.error("[TemperatureModal] Error updating data:", error);
27753
- state.isLoading = false;
27754
- renderModal(modalContainer, state, modalId, error);
28123
+ state2.isLoading = false;
28124
+ renderModal(modalContainer, state2, modalId, error);
27755
28125
  }
27756
28126
  }
27757
28127
  };
27758
28128
  }
27759
- function renderModal(container, state, modalId, error) {
27760
- const colors = getThemeColors(state.theme);
27761
- const startDateStr = new Date(state.startTs).toLocaleDateString(state.locale);
27762
- const endDateStr = new Date(state.endTs).toLocaleDateString(state.locale);
27763
- const statusText = state.temperatureStatus === "ok" ? "Dentro da faixa" : state.temperatureStatus === "above" ? "Acima do limite" : state.temperatureStatus === "below" ? "Abaixo do limite" : "N/A";
27764
- const statusColor = state.temperatureStatus === "ok" ? colors.success : state.temperatureStatus === "above" ? colors.danger : state.temperatureStatus === "below" ? colors.primary : colors.textMuted;
27765
- const rangeText = state.temperatureMin !== null && state.temperatureMax !== null ? `${state.temperatureMin}\xB0C - ${state.temperatureMax}\xB0C` : "N\xE3o definida";
27766
- const startDateInput = new Date(state.startTs).toISOString().slice(0, 16);
27767
- const endDateInput = new Date(state.endTs).toISOString().slice(0, 16);
28129
+ function renderModal(container, state2, modalId, error) {
28130
+ const colors = getThemeColors(state2.theme);
28131
+ const startDateStr = new Date(state2.startTs).toLocaleDateString(state2.locale);
28132
+ const endDateStr = new Date(state2.endTs).toLocaleDateString(state2.locale);
28133
+ const statusText = state2.temperatureStatus === "ok" ? "Dentro da faixa" : state2.temperatureStatus === "above" ? "Acima do limite" : state2.temperatureStatus === "below" ? "Abaixo do limite" : "N/A";
28134
+ const statusColor = state2.temperatureStatus === "ok" ? colors.success : state2.temperatureStatus === "above" ? colors.danger : state2.temperatureStatus === "below" ? colors.primary : colors.textMuted;
28135
+ const rangeText = state2.temperatureMin !== null && state2.temperatureMax !== null ? `${state2.temperatureMin}\xB0C - ${state2.temperatureMax}\xB0C` : "N\xE3o definida";
28136
+ const startDateInput = new Date(state2.startTs).toISOString().slice(0, 16);
28137
+ const endDateInput = new Date(state2.endTs).toISOString().slice(0, 16);
27768
28138
  const isMaximized = container.__isMaximized || false;
27769
28139
  const contentMaxWidth = isMaximized ? "100%" : "900px";
27770
28140
  const contentMaxHeight = isMaximized ? "100vh" : "95vh";
@@ -27791,7 +28161,7 @@ function renderModal(container, state, modalId, error) {
27791
28161
  min-height: 20px;
27792
28162
  ">
27793
28163
  <h2 style="margin: 6px; font-size: 18px; font-weight: 600; color: white; line-height: 2;">
27794
- \u{1F321}\uFE0F ${state.label} - Hist\xF3rico de Temperatura
28164
+ \u{1F321}\uFE0F ${state2.label} - Hist\xF3rico de Temperatura
27795
28165
  </h2>
27796
28166
  <div style="display: flex; gap: 4px; align-items: center;">
27797
28167
  <!-- Theme Toggle -->
@@ -27799,7 +28169,7 @@ function renderModal(container, state, modalId, error) {
27799
28169
  background: none; border: none; font-size: 16px; cursor: pointer;
27800
28170
  padding: 4px 8px; border-radius: 6px; color: rgba(255,255,255,0.8);
27801
28171
  transition: background-color 0.2s;
27802
- ">${state.theme === "dark" ? "\u2600\uFE0F" : "\u{1F319}"}</button>
28172
+ ">${state2.theme === "dark" ? "\u2600\uFE0F" : "\u{1F319}"}</button>
27803
28173
  <!-- Maximize Button -->
27804
28174
  <button id="${modalId}-maximize" title="${isMaximized ? "Restaurar" : "Maximizar"}" style="
27805
28175
  background: none; border: none; font-size: 16px; cursor: pointer;
@@ -27821,7 +28191,7 @@ function renderModal(container, state, modalId, error) {
27821
28191
  <!-- Controls Row -->
27822
28192
  <div style="
27823
28193
  display: flex; gap: 16px; flex-wrap: wrap; align-items: flex-end;
27824
- margin-bottom: 16px; padding: 16px; background: ${state.theme === "dark" ? "rgba(255,255,255,0.05)" : "#f7f7f7"};
28194
+ margin-bottom: 16px; padding: 16px; background: ${state2.theme === "dark" ? "rgba(255,255,255,0.05)" : "#f7f7f7"};
27825
28195
  border-radius: 6px; border: 1px solid ${colors.border};
27826
28196
  ">
27827
28197
  <!-- Granularity Select -->
@@ -27834,8 +28204,8 @@ function renderModal(container, state, modalId, error) {
27834
28204
  font-size: 14px; color: ${colors.text}; background: ${colors.surface};
27835
28205
  cursor: pointer; min-width: 130px;
27836
28206
  ">
27837
- <option value="hour" ${state.granularity === "hour" ? "selected" : ""}>Hora (30 min)</option>
27838
- <option value="day" ${state.granularity === "day" ? "selected" : ""}>Dia (m\xE9dia)</option>
28207
+ <option value="hour" ${state2.granularity === "hour" ? "selected" : ""}>Hora (30 min)</option>
28208
+ <option value="day" ${state2.granularity === "day" ? "selected" : ""}>Dia (m\xE9dia)</option>
27839
28209
  </select>
27840
28210
  </div>
27841
28211
  <!-- Day Period Filter (Multiselect) -->
@@ -27849,7 +28219,7 @@ function renderModal(container, state, modalId, error) {
27849
28219
  cursor: pointer; min-width: 180px; text-align: left;
27850
28220
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
27851
28221
  ">
27852
- <span>${getSelectedPeriodsLabel(state.selectedPeriods)}</span>
28222
+ <span>${getSelectedPeriodsLabel(state2.selectedPeriods)}</span>
27853
28223
  <span style="font-size: 10px;">\u25BC</span>
27854
28224
  </button>
27855
28225
  <div id="${modalId}-period-dropdown" style="
@@ -27862,12 +28232,12 @@ function renderModal(container, state, modalId, error) {
27862
28232
  <label style="
27863
28233
  display: flex; align-items: center; gap: 8px; padding: 8px 12px;
27864
28234
  cursor: pointer; font-size: 13px; color: ${colors.text};
27865
- " onmouseover="this.style.background='${state.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"}'"
28235
+ " onmouseover="this.style.background='${state2.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"}'"
27866
28236
  onmouseout="this.style.background='transparent'">
27867
28237
  <input type="checkbox"
27868
28238
  name="${modalId}-period"
27869
28239
  value="${period.id}"
27870
- ${state.selectedPeriods.includes(period.id) ? "checked" : ""}
28240
+ ${state2.selectedPeriods.includes(period.id) ? "checked" : ""}
27871
28241
  style="width: 16px; height: 16px; cursor: pointer; accent-color: #3e1a7d;">
27872
28242
  ${period.label}
27873
28243
  </label>
@@ -27875,13 +28245,13 @@ function renderModal(container, state, modalId, error) {
27875
28245
  <div style="border-top: 1px solid ${colors.border}; margin-top: 8px; padding-top: 8px;">
27876
28246
  <button id="${modalId}-period-select-all" type="button" style="
27877
28247
  width: calc(100% - 16px); margin: 0 8px 4px; padding: 6px;
27878
- background: ${state.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"};
28248
+ background: ${state2.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"};
27879
28249
  border: none; border-radius: 4px; cursor: pointer;
27880
28250
  font-size: 12px; color: ${colors.text};
27881
28251
  ">Selecionar Todos</button>
27882
28252
  <button id="${modalId}-period-clear" type="button" style="
27883
28253
  width: calc(100% - 16px); margin: 0 8px; padding: 6px;
27884
- background: ${state.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"};
28254
+ background: ${state2.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"};
27885
28255
  border: none; border-radius: 4px; cursor: pointer;
27886
28256
  font-size: 12px; color: ${colors.text};
27887
28257
  ">Limpar Sele\xE7\xE3o</button>
@@ -27906,8 +28276,8 @@ function renderModal(container, state, modalId, error) {
27906
28276
  font-size: 14px; font-weight: 500; height: 38px;
27907
28277
  display: flex; align-items: center; gap: 8px;
27908
28278
  font-family: 'Roboto', Arial, sans-serif;
27909
- " ${state.isLoading ? "disabled" : ""}>
27910
- ${state.isLoading ? '<span style="animation: spin 1s linear infinite; display: inline-block;">\u21BB</span> Carregando...' : "Carregar"}
28279
+ " ${state2.isLoading ? "disabled" : ""}>
28280
+ ${state2.isLoading ? '<span style="animation: spin 1s linear infinite; display: inline-block;">\u21BB</span> Carregando...' : "Carregar"}
27911
28281
  </button>
27912
28282
  </div>
27913
28283
 
@@ -27918,40 +28288,40 @@ function renderModal(container, state, modalId, error) {
27918
28288
  ">
27919
28289
  <!-- Current Temperature -->
27920
28290
  <div style="
27921
- padding: 16px; background: ${state.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
28291
+ padding: 16px; background: ${state2.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
27922
28292
  border-radius: 12px; border: 1px solid ${colors.border};
27923
28293
  ">
27924
28294
  <span style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500;">Temperatura Atual</span>
27925
28295
  <div style="font-weight: 700; font-size: 24px; color: ${statusColor}; margin-top: 4px;">
27926
- ${state.currentTemperature !== null ? formatTemperature(state.currentTemperature) : "N/A"}
28296
+ ${state2.currentTemperature !== null ? formatTemperature(state2.currentTemperature) : "N/A"}
27927
28297
  </div>
27928
28298
  <div style="font-size: 11px; color: ${statusColor}; margin-top: 2px;">${statusText}</div>
27929
28299
  </div>
27930
28300
  <!-- Average -->
27931
28301
  <div style="
27932
- padding: 16px; background: ${state.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
28302
+ padding: 16px; background: ${state2.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
27933
28303
  border-radius: 12px; border: 1px solid ${colors.border};
27934
28304
  ">
27935
28305
  <span style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500;">M\xE9dia do Per\xEDodo</span>
27936
28306
  <div id="${modalId}-avg" style="font-weight: 600; font-size: 20px; color: ${colors.text}; margin-top: 4px;">
27937
- ${state.stats.count > 0 ? formatTemperature(state.stats.avg) : "N/A"}
28307
+ ${state2.stats.count > 0 ? formatTemperature(state2.stats.avg) : "N/A"}
27938
28308
  </div>
27939
28309
  <div style="font-size: 11px; color: ${colors.textMuted}; margin-top: 2px;">${startDateStr} - ${endDateStr}</div>
27940
28310
  </div>
27941
28311
  <!-- Min/Max -->
27942
28312
  <div style="
27943
- padding: 16px; background: ${state.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
28313
+ padding: 16px; background: ${state2.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
27944
28314
  border-radius: 12px; border: 1px solid ${colors.border};
27945
28315
  ">
27946
28316
  <span style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500;">Min / Max</span>
27947
28317
  <div id="${modalId}-minmax" style="font-weight: 600; font-size: 20px; color: ${colors.text}; margin-top: 4px;">
27948
- ${state.stats.count > 0 ? `${formatTemperature(state.stats.min)} / ${formatTemperature(state.stats.max)}` : "N/A"}
28318
+ ${state2.stats.count > 0 ? `${formatTemperature(state2.stats.min)} / ${formatTemperature(state2.stats.max)}` : "N/A"}
27949
28319
  </div>
27950
- <div style="font-size: 11px; color: ${colors.textMuted}; margin-top: 2px;">${state.stats.count} leituras</div>
28320
+ <div style="font-size: 11px; color: ${colors.textMuted}; margin-top: 2px;">${state2.stats.count} leituras</div>
27951
28321
  </div>
27952
28322
  <!-- Ideal Range -->
27953
28323
  <div style="
27954
- padding: 16px; background: ${state.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
28324
+ padding: 16px; background: ${state2.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
27955
28325
  border-radius: 12px; border: 1px solid ${colors.border};
27956
28326
  ">
27957
28327
  <span style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500;">Faixa Ideal</span>
@@ -27967,18 +28337,18 @@ function renderModal(container, state, modalId, error) {
27967
28337
  Hist\xF3rico de Temperatura
27968
28338
  </h3>
27969
28339
  <div id="${modalId}-chart" style="
27970
- height: 320px; background: ${state.theme === "dark" ? "rgba(255,255,255,0.03)" : "#fafafa"};
28340
+ height: 320px; background: ${state2.theme === "dark" ? "rgba(255,255,255,0.03)" : "#fafafa"};
27971
28341
  border-radius: 12px; display: flex; justify-content: center; align-items: center;
27972
28342
  border: 1px solid ${colors.border}; position: relative;
27973
28343
  ">
27974
- ${state.isLoading ? `<div style="text-align: center; color: ${colors.textMuted};">
28344
+ ${state2.isLoading ? `<div style="text-align: center; color: ${colors.textMuted};">
27975
28345
  <div style="animation: spin 1s linear infinite; font-size: 32px; margin-bottom: 8px;">\u21BB</div>
27976
28346
  <div>Carregando dados...</div>
27977
28347
  </div>` : error ? `<div style="text-align: center; color: ${colors.danger};">
27978
28348
  <div style="font-size: 32px; margin-bottom: 8px;">\u26A0\uFE0F</div>
27979
28349
  <div>Erro ao carregar dados</div>
27980
28350
  <div style="font-size: 12px; margin-top: 4px;">${error.message}</div>
27981
- </div>` : state.data.length === 0 ? `<div style="text-align: center; color: ${colors.textMuted};">
28351
+ </div>` : state2.data.length === 0 ? `<div style="text-align: center; color: ${colors.textMuted};">
27982
28352
  <div style="font-size: 32px; margin-bottom: 8px;">\u{1F4ED}</div>
27983
28353
  <div>Sem dados para o per\xEDodo selecionado</div>
27984
28354
  </div>` : `<canvas id="${modalId}-canvas" style="width: 100%; height: 100%;"></canvas>`}
@@ -27988,12 +28358,12 @@ function renderModal(container, state, modalId, error) {
27988
28358
  <!-- Actions -->
27989
28359
  <div style="display: flex; justify-content: flex-end; gap: 12px;">
27990
28360
  <button id="${modalId}-export" style="
27991
- background: ${state.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f7f7f7"};
28361
+ background: ${state2.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f7f7f7"};
27992
28362
  color: ${colors.text}; border: 1px solid ${colors.border};
27993
28363
  padding: 8px 16px; border-radius: 6px; cursor: pointer;
27994
28364
  font-size: 14px; display: flex; align-items: center; gap: 8px;
27995
28365
  font-family: 'Roboto', Arial, sans-serif;
27996
- " ${state.data.length === 0 ? "disabled" : ""}>
28366
+ " ${state2.data.length === 0 ? "disabled" : ""}>
27997
28367
  \u{1F4E5} Exportar CSV
27998
28368
  </button>
27999
28369
  <button id="${modalId}-close-btn" style="
@@ -28044,14 +28414,14 @@ function renderModal(container, state, modalId, error) {
28044
28414
  </style>
28045
28415
  `;
28046
28416
  }
28047
- function drawChart(modalId, state) {
28417
+ function drawChart(modalId, state2) {
28048
28418
  const chartContainer = document.getElementById(`${modalId}-chart`);
28049
28419
  const canvas = document.getElementById(`${modalId}-canvas`);
28050
- if (!chartContainer || !canvas || state.data.length === 0) return;
28420
+ if (!chartContainer || !canvas || state2.data.length === 0) return;
28051
28421
  const ctx = canvas.getContext("2d");
28052
28422
  if (!ctx) return;
28053
- const colors = getThemeColors(state.theme);
28054
- const filteredData = filterByDayPeriods(state.data, state.selectedPeriods);
28423
+ const colors = getThemeColors(state2.theme);
28424
+ const filteredData = filterByDayPeriods(state2.data, state2.selectedPeriods);
28055
28425
  if (filteredData.length === 0) {
28056
28426
  canvas.width = chartContainer.clientWidth;
28057
28427
  canvas.height = chartContainer.clientHeight;
@@ -28062,14 +28432,14 @@ function drawChart(modalId, state) {
28062
28432
  return;
28063
28433
  }
28064
28434
  let chartData;
28065
- if (state.granularity === "hour") {
28435
+ if (state2.granularity === "hour") {
28066
28436
  const interpolated = interpolateTemperature(filteredData, {
28067
28437
  intervalMinutes: 30,
28068
- startTs: state.startTs,
28069
- endTs: state.endTs,
28070
- clampRange: state.clampRange
28438
+ startTs: state2.startTs,
28439
+ endTs: state2.endTs,
28440
+ clampRange: state2.clampRange
28071
28441
  });
28072
- const filteredInterpolated = filterByDayPeriods(interpolated, state.selectedPeriods);
28442
+ const filteredInterpolated = filterByDayPeriods(interpolated, state2.selectedPeriods);
28073
28443
  chartData = filteredInterpolated.map((item) => ({
28074
28444
  x: item.ts,
28075
28445
  y: Number(item.value),
@@ -28077,7 +28447,7 @@ function drawChart(modalId, state) {
28077
28447
  screenY: 0
28078
28448
  }));
28079
28449
  } else {
28080
- const daily = aggregateByDay(filteredData, state.clampRange);
28450
+ const daily = aggregateByDay(filteredData, state2.clampRange);
28081
28451
  chartData = daily.map((item) => ({
28082
28452
  x: item.dateTs,
28083
28453
  y: item.avg,
@@ -28095,12 +28465,12 @@ function drawChart(modalId, state) {
28095
28465
  const paddingRight = 20;
28096
28466
  const paddingTop = 20;
28097
28467
  const paddingBottom = 55;
28098
- const isPeriodsFiltered = state.selectedPeriods.length < 4 && state.selectedPeriods.length > 0;
28468
+ const isPeriodsFiltered = state2.selectedPeriods.length < 4 && state2.selectedPeriods.length > 0;
28099
28469
  const values = chartData.map((d) => d.y);
28100
28470
  const dataMin = Math.min(...values);
28101
28471
  const dataMax = Math.max(...values);
28102
- const thresholdMin = state.temperatureMin !== null ? state.temperatureMin : dataMin;
28103
- const thresholdMax = state.temperatureMax !== null ? state.temperatureMax : dataMax;
28472
+ const thresholdMin = state2.temperatureMin !== null ? state2.temperatureMin : dataMin;
28473
+ const thresholdMax = state2.temperatureMax !== null ? state2.temperatureMax : dataMax;
28104
28474
  const minY = Math.min(dataMin, thresholdMin) - 1;
28105
28475
  const maxY = Math.max(dataMax, thresholdMax) + 1;
28106
28476
  const chartWidth = width - paddingLeft - paddingRight;
@@ -28132,9 +28502,9 @@ function drawChart(modalId, state) {
28132
28502
  ctx.lineTo(width - paddingRight, y);
28133
28503
  ctx.stroke();
28134
28504
  }
28135
- if (state.temperatureMin !== null && state.temperatureMax !== null) {
28136
- const rangeMinY = height - paddingBottom - (state.temperatureMin - minY) * scaleY;
28137
- const rangeMaxY = height - paddingBottom - (state.temperatureMax - minY) * scaleY;
28505
+ if (state2.temperatureMin !== null && state2.temperatureMax !== null) {
28506
+ const rangeMinY = height - paddingBottom - (state2.temperatureMin - minY) * scaleY;
28507
+ const rangeMaxY = height - paddingBottom - (state2.temperatureMax - minY) * scaleY;
28138
28508
  ctx.fillStyle = "rgba(76, 175, 80, 0.1)";
28139
28509
  ctx.fillRect(paddingLeft, rangeMaxY, chartWidth, rangeMinY - rangeMaxY);
28140
28510
  ctx.strokeStyle = colors.success;
@@ -28176,10 +28546,10 @@ function drawChart(modalId, state) {
28176
28546
  const point = chartData[i];
28177
28547
  const date = new Date(point.x);
28178
28548
  let label;
28179
- if (state.granularity === "hour") {
28180
- label = date.toLocaleTimeString(state.locale, { hour: "2-digit", minute: "2-digit" });
28549
+ if (state2.granularity === "hour") {
28550
+ label = date.toLocaleTimeString(state2.locale, { hour: "2-digit", minute: "2-digit" });
28181
28551
  } else {
28182
- label = date.toLocaleDateString(state.locale, { day: "2-digit", month: "2-digit" });
28552
+ label = date.toLocaleDateString(state2.locale, { day: "2-digit", month: "2-digit" });
28183
28553
  }
28184
28554
  ctx.strokeStyle = colors.chartGrid;
28185
28555
  ctx.lineWidth = 1;
@@ -28197,16 +28567,16 @@ function drawChart(modalId, state) {
28197
28567
  ctx.lineTo(paddingLeft, height - paddingBottom);
28198
28568
  ctx.lineTo(width - paddingRight, height - paddingBottom);
28199
28569
  ctx.stroke();
28200
- setupChartTooltip(canvas, chartContainer, chartData, state, colors);
28570
+ setupChartTooltip(canvas, chartContainer, chartData, state2, colors);
28201
28571
  }
28202
- function setupChartTooltip(canvas, container, chartData, state, colors) {
28572
+ function setupChartTooltip(canvas, container, chartData, state2, colors) {
28203
28573
  const existingTooltip = container.querySelector(".myio-chart-tooltip");
28204
28574
  if (existingTooltip) existingTooltip.remove();
28205
28575
  const tooltip = document.createElement("div");
28206
28576
  tooltip.className = "myio-chart-tooltip";
28207
28577
  tooltip.style.cssText = `
28208
28578
  position: absolute;
28209
- background: ${state.theme === "dark" ? "rgba(30, 30, 40, 0.95)" : "rgba(255, 255, 255, 0.98)"};
28579
+ background: ${state2.theme === "dark" ? "rgba(30, 30, 40, 0.95)" : "rgba(255, 255, 255, 0.98)"};
28210
28580
  border: 1px solid ${colors.border};
28211
28581
  border-radius: 8px;
28212
28582
  padding: 10px 14px;
@@ -28243,17 +28613,17 @@ function setupChartTooltip(canvas, container, chartData, state, colors) {
28243
28613
  if (point) {
28244
28614
  const date = new Date(point.x);
28245
28615
  let dateStr;
28246
- if (state.granularity === "hour") {
28247
- dateStr = date.toLocaleDateString(state.locale, {
28616
+ if (state2.granularity === "hour") {
28617
+ dateStr = date.toLocaleDateString(state2.locale, {
28248
28618
  day: "2-digit",
28249
28619
  month: "2-digit",
28250
28620
  year: "numeric"
28251
- }) + " " + date.toLocaleTimeString(state.locale, {
28621
+ }) + " " + date.toLocaleTimeString(state2.locale, {
28252
28622
  hour: "2-digit",
28253
28623
  minute: "2-digit"
28254
28624
  });
28255
28625
  } else {
28256
- dateStr = date.toLocaleDateString(state.locale, {
28626
+ dateStr = date.toLocaleDateString(state2.locale, {
28257
28627
  day: "2-digit",
28258
28628
  month: "2-digit",
28259
28629
  year: "numeric"
@@ -28291,7 +28661,7 @@ function setupChartTooltip(canvas, container, chartData, state, colors) {
28291
28661
  canvas.style.cursor = "default";
28292
28662
  });
28293
28663
  }
28294
- async function setupEventListeners(container, state, modalId, onClose) {
28664
+ async function setupEventListeners(container, state2, modalId, onClose) {
28295
28665
  const closeModal = () => {
28296
28666
  container.remove();
28297
28667
  onClose?.();
@@ -28302,19 +28672,19 @@ async function setupEventListeners(container, state, modalId, onClose) {
28302
28672
  document.getElementById(`${modalId}-close`)?.addEventListener("click", closeModal);
28303
28673
  document.getElementById(`${modalId}-close-btn`)?.addEventListener("click", closeModal);
28304
28674
  const dateRangeInput = document.getElementById(`${modalId}-date-range`);
28305
- if (dateRangeInput && !state.dateRangePicker) {
28675
+ if (dateRangeInput && !state2.dateRangePicker) {
28306
28676
  try {
28307
- state.dateRangePicker = await createDateRangePicker2(dateRangeInput, {
28308
- presetStart: new Date(state.startTs).toISOString(),
28309
- presetEnd: new Date(state.endTs).toISOString(),
28677
+ state2.dateRangePicker = await createDateRangePicker2(dateRangeInput, {
28678
+ presetStart: new Date(state2.startTs).toISOString(),
28679
+ presetEnd: new Date(state2.endTs).toISOString(),
28310
28680
  includeTime: true,
28311
28681
  timePrecision: "minute",
28312
28682
  maxRangeDays: 90,
28313
- locale: state.locale,
28683
+ locale: state2.locale,
28314
28684
  parentEl: container.querySelector(".myio-temp-modal-content"),
28315
28685
  onApply: (result) => {
28316
- state.startTs = new Date(result.startISO).getTime();
28317
- state.endTs = new Date(result.endISO).getTime();
28686
+ state2.startTs = new Date(result.startISO).getTime();
28687
+ state2.endTs = new Date(result.endISO).getTime();
28318
28688
  console.log("[TemperatureModal] Date range applied:", result);
28319
28689
  }
28320
28690
  });
@@ -28323,19 +28693,19 @@ async function setupEventListeners(container, state, modalId, onClose) {
28323
28693
  }
28324
28694
  }
28325
28695
  document.getElementById(`${modalId}-theme-toggle`)?.addEventListener("click", async () => {
28326
- state.theme = state.theme === "dark" ? "light" : "dark";
28327
- localStorage.setItem("myio-temp-modal-theme", state.theme);
28328
- state.dateRangePicker = null;
28329
- renderModal(container, state, modalId);
28330
- if (state.data.length > 0) drawChart(modalId, state);
28331
- await setupEventListeners(container, state, modalId, onClose);
28696
+ state2.theme = state2.theme === "dark" ? "light" : "dark";
28697
+ localStorage.setItem("myio-temp-modal-theme", state2.theme);
28698
+ state2.dateRangePicker = null;
28699
+ renderModal(container, state2, modalId);
28700
+ if (state2.data.length > 0) drawChart(modalId, state2);
28701
+ await setupEventListeners(container, state2, modalId, onClose);
28332
28702
  });
28333
28703
  document.getElementById(`${modalId}-maximize`)?.addEventListener("click", async () => {
28334
28704
  container.__isMaximized = !container.__isMaximized;
28335
- state.dateRangePicker = null;
28336
- renderModal(container, state, modalId);
28337
- if (state.data.length > 0) drawChart(modalId, state);
28338
- await setupEventListeners(container, state, modalId, onClose);
28705
+ state2.dateRangePicker = null;
28706
+ renderModal(container, state2, modalId);
28707
+ if (state2.data.length > 0) drawChart(modalId, state2);
28708
+ await setupEventListeners(container, state2, modalId, onClose);
28339
28709
  });
28340
28710
  const periodBtn = document.getElementById(`${modalId}-period-btn`);
28341
28711
  const periodDropdown = document.getElementById(`${modalId}-period-dropdown`);
@@ -28354,71 +28724,71 @@ async function setupEventListeners(container, state, modalId, onClose) {
28354
28724
  periodCheckboxes.forEach((checkbox) => {
28355
28725
  checkbox.addEventListener("change", () => {
28356
28726
  const checked = Array.from(periodCheckboxes).filter((cb) => cb.checked).map((cb) => cb.value);
28357
- state.selectedPeriods = checked;
28727
+ state2.selectedPeriods = checked;
28358
28728
  const btnLabel = periodBtn?.querySelector("span:first-child");
28359
28729
  if (btnLabel) {
28360
- btnLabel.textContent = getSelectedPeriodsLabel(state.selectedPeriods);
28730
+ btnLabel.textContent = getSelectedPeriodsLabel(state2.selectedPeriods);
28361
28731
  }
28362
- if (state.data.length > 0) drawChart(modalId, state);
28732
+ if (state2.data.length > 0) drawChart(modalId, state2);
28363
28733
  });
28364
28734
  });
28365
28735
  document.getElementById(`${modalId}-period-select-all`)?.addEventListener("click", () => {
28366
28736
  periodCheckboxes.forEach((cb) => {
28367
28737
  cb.checked = true;
28368
28738
  });
28369
- state.selectedPeriods = ["madrugada", "manha", "tarde", "noite"];
28739
+ state2.selectedPeriods = ["madrugada", "manha", "tarde", "noite"];
28370
28740
  const btnLabel = periodBtn?.querySelector("span:first-child");
28371
28741
  if (btnLabel) {
28372
- btnLabel.textContent = getSelectedPeriodsLabel(state.selectedPeriods);
28742
+ btnLabel.textContent = getSelectedPeriodsLabel(state2.selectedPeriods);
28373
28743
  }
28374
- if (state.data.length > 0) drawChart(modalId, state);
28744
+ if (state2.data.length > 0) drawChart(modalId, state2);
28375
28745
  });
28376
28746
  document.getElementById(`${modalId}-period-clear`)?.addEventListener("click", () => {
28377
28747
  periodCheckboxes.forEach((cb) => {
28378
28748
  cb.checked = false;
28379
28749
  });
28380
- state.selectedPeriods = [];
28750
+ state2.selectedPeriods = [];
28381
28751
  const btnLabel = periodBtn?.querySelector("span:first-child");
28382
28752
  if (btnLabel) {
28383
- btnLabel.textContent = getSelectedPeriodsLabel(state.selectedPeriods);
28753
+ btnLabel.textContent = getSelectedPeriodsLabel(state2.selectedPeriods);
28384
28754
  }
28385
- if (state.data.length > 0) drawChart(modalId, state);
28755
+ if (state2.data.length > 0) drawChart(modalId, state2);
28386
28756
  });
28387
28757
  document.getElementById(`${modalId}-granularity`)?.addEventListener("change", (e) => {
28388
- state.granularity = e.target.value;
28389
- localStorage.setItem("myio-temp-modal-granularity", state.granularity);
28390
- if (state.data.length > 0) drawChart(modalId, state);
28758
+ state2.granularity = e.target.value;
28759
+ localStorage.setItem("myio-temp-modal-granularity", state2.granularity);
28760
+ if (state2.data.length > 0) drawChart(modalId, state2);
28391
28761
  });
28392
28762
  document.getElementById(`${modalId}-query`)?.addEventListener("click", async () => {
28393
- if (state.startTs >= state.endTs) {
28763
+ if (state2.startTs >= state2.endTs) {
28394
28764
  alert("Por favor, selecione um per\xEDodo v\xE1lido");
28395
28765
  return;
28396
28766
  }
28397
- state.isLoading = true;
28398
- state.dateRangePicker = null;
28399
- renderModal(container, state, modalId);
28767
+ state2.isLoading = true;
28768
+ state2.dateRangePicker = null;
28769
+ renderModal(container, state2, modalId);
28400
28770
  try {
28401
- state.data = await fetchTemperatureData(state.token, state.deviceId, state.startTs, state.endTs);
28402
- state.stats = calculateStats(state.data, state.clampRange);
28403
- state.isLoading = false;
28404
- renderModal(container, state, modalId);
28405
- drawChart(modalId, state);
28406
- await setupEventListeners(container, state, modalId, onClose);
28771
+ state2.data = await fetchTemperatureData(state2.token, state2.deviceId, state2.startTs, state2.endTs);
28772
+ state2.stats = calculateStats(state2.data, state2.clampRange);
28773
+ state2.isLoading = false;
28774
+ renderModal(container, state2, modalId);
28775
+ drawChart(modalId, state2);
28776
+ await setupEventListeners(container, state2, modalId, onClose);
28407
28777
  } catch (error) {
28408
28778
  console.error("[TemperatureModal] Error fetching data:", error);
28409
- state.isLoading = false;
28410
- renderModal(container, state, modalId, error);
28411
- await setupEventListeners(container, state, modalId, onClose);
28779
+ state2.isLoading = false;
28780
+ renderModal(container, state2, modalId, error);
28781
+ await setupEventListeners(container, state2, modalId, onClose);
28412
28782
  }
28413
28783
  });
28414
28784
  document.getElementById(`${modalId}-export`)?.addEventListener("click", () => {
28415
- if (state.data.length === 0) return;
28416
- const startDateStr = new Date(state.startTs).toLocaleDateString(state.locale).replace(/\//g, "-");
28417
- const endDateStr = new Date(state.endTs).toLocaleDateString(state.locale).replace(/\//g, "-");
28785
+ if (state2.data.length === 0) return;
28786
+ const startDateStr = new Date(state2.startTs).toLocaleDateString(state2.locale).replace(/\//g, "-");
28787
+ const endDateStr = new Date(state2.endTs).toLocaleDateString(state2.locale).replace(/\//g, "-");
28418
28788
  exportTemperatureCSV(
28419
- state.data,
28420
- state.label,
28421
- state.stats,
28789
+ state2.data,
28790
+ state2.label,
28791
+ state2.stats,
28422
28792
  startDateStr,
28423
28793
  endDateStr
28424
28794
  );
@@ -28431,7 +28801,7 @@ async function openTemperatureComparisonModal(params) {
28431
28801
  const defaultDateRange = getTodaySoFar();
28432
28802
  const startTs = params.startDate ? new Date(params.startDate).getTime() : defaultDateRange.startTs;
28433
28803
  const endTs = params.endDate ? new Date(params.endDate).getTime() : defaultDateRange.endTs;
28434
- const state = {
28804
+ const state2 = {
28435
28805
  token: params.token,
28436
28806
  devices: params.devices,
28437
28807
  startTs,
@@ -28450,44 +28820,44 @@ async function openTemperatureComparisonModal(params) {
28450
28820
  };
28451
28821
  const savedGranularity = localStorage.getItem("myio-temp-comparison-granularity");
28452
28822
  const savedTheme = localStorage.getItem("myio-temp-comparison-theme");
28453
- if (savedGranularity) state.granularity = savedGranularity;
28454
- if (savedTheme) state.theme = savedTheme;
28823
+ if (savedGranularity) state2.granularity = savedGranularity;
28824
+ if (savedTheme) state2.theme = savedTheme;
28455
28825
  const modalContainer = document.createElement("div");
28456
28826
  modalContainer.id = modalId;
28457
28827
  document.body.appendChild(modalContainer);
28458
- renderModal2(modalContainer, state, modalId);
28459
- await fetchAllDevicesData(state);
28460
- renderModal2(modalContainer, state, modalId);
28461
- drawComparisonChart(modalId, state);
28462
- await setupEventListeners2(modalContainer, state, modalId, params.onClose);
28828
+ renderModal2(modalContainer, state2, modalId);
28829
+ await fetchAllDevicesData(state2);
28830
+ renderModal2(modalContainer, state2, modalId);
28831
+ drawComparisonChart(modalId, state2);
28832
+ await setupEventListeners2(modalContainer, state2, modalId, params.onClose);
28463
28833
  return {
28464
28834
  destroy: () => {
28465
28835
  modalContainer.remove();
28466
28836
  params.onClose?.();
28467
28837
  },
28468
28838
  updateData: async (startDate, endDate, granularity) => {
28469
- state.startTs = new Date(startDate).getTime();
28470
- state.endTs = new Date(endDate).getTime();
28471
- if (granularity) state.granularity = granularity;
28472
- state.isLoading = true;
28473
- renderModal2(modalContainer, state, modalId);
28474
- await fetchAllDevicesData(state);
28475
- renderModal2(modalContainer, state, modalId);
28476
- drawComparisonChart(modalId, state);
28477
- setupEventListeners2(modalContainer, state, modalId, params.onClose);
28839
+ state2.startTs = new Date(startDate).getTime();
28840
+ state2.endTs = new Date(endDate).getTime();
28841
+ if (granularity) state2.granularity = granularity;
28842
+ state2.isLoading = true;
28843
+ renderModal2(modalContainer, state2, modalId);
28844
+ await fetchAllDevicesData(state2);
28845
+ renderModal2(modalContainer, state2, modalId);
28846
+ drawComparisonChart(modalId, state2);
28847
+ setupEventListeners2(modalContainer, state2, modalId, params.onClose);
28478
28848
  }
28479
28849
  };
28480
28850
  }
28481
- async function fetchAllDevicesData(state) {
28482
- state.isLoading = true;
28483
- state.deviceData = [];
28851
+ async function fetchAllDevicesData(state2) {
28852
+ state2.isLoading = true;
28853
+ state2.deviceData = [];
28484
28854
  try {
28485
28855
  const results = await Promise.all(
28486
- state.devices.map(async (device, index) => {
28856
+ state2.devices.map(async (device, index) => {
28487
28857
  const deviceId = device.tbId || device.id;
28488
28858
  try {
28489
- const data = await fetchTemperatureData(state.token, deviceId, state.startTs, state.endTs);
28490
- const stats = calculateStats(data, state.clampRange);
28859
+ const data = await fetchTemperatureData(state2.token, deviceId, state2.startTs, state2.endTs);
28860
+ const stats = calculateStats(data, state2.clampRange);
28491
28861
  return {
28492
28862
  device,
28493
28863
  data,
@@ -28505,21 +28875,21 @@ async function fetchAllDevicesData(state) {
28505
28875
  }
28506
28876
  })
28507
28877
  );
28508
- state.deviceData = results;
28878
+ state2.deviceData = results;
28509
28879
  } catch (error) {
28510
28880
  console.error("[TemperatureComparisonModal] Error fetching data:", error);
28511
28881
  }
28512
- state.isLoading = false;
28882
+ state2.isLoading = false;
28513
28883
  }
28514
- function renderModal2(container, state, modalId) {
28515
- const colors = getThemeColors(state.theme);
28516
- const startDateStr = new Date(state.startTs).toLocaleDateString(state.locale);
28517
- const endDateStr = new Date(state.endTs).toLocaleDateString(state.locale);
28518
- const startDateInput = new Date(state.startTs).toISOString().slice(0, 16);
28519
- const endDateInput = new Date(state.endTs).toISOString().slice(0, 16);
28520
- const legendHTML = state.deviceData.map((dd) => `
28884
+ function renderModal2(container, state2, modalId) {
28885
+ const colors = getThemeColors(state2.theme);
28886
+ const startDateStr = new Date(state2.startTs).toLocaleDateString(state2.locale);
28887
+ const endDateStr = new Date(state2.endTs).toLocaleDateString(state2.locale);
28888
+ const startDateInput = new Date(state2.startTs).toISOString().slice(0, 16);
28889
+ const endDateInput = new Date(state2.endTs).toISOString().slice(0, 16);
28890
+ const legendHTML = state2.deviceData.map((dd) => `
28521
28891
  <div style="display: flex; align-items: center; gap: 8px; padding: 8px 12px;
28522
- background: ${state.theme === "dark" ? "rgba(255,255,255,0.05)" : "rgba(0,0,0,0.03)"};
28892
+ background: ${state2.theme === "dark" ? "rgba(255,255,255,0.05)" : "rgba(0,0,0,0.03)"};
28523
28893
  border-radius: 8px;">
28524
28894
  <span style="width: 12px; height: 12px; border-radius: 50%; background: ${dd.color};"></span>
28525
28895
  <span style="color: ${colors.text}; font-size: 13px;">${dd.device.label}</span>
@@ -28528,9 +28898,9 @@ function renderModal2(container, state, modalId) {
28528
28898
  </span>
28529
28899
  </div>
28530
28900
  `).join("");
28531
- const statsHTML = state.deviceData.map((dd) => `
28901
+ const statsHTML = state2.deviceData.map((dd) => `
28532
28902
  <div style="
28533
- padding: 12px; background: ${state.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
28903
+ padding: 12px; background: ${state2.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
28534
28904
  border-radius: 10px; border-left: 4px solid ${dd.color};
28535
28905
  min-width: 150px;
28536
28906
  ">
@@ -28581,7 +28951,7 @@ function renderModal2(container, state, modalId) {
28581
28951
  min-height: 20px;
28582
28952
  ">
28583
28953
  <h2 style="margin: 6px; font-size: 18px; font-weight: 600; color: white; line-height: 2;">
28584
- \u{1F321}\uFE0F Compara\xE7\xE3o de Temperatura - ${state.devices.length} sensores
28954
+ \u{1F321}\uFE0F Compara\xE7\xE3o de Temperatura - ${state2.devices.length} sensores
28585
28955
  </h2>
28586
28956
  <div style="display: flex; gap: 4px; align-items: center;">
28587
28957
  <!-- Theme Toggle -->
@@ -28589,7 +28959,7 @@ function renderModal2(container, state, modalId) {
28589
28959
  background: none; border: none; font-size: 16px; cursor: pointer;
28590
28960
  padding: 4px 8px; border-radius: 6px; color: rgba(255,255,255,0.8);
28591
28961
  transition: background-color 0.2s;
28592
- ">${state.theme === "dark" ? "\u2600\uFE0F" : "\u{1F319}"}</button>
28962
+ ">${state2.theme === "dark" ? "\u2600\uFE0F" : "\u{1F319}"}</button>
28593
28963
  <!-- Maximize Button -->
28594
28964
  <button id="${modalId}-maximize" title="${isMaximized ? "Restaurar" : "Maximizar"}" style="
28595
28965
  background: none; border: none; font-size: 16px; cursor: pointer;
@@ -28612,7 +28982,7 @@ function renderModal2(container, state, modalId) {
28612
28982
  <div style="
28613
28983
  display: flex; gap: 16px; flex-wrap: wrap; align-items: flex-end;
28614
28984
  margin-bottom: 16px; padding: 16px;
28615
- background: ${state.theme === "dark" ? "rgba(255,255,255,0.05)" : "#f7f7f7"};
28985
+ background: ${state2.theme === "dark" ? "rgba(255,255,255,0.05)" : "#f7f7f7"};
28616
28986
  border-radius: 6px; border: 1px solid ${colors.border};
28617
28987
  ">
28618
28988
  <!-- Granularity Select -->
@@ -28625,8 +28995,8 @@ function renderModal2(container, state, modalId) {
28625
28995
  font-size: 14px; color: ${colors.text}; background: ${colors.surface};
28626
28996
  cursor: pointer; min-width: 130px;
28627
28997
  ">
28628
- <option value="hour" ${state.granularity === "hour" ? "selected" : ""}>Hora (30 min)</option>
28629
- <option value="day" ${state.granularity === "day" ? "selected" : ""}>Dia (m\xE9dia)</option>
28998
+ <option value="hour" ${state2.granularity === "hour" ? "selected" : ""}>Hora (30 min)</option>
28999
+ <option value="day" ${state2.granularity === "day" ? "selected" : ""}>Dia (m\xE9dia)</option>
28630
29000
  </select>
28631
29001
  </div>
28632
29002
  <!-- Day Period Filter (Multiselect) -->
@@ -28640,7 +29010,7 @@ function renderModal2(container, state, modalId) {
28640
29010
  cursor: pointer; min-width: 180px; text-align: left;
28641
29011
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
28642
29012
  ">
28643
- <span>${getSelectedPeriodsLabel(state.selectedPeriods)}</span>
29013
+ <span>${getSelectedPeriodsLabel(state2.selectedPeriods)}</span>
28644
29014
  <span style="font-size: 10px;">\u25BC</span>
28645
29015
  </button>
28646
29016
  <div id="${modalId}-period-dropdown" style="
@@ -28653,12 +29023,12 @@ function renderModal2(container, state, modalId) {
28653
29023
  <label style="
28654
29024
  display: flex; align-items: center; gap: 8px; padding: 8px 12px;
28655
29025
  cursor: pointer; font-size: 13px; color: ${colors.text};
28656
- " onmouseover="this.style.background='${state.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"}'"
29026
+ " onmouseover="this.style.background='${state2.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"}'"
28657
29027
  onmouseout="this.style.background='transparent'">
28658
29028
  <input type="checkbox"
28659
29029
  name="${modalId}-period"
28660
29030
  value="${period.id}"
28661
- ${state.selectedPeriods.includes(period.id) ? "checked" : ""}
29031
+ ${state2.selectedPeriods.includes(period.id) ? "checked" : ""}
28662
29032
  style="width: 16px; height: 16px; cursor: pointer; accent-color: #3e1a7d;">
28663
29033
  ${period.label}
28664
29034
  </label>
@@ -28666,13 +29036,13 @@ function renderModal2(container, state, modalId) {
28666
29036
  <div style="border-top: 1px solid ${colors.border}; margin-top: 8px; padding-top: 8px;">
28667
29037
  <button id="${modalId}-period-select-all" type="button" style="
28668
29038
  width: calc(100% - 16px); margin: 0 8px 4px; padding: 6px;
28669
- background: ${state.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"};
29039
+ background: ${state2.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"};
28670
29040
  border: none; border-radius: 4px; cursor: pointer;
28671
29041
  font-size: 12px; color: ${colors.text};
28672
29042
  ">Selecionar Todos</button>
28673
29043
  <button id="${modalId}-period-clear" type="button" style="
28674
29044
  width: calc(100% - 16px); margin: 0 8px; padding: 6px;
28675
- background: ${state.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"};
29045
+ background: ${state2.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"};
28676
29046
  border: none; border-radius: 4px; cursor: pointer;
28677
29047
  font-size: 12px; color: ${colors.text};
28678
29048
  ">Limpar Sele\xE7\xE3o</button>
@@ -28697,8 +29067,8 @@ function renderModal2(container, state, modalId) {
28697
29067
  font-size: 14px; font-weight: 500; height: 38px;
28698
29068
  display: flex; align-items: center; gap: 8px;
28699
29069
  font-family: 'Roboto', Arial, sans-serif;
28700
- " ${state.isLoading ? "disabled" : ""}>
28701
- ${state.isLoading ? '<span style="animation: spin 1s linear infinite; display: inline-block;">\u21BB</span> Carregando...' : "Carregar"}
29070
+ " ${state2.isLoading ? "disabled" : ""}>
29071
+ ${state2.isLoading ? '<span style="animation: spin 1s linear infinite; display: inline-block;">\u21BB</span> Carregando...' : "Carregar"}
28702
29072
  </button>
28703
29073
  </div>
28704
29074
 
@@ -28714,14 +29084,14 @@ function renderModal2(container, state, modalId) {
28714
29084
  <div style="margin-bottom: 24px;">
28715
29085
  <div id="${modalId}-chart" style="
28716
29086
  height: 380px;
28717
- background: ${state.theme === "dark" ? "rgba(255,255,255,0.03)" : "#fafafa"};
29087
+ background: ${state2.theme === "dark" ? "rgba(255,255,255,0.03)" : "#fafafa"};
28718
29088
  border-radius: 14px; display: flex; justify-content: center; align-items: center;
28719
29089
  border: 1px solid ${colors.border}; position: relative;
28720
29090
  ">
28721
- ${state.isLoading ? `<div style="text-align: center; color: ${colors.textMuted};">
29091
+ ${state2.isLoading ? `<div style="text-align: center; color: ${colors.textMuted};">
28722
29092
  <div style="animation: spin 1s linear infinite; font-size: 36px; margin-bottom: 12px;">\u21BB</div>
28723
- <div style="font-size: 15px;">Carregando dados de ${state.devices.length} sensores...</div>
28724
- </div>` : state.deviceData.every((dd) => dd.data.length === 0) ? `<div style="text-align: center; color: ${colors.textMuted};">
29093
+ <div style="font-size: 15px;">Carregando dados de ${state2.devices.length} sensores...</div>
29094
+ </div>` : state2.deviceData.every((dd) => dd.data.length === 0) ? `<div style="text-align: center; color: ${colors.textMuted};">
28725
29095
  <div style="font-size: 48px; margin-bottom: 12px;">\u{1F4ED}</div>
28726
29096
  <div style="font-size: 16px;">Sem dados para o per\xEDodo selecionado</div>
28727
29097
  </div>` : `<canvas id="${modalId}-canvas" style="width: 100%; height: 100%;"></canvas>`}
@@ -28739,12 +29109,12 @@ function renderModal2(container, state, modalId) {
28739
29109
  <!-- Actions -->
28740
29110
  <div style="display: flex; justify-content: flex-end; gap: 12px;">
28741
29111
  <button id="${modalId}-export" style="
28742
- background: ${state.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f7f7f7"};
29112
+ background: ${state2.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f7f7f7"};
28743
29113
  color: ${colors.text}; border: 1px solid ${colors.border};
28744
29114
  padding: 8px 16px; border-radius: 6px; cursor: pointer;
28745
29115
  font-size: 14px; display: flex; align-items: center; gap: 8px;
28746
29116
  font-family: 'Roboto', Arial, sans-serif;
28747
- " ${state.deviceData.every((dd) => dd.data.length === 0) ? "disabled" : ""}>
29117
+ " ${state2.deviceData.every((dd) => dd.data.length === 0) ? "disabled" : ""}>
28748
29118
  \u{1F4E5} Exportar CSV
28749
29119
  </button>
28750
29120
  <button id="${modalId}-close-btn" style="
@@ -28779,15 +29149,15 @@ function renderModal2(container, state, modalId) {
28779
29149
  </style>
28780
29150
  `;
28781
29151
  }
28782
- function drawComparisonChart(modalId, state) {
29152
+ function drawComparisonChart(modalId, state2) {
28783
29153
  const chartContainer = document.getElementById(`${modalId}-chart`);
28784
29154
  const canvas = document.getElementById(`${modalId}-canvas`);
28785
29155
  if (!chartContainer || !canvas) return;
28786
- const hasData = state.deviceData.some((dd) => dd.data.length > 0);
29156
+ const hasData = state2.deviceData.some((dd) => dd.data.length > 0);
28787
29157
  if (!hasData) return;
28788
29158
  const ctx = canvas.getContext("2d");
28789
29159
  if (!ctx) return;
28790
- const colors = getThemeColors(state.theme);
29160
+ const colors = getThemeColors(state2.theme);
28791
29161
  const width = chartContainer.clientWidth - 2;
28792
29162
  const height = 380;
28793
29163
  canvas.width = width;
@@ -28798,19 +29168,19 @@ function drawComparisonChart(modalId, state) {
28798
29168
  const paddingBottom = 55;
28799
29169
  ctx.clearRect(0, 0, width, height);
28800
29170
  const processedData = [];
28801
- state.deviceData.forEach((dd) => {
29171
+ state2.deviceData.forEach((dd) => {
28802
29172
  if (dd.data.length === 0) return;
28803
- const filteredData = filterByDayPeriods(dd.data, state.selectedPeriods);
29173
+ const filteredData = filterByDayPeriods(dd.data, state2.selectedPeriods);
28804
29174
  if (filteredData.length === 0) return;
28805
29175
  let points;
28806
- if (state.granularity === "hour") {
29176
+ if (state2.granularity === "hour") {
28807
29177
  const interpolated = interpolateTemperature(filteredData, {
28808
29178
  intervalMinutes: 30,
28809
- startTs: state.startTs,
28810
- endTs: state.endTs,
28811
- clampRange: state.clampRange
29179
+ startTs: state2.startTs,
29180
+ endTs: state2.endTs,
29181
+ clampRange: state2.clampRange
28812
29182
  });
28813
- const filteredInterpolated = filterByDayPeriods(interpolated, state.selectedPeriods);
29183
+ const filteredInterpolated = filterByDayPeriods(interpolated, state2.selectedPeriods);
28814
29184
  points = filteredInterpolated.map((item) => ({
28815
29185
  x: item.ts,
28816
29186
  y: Number(item.value),
@@ -28820,7 +29190,7 @@ function drawComparisonChart(modalId, state) {
28820
29190
  deviceColor: dd.color
28821
29191
  }));
28822
29192
  } else {
28823
- const daily = aggregateByDay(filteredData, state.clampRange);
29193
+ const daily = aggregateByDay(filteredData, state2.clampRange);
28824
29194
  points = daily.map((item) => ({
28825
29195
  x: item.dateTs,
28826
29196
  y: item.avg,
@@ -28841,7 +29211,7 @@ function drawComparisonChart(modalId, state) {
28841
29211
  ctx.fillText("Nenhum dado para os per\xEDodos selecionados", width / 2, height / 2);
28842
29212
  return;
28843
29213
  }
28844
- const isPeriodsFiltered = state.selectedPeriods.length < 4 && state.selectedPeriods.length > 0;
29214
+ const isPeriodsFiltered = state2.selectedPeriods.length < 4 && state2.selectedPeriods.length > 0;
28845
29215
  let dataMinY = Infinity;
28846
29216
  let dataMaxY = -Infinity;
28847
29217
  processedData.forEach(({ points }) => {
@@ -28851,7 +29221,7 @@ function drawComparisonChart(modalId, state) {
28851
29221
  });
28852
29222
  });
28853
29223
  const rangeMap = /* @__PURE__ */ new Map();
28854
- state.deviceData.forEach((dd, index) => {
29224
+ state2.deviceData.forEach((dd, index) => {
28855
29225
  const device = dd.device;
28856
29226
  const min = device.temperatureMin;
28857
29227
  const max = device.temperatureMax;
@@ -28870,10 +29240,10 @@ function drawComparisonChart(modalId, state) {
28870
29240
  }
28871
29241
  }
28872
29242
  });
28873
- if (rangeMap.size === 0 && state.temperatureMin !== null && state.temperatureMax !== null) {
29243
+ if (rangeMap.size === 0 && state2.temperatureMin !== null && state2.temperatureMax !== null) {
28874
29244
  rangeMap.set("global", {
28875
- min: state.temperatureMin,
28876
- max: state.temperatureMax,
29245
+ min: state2.temperatureMin,
29246
+ max: state2.temperatureMax,
28877
29247
  customerName: "Global",
28878
29248
  color: colors.success,
28879
29249
  deviceLabels: []
@@ -28994,10 +29364,10 @@ function drawComparisonChart(modalId, state) {
28994
29364
  const point = xAxisPoints[i];
28995
29365
  const date = new Date(point.x);
28996
29366
  let label;
28997
- if (state.granularity === "hour") {
28998
- label = date.toLocaleTimeString(state.locale, { hour: "2-digit", minute: "2-digit" });
29367
+ if (state2.granularity === "hour") {
29368
+ label = date.toLocaleTimeString(state2.locale, { hour: "2-digit", minute: "2-digit" });
28999
29369
  } else {
29000
- label = date.toLocaleDateString(state.locale, { day: "2-digit", month: "2-digit" });
29370
+ label = date.toLocaleDateString(state2.locale, { day: "2-digit", month: "2-digit" });
29001
29371
  }
29002
29372
  ctx.strokeStyle = colors.chartGrid;
29003
29373
  ctx.lineWidth = 1;
@@ -29016,16 +29386,16 @@ function drawComparisonChart(modalId, state) {
29016
29386
  ctx.lineTo(width - paddingRight, height - paddingBottom);
29017
29387
  ctx.stroke();
29018
29388
  const allChartPoints = processedData.flatMap((pd) => pd.points);
29019
- setupComparisonChartTooltip(canvas, chartContainer, allChartPoints, state, colors);
29389
+ setupComparisonChartTooltip(canvas, chartContainer, allChartPoints, state2, colors);
29020
29390
  }
29021
- function setupComparisonChartTooltip(canvas, container, chartData, state, colors) {
29391
+ function setupComparisonChartTooltip(canvas, container, chartData, state2, colors) {
29022
29392
  const existingTooltip = container.querySelector(".myio-chart-tooltip");
29023
29393
  if (existingTooltip) existingTooltip.remove();
29024
29394
  const tooltip = document.createElement("div");
29025
29395
  tooltip.className = "myio-chart-tooltip";
29026
29396
  tooltip.style.cssText = `
29027
29397
  position: absolute;
29028
- background: ${state.theme === "dark" ? "rgba(30, 30, 40, 0.95)" : "rgba(255, 255, 255, 0.98)"};
29398
+ background: ${state2.theme === "dark" ? "rgba(30, 30, 40, 0.95)" : "rgba(255, 255, 255, 0.98)"};
29029
29399
  border: 1px solid ${colors.border};
29030
29400
  border-radius: 8px;
29031
29401
  padding: 10px 14px;
@@ -29062,17 +29432,17 @@ function setupComparisonChartTooltip(canvas, container, chartData, state, colors
29062
29432
  if (point) {
29063
29433
  const date = new Date(point.x);
29064
29434
  let dateStr;
29065
- if (state.granularity === "hour") {
29066
- dateStr = date.toLocaleDateString(state.locale, {
29435
+ if (state2.granularity === "hour") {
29436
+ dateStr = date.toLocaleDateString(state2.locale, {
29067
29437
  day: "2-digit",
29068
29438
  month: "2-digit",
29069
29439
  year: "numeric"
29070
- }) + " " + date.toLocaleTimeString(state.locale, {
29440
+ }) + " " + date.toLocaleTimeString(state2.locale, {
29071
29441
  hour: "2-digit",
29072
29442
  minute: "2-digit"
29073
29443
  });
29074
29444
  } else {
29075
- dateStr = date.toLocaleDateString(state.locale, {
29445
+ dateStr = date.toLocaleDateString(state2.locale, {
29076
29446
  day: "2-digit",
29077
29447
  month: "2-digit",
29078
29448
  year: "numeric"
@@ -29114,7 +29484,7 @@ function setupComparisonChartTooltip(canvas, container, chartData, state, colors
29114
29484
  canvas.style.cursor = "default";
29115
29485
  });
29116
29486
  }
29117
- async function setupEventListeners2(container, state, modalId, onClose) {
29487
+ async function setupEventListeners2(container, state2, modalId, onClose) {
29118
29488
  const closeModal = () => {
29119
29489
  container.remove();
29120
29490
  onClose?.();
@@ -29125,19 +29495,19 @@ async function setupEventListeners2(container, state, modalId, onClose) {
29125
29495
  document.getElementById(`${modalId}-close`)?.addEventListener("click", closeModal);
29126
29496
  document.getElementById(`${modalId}-close-btn`)?.addEventListener("click", closeModal);
29127
29497
  const dateRangeInput = document.getElementById(`${modalId}-date-range`);
29128
- if (dateRangeInput && !state.dateRangePicker) {
29498
+ if (dateRangeInput && !state2.dateRangePicker) {
29129
29499
  try {
29130
- state.dateRangePicker = await createDateRangePicker2(dateRangeInput, {
29131
- presetStart: new Date(state.startTs).toISOString(),
29132
- presetEnd: new Date(state.endTs).toISOString(),
29500
+ state2.dateRangePicker = await createDateRangePicker2(dateRangeInput, {
29501
+ presetStart: new Date(state2.startTs).toISOString(),
29502
+ presetEnd: new Date(state2.endTs).toISOString(),
29133
29503
  includeTime: true,
29134
29504
  timePrecision: "minute",
29135
29505
  maxRangeDays: 90,
29136
- locale: state.locale,
29506
+ locale: state2.locale,
29137
29507
  parentEl: container.querySelector(".myio-temp-comparison-content"),
29138
29508
  onApply: (result) => {
29139
- state.startTs = new Date(result.startISO).getTime();
29140
- state.endTs = new Date(result.endISO).getTime();
29509
+ state2.startTs = new Date(result.startISO).getTime();
29510
+ state2.endTs = new Date(result.endISO).getTime();
29141
29511
  console.log("[TemperatureComparisonModal] Date range applied:", result);
29142
29512
  }
29143
29513
  });
@@ -29146,23 +29516,23 @@ async function setupEventListeners2(container, state, modalId, onClose) {
29146
29516
  }
29147
29517
  }
29148
29518
  document.getElementById(`${modalId}-theme-toggle`)?.addEventListener("click", async () => {
29149
- state.theme = state.theme === "dark" ? "light" : "dark";
29150
- localStorage.setItem("myio-temp-comparison-theme", state.theme);
29151
- state.dateRangePicker = null;
29152
- renderModal2(container, state, modalId);
29153
- if (state.deviceData.some((dd) => dd.data.length > 0)) {
29154
- drawComparisonChart(modalId, state);
29155
- }
29156
- await setupEventListeners2(container, state, modalId, onClose);
29519
+ state2.theme = state2.theme === "dark" ? "light" : "dark";
29520
+ localStorage.setItem("myio-temp-comparison-theme", state2.theme);
29521
+ state2.dateRangePicker = null;
29522
+ renderModal2(container, state2, modalId);
29523
+ if (state2.deviceData.some((dd) => dd.data.length > 0)) {
29524
+ drawComparisonChart(modalId, state2);
29525
+ }
29526
+ await setupEventListeners2(container, state2, modalId, onClose);
29157
29527
  });
29158
29528
  document.getElementById(`${modalId}-maximize`)?.addEventListener("click", async () => {
29159
29529
  container.__isMaximized = !container.__isMaximized;
29160
- state.dateRangePicker = null;
29161
- renderModal2(container, state, modalId);
29162
- if (state.deviceData.some((dd) => dd.data.length > 0)) {
29163
- drawComparisonChart(modalId, state);
29530
+ state2.dateRangePicker = null;
29531
+ renderModal2(container, state2, modalId);
29532
+ if (state2.deviceData.some((dd) => dd.data.length > 0)) {
29533
+ drawComparisonChart(modalId, state2);
29164
29534
  }
29165
- await setupEventListeners2(container, state, modalId, onClose);
29535
+ await setupEventListeners2(container, state2, modalId, onClose);
29166
29536
  });
29167
29537
  const periodBtn = document.getElementById(`${modalId}-period-btn`);
29168
29538
  const periodDropdown = document.getElementById(`${modalId}-period-dropdown`);
@@ -29181,13 +29551,13 @@ async function setupEventListeners2(container, state, modalId, onClose) {
29181
29551
  periodCheckboxes.forEach((checkbox) => {
29182
29552
  checkbox.addEventListener("change", () => {
29183
29553
  const checked = Array.from(periodCheckboxes).filter((cb) => cb.checked).map((cb) => cb.value);
29184
- state.selectedPeriods = checked;
29554
+ state2.selectedPeriods = checked;
29185
29555
  const btnLabel = periodBtn?.querySelector("span:first-child");
29186
29556
  if (btnLabel) {
29187
- btnLabel.textContent = getSelectedPeriodsLabel(state.selectedPeriods);
29557
+ btnLabel.textContent = getSelectedPeriodsLabel(state2.selectedPeriods);
29188
29558
  }
29189
- if (state.deviceData.some((dd) => dd.data.length > 0)) {
29190
- drawComparisonChart(modalId, state);
29559
+ if (state2.deviceData.some((dd) => dd.data.length > 0)) {
29560
+ drawComparisonChart(modalId, state2);
29191
29561
  }
29192
29562
  });
29193
29563
  });
@@ -29195,77 +29565,77 @@ async function setupEventListeners2(container, state, modalId, onClose) {
29195
29565
  periodCheckboxes.forEach((cb) => {
29196
29566
  cb.checked = true;
29197
29567
  });
29198
- state.selectedPeriods = ["madrugada", "manha", "tarde", "noite"];
29568
+ state2.selectedPeriods = ["madrugada", "manha", "tarde", "noite"];
29199
29569
  const btnLabel = periodBtn?.querySelector("span:first-child");
29200
29570
  if (btnLabel) {
29201
- btnLabel.textContent = getSelectedPeriodsLabel(state.selectedPeriods);
29571
+ btnLabel.textContent = getSelectedPeriodsLabel(state2.selectedPeriods);
29202
29572
  }
29203
- if (state.deviceData.some((dd) => dd.data.length > 0)) {
29204
- drawComparisonChart(modalId, state);
29573
+ if (state2.deviceData.some((dd) => dd.data.length > 0)) {
29574
+ drawComparisonChart(modalId, state2);
29205
29575
  }
29206
29576
  });
29207
29577
  document.getElementById(`${modalId}-period-clear`)?.addEventListener("click", () => {
29208
29578
  periodCheckboxes.forEach((cb) => {
29209
29579
  cb.checked = false;
29210
29580
  });
29211
- state.selectedPeriods = [];
29581
+ state2.selectedPeriods = [];
29212
29582
  const btnLabel = periodBtn?.querySelector("span:first-child");
29213
29583
  if (btnLabel) {
29214
- btnLabel.textContent = getSelectedPeriodsLabel(state.selectedPeriods);
29584
+ btnLabel.textContent = getSelectedPeriodsLabel(state2.selectedPeriods);
29215
29585
  }
29216
- if (state.deviceData.some((dd) => dd.data.length > 0)) {
29217
- drawComparisonChart(modalId, state);
29586
+ if (state2.deviceData.some((dd) => dd.data.length > 0)) {
29587
+ drawComparisonChart(modalId, state2);
29218
29588
  }
29219
29589
  });
29220
29590
  document.getElementById(`${modalId}-granularity`)?.addEventListener("change", (e) => {
29221
- state.granularity = e.target.value;
29222
- localStorage.setItem("myio-temp-comparison-granularity", state.granularity);
29223
- if (state.deviceData.some((dd) => dd.data.length > 0)) {
29224
- drawComparisonChart(modalId, state);
29591
+ state2.granularity = e.target.value;
29592
+ localStorage.setItem("myio-temp-comparison-granularity", state2.granularity);
29593
+ if (state2.deviceData.some((dd) => dd.data.length > 0)) {
29594
+ drawComparisonChart(modalId, state2);
29225
29595
  }
29226
29596
  });
29227
29597
  document.getElementById(`${modalId}-query`)?.addEventListener("click", async () => {
29228
- if (state.startTs >= state.endTs) {
29598
+ if (state2.startTs >= state2.endTs) {
29229
29599
  alert("Por favor, selecione um per\xEDodo v\xE1lido");
29230
29600
  return;
29231
29601
  }
29232
- state.isLoading = true;
29233
- state.dateRangePicker = null;
29234
- renderModal2(container, state, modalId);
29235
- await fetchAllDevicesData(state);
29236
- renderModal2(container, state, modalId);
29237
- drawComparisonChart(modalId, state);
29238
- await setupEventListeners2(container, state, modalId, onClose);
29602
+ state2.isLoading = true;
29603
+ state2.dateRangePicker = null;
29604
+ renderModal2(container, state2, modalId);
29605
+ await fetchAllDevicesData(state2);
29606
+ renderModal2(container, state2, modalId);
29607
+ drawComparisonChart(modalId, state2);
29608
+ await setupEventListeners2(container, state2, modalId, onClose);
29239
29609
  });
29240
29610
  document.getElementById(`${modalId}-export`)?.addEventListener("click", () => {
29241
- if (state.deviceData.every((dd) => dd.data.length === 0)) return;
29242
- exportComparisonCSV(state);
29611
+ if (state2.deviceData.every((dd) => dd.data.length === 0)) return;
29612
+ exportComparisonCSV(state2);
29243
29613
  });
29244
29614
  }
29245
- function exportComparisonCSV(state) {
29246
- const startDateStr = new Date(state.startTs).toLocaleDateString(state.locale).replace(/\//g, "-");
29247
- const endDateStr = new Date(state.endTs).toLocaleDateString(state.locale).replace(/\//g, "-");
29615
+ function exportComparisonCSV(state2) {
29616
+ const startDateStr = new Date(state2.startTs).toLocaleDateString(state2.locale).replace(/\//g, "-");
29617
+ const endDateStr = new Date(state2.endTs).toLocaleDateString(state2.locale).replace(/\//g, "-");
29248
29618
  const BOM = "\uFEFF";
29249
29619
  let csvContent = BOM;
29250
29620
  csvContent += `Compara\xE7\xE3o de Temperatura
29251
29621
  `;
29252
29622
  csvContent += `Per\xEDodo: ${startDateStr} at\xE9 ${endDateStr}
29253
29623
  `;
29254
- csvContent += `Sensores: ${state.devices.map((d) => d.label).join(", ")}
29624
+ csvContent += `Sensores: ${state2.devices.map((d) => d.label).join(", ")}
29255
29625
  `;
29256
29626
  csvContent += "\n";
29257
29627
  csvContent += "Estat\xEDsticas por Sensor:\n";
29258
29628
  csvContent += "Sensor,M\xE9dia (\xB0C),Min (\xB0C),Max (\xB0C),Leituras\n";
29259
- state.deviceData.forEach((dd) => {
29629
+ state2.deviceData.forEach((dd) => {
29260
29630
  csvContent += `"${dd.device.label}",${dd.stats.avg.toFixed(2)},${dd.stats.min.toFixed(2)},${dd.stats.max.toFixed(2)},${dd.stats.count}
29261
29631
  `;
29262
29632
  });
29263
29633
  csvContent += "\n";
29264
29634
  csvContent += "Dados Detalhados:\n";
29265
29635
  csvContent += "Data/Hora,Sensor,Temperatura (\xB0C)\n";
29266
- state.deviceData.forEach((dd) => {
29636
+ state2.deviceData.forEach((dd) => {
29267
29637
  dd.data.forEach((item) => {
29268
- const date = new Date(item.ts).toLocaleString(state.locale);
29638
+ const date = new Date(item.ts).toLocaleString(state2.locale);
29269
29639
  const temp = Number(item.value).toFixed(2);
29270
29640
  csvContent += `"${date}","${dd.device.label}",${temp}
29271
29641
  `;
@@ -29373,10 +29743,10 @@ async function saveCustomerAttributes(customerId, token, minTemperature, maxTemp
29373
29743
  throw new Error(`Failed to save attributes: ${response.status}`);
29374
29744
  }
29375
29745
  }
29376
- function renderModal3(container, state, modalId, onClose, onSave) {
29377
- const colors = getColors(state.theme);
29378
- const minValue = state.minTemperature !== null ? state.minTemperature : "";
29379
- const maxValue = state.maxTemperature !== null ? state.maxTemperature : "";
29746
+ function renderModal3(container, state2, modalId, onClose, onSave) {
29747
+ const colors = getColors(state2.theme);
29748
+ const minValue = state2.minTemperature !== null ? state2.minTemperature : "";
29749
+ const maxValue = state2.maxTemperature !== null ? state2.maxTemperature : "";
29380
29750
  container.innerHTML = `
29381
29751
  <style>
29382
29752
  @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
@@ -29641,23 +30011,23 @@ function renderModal3(container, state, modalId, onClose, onSave) {
29641
30011
  </div>
29642
30012
 
29643
30013
  <div class="modal-body">
29644
- ${state.isLoading ? `
30014
+ ${state2.isLoading ? `
29645
30015
  <div class="loading-overlay">
29646
30016
  <div class="loading-spinner"></div>
29647
30017
  <div>Carregando configura\xE7\xF5es...</div>
29648
30018
  </div>
29649
30019
  ` : `
29650
- ${state.error ? `
29651
- <div class="message message-error">${state.error}</div>
30020
+ ${state2.error ? `
30021
+ <div class="message message-error">${state2.error}</div>
29652
30022
  ` : ""}
29653
30023
 
29654
- ${state.successMessage ? `
29655
- <div class="message message-success">${state.successMessage}</div>
30024
+ ${state2.successMessage ? `
30025
+ <div class="message message-success">${state2.successMessage}</div>
29656
30026
  ` : ""}
29657
30027
 
29658
30028
  <div class="customer-info">
29659
30029
  <div class="customer-label">Shopping / Cliente</div>
29660
- <div class="customer-name">${state.customerName || "N\xE3o identificado"}</div>
30030
+ <div class="customer-name">${state2.customerName || "N\xE3o identificado"}</div>
29661
30031
  </div>
29662
30032
 
29663
30033
  <div class="form-group">
@@ -29701,11 +30071,11 @@ function renderModal3(container, state, modalId, onClose, onSave) {
29701
30071
  `}
29702
30072
  </div>
29703
30073
 
29704
- ${!state.isLoading ? `
30074
+ ${!state2.isLoading ? `
29705
30075
  <div class="modal-footer">
29706
30076
  <button class="btn btn-secondary" id="${modalId}-cancel">Cancelar</button>
29707
- <button class="btn btn-primary" id="${modalId}-save" ${state.isSaving ? "disabled" : ""}>
29708
- ${state.isSaving ? '<div class="spinner"></div> Salvando...' : "Salvar"}
30077
+ <button class="btn btn-primary" id="${modalId}-save" ${state2.isSaving ? "disabled" : ""}>
30078
+ ${state2.isSaving ? '<div class="spinner"></div> Salvando...' : "Salvar"}
29709
30079
  </button>
29710
30080
  </div>
29711
30081
  ` : ""}
@@ -29742,18 +30112,18 @@ function renderModal3(container, state, modalId, onClose, onSave) {
29742
30112
  const min = parseFloat(minInput.value);
29743
30113
  const max = parseFloat(maxInput.value);
29744
30114
  if (isNaN(min) || isNaN(max)) {
29745
- state.error = "Por favor, preencha ambos os valores.";
29746
- renderModal3(container, state, modalId, onClose, onSave);
30115
+ state2.error = "Por favor, preencha ambos os valores.";
30116
+ renderModal3(container, state2, modalId, onClose, onSave);
29747
30117
  return;
29748
30118
  }
29749
30119
  if (min >= max) {
29750
- state.error = "A temperatura m\xEDnima deve ser menor que a m\xE1xima.";
29751
- renderModal3(container, state, modalId, onClose, onSave);
30120
+ state2.error = "A temperatura m\xEDnima deve ser menor que a m\xE1xima.";
30121
+ renderModal3(container, state2, modalId, onClose, onSave);
29752
30122
  return;
29753
30123
  }
29754
30124
  if (min < 0 || max > 50) {
29755
- state.error = "Os valores devem estar entre 0\xB0C e 50\xB0C.";
29756
- renderModal3(container, state, modalId, onClose, onSave);
30125
+ state2.error = "Os valores devem estar entre 0\xB0C e 50\xB0C.";
30126
+ renderModal3(container, state2, modalId, onClose, onSave);
29757
30127
  return;
29758
30128
  }
29759
30129
  await onSave(min, max);
@@ -29761,7 +30131,7 @@ function renderModal3(container, state, modalId, onClose, onSave) {
29761
30131
  }
29762
30132
  function openTemperatureSettingsModal(params) {
29763
30133
  const modalId = `myio-temp-settings-${Date.now()}`;
29764
- const state = {
30134
+ const state2 = {
29765
30135
  customerId: params.customerId,
29766
30136
  customerName: params.customerName || "",
29767
30137
  token: params.token,
@@ -29781,37 +30151,37 @@ function openTemperatureSettingsModal(params) {
29781
30151
  params.onClose?.();
29782
30152
  };
29783
30153
  const handleSave = async (min, max) => {
29784
- state.isSaving = true;
29785
- state.error = null;
29786
- state.successMessage = null;
29787
- renderModal3(container, state, modalId, destroy, handleSave);
30154
+ state2.isSaving = true;
30155
+ state2.error = null;
30156
+ state2.successMessage = null;
30157
+ renderModal3(container, state2, modalId, destroy, handleSave);
29788
30158
  try {
29789
- await saveCustomerAttributes(state.customerId, state.token, min, max, params.onError);
29790
- state.minTemperature = min;
29791
- state.maxTemperature = max;
29792
- state.isSaving = false;
29793
- state.successMessage = "Configura\xE7\xF5es salvas com sucesso!";
29794
- renderModal3(container, state, modalId, destroy, handleSave);
30159
+ await saveCustomerAttributes(state2.customerId, state2.token, min, max, params.onError);
30160
+ state2.minTemperature = min;
30161
+ state2.maxTemperature = max;
30162
+ state2.isSaving = false;
30163
+ state2.successMessage = "Configura\xE7\xF5es salvas com sucesso!";
30164
+ renderModal3(container, state2, modalId, destroy, handleSave);
29795
30165
  params.onSave?.({ minTemperature: min, maxTemperature: max });
29796
30166
  setTimeout(() => {
29797
30167
  destroy();
29798
30168
  }, 1500);
29799
30169
  } catch (error) {
29800
- state.isSaving = false;
29801
- state.error = `Erro ao salvar: ${error.message}`;
29802
- renderModal3(container, state, modalId, destroy, handleSave);
30170
+ state2.isSaving = false;
30171
+ state2.error = `Erro ao salvar: ${error.message}`;
30172
+ renderModal3(container, state2, modalId, destroy, handleSave);
29803
30173
  }
29804
30174
  };
29805
- renderModal3(container, state, modalId, destroy, handleSave);
29806
- fetchCustomerAttributes(state.customerId, state.token, params.onError).then(({ minTemperature, maxTemperature }) => {
29807
- state.minTemperature = minTemperature;
29808
- state.maxTemperature = maxTemperature;
29809
- state.isLoading = false;
29810
- renderModal3(container, state, modalId, destroy, handleSave);
30175
+ renderModal3(container, state2, modalId, destroy, handleSave);
30176
+ fetchCustomerAttributes(state2.customerId, state2.token, params.onError).then(({ minTemperature, maxTemperature }) => {
30177
+ state2.minTemperature = minTemperature;
30178
+ state2.maxTemperature = maxTemperature;
30179
+ state2.isLoading = false;
30180
+ renderModal3(container, state2, modalId, destroy, handleSave);
29811
30181
  }).catch((error) => {
29812
- state.isLoading = false;
29813
- state.error = `Erro ao carregar: ${error.message}`;
29814
- renderModal3(container, state, modalId, destroy, handleSave);
30182
+ state2.isLoading = false;
30183
+ state2.error = `Erro ao carregar: ${error.message}`;
30184
+ renderModal3(container, state2, modalId, destroy, handleSave);
29815
30185
  });
29816
30186
  return { destroy };
29817
30187
  }
@@ -29847,11 +30217,13 @@ var ENERGY_SUMMARY_TOOLTIP_CSS = `
29847
30217
  border: 1px solid #e2e8f0;
29848
30218
  border-radius: 12px;
29849
30219
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.15), 0 2px 10px rgba(0, 0, 0, 0.08);
30220
+ min-width: 380px;
29850
30221
  width: max-content;
29851
30222
  max-width: 90vw;
29852
30223
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
29853
30224
  font-size: 12px;
29854
30225
  color: #1e293b;
30226
+ overflow: hidden;
29855
30227
  }
29856
30228
 
29857
30229
  .energy-summary-tooltip__header {
@@ -30157,8 +30529,7 @@ var ENERGY_SUMMARY_TOOLTIP_CSS = `
30157
30529
  align-items: center;
30158
30530
  padding: 10px 14px;
30159
30531
  background: linear-gradient(135deg, #047857 0%, #059669 100%);
30160
- margin: 12px -14px -14px;
30161
- border-radius: 0 0 12px 12px;
30532
+ border-radius: 0 0 11px 11px;
30162
30533
  }
30163
30534
 
30164
30535
  .energy-summary-tooltip__total-label {
@@ -30301,7 +30672,7 @@ var EnergySummaryTooltip = {
30301
30672
  { key: "failure", label: "Falha", count: status.failure },
30302
30673
  { key: "standby", label: "Standby", count: status.standby },
30303
30674
  { key: "offline", label: "Offline", count: status.offline },
30304
- { key: "no-consumption", label: "Sem Dados", count: status.noConsumption }
30675
+ { key: "no-consumption", label: "Sem Consumo", count: status.noConsumption }
30305
30676
  ];
30306
30677
  return items.map((item) => `
30307
30678
  <div class="energy-summary-tooltip__status-item ${item.key}">
@@ -30746,7 +31117,7 @@ var EnergySummaryTooltip = {
30746
31117
  * Build summary data from TELEMETRY_INFO STATE
30747
31118
  * This is called by the widget controller to get data for the tooltip
30748
31119
  */
30749
- buildSummaryFromState(state, receivedData) {
31120
+ buildSummaryFromState(state2, receivedData) {
30750
31121
  const summary = {
30751
31122
  totalDevices: 0,
30752
31123
  totalConsumption: 0,
@@ -30762,22 +31133,22 @@ var EnergySummaryTooltip = {
30762
31133
  },
30763
31134
  lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
30764
31135
  };
30765
- if (!state) return summary;
31136
+ if (!state2) return summary;
30766
31137
  const entrada = {
30767
31138
  id: "entrada",
30768
31139
  name: "Entrada",
30769
31140
  icon: CATEGORY_ICONS.entrada,
30770
- deviceCount: state.entrada?.devices?.length || (receivedData?.entrada_total?.device_count || 0),
30771
- consumption: state.entrada?.total || 0,
31141
+ deviceCount: state2.entrada?.devices?.length || (receivedData?.entrada_total?.device_count || 0),
31142
+ consumption: state2.entrada?.total || 0,
30772
31143
  percentage: 100
30773
31144
  };
30774
31145
  const lojas = {
30775
31146
  id: "lojas",
30776
31147
  name: "Lojas",
30777
31148
  icon: CATEGORY_ICONS.lojas,
30778
- deviceCount: state.consumidores?.lojas?.devices?.length || (receivedData?.lojas_total?.device_count || 0),
30779
- consumption: state.consumidores?.lojas?.total || 0,
30780
- percentage: state.consumidores?.lojas?.perc || 0
31149
+ deviceCount: state2.consumidores?.lojas?.devices?.length || (receivedData?.lojas_total?.device_count || 0),
31150
+ consumption: state2.consumidores?.lojas?.total || 0,
31151
+ percentage: state2.consumidores?.lojas?.perc || 0
30781
31152
  };
30782
31153
  const climatizacaoData = receivedData?.climatizacao || {};
30783
31154
  const elevadoresData = receivedData?.elevadores || {};
@@ -30788,9 +31159,9 @@ var EnergySummaryTooltip = {
30788
31159
  id: "climatizacao",
30789
31160
  name: "Climatizacao",
30790
31161
  icon: CATEGORY_ICONS.climatizacao,
30791
- deviceCount: climatizacaoData.count || state.consumidores?.climatizacao?.devices?.length || 0,
30792
- consumption: state.consumidores?.climatizacao?.total || 0,
30793
- percentage: state.consumidores?.climatizacao?.perc || 0
31162
+ deviceCount: climatizacaoData.count || state2.consumidores?.climatizacao?.devices?.length || 0,
31163
+ consumption: state2.consumidores?.climatizacao?.total || 0,
31164
+ percentage: state2.consumidores?.climatizacao?.perc || 0
30794
31165
  };
30795
31166
  if (climatizacaoData.subcategories) {
30796
31167
  climatizacao.children = [];
@@ -30841,54 +31212,67 @@ var EnergySummaryTooltip = {
30841
31212
  id: "elevadores",
30842
31213
  name: "Elevadores",
30843
31214
  icon: CATEGORY_ICONS.elevadores,
30844
- deviceCount: elevadoresData.count || state.consumidores?.elevadores?.devices?.length || 0,
30845
- consumption: state.consumidores?.elevadores?.total || 0,
30846
- percentage: state.consumidores?.elevadores?.perc || 0
31215
+ deviceCount: elevadoresData.count || state2.consumidores?.elevadores?.devices?.length || 0,
31216
+ consumption: state2.consumidores?.elevadores?.total || 0,
31217
+ percentage: state2.consumidores?.elevadores?.perc || 0
30847
31218
  });
30848
31219
  areaComumChildren.push({
30849
31220
  id: "escadasRolantes",
30850
31221
  name: "Esc. Rolantes",
30851
31222
  icon: CATEGORY_ICONS.escadas,
30852
- deviceCount: escadasData.count || state.consumidores?.escadasRolantes?.devices?.length || 0,
30853
- consumption: state.consumidores?.escadasRolantes?.total || 0,
30854
- percentage: state.consumidores?.escadasRolantes?.perc || 0
31223
+ deviceCount: escadasData.count || state2.consumidores?.escadasRolantes?.devices?.length || 0,
31224
+ consumption: state2.consumidores?.escadasRolantes?.total || 0,
31225
+ percentage: state2.consumidores?.escadasRolantes?.perc || 0
30855
31226
  });
30856
31227
  areaComumChildren.push({
30857
31228
  id: "outros",
30858
31229
  name: "Outros",
30859
31230
  icon: CATEGORY_ICONS.outros,
30860
- deviceCount: outrosData.count || state.consumidores?.outros?.devices?.length || 0,
30861
- consumption: state.consumidores?.outros?.total || 0,
30862
- percentage: state.consumidores?.outros?.perc || 0
31231
+ deviceCount: outrosData.count || state2.consumidores?.outros?.devices?.length || 0,
31232
+ consumption: state2.consumidores?.outros?.total || 0,
31233
+ percentage: state2.consumidores?.outros?.perc || 0
30863
31234
  });
30864
31235
  const areaComumDeviceCount = areaComumChildren.reduce((sum, c) => sum + c.deviceCount, 0);
30865
- const areaComumConsumption = state.consumidores?.areaComum?.total || areaComumChildren.reduce((sum, c) => sum + c.consumption, 0);
31236
+ const areaComumConsumption = state2.consumidores?.areaComum?.total || areaComumChildren.reduce((sum, c) => sum + c.consumption, 0);
30866
31237
  const areaComum = {
30867
31238
  id: "areaComum",
30868
31239
  name: "Area Comum",
30869
31240
  icon: CATEGORY_ICONS.areaComum,
30870
31241
  deviceCount: areaComumDeviceCount,
30871
31242
  consumption: areaComumConsumption,
30872
- percentage: state.consumidores?.areaComum?.perc || 0,
31243
+ percentage: state2.consumidores?.areaComum?.perc || 0,
30873
31244
  children: areaComumChildren
30874
31245
  };
30875
31246
  summary.byCategory = [entrada, lojas, areaComum];
30876
31247
  summary.totalDevices = entrada.deviceCount + lojas.deviceCount + areaComumDeviceCount;
30877
- summary.totalConsumption = state.grandTotal || entrada.consumption;
31248
+ summary.totalConsumption = state2.grandTotal || entrada.consumption;
30878
31249
  const totalDevices = summary.totalDevices;
30879
- summary.byStatus = {
30880
- normal: Math.floor(totalDevices * 0.85),
30881
- // Estimate 85% normal
30882
- alert: Math.floor(totalDevices * 0.08),
30883
- // Estimate 8% alert
30884
- failure: Math.floor(totalDevices * 0.02),
30885
- // Estimate 2% failure
30886
- standby: Math.floor(totalDevices * 0.03),
30887
- // Estimate 3% standby
30888
- offline: Math.floor(totalDevices * 0.02),
30889
- // Estimate 2% offline
30890
- noConsumption: 0
30891
- };
31250
+ const statusData = receivedData?.statusCounts || receivedData?.deviceStatus || null;
31251
+ if (statusData && typeof statusData === "object") {
31252
+ summary.byStatus = {
31253
+ normal: statusData.normal || 0,
31254
+ alert: statusData.alert || 0,
31255
+ failure: statusData.failure || 0,
31256
+ standby: statusData.standby || 0,
31257
+ offline: statusData.offline || 0,
31258
+ noConsumption: statusData.noConsumption || statusData.zeroConsumption || 0
31259
+ };
31260
+ } else {
31261
+ summary.byStatus = {
31262
+ normal: Math.floor(totalDevices * 0.75),
31263
+ // Estimate 75% normal (with consumption)
31264
+ alert: Math.floor(totalDevices * 0.06),
31265
+ // Estimate 6% alert
31266
+ failure: Math.floor(totalDevices * 0.02),
31267
+ // Estimate 2% failure
31268
+ standby: Math.floor(totalDevices * 0.02),
31269
+ // Estimate 2% standby
31270
+ offline: Math.floor(totalDevices * 0.03),
31271
+ // Estimate 3% offline
31272
+ noConsumption: Math.floor(totalDevices * 0.12)
31273
+ // Estimate 12% sem consumo
31274
+ };
31275
+ }
30892
31276
  const statusSum = Object.values(summary.byStatus).reduce((a, b) => a + b, 0);
30893
31277
  if (statusSum !== totalDevices && totalDevices > 0) {
30894
31278
  summary.byStatus.normal += totalDevices - statusSum;
@@ -30897,6 +31281,654 @@ var EnergySummaryTooltip = {
30897
31281
  }
30898
31282
  };
30899
31283
 
31284
+ // src/utils/InfoTooltip.ts
31285
+ var INFO_TOOLTIP_CSS = `
31286
+ /* ============================================
31287
+ Info Tooltip (RFC-0105)
31288
+ Premium draggable tooltip with actions
31289
+ ============================================ */
31290
+
31291
+ .myio-info-tooltip {
31292
+ position: fixed;
31293
+ z-index: 99999;
31294
+ pointer-events: none;
31295
+ opacity: 0;
31296
+ transition: opacity 0.25s ease, transform 0.25s ease;
31297
+ transform: translateY(5px);
31298
+ }
31299
+
31300
+ .myio-info-tooltip.visible {
31301
+ opacity: 1;
31302
+ pointer-events: auto;
31303
+ transform: translateY(0);
31304
+ }
31305
+
31306
+ .myio-info-tooltip.closing {
31307
+ opacity: 0;
31308
+ transform: translateY(8px);
31309
+ transition: opacity 0.4s ease, transform 0.4s ease;
31310
+ }
31311
+
31312
+ .myio-info-tooltip.pinned {
31313
+ box-shadow: 0 0 0 2px #047857, 0 10px 40px rgba(0, 0, 0, 0.2);
31314
+ border-radius: 12px;
31315
+ }
31316
+
31317
+ .myio-info-tooltip.dragging {
31318
+ transition: none !important;
31319
+ cursor: move;
31320
+ }
31321
+
31322
+ .myio-info-tooltip.maximized {
31323
+ top: 20px !important;
31324
+ left: 20px !important;
31325
+ right: 20px !important;
31326
+ bottom: 20px !important;
31327
+ width: auto !important;
31328
+ max-width: none !important;
31329
+ }
31330
+
31331
+ .myio-info-tooltip.maximized .myio-info-tooltip__panel {
31332
+ width: 100%;
31333
+ height: 100%;
31334
+ max-width: none;
31335
+ display: flex;
31336
+ flex-direction: column;
31337
+ }
31338
+
31339
+ .myio-info-tooltip.maximized .myio-info-tooltip__content {
31340
+ flex: 1;
31341
+ overflow-y: auto;
31342
+ }
31343
+
31344
+ .myio-info-tooltip__panel {
31345
+ background: #ffffff;
31346
+ border: 1px solid #e2e8f0;
31347
+ border-radius: 12px;
31348
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.12), 0 2px 10px rgba(0, 0, 0, 0.08);
31349
+ min-width: 320px;
31350
+ max-width: 400px;
31351
+ font-size: 12px;
31352
+ color: #1e293b;
31353
+ overflow: hidden;
31354
+ font-family: Inter, system-ui, -apple-system, sans-serif;
31355
+ }
31356
+
31357
+ .myio-info-tooltip__header {
31358
+ display: flex;
31359
+ align-items: center;
31360
+ gap: 8px;
31361
+ padding: 12px 16px;
31362
+ background: linear-gradient(90deg, #f1f5f9 0%, #e2e8f0 100%);
31363
+ border-bottom: 1px solid #cbd5e1;
31364
+ cursor: move;
31365
+ user-select: none;
31366
+ }
31367
+
31368
+ .myio-info-tooltip__icon {
31369
+ font-size: 18px;
31370
+ }
31371
+
31372
+ .myio-info-tooltip__title {
31373
+ font-weight: 700;
31374
+ font-size: 14px;
31375
+ color: #475569;
31376
+ letter-spacing: 0.3px;
31377
+ flex: 1;
31378
+ }
31379
+
31380
+ .myio-info-tooltip__header-actions {
31381
+ display: flex;
31382
+ align-items: center;
31383
+ gap: 4px;
31384
+ }
31385
+
31386
+ .myio-info-tooltip__header-btn {
31387
+ width: 24px;
31388
+ height: 24px;
31389
+ border: none;
31390
+ background: rgba(255, 255, 255, 0.6);
31391
+ border-radius: 4px;
31392
+ cursor: pointer;
31393
+ display: flex;
31394
+ align-items: center;
31395
+ justify-content: center;
31396
+ transition: all 0.15s ease;
31397
+ color: #64748b;
31398
+ }
31399
+
31400
+ .myio-info-tooltip__header-btn:hover {
31401
+ background: rgba(255, 255, 255, 0.9);
31402
+ color: #1e293b;
31403
+ }
31404
+
31405
+ .myio-info-tooltip__header-btn.pinned {
31406
+ background: #047857;
31407
+ color: white;
31408
+ }
31409
+
31410
+ .myio-info-tooltip__header-btn.pinned:hover {
31411
+ background: #065f46;
31412
+ color: white;
31413
+ }
31414
+
31415
+ .myio-info-tooltip__header-btn svg {
31416
+ width: 14px;
31417
+ height: 14px;
31418
+ }
31419
+
31420
+ .myio-info-tooltip__content {
31421
+ padding: 16px;
31422
+ max-height: 500px;
31423
+ overflow-y: auto;
31424
+ }
31425
+
31426
+ /* Content styles */
31427
+ .myio-info-tooltip__section {
31428
+ margin-bottom: 14px;
31429
+ padding-bottom: 12px;
31430
+ border-bottom: 1px solid #f1f5f9;
31431
+ }
31432
+
31433
+ .myio-info-tooltip__section:last-child {
31434
+ margin-bottom: 0;
31435
+ padding-bottom: 0;
31436
+ border-bottom: none;
31437
+ }
31438
+
31439
+ .myio-info-tooltip__section-title {
31440
+ font-size: 11px;
31441
+ font-weight: 600;
31442
+ color: #64748b;
31443
+ text-transform: uppercase;
31444
+ letter-spacing: 0.8px;
31445
+ margin-bottom: 10px;
31446
+ display: flex;
31447
+ align-items: center;
31448
+ gap: 6px;
31449
+ }
31450
+
31451
+ .myio-info-tooltip__row {
31452
+ display: flex;
31453
+ justify-content: space-between;
31454
+ align-items: center;
31455
+ padding: 5px 0;
31456
+ gap: 12px;
31457
+ }
31458
+
31459
+ .myio-info-tooltip__label {
31460
+ color: #64748b;
31461
+ font-size: 12px;
31462
+ flex-shrink: 0;
31463
+ }
31464
+
31465
+ .myio-info-tooltip__value {
31466
+ color: #1e293b;
31467
+ font-weight: 600;
31468
+ text-align: right;
31469
+ }
31470
+
31471
+ .myio-info-tooltip__value--highlight {
31472
+ color: #10b981;
31473
+ font-weight: 700;
31474
+ font-size: 14px;
31475
+ }
31476
+
31477
+ .myio-info-tooltip__notice {
31478
+ display: flex;
31479
+ align-items: flex-start;
31480
+ gap: 10px;
31481
+ padding: 12px 14px;
31482
+ background: #f0fdf4;
31483
+ border: 1px solid #bbf7d0;
31484
+ border-radius: 8px;
31485
+ margin-top: 12px;
31486
+ }
31487
+
31488
+ .myio-info-tooltip__notice-icon {
31489
+ font-size: 14px;
31490
+ flex-shrink: 0;
31491
+ margin-top: 1px;
31492
+ }
31493
+
31494
+ .myio-info-tooltip__notice-text {
31495
+ font-size: 11px;
31496
+ color: #475569;
31497
+ line-height: 1.5;
31498
+ }
31499
+
31500
+ .myio-info-tooltip__notice-text strong {
31501
+ font-weight: 700;
31502
+ color: #334155;
31503
+ }
31504
+
31505
+ .myio-info-tooltip__category {
31506
+ display: flex;
31507
+ align-items: center;
31508
+ gap: 10px;
31509
+ padding: 8px 12px;
31510
+ background: #f8fafc;
31511
+ border-radius: 8px;
31512
+ margin-bottom: 6px;
31513
+ border-left: 3px solid #94a3b8;
31514
+ }
31515
+
31516
+ .myio-info-tooltip__category:last-child {
31517
+ margin-bottom: 0;
31518
+ }
31519
+
31520
+ .myio-info-tooltip__category--climatizacao {
31521
+ border-left-color: #00C896;
31522
+ background: #ecfdf5;
31523
+ }
31524
+
31525
+ .myio-info-tooltip__category--outros {
31526
+ border-left-color: #9C27B0;
31527
+ background: #fdf4ff;
31528
+ }
31529
+
31530
+ .myio-info-tooltip__category-icon {
31531
+ font-size: 14px;
31532
+ flex-shrink: 0;
31533
+ }
31534
+
31535
+ .myio-info-tooltip__category-info {
31536
+ flex: 1;
31537
+ }
31538
+
31539
+ .myio-info-tooltip__category-name {
31540
+ font-weight: 600;
31541
+ color: #334155;
31542
+ font-size: 12px;
31543
+ }
31544
+
31545
+ .myio-info-tooltip__category-desc {
31546
+ font-size: 10px;
31547
+ color: #64748b;
31548
+ margin-top: 2px;
31549
+ }
31550
+
31551
+ .myio-info-tooltip__category-value {
31552
+ font-weight: 700;
31553
+ color: #334155;
31554
+ font-size: 13px;
31555
+ }
31556
+ `;
31557
+ var cssInjected4 = false;
31558
+ function injectCSS4() {
31559
+ if (cssInjected4) return;
31560
+ if (typeof document === "undefined") return;
31561
+ const styleId = "myio-info-tooltip-styles";
31562
+ if (document.getElementById(styleId)) {
31563
+ cssInjected4 = true;
31564
+ return;
31565
+ }
31566
+ const style = document.createElement("style");
31567
+ style.id = styleId;
31568
+ style.textContent = INFO_TOOLTIP_CSS;
31569
+ document.head.appendChild(style);
31570
+ cssInjected4 = true;
31571
+ }
31572
+ var state = {
31573
+ hideTimer: null,
31574
+ isMouseOverTooltip: false,
31575
+ isMaximized: false,
31576
+ isDragging: false,
31577
+ dragOffset: { x: 0, y: 0 },
31578
+ savedPosition: null,
31579
+ pinnedCounter: 0
31580
+ };
31581
+ function generateHeaderHTML(icon, title) {
31582
+ return `
31583
+ <div class="myio-info-tooltip__header" data-drag-handle>
31584
+ <span class="myio-info-tooltip__icon">${icon}</span>
31585
+ <span class="myio-info-tooltip__title">${title}</span>
31586
+ <div class="myio-info-tooltip__header-actions">
31587
+ <button class="myio-info-tooltip__header-btn" data-action="pin" title="Fixar na tela">
31588
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
31589
+ <path d="M9 4v6l-2 4v2h10v-2l-2-4V4"/>
31590
+ <line x1="12" y1="16" x2="12" y2="21"/>
31591
+ <line x1="8" y1="4" x2="16" y2="4"/>
31592
+ </svg>
31593
+ </button>
31594
+ <button class="myio-info-tooltip__header-btn" data-action="maximize" title="Maximizar">
31595
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
31596
+ <rect x="3" y="3" width="18" height="18" rx="2"/>
31597
+ </svg>
31598
+ </button>
31599
+ <button class="myio-info-tooltip__header-btn" data-action="close" title="Fechar">
31600
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
31601
+ <path d="M18 6L6 18M6 6l12 12"/>
31602
+ </svg>
31603
+ </button>
31604
+ </div>
31605
+ </div>
31606
+ `;
31607
+ }
31608
+ function setupHoverListeners(container) {
31609
+ container.onmouseenter = () => {
31610
+ state.isMouseOverTooltip = true;
31611
+ if (state.hideTimer) {
31612
+ clearTimeout(state.hideTimer);
31613
+ state.hideTimer = null;
31614
+ }
31615
+ };
31616
+ container.onmouseleave = () => {
31617
+ state.isMouseOverTooltip = false;
31618
+ startDelayedHide();
31619
+ };
31620
+ }
31621
+ function setupButtonListeners(container) {
31622
+ const buttons = container.querySelectorAll("[data-action]");
31623
+ buttons.forEach((btn) => {
31624
+ btn.onclick = (e) => {
31625
+ e.stopPropagation();
31626
+ const action = btn.dataset.action;
31627
+ switch (action) {
31628
+ case "pin":
31629
+ createPinnedClone(container);
31630
+ break;
31631
+ case "maximize":
31632
+ toggleMaximize(container);
31633
+ break;
31634
+ case "close":
31635
+ InfoTooltip.close();
31636
+ break;
31637
+ }
31638
+ };
31639
+ });
31640
+ }
31641
+ function setupDragListeners(container) {
31642
+ const header = container.querySelector("[data-drag-handle]");
31643
+ if (!header) return;
31644
+ header.onmousedown = (e) => {
31645
+ if (e.target.closest("[data-action]")) return;
31646
+ if (state.isMaximized) return;
31647
+ state.isDragging = true;
31648
+ container.classList.add("dragging");
31649
+ const rect = container.getBoundingClientRect();
31650
+ state.dragOffset = {
31651
+ x: e.clientX - rect.left,
31652
+ y: e.clientY - rect.top
31653
+ };
31654
+ const onMouseMove = (e2) => {
31655
+ if (!state.isDragging) return;
31656
+ const newLeft = e2.clientX - state.dragOffset.x;
31657
+ const newTop = e2.clientY - state.dragOffset.y;
31658
+ const maxLeft = window.innerWidth - container.offsetWidth;
31659
+ const maxTop = window.innerHeight - container.offsetHeight;
31660
+ container.style.left = Math.max(0, Math.min(newLeft, maxLeft)) + "px";
31661
+ container.style.top = Math.max(0, Math.min(newTop, maxTop)) + "px";
31662
+ };
31663
+ const onMouseUp = () => {
31664
+ state.isDragging = false;
31665
+ container.classList.remove("dragging");
31666
+ document.removeEventListener("mousemove", onMouseMove);
31667
+ document.removeEventListener("mouseup", onMouseUp);
31668
+ };
31669
+ document.addEventListener("mousemove", onMouseMove);
31670
+ document.addEventListener("mouseup", onMouseUp);
31671
+ };
31672
+ }
31673
+ function createPinnedClone(container) {
31674
+ state.pinnedCounter++;
31675
+ const pinnedId = `myio-info-tooltip-pinned-${state.pinnedCounter}`;
31676
+ const clone = container.cloneNode(true);
31677
+ clone.id = pinnedId;
31678
+ clone.classList.add("pinned");
31679
+ clone.classList.remove("closing");
31680
+ const pinBtn = clone.querySelector('[data-action="pin"]');
31681
+ if (pinBtn) {
31682
+ pinBtn.classList.add("pinned");
31683
+ pinBtn.setAttribute("title", "Desafixar");
31684
+ pinBtn.innerHTML = `
31685
+ <svg viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="1">
31686
+ <path d="M9 4v6l-2 4v2h10v-2l-2-4V4"/>
31687
+ <line x1="12" y1="16" x2="12" y2="21"/>
31688
+ <line x1="8" y1="4" x2="16" y2="4"/>
31689
+ </svg>
31690
+ `;
31691
+ }
31692
+ document.body.appendChild(clone);
31693
+ setupPinnedCloneListeners(clone, pinnedId);
31694
+ InfoTooltip.hide();
31695
+ }
31696
+ function setupPinnedCloneListeners(clone, cloneId) {
31697
+ let isMaximized = false;
31698
+ let savedPosition = null;
31699
+ const pinBtn = clone.querySelector('[data-action="pin"]');
31700
+ if (pinBtn) {
31701
+ pinBtn.onclick = (e) => {
31702
+ e.stopPropagation();
31703
+ closePinnedClone(cloneId);
31704
+ };
31705
+ }
31706
+ const closeBtn = clone.querySelector('[data-action="close"]');
31707
+ if (closeBtn) {
31708
+ closeBtn.onclick = (e) => {
31709
+ e.stopPropagation();
31710
+ closePinnedClone(cloneId);
31711
+ };
31712
+ }
31713
+ const maxBtn = clone.querySelector('[data-action="maximize"]');
31714
+ if (maxBtn) {
31715
+ maxBtn.onclick = (e) => {
31716
+ e.stopPropagation();
31717
+ isMaximized = !isMaximized;
31718
+ if (isMaximized) {
31719
+ savedPosition = { left: clone.style.left, top: clone.style.top };
31720
+ 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>`;
31721
+ maxBtn.setAttribute("title", "Restaurar");
31722
+ } else {
31723
+ 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>`;
31724
+ maxBtn.setAttribute("title", "Maximizar");
31725
+ if (savedPosition) {
31726
+ clone.style.left = savedPosition.left;
31727
+ clone.style.top = savedPosition.top;
31728
+ }
31729
+ }
31730
+ clone.classList.toggle("maximized", isMaximized);
31731
+ };
31732
+ }
31733
+ const header = clone.querySelector("[data-drag-handle]");
31734
+ if (header) {
31735
+ let isDragging = false;
31736
+ let dragOffset = { x: 0, y: 0 };
31737
+ header.onmousedown = (e) => {
31738
+ if (e.target.closest("[data-action]")) return;
31739
+ if (isMaximized) return;
31740
+ isDragging = true;
31741
+ clone.classList.add("dragging");
31742
+ const rect = clone.getBoundingClientRect();
31743
+ dragOffset = { x: e.clientX - rect.left, y: e.clientY - rect.top };
31744
+ const onMouseMove = (e2) => {
31745
+ if (!isDragging) return;
31746
+ const newLeft = e2.clientX - dragOffset.x;
31747
+ const newTop = e2.clientY - dragOffset.y;
31748
+ const maxLeft = window.innerWidth - clone.offsetWidth;
31749
+ const maxTop = window.innerHeight - clone.offsetHeight;
31750
+ clone.style.left = Math.max(0, Math.min(newLeft, maxLeft)) + "px";
31751
+ clone.style.top = Math.max(0, Math.min(newTop, maxTop)) + "px";
31752
+ };
31753
+ const onMouseUp = () => {
31754
+ isDragging = false;
31755
+ clone.classList.remove("dragging");
31756
+ document.removeEventListener("mousemove", onMouseMove);
31757
+ document.removeEventListener("mouseup", onMouseUp);
31758
+ };
31759
+ document.addEventListener("mousemove", onMouseMove);
31760
+ document.addEventListener("mouseup", onMouseUp);
31761
+ };
31762
+ }
31763
+ }
31764
+ function closePinnedClone(cloneId) {
31765
+ const clone = document.getElementById(cloneId);
31766
+ if (clone) {
31767
+ clone.classList.add("closing");
31768
+ setTimeout(() => clone.remove(), 400);
31769
+ }
31770
+ }
31771
+ function toggleMaximize(container) {
31772
+ state.isMaximized = !state.isMaximized;
31773
+ if (state.isMaximized) {
31774
+ state.savedPosition = {
31775
+ left: container.style.left,
31776
+ top: container.style.top
31777
+ };
31778
+ }
31779
+ container.classList.toggle("maximized", state.isMaximized);
31780
+ const maxBtn = container.querySelector('[data-action="maximize"]');
31781
+ if (maxBtn) {
31782
+ if (state.isMaximized) {
31783
+ 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>`;
31784
+ maxBtn.setAttribute("title", "Restaurar");
31785
+ } else {
31786
+ 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>`;
31787
+ maxBtn.setAttribute("title", "Maximizar");
31788
+ if (state.savedPosition) {
31789
+ container.style.left = state.savedPosition.left;
31790
+ container.style.top = state.savedPosition.top;
31791
+ }
31792
+ }
31793
+ }
31794
+ }
31795
+ function startDelayedHide() {
31796
+ if (state.isMouseOverTooltip) return;
31797
+ if (state.hideTimer) {
31798
+ clearTimeout(state.hideTimer);
31799
+ }
31800
+ state.hideTimer = setTimeout(() => {
31801
+ hideWithAnimation();
31802
+ }, 1500);
31803
+ }
31804
+ function hideWithAnimation() {
31805
+ const container = document.getElementById("myio-info-tooltip");
31806
+ if (container && container.classList.contains("visible")) {
31807
+ container.classList.add("closing");
31808
+ setTimeout(() => {
31809
+ container.classList.remove("visible", "closing");
31810
+ }, 400);
31811
+ }
31812
+ }
31813
+ function positionTooltip(container, triggerElement) {
31814
+ const rect = triggerElement.getBoundingClientRect();
31815
+ let left = rect.left;
31816
+ let top = rect.bottom + 8;
31817
+ const tooltipWidth = 380;
31818
+ if (left + tooltipWidth > window.innerWidth - 20) {
31819
+ left = window.innerWidth - tooltipWidth - 20;
31820
+ }
31821
+ if (left < 10) left = 10;
31822
+ if (top + 400 > window.innerHeight) {
31823
+ top = rect.top - 8 - 400;
31824
+ if (top < 10) top = 10;
31825
+ }
31826
+ container.style.left = left + "px";
31827
+ container.style.top = top + "px";
31828
+ }
31829
+ var InfoTooltip = {
31830
+ containerId: "myio-info-tooltip",
31831
+ /**
31832
+ * Get or create container
31833
+ */
31834
+ getContainer() {
31835
+ injectCSS4();
31836
+ let container = document.getElementById(this.containerId);
31837
+ if (!container) {
31838
+ container = document.createElement("div");
31839
+ container.id = this.containerId;
31840
+ container.className = "myio-info-tooltip";
31841
+ document.body.appendChild(container);
31842
+ }
31843
+ return container;
31844
+ },
31845
+ /**
31846
+ * Show tooltip
31847
+ */
31848
+ show(triggerElement, options) {
31849
+ if (state.hideTimer) {
31850
+ clearTimeout(state.hideTimer);
31851
+ state.hideTimer = null;
31852
+ }
31853
+ const container = this.getContainer();
31854
+ container.classList.remove("closing");
31855
+ container.innerHTML = `
31856
+ <div class="myio-info-tooltip__panel">
31857
+ ${generateHeaderHTML(options.icon, options.title)}
31858
+ <div class="myio-info-tooltip__content">
31859
+ ${options.content}
31860
+ </div>
31861
+ </div>
31862
+ `;
31863
+ positionTooltip(container, triggerElement);
31864
+ container.classList.add("visible");
31865
+ setupHoverListeners(container);
31866
+ setupButtonListeners(container);
31867
+ setupDragListeners(container);
31868
+ },
31869
+ /**
31870
+ * Start delayed hide
31871
+ */
31872
+ startDelayedHide() {
31873
+ startDelayedHide();
31874
+ },
31875
+ /**
31876
+ * Hide immediately
31877
+ */
31878
+ hide() {
31879
+ if (state.hideTimer) {
31880
+ clearTimeout(state.hideTimer);
31881
+ state.hideTimer = null;
31882
+ }
31883
+ state.isMouseOverTooltip = false;
31884
+ const container = document.getElementById(this.containerId);
31885
+ if (container) {
31886
+ container.classList.remove("visible", "closing");
31887
+ }
31888
+ },
31889
+ /**
31890
+ * Close and reset all states
31891
+ */
31892
+ close() {
31893
+ state.isMaximized = false;
31894
+ state.isDragging = false;
31895
+ state.savedPosition = null;
31896
+ if (state.hideTimer) {
31897
+ clearTimeout(state.hideTimer);
31898
+ state.hideTimer = null;
31899
+ }
31900
+ state.isMouseOverTooltip = false;
31901
+ const container = document.getElementById(this.containerId);
31902
+ if (container) {
31903
+ container.classList.remove("visible", "pinned", "maximized", "dragging", "closing");
31904
+ }
31905
+ },
31906
+ /**
31907
+ * Attach tooltip to trigger element with hover behavior
31908
+ */
31909
+ attach(triggerElement, getOptions) {
31910
+ const self = this;
31911
+ const handleMouseEnter = () => {
31912
+ if (state.hideTimer) {
31913
+ clearTimeout(state.hideTimer);
31914
+ state.hideTimer = null;
31915
+ }
31916
+ const options = getOptions();
31917
+ self.show(triggerElement, options);
31918
+ };
31919
+ const handleMouseLeave = () => {
31920
+ startDelayedHide();
31921
+ };
31922
+ triggerElement.addEventListener("mouseenter", handleMouseEnter);
31923
+ triggerElement.addEventListener("mouseleave", handleMouseLeave);
31924
+ return () => {
31925
+ triggerElement.removeEventListener("mouseenter", handleMouseEnter);
31926
+ triggerElement.removeEventListener("mouseleave", handleMouseLeave);
31927
+ self.hide();
31928
+ };
31929
+ }
31930
+ };
31931
+
30900
31932
  // src/components/ModalHeader/index.ts
30901
31933
  var DEFAULT_BG_COLOR = "#3e1a7d";
30902
31934
  var DEFAULT_TEXT_COLOR = "white";
@@ -34676,6 +35708,7 @@ function createDistributionChartWidget(config) {
34676
35708
  IMPORTANCE_COLORS,
34677
35709
  IMPORTANCE_LABELS,
34678
35710
  IMPORTANCE_LABELS_EN,
35711
+ InfoTooltip,
34679
35712
  MyIOChartModal,
34680
35713
  MyIODraggableCard,
34681
35714
  MyIOSelectionStore,