myio-js-library 0.1.160 → 0.1.162

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
@@ -569,9 +569,16 @@ var init_template_card = __esm({
569
569
  var index_exports = {};
570
570
  __export(index_exports, {
571
571
  CHART_COLORS: () => CHART_COLORS,
572
+ CONSUMPTION_CHART_COLORS: () => DEFAULT_COLORS,
573
+ CONSUMPTION_CHART_DEFAULTS: () => DEFAULT_CONFIG,
574
+ CONSUMPTION_THEME_COLORS: () => THEME_COLORS,
572
575
  ConnectionStatusType: () => ConnectionStatusType,
573
576
  DEFAULT_CLAMP_RANGE: () => DEFAULT_CLAMP_RANGE,
574
577
  DeviceStatusType: () => DeviceStatusType,
578
+ EXPORT_DEFAULT_COLORS: () => EXPORT_DEFAULT_COLORS,
579
+ EXPORT_DOMAIN_ICONS: () => EXPORT_DOMAIN_ICONS,
580
+ EXPORT_DOMAIN_LABELS: () => EXPORT_DOMAIN_LABELS,
581
+ EXPORT_DOMAIN_UNITS: () => EXPORT_DOMAIN_UNITS,
575
582
  MyIOChartModal: () => MyIOChartModal,
576
583
  MyIODraggableCard: () => MyIODraggableCard,
577
584
  MyIOSelectionStore: () => MyIOSelectionStore,
@@ -583,11 +590,13 @@ __export(index_exports, {
583
590
  averageByDay: () => averageByDay,
584
591
  buildListItemsThingsboardByUniqueDatasource: () => buildListItemsThingsboardByUniqueDatasource,
585
592
  buildMyioIngestionAuth: () => buildMyioIngestionAuth,
593
+ buildTemplateExport: () => buildTemplateExport,
586
594
  buildWaterReportCSV: () => buildWaterReportCSV,
587
595
  buildWaterStoresCSV: () => buildWaterStoresCSV,
588
596
  calcDeltaPercent: () => calcDeltaPercent,
589
597
  calculateDeviceStatus: () => calculateDeviceStatus,
590
598
  calculateDeviceStatusWithRanges: () => calculateDeviceStatusWithRanges,
599
+ calculateExportStats: () => calculateStats2,
591
600
  calculateStats: () => calculateStats,
592
601
  clampTemperature: () => clampTemperature,
593
602
  classify: () => classify,
@@ -595,8 +604,11 @@ __export(index_exports, {
595
604
  classifyWaterLabels: () => classifyWaterLabels,
596
605
  clearAllAuthCaches: () => clearAllAuthCaches,
597
606
  connectionStatusIcons: () => connectionStatusIcons,
607
+ createConsumption7DaysChart: () => createConsumption7DaysChart,
608
+ createConsumptionModal: () => createConsumptionModal,
598
609
  createDateRangePicker: () => createDateRangePicker2,
599
610
  createInputDateRangePickerInsideDIV: () => createInputDateRangePickerInsideDIV,
611
+ createModalHeader: () => createModalHeader,
600
612
  decodePayload: () => decodePayload,
601
613
  decodePayloadBase64Xor: () => decodePayloadBase64Xor,
602
614
  detectDeviceType: () => detectDeviceType,
@@ -610,6 +622,7 @@ __export(index_exports, {
610
622
  fetchThingsboardCustomerAttrsFromStorage: () => fetchThingsboardCustomerAttrsFromStorage,
611
623
  fetchThingsboardCustomerServerScopeAttrs: () => fetchThingsboardCustomerServerScopeAttrs,
612
624
  findValue: () => findValue,
625
+ findValueWithDefault: () => findValueWithDefault,
613
626
  fmtPerc: () => fmtPerc,
614
627
  fmtPercLegacy: () => fmtPerc2,
615
628
  formatAllInSameUnit: () => formatAllInSameUnit,
@@ -617,18 +630,24 @@ __export(index_exports, {
617
630
  formatDateForInput: () => formatDateForInput,
618
631
  formatDateToYMD: () => formatDateToYMD,
619
632
  formatDateWithTimezoneOffset: () => formatDateWithTimezoneOffset,
633
+ formatDuration: () => formatDuration,
620
634
  formatEnergy: () => formatEnergy,
621
635
  formatNumberReadable: () => formatNumberReadable,
636
+ formatRelativeTime: () => formatRelativeTime,
622
637
  formatTankHeadFromCm: () => formatTankHeadFromCm,
623
638
  formatTemperature: () => formatTemperature2,
639
+ formatWater: () => formatWater,
624
640
  formatWaterByGroup: () => formatWaterByGroup,
625
641
  formatWaterVolumeM3: () => formatWaterVolumeM3,
642
+ formatarDuracao: () => formatarDuracao,
643
+ generateExportFilename: () => generateFilename,
626
644
  getAuthCacheStats: () => getAuthCacheStats,
627
645
  getAvailableContexts: () => getAvailableContexts,
628
646
  getConnectionStatusIcon: () => getConnectionStatusIcon,
629
647
  getDateRangeArray: () => getDateRangeArray,
630
648
  getDeviceStatusIcon: () => getDeviceStatusIcon,
631
649
  getDeviceStatusInfo: () => getDeviceStatusInfo,
650
+ getModalHeaderStyles: () => getModalHeaderStyles,
632
651
  getSaoPauloISOString: () => getSaoPauloISOString,
633
652
  getSaoPauloISOStringFixed: () => getSaoPauloISOStringFixed,
634
653
  getValueByDatakey: () => getValueByDatakey,
@@ -640,8 +659,10 @@ __export(index_exports, {
640
659
  isValidConnectionStatus: () => isValidConnectionStatus,
641
660
  isValidDeviceStatus: () => isValidDeviceStatus,
642
661
  isWaterCategory: () => isWaterCategory,
662
+ mapConnectionStatus: () => mapConnectionStatus,
643
663
  mapDeviceStatusToCardStatus: () => mapDeviceStatusToCardStatus,
644
664
  mapDeviceToConnectionStatus: () => mapDeviceToConnectionStatus,
665
+ myioExportData: () => myioExportData,
645
666
  normalizeRecipients: () => normalizeRecipients,
646
667
  numbers: () => numbers_exports,
647
668
  openDashboardPopup: () => openDashboardPopup,
@@ -752,6 +773,10 @@ function formatNumberReadable(value, locale = "pt-BR", minimumFractionDigits = 2
752
773
  }
753
774
 
754
775
  // src/format/water.ts
776
+ function formatWater(value) {
777
+ const num = Number(value) || 0;
778
+ return `${num.toFixed(2)} m\xB3`;
779
+ }
755
780
  function formatWaterVolumeM3(value, locale = "pt-BR") {
756
781
  if (value === null || value === void 0 || isNaN(value)) {
757
782
  return "-";
@@ -829,6 +854,76 @@ function formatAllInSameWaterUnit(values) {
829
854
  };
830
855
  }
831
856
 
857
+ // src/format/time.ts
858
+ function formatRelativeTime(timestamp) {
859
+ if (!timestamp || timestamp <= 0) {
860
+ return "\u2014";
861
+ }
862
+ const now = Date.now();
863
+ const diffSeconds = Math.round((now - timestamp) / 1e3);
864
+ if (diffSeconds < 10) {
865
+ return "agora";
866
+ }
867
+ if (diffSeconds < 60) {
868
+ return `h\xE1 ${diffSeconds}s`;
869
+ }
870
+ const diffMinutes = Math.round(diffSeconds / 60);
871
+ if (diffMinutes === 1) {
872
+ return "h\xE1 1 min";
873
+ }
874
+ if (diffMinutes < 60) {
875
+ return `h\xE1 ${diffMinutes} mins`;
876
+ }
877
+ const diffHours = Math.round(diffMinutes / 60);
878
+ if (diffHours === 1) {
879
+ return "h\xE1 1 hora";
880
+ }
881
+ if (diffHours < 24) {
882
+ return `h\xE1 ${diffHours} horas`;
883
+ }
884
+ const diffDays = Math.round(diffHours / 24);
885
+ if (diffDays === 1) {
886
+ return "ontem";
887
+ }
888
+ if (diffDays <= 30) {
889
+ return `h\xE1 ${diffDays} dias`;
890
+ }
891
+ return new Date(timestamp).toLocaleDateString("pt-BR");
892
+ }
893
+ function formatarDuracao(ms) {
894
+ if (typeof ms !== "number" || ms < 0 || !isFinite(ms)) {
895
+ return "0s";
896
+ }
897
+ if (ms === 0) {
898
+ return "0s";
899
+ }
900
+ const segundos = Math.floor(ms / 1e3 % 60);
901
+ const minutos = Math.floor(ms / (1e3 * 60) % 60);
902
+ const horas = Math.floor(ms / (1e3 * 60 * 60) % 24);
903
+ const dias = Math.floor(ms / (1e3 * 60 * 60 * 24));
904
+ const parts = [];
905
+ if (dias > 0) {
906
+ parts.push(`${dias}d`);
907
+ if (horas > 0) {
908
+ parts.push(`${horas}h`);
909
+ }
910
+ } else if (horas > 0) {
911
+ parts.push(`${horas}h`);
912
+ if (minutos > 0) {
913
+ parts.push(`${minutos}m`);
914
+ }
915
+ } else if (minutos > 0) {
916
+ parts.push(`${minutos}m`);
917
+ if (segundos > 0) {
918
+ parts.push(`${segundos}s`);
919
+ }
920
+ } else {
921
+ parts.push(`${segundos}s`);
922
+ }
923
+ return parts.length > 0 ? parts.join(" ") : "0s";
924
+ }
925
+ var formatDuration = formatarDuracao;
926
+
832
927
  // src/date/ymd.ts
833
928
  function formatDateToYMD(date) {
834
929
  if (!date) {
@@ -1337,6 +1432,11 @@ function findValue(data, keyOrPath, legacyDataKey) {
1337
1432
  }
1338
1433
  return getValueByDatakey(data, keyOrPath);
1339
1434
  }
1435
+ function findValueWithDefault(values, key, defaultValue = null) {
1436
+ if (!Array.isArray(values)) return defaultValue;
1437
+ const found = values.find((v) => v.key === key || v.dataType === key);
1438
+ return found ? found.value : defaultValue;
1439
+ }
1340
1440
 
1341
1441
  // src/utils/deviceStatus.js
1342
1442
  var DeviceStatusType = {
@@ -1393,6 +1493,16 @@ function mapDeviceToConnectionStatus(deviceStatus) {
1393
1493
  }
1394
1494
  return ConnectionStatusType.CONNECTED;
1395
1495
  }
1496
+ function mapConnectionStatus(rawStatus) {
1497
+ const statusLower = String(rawStatus || "").toLowerCase().trim();
1498
+ if (statusLower === "online" || statusLower === "ok" || statusLower === "running") {
1499
+ return "online";
1500
+ }
1501
+ if (statusLower === "waiting" || statusLower === "connecting" || statusLower === "pending") {
1502
+ return "waiting";
1503
+ }
1504
+ return "offline";
1505
+ }
1396
1506
  function mapDeviceStatusToCardStatus(deviceStatus) {
1397
1507
  const statusMap = {
1398
1508
  [DeviceStatusType.POWER_ON]: "ok",
@@ -2161,8 +2271,9 @@ var MyIOSelectionStoreClass = class _MyIOSelectionStoreClass {
2161
2271
  lastValue: Number(entity.lastValue) || 0,
2162
2272
  unit: entity.unit || "",
2163
2273
  status: entity.status || "unknown",
2164
- ingestionId: entity.ingestionId || entity.id
2274
+ ingestionId: entity.ingestionId || entity.id,
2165
2275
  // ⭐ ADD: Store ingestionId for API calls
2276
+ customerName: entity.customerName || ""
2166
2277
  };
2167
2278
  this.entities.set(entity.id, normalizedEntity);
2168
2279
  }
@@ -4790,6 +4901,13 @@ var Icons = {
4790
4901
  <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"
4791
4902
  viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" focusable="false">
4792
4903
  <circle cx="12" cy="12" r="5"/>
4904
+ </svg>`,
4905
+ waterDrop: `
4906
+ <svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
4907
+ <g id="water" transform="translate(-4 -2)">
4908
+ <path id="secondary" fill="#2ca9bc" d="M19,14A7,7,0,1,1,5,14C5,8,12,3,12,3S19,8,19,14Z"/>
4909
+ <path id="primary" d="M19,14A7,7,0,1,1,5,14C5,8,12,3,12,3S19,8,19,14Z" fill="none" stroke="#000000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/>
4910
+ </g>
4793
4911
  </svg>`,
4794
4912
  // ⋮ Kebab (for actions menu)
4795
4913
  kebab: `
@@ -4818,6 +4936,7 @@ var ICON_MAP = {
4818
4936
  var DEFAULT_I18N = {
4819
4937
  // Status labels with icons
4820
4938
  in_operation: "\u26A1 Normal",
4939
+ in_operation_water: "\u{1F4A7} Normal",
4821
4940
  standby: "\u{1F4A4} Em standby",
4822
4941
  alert: "\u26A0\uFE0F Alerta",
4823
4942
  failure: "\u{1F6A8} Falha",
@@ -4874,7 +4993,13 @@ function normalizeParams(params) {
4874
4993
  }
4875
4994
  };
4876
4995
  }
4877
- function getIconSvg(deviceType) {
4996
+ function getIconSvg(deviceType, domain) {
4997
+ if (domain === "water") {
4998
+ return Icons.waterDrop;
4999
+ }
5000
+ if (domain === "temperature") {
5001
+ return Icons.thermometer;
5002
+ }
4878
5003
  return ICON_MAP[deviceType] || ICON_MAP.DEFAULT;
4879
5004
  }
4880
5005
  function formatPower(valueInWatts) {
@@ -4914,9 +5039,23 @@ function calculateConsumptionPercentage(target, consumption) {
4914
5039
  const percentage = numericConsumption / numericTarget * 100;
4915
5040
  return percentage;
4916
5041
  }
4917
- function getStatusInfo(deviceStatus, i18n) {
5042
+ function getStatusInfo(deviceStatus, i18n, domain) {
4918
5043
  switch (deviceStatus) {
5044
+ // --- Novos Status de Temperatura ---
5045
+ case "normal":
5046
+ return { chipClass: "chip--ok", label: "Normal" };
5047
+ // Verde/Azul
5048
+ case "cold":
5049
+ return { chipClass: "chip--standby", label: "Frio" };
5050
+ // Azul claro/Ciano
5051
+ case "hot":
5052
+ return { chipClass: "chip--alert", label: "Quente" };
5053
+ // Laranja/Amarelo
5054
+ // --- Status Existentes ---
4919
5055
  case DeviceStatusType.POWER_ON:
5056
+ if (domain === "water") {
5057
+ return { chipClass: "chip--ok", label: i18n.in_operation_water };
5058
+ }
4920
5059
  return { chipClass: "chip--ok", label: i18n.in_operation };
4921
5060
  case DeviceStatusType.STANDBY:
4922
5061
  return { chipClass: "chip--standby", label: i18n.standby };
@@ -4929,6 +5068,7 @@ function getStatusInfo(deviceStatus, i18n) {
4929
5068
  return { chipClass: "chip--alert", label: i18n.maintenance };
4930
5069
  case DeviceStatusType.NOT_INSTALLED:
4931
5070
  return { chipClass: "chip--offline", label: i18n.not_installed };
5071
+ // Default (Cai aqui se não achar 'normal', 'hot' etc)
4932
5072
  case DeviceStatusType.NO_INFO:
4933
5073
  default:
4934
5074
  return { chipClass: "chip--offline", label: i18n.offline };
@@ -4960,6 +5100,14 @@ function getCardStateClass(deviceStatus) {
4960
5100
  }
4961
5101
  function getStatusDotClass(deviceStatus) {
4962
5102
  switch (deviceStatus) {
5103
+ // --- Novos Status de Temperatura ---
5104
+ case "normal":
5105
+ return "dot--ok";
5106
+ case "cold":
5107
+ return "dot--standby";
5108
+ case "hot":
5109
+ return "dot--alert";
5110
+ // --- Status Existentes ---
4963
5111
  case DeviceStatusType.POWER_ON:
4964
5112
  return "dot--ok";
4965
5113
  case DeviceStatusType.STANDBY:
@@ -4987,7 +5135,7 @@ function buildDOM(state) {
4987
5135
  header.className = "myio-ho-card__header";
4988
5136
  const iconContainer = document.createElement("div");
4989
5137
  iconContainer.className = "myio-ho-card__icon";
4990
- iconContainer.innerHTML = getIconSvg(entityObject.deviceType);
5138
+ iconContainer.innerHTML = getIconSvg(entityObject.deviceType, entityObject.domain);
4991
5139
  header.appendChild(iconContainer);
4992
5140
  const titleSection = document.createElement("div");
4993
5141
  titleSection.className = "myio-ho-card__title";
@@ -5109,7 +5257,11 @@ function buildDOM(state) {
5109
5257
  powerMetric.appendChild(statusDot);
5110
5258
  const powerLabel = document.createElement("div");
5111
5259
  powerLabel.className = "label";
5112
- powerLabel.textContent = i18n.instantaneous_power || "Pot\xEAncia";
5260
+ if (entityObject.domain === "water") {
5261
+ powerLabel.textContent = "Leitura";
5262
+ } else {
5263
+ powerLabel.textContent = i18n.instantaneous_power || "Pot\xEAncia";
5264
+ }
5113
5265
  powerMetric.appendChild(powerLabel);
5114
5266
  const powerVal = document.createElement("div");
5115
5267
  powerVal.className = "val";
@@ -5133,7 +5285,7 @@ function verifyOfflineStatus(entityObject, delayTimeInMins = 15) {
5133
5285
  return true;
5134
5286
  }
5135
5287
  function paint(root, state) {
5136
- const { entityObject, i18n, delayTimeConnectionInMins } = state;
5288
+ const { entityObject, i18n, delayTimeConnectionInMins, isSelected } = state;
5137
5289
  if (entityObject.connectionStatus) {
5138
5290
  if (entityObject.connectionStatus === "offline") {
5139
5291
  entityObject.deviceStatus = DeviceStatusType.NO_INFO;
@@ -5145,16 +5297,23 @@ function paint(root, state) {
5145
5297
  }
5146
5298
  const stateClass = getCardStateClass(entityObject.deviceStatus);
5147
5299
  root.className = `myio-ho-card ${stateClass}`;
5148
- const statusInfo = getStatusInfo(entityObject.deviceStatus, i18n);
5300
+ const statusInfo = getStatusInfo(entityObject.deviceStatus, i18n, entityObject.domain);
5149
5301
  const chip = root.querySelector(".chip");
5150
5302
  chip.className = `chip ${statusInfo.chipClass}`;
5151
- chip.textContent = statusInfo.label;
5303
+ chip.innerHTML = statusInfo.label;
5152
5304
  const primaryValue = formatValueByDomain(entityObject.val, entityObject.domain);
5153
5305
  const numSpan = root.querySelector(".myio-ho-card__value .num");
5154
5306
  const unitSpan = root.querySelector(".myio-ho-card__value .unit");
5155
5307
  numSpan.textContent = primaryValue;
5156
5308
  const barContainer = root.querySelector(".bar");
5157
5309
  const effContainer = root.querySelector(".myio-ho-card__eff");
5310
+ if (state.enableSelection) {
5311
+ const checkbox = root.querySelector('.myio-ho-card__select input[type="checkbox"]');
5312
+ if (checkbox) {
5313
+ checkbox.checked = !!isSelected;
5314
+ }
5315
+ root.classList.toggle("is-selected", !!isSelected);
5316
+ }
5158
5317
  const targetValue = entityObject.consumptionTargetValue;
5159
5318
  if (targetValue) {
5160
5319
  barContainer.style.display = "";
@@ -5176,9 +5335,14 @@ function paint(root, state) {
5176
5335
  }
5177
5336
  const powerVal = root.querySelector(".myio-ho-card__footer .metric:nth-child(2) .val");
5178
5337
  if (powerVal) {
5179
- const instantPower = entityObject.instantaneousPower ?? entityObject.consumption_power ?? null;
5180
- const powerFormatted = formatPower(instantPower);
5181
- powerVal.textContent = instantPower !== null ? `${powerFormatted.num} ${powerFormatted.unit}` : "-";
5338
+ if (entityObject.domain === "water") {
5339
+ const pulses = entityObject.pulses ?? 0;
5340
+ powerVal.textContent = `${pulses} L`;
5341
+ } else {
5342
+ const instantPower = entityObject.instantaneousPower ?? entityObject.consumption_power ?? null;
5343
+ const powerFormatted = formatPower(instantPower);
5344
+ powerVal.textContent = instantPower !== null ? `${powerFormatted.num} ${powerFormatted.unit}` : "-";
5345
+ }
5182
5346
  }
5183
5347
  const statusDot = root.querySelector(".myio-ho-card__footer .metric:nth-child(2) .status-dot");
5184
5348
  if (statusDot) {
@@ -5238,6 +5402,26 @@ function bindEvents(root, state, callbacks) {
5238
5402
  callbacks.handleActionSettings(e, entityObject);
5239
5403
  });
5240
5404
  }
5405
+ const MyIOSelectionStore2 = window.MyIOLibrary?.MyIOSelectionStore || window.MyIOSelectionStore;
5406
+ if (MyIOSelectionStore2) {
5407
+ const onSelectionChange = () => {
5408
+ const selectedIds = MyIOSelectionStore2.getSelectedIds();
5409
+ const isSelected = selectedIds.includes(entityObject.entityId);
5410
+ if (state.isSelected !== isSelected) {
5411
+ state.isSelected = isSelected;
5412
+ paint(root, state);
5413
+ }
5414
+ };
5415
+ MyIOSelectionStore2.on("selection:change", onSelectionChange);
5416
+ root._selectionListener = onSelectionChange;
5417
+ }
5418
+ root._cleanup = () => {
5419
+ document.removeEventListener("click", closeMenu);
5420
+ document.removeEventListener("keydown", closeMenu);
5421
+ if (MyIOSelectionStore2 && root._selectionListener) {
5422
+ MyIOSelectionStore2.off("selection:change", root._selectionListener);
5423
+ }
5424
+ };
5241
5425
  const checkbox = root.querySelector('.myio-ho-card__select input[type="checkbox"]');
5242
5426
  if (checkbox && callbacks.handleSelect) {
5243
5427
  checkbox.addEventListener("change", (e) => {
@@ -5305,6 +5489,7 @@ function renderCardComponentHeadOffice(containerEl, params) {
5305
5489
  ensureCss();
5306
5490
  const state = normalizeParams(params);
5307
5491
  const root = buildDOM(state);
5492
+ state.isSelected = params.isSelected || false;
5308
5493
  containerEl.appendChild(root);
5309
5494
  bindEvents(root, state, state.callbacks);
5310
5495
  paint(root, state);
@@ -7731,14 +7916,14 @@ async function openRealTimeTelemetryModal(params) {
7731
7916
  return `${value.toFixed(config.decimals)} ${config.unit}`;
7732
7917
  }
7733
7918
  function initializeChart() {
7734
- const Chart = window.Chart;
7735
- if (!Chart) {
7919
+ const Chart2 = window.Chart;
7920
+ if (!Chart2) {
7736
7921
  console.warn("[RealTimeTelemetry] Chart.js not loaded");
7737
7922
  return;
7738
7923
  }
7739
7924
  chartContainer.style.display = "block";
7740
7925
  const config = TELEMETRY_CONFIG[selectedChartKey] || { label: selectedChartKey, unit: "" };
7741
- chart = new Chart(chartCanvas, {
7926
+ chart = new Chart2(chartCanvas, {
7742
7927
  type: "line",
7743
7928
  data: {
7744
7929
  datasets: [{
@@ -10507,7 +10692,7 @@ async function openDemandModal(params) {
10507
10692
  params.timezoneOffset
10508
10693
  );
10509
10694
  if (!newChartData.isEmpty && chart && chartData) {
10510
- const Chart = window.Chart;
10695
+ const Chart2 = window.Chart;
10511
10696
  newChartData.series.forEach((newSeries, seriesIndex) => {
10512
10697
  if (newSeries.points.length > 0 && chart.data.datasets[seriesIndex]) {
10513
10698
  newSeries.points.forEach((point) => {
@@ -10736,8 +10921,8 @@ async function openDemandModal(params) {
10736
10921
  peakEl.textContent = `${strings.maximum}: ${peak.formattedValue} kW ${peak.key ? `(${peak.key}) ` : ""}${strings.at} ${dateStr}`;
10737
10922
  peakEl.style.display = "block";
10738
10923
  }
10739
- const Chart = window.Chart;
10740
- Chart.register(window.ChartZoom);
10924
+ const Chart2 = window.Chart;
10925
+ Chart2.register(window.ChartZoom);
10741
10926
  if (chart) {
10742
10927
  chart.data.datasets = chartData.series.map((series) => ({
10743
10928
  label: series.label,
@@ -10789,7 +10974,7 @@ async function openDemandModal(params) {
10789
10974
  };
10790
10975
  chart.update();
10791
10976
  } else {
10792
- chart = new Chart(chartCanvas, {
10977
+ chart = new Chart2(chartCanvas, {
10793
10978
  type: "line",
10794
10979
  data: {
10795
10980
  datasets: chartData.series.map((series) => ({
@@ -11048,11 +11233,13 @@ var EnergyModalView = class {
11048
11233
  console.log("[EnergyModalView] Bar mode toggled to:", this.currentBarMode);
11049
11234
  }
11050
11235
  /**
11051
- * Applies the current theme to the modal and charts
11052
- */
11236
+ * Applies the current theme to the modal and charts (Updated with #root override)
11237
+ */
11053
11238
  applyTheme() {
11054
11239
  const themeToggleBtn = document.getElementById("theme-toggle-btn");
11055
- const modalContent = document.querySelector(".myio-energy-modal-scope");
11240
+ const modalContent = this.container;
11241
+ const externalBody = this.container?.closest(".myio-modal-body") || document.querySelector(".myio-modal-body");
11242
+ const rootDiv = document.querySelector("#root > div");
11056
11243
  if (themeToggleBtn) {
11057
11244
  const sunIcon = themeToggleBtn.querySelector(".myio-theme-icon-sun");
11058
11245
  const moonIcon = themeToggleBtn.querySelector(".myio-theme-icon-moon");
@@ -11065,6 +11252,14 @@ var EnergyModalView = class {
11065
11252
  moonIcon.style.opacity = "0";
11066
11253
  moonIcon.style.transform = "translate(-50%, -50%) rotate(90deg) scale(0)";
11067
11254
  }
11255
+ if (externalBody) {
11256
+ externalBody.style.backgroundColor = "#ffffff";
11257
+ externalBody.style.color = "#1f2937";
11258
+ }
11259
+ if (rootDiv) {
11260
+ rootDiv.style.backgroundColor = "#ffffff";
11261
+ rootDiv.style.color = "#1f2937";
11262
+ }
11068
11263
  } else {
11069
11264
  if (sunIcon) {
11070
11265
  sunIcon.style.opacity = "0";
@@ -11074,6 +11269,14 @@ var EnergyModalView = class {
11074
11269
  moonIcon.style.opacity = "1";
11075
11270
  moonIcon.style.transform = "translate(-50%, -50%) rotate(0deg) scale(1)";
11076
11271
  }
11272
+ if (externalBody) {
11273
+ externalBody.style.backgroundColor = "#1f1f1f";
11274
+ externalBody.style.color = "#f3f4f6";
11275
+ }
11276
+ if (rootDiv) {
11277
+ rootDiv.style.backgroundColor = "#1f1f1f";
11278
+ rootDiv.style.color = "#f3f4f6";
11279
+ }
11077
11280
  }
11078
11281
  }
11079
11282
  if (modalContent) {
@@ -11302,7 +11505,7 @@ var EnergyModalView = class {
11302
11505
  ${this.config.params.mode === "comparison" ? `
11303
11506
  <!-- RFC-0097: Granularity Selector (only 1h and 1d supported) -->
11304
11507
  <div class="myio-granularity-selector" style="display: flex; align-items: center; gap: 4px; margin-left: 8px; padding: 4px 8px; background: rgba(0,0,0,0.05); border-radius: 8px;">
11305
- <span style="font-size: 11px; color: #666; margin-right: 4px; white-space: nowrap;">Granularidade:</span>
11508
+ <span class="myio-label-secondary" style="font-size: 11px; margin-right: 4px; white-space: nowrap;">Granularidade:</span>
11306
11509
  <button class="myio-btn myio-btn-granularity ${this.currentGranularity === "1h" ? "active" : ""}" data-granularity="1h" title="Hora">1h</button>
11307
11510
  <button class="myio-btn myio-btn-granularity ${this.currentGranularity === "1d" ? "active" : ""}" data-granularity="1d" title="Dia">1d</button>
11308
11511
  </div>
@@ -11984,93 +12187,159 @@ var EnergyModalView = class {
11984
12187
  return i18n[key] || DEFAULT_I18N2[key];
11985
12188
  }
11986
12189
  /**
11987
- * Gets modal styles with header color matching openDashboardPopupEnergy
11988
- */
12190
+ * Gets modal styles with fixed Dark/Light contrast
12191
+ */
12192
+ /**
12193
+ * Gets modal styles with fixed Chart Border for Light Mode
12194
+ */
11989
12195
  getModalStyles() {
11990
12196
  const styles = this.config.params.styles || {};
12197
+ const defaultPrimary = styles.primaryColor || "#4A148C";
12198
+ const defaultFont = styles.fontFamily || '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
11991
12199
  return `
12200
+ /* --- VARI\xC1VEIS DE TEMA (LIGHT MODE - PADR\xC3O) --- */
11992
12201
  .myio-energy-modal-scope {
11993
- --myio-energy-primary: ${styles.primaryColor || "#4A148C"};
11994
- --myio-energy-bg: ${styles.backgroundColor || "#ffffff"};
11995
- --myio-energy-text: ${styles.textColor || "#1f2937"};
11996
- --myio-energy-border: ${styles.borderColor || "#e5e7eb"};
11997
- --myio-energy-radius: ${styles.borderRadius || "8px"};
11998
- --myio-energy-font: ${styles.fontFamily || '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'};
12202
+ --myio-energy-primary: ${defaultPrimary};
12203
+ --myio-energy-font: ${defaultFont};
12204
+ --myio-energy-radius: 8px;
12205
+
12206
+ /* Cores Gerais (Light Mode) */
12207
+ --myio-energy-bg: #ffffff;
12208
+ --myio-energy-text: #1f2937;
12209
+ --myio-energy-text-secondary: #6b7280;
12210
+
12211
+ /* Borda GERAL (para bot\xF5es e inputs) - Cinza suave */
12212
+ --myio-energy-border: #e5e7eb;
12213
+
12214
+ /* Borda ESPEC\xCDFICA DO GR\xC1FICO (Aqui est\xE1 a corre\xE7\xE3o) */
12215
+ /* No Light Mode, definimos como transparente ou branco para "sumir" */
12216
+ --myio-chart-border: transparent;
12217
+
12218
+ --myio-energy-btn-bg: #f3f4f6;
12219
+ --myio-energy-btn-hover: #e5e7eb;
12220
+ --myio-energy-input-bg: #ffffff;
12221
+ --myio-granularity-bg: #f9fafb;
11999
12222
 
12000
12223
  font-family: var(--myio-energy-font);
12224
+ background-color: var(--myio-energy-bg);
12001
12225
  color: var(--myio-energy-text);
12002
12226
  height: -webkit-fill-available;
12227
+ transition: background-color 0.3s ease, color 0.3s ease;
12003
12228
  }
12004
12229
 
12005
- .myio-btn {
12006
- padding: 8px 16px;
12007
- border-radius: var(--myio-energy-radius);
12008
- border: 1px solid var(--myio-energy-border);
12009
- background: var(--myio-energy-bg);
12010
- color: var(--myio-energy-text);
12011
- cursor: pointer;
12012
- font-size: 14px;
12013
- transition: all 0.2s;
12014
- }
12230
+ /* --- DARK MODE OVERRIDES --- */
12231
+ .myio-energy-modal-scope[data-theme="dark"] {
12232
+ --myio-energy-bg: #1f1f1f;
12233
+ --myio-energy-text: #f3f4f6;
12234
+ --myio-energy-text-secondary: #9ca3af;
12235
+
12236
+ /* No Dark Mode, as bordas precisam aparecer */
12237
+ --myio-energy-border: #374151;
12238
+ --myio-chart-border: #374151; /* Borda vis\xEDvel no escuro */
12015
12239
 
12016
- .myio-btn:hover:not(:disabled) {
12017
- background: #f3f4f6;
12240
+ --myio-energy-btn-bg: #374151;
12241
+ --myio-energy-btn-hover: #4b5563;
12242
+ --myio-energy-input-bg: #111827;
12243
+ --myio-granularity-bg: #111827;
12018
12244
  }
12019
12245
 
12020
- .myio-btn:disabled {
12021
- opacity: 0.5;
12022
- cursor: not-allowed;
12023
- }
12246
+ /* --- COMPONENTES --- */
12024
12247
 
12025
- .myio-btn-primary {
12026
- background: var(--myio-energy-primary);
12027
- color: white;
12028
- border-color: var(--myio-energy-primary);
12248
+ .myio-energy-chart-container {
12249
+ flex: 1 !important;
12250
+ min-height: 353px !important;
12251
+ height: 353px !important;
12252
+ background: var(--myio-energy-bg);
12253
+ border-radius: var(--myio-energy-radius);
12254
+
12255
+ /* USO DA VARI\xC1VEL ESPEC\xCDFICA AQUI */
12256
+ border: 1px solid var(--myio-chart-border);
12257
+
12258
+ padding: 10px !important;
12259
+ display: block !important;
12260
+ overflow: hidden !important;
12029
12261
  }
12030
12262
 
12031
- .myio-btn-primary:hover:not(:disabled) {
12032
- background: #6A1B9A;
12263
+ .myio-chart-container {
12264
+ /* Aplica a mesma l\xF3gica para o gr\xE1fico de fallback */
12265
+ border: 1px solid var(--myio-chart-border);
12266
+ border-radius: var(--myio-energy-radius);
12267
+ overflow: hidden;
12033
12268
  }
12034
12269
 
12035
- .myio-btn-secondary {
12036
- background: #f3f4f6;
12037
- color: var(--myio-energy-text);
12038
- border-color: var(--myio-energy-border);
12270
+ /* --- Resto dos estilos (Bot\xF5es, Labels, etc.) --- */
12271
+
12272
+ .myio-label-secondary {
12273
+ color: var(--myio-energy-text-secondary);
12274
+ font-weight: 500;
12039
12275
  }
12040
12276
 
12041
- .myio-btn-secondary:hover:not(:disabled) {
12042
- background: #e5e7eb;
12277
+ .myio-granularity-selector {
12278
+ display: flex;
12279
+ align-items: center;
12280
+ gap: 4px;
12281
+ margin-left: 8px;
12282
+ padding: 4px 8px;
12283
+ border-radius: 8px;
12284
+ background: var(--myio-granularity-bg);
12285
+ border: 1px solid var(--myio-energy-border); /* Granularidade mant\xE9m borda suave */
12043
12286
  }
12044
12287
 
12045
- /* RFC-0097: Granularity selector buttons */
12046
12288
  .myio-btn-granularity {
12047
12289
  padding: 4px 10px;
12048
12290
  font-size: 12px;
12049
12291
  font-weight: 600;
12050
12292
  border-radius: 6px;
12051
- border: 1px solid var(--myio-energy-border);
12052
- background: var(--myio-energy-bg);
12053
- color: var(--myio-energy-text);
12054
12293
  cursor: pointer;
12055
12294
  transition: all 0.2s ease;
12056
12295
  min-width: 36px;
12296
+ background: transparent;
12297
+ border: 1px solid transparent;
12298
+ color: var(--myio-energy-text-secondary);
12057
12299
  }
12058
12300
 
12059
12301
  .myio-btn-granularity:hover:not(.active) {
12060
- background: #f3f4f6;
12061
- border-color: var(--myio-energy-primary);
12062
- color: var(--myio-energy-primary);
12302
+ background: var(--myio-energy-btn-hover);
12303
+ color: var(--myio-energy-text);
12063
12304
  }
12064
12305
 
12065
12306
  .myio-btn-granularity.active {
12066
12307
  background: var(--myio-energy-primary);
12067
12308
  color: white;
12068
12309
  border-color: var(--myio-energy-primary);
12069
- box-shadow: 0 2px 4px rgba(74, 20, 140, 0.25);
12310
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
12070
12311
  }
12071
12312
 
12072
- .myio-granularity-selector {
12313
+ .myio-btn {
12314
+ padding: 8px 16px;
12315
+ border-radius: var(--myio-energy-radius);
12073
12316
  border: 1px solid var(--myio-energy-border);
12317
+ background: var(--myio-energy-btn-bg);
12318
+ color: var(--myio-energy-text);
12319
+ cursor: pointer;
12320
+ font-size: 14px;
12321
+ transition: all 0.2s;
12322
+ }
12323
+
12324
+ .myio-btn:hover:not(:disabled) {
12325
+ background: var(--myio-energy-btn-hover);
12326
+ border-color: var(--myio-energy-border);
12327
+ }
12328
+
12329
+ .myio-btn-primary {
12330
+ background: var(--myio-energy-primary);
12331
+ color: white;
12332
+ border-color: var(--myio-energy-primary);
12333
+ }
12334
+
12335
+ .myio-btn-primary:hover:not(:disabled) {
12336
+ opacity: 0.9;
12337
+ }
12338
+
12339
+ .myio-btn-secondary {
12340
+ background: var(--myio-energy-btn-bg);
12341
+ color: var(--myio-energy-text);
12342
+ border-color: var(--myio-energy-border);
12074
12343
  }
12075
12344
 
12076
12345
  .myio-modal-scope {
@@ -12079,42 +12348,30 @@ var EnergyModalView = class {
12079
12348
  flex-direction: column !important;
12080
12349
  }
12081
12350
 
12082
- .myio-energy-chart-container {
12083
- flex: 1 !important;
12084
- min-height: 353px !important;
12085
- height: 353px !important;
12086
- background: var(--myio-energy-bg);
12087
- border-radius: var(--myio-energy-radius);
12088
- border: 1px solid var(--myio-energy-border);
12089
- padding: 10px !important;
12090
- display: block !important;
12091
- overflow: hidden !important;
12092
- }
12093
-
12094
- .myio-energy-chart-container > iframe {
12095
- width: 100% !important;
12096
- height: 100% !important;
12097
- min-height: 408px !important;
12098
- border: none !important;
12351
+ .myio-form-group {
12352
+ display: flex;
12353
+ flex-direction: column;
12099
12354
  }
12100
12355
 
12101
- .myio-energy-chart-container iframe,
12102
- .myio-energy-chart-container iframe body,
12103
- .myio-energy-chart-container iframe html {
12104
- height: 100% !important;
12105
- min-height: 408px !important;
12356
+ .myio-label {
12357
+ font-weight: 500;
12358
+ margin-bottom: 5px;
12359
+ color: var(--myio-energy-text);
12106
12360
  }
12107
12361
 
12108
- .myio-energy-chart-container .chart-wrapper,
12109
- .myio-energy-chart-container .chart-container,
12110
- .myio-energy-chart-container canvas,
12111
- .myio-energy-chart-container svg {
12112
- height: 408px !important;
12113
- min-height: 408px !important;
12362
+ .myio-input {
12363
+ padding: 8px 12px;
12364
+ border: 1px solid var(--myio-energy-border);
12365
+ border-radius: var(--myio-energy-radius);
12366
+ font-size: 14px;
12367
+ background: var(--myio-energy-input-bg);
12368
+ color: var(--myio-energy-text);
12114
12369
  }
12115
12370
 
12116
- .myio-loading-state {
12117
- text-align: center;
12371
+ .myio-input:focus {
12372
+ outline: none;
12373
+ border-color: var(--myio-energy-primary);
12374
+ box-shadow: 0 0 0 1px var(--myio-energy-primary);
12118
12375
  }
12119
12376
 
12120
12377
  .myio-spinner {
@@ -12126,78 +12383,40 @@ var EnergyModalView = class {
12126
12383
  animation: spin 1s linear infinite;
12127
12384
  margin: 0 auto 16px;
12128
12385
  }
12129
-
12386
+
12130
12387
  @keyframes spin {
12131
12388
  0% { transform: rotate(0deg); }
12132
12389
  100% { transform: rotate(360deg); }
12133
12390
  }
12134
12391
 
12392
+ .myio-loading-state p {
12393
+ color: var(--myio-energy-text);
12394
+ }
12395
+
12135
12396
  .myio-energy-error {
12136
- background: #fef2f2;
12137
- border: 1px solid #fecaca;
12397
+ background: rgba(254, 202, 202, 0.15);
12398
+ border: 1px solid rgba(248, 113, 113, 0.5);
12138
12399
  border-radius: var(--myio-energy-radius);
12139
12400
  padding: 16px;
12140
12401
  }
12141
-
12142
- .myio-error-content {
12143
- display: flex;
12144
- align-items: center;
12145
- gap: 12px;
12146
- }
12147
-
12148
- .myio-error-icon {
12149
- font-size: 24px;
12150
- }
12151
-
12402
+
12152
12403
  .myio-error-message {
12153
- color: #dc2626;
12404
+ color: #ef4444;
12154
12405
  font-weight: 500;
12155
12406
  }
12156
12407
 
12157
-
12158
-
12159
- .myio-fallback-chart h4 {
12160
- margin: 0 0 16px 0;
12161
- text-align: center;
12162
- }
12163
-
12164
- .myio-chart-container {
12165
- border: 1px solid var(--myio-energy-border);
12166
- border-radius: var(--myio-energy-radius);
12167
- overflow: hidden;
12168
- }
12169
-
12170
12408
  .myio-chart-note {
12171
12409
  margin: 12px 0 0 0;
12172
12410
  font-size: 12px;
12173
- color: #666;
12174
- text-align: center;
12175
- }
12176
-
12177
- .myio-form-group {
12178
- display: flex;
12179
- flex-direction: column;
12180
- }
12181
-
12182
- .myio-label {
12183
- font-weight: 500;
12184
- margin-bottom: 5px;
12185
- color: var(--myio-energy-text);
12186
- }
12187
-
12188
- .myio-input {
12189
- padding: 8px 12px;
12190
- border: 1px solid var(--myio-energy-border);
12191
- border-radius: var(--myio-energy-radius);
12192
- font-size: 14px;
12193
- background: var(--myio-energy-bg);
12194
12411
  color: var(--myio-energy-text);
12412
+ opacity: 0.7;
12413
+ text-align: center;
12195
12414
  }
12196
12415
 
12197
- .myio-input:focus {
12198
- outline: none;
12199
- border-color: var(--myio-energy-primary);
12200
- box-shadow: 0 0 0 3px rgba(74, 20, 140, 0.1);
12416
+ .myio-energy-chart-container > iframe {
12417
+ width: 100% !important;
12418
+ height: 100% !important;
12419
+ border: none !important;
12201
12420
  }
12202
12421
 
12203
12422
  @media (max-width: 768px) {
@@ -12205,12 +12424,6 @@ var EnergyModalView = class {
12205
12424
  grid-template-columns: 1fr;
12206
12425
  grid-template-rows: auto 1fr;
12207
12426
  }
12208
-
12209
- .myio-energy-header {
12210
- flex-direction: column;
12211
- gap: 12px;
12212
- align-items: stretch;
12213
- }
12214
12427
  }
12215
12428
  `;
12216
12429
  }
@@ -18934,8 +19147,8 @@ function openGoalsPanel(params) {
18934
19147
  <div class="myio-goals-progress-fill" style="width: ${Math.min(progress, 100)}%"></div>
18935
19148
  </div>
18936
19149
  <div class="myio-goals-progress-text">
18937
- <span>${formatNumber2(monthlySum, locale)} ${annual.unit}</span>
18938
- <span>${formatNumber2(annual.total, locale)} ${annual.unit}</span>
19150
+ <span>${formatNumber3(monthlySum, locale)} ${annual.unit}</span>
19151
+ <span>${formatNumber3(annual.total, locale)} ${annual.unit}</span>
18939
19152
  </div>
18940
19153
 
18941
19154
  <!-- Monthly Grid -->
@@ -19009,7 +19222,7 @@ function openGoalsPanel(params) {
19009
19222
  <span>${assetData.label || assetId}</span>
19010
19223
  </div>
19011
19224
  <div class="myio-goals-asset-total">
19012
- ${formatNumber2(assetData.annual?.total || 0, locale)} ${assetData.annual?.unit || "kWh"}
19225
+ ${formatNumber3(assetData.annual?.total || 0, locale)} ${assetData.annual?.unit || "kWh"}
19013
19226
  </div>
19014
19227
  <button class="myio-goals-btn-icon" data-action="delete-asset" data-asset-id="${assetId}" aria-label="${i18n.deleteAsset}">
19015
19228
  <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
@@ -19324,7 +19537,7 @@ function openGoalsPanel(params) {
19324
19537
  monthlySum += value;
19325
19538
  }
19326
19539
  if (monthlySum > annualTotal && annualTotal > 0) {
19327
- errors.push(`${i18n.errorMonthlyExceedsAnnual} (${formatNumber2(monthlySum, locale)} > ${formatNumber2(annualTotal, locale)})`);
19540
+ errors.push(`${i18n.errorMonthlyExceedsAnnual} (${formatNumber3(monthlySum, locale)} > ${formatNumber3(annualTotal, locale)})`);
19328
19541
  }
19329
19542
  }
19330
19543
  return errors;
@@ -19415,8 +19628,8 @@ function openGoalsPanel(params) {
19415
19628
  }
19416
19629
  if (progressTexts.length === 2) {
19417
19630
  const unit = document.getElementById("unit-select")?.value || "kWh";
19418
- progressTexts[0].textContent = `${formatNumber2(monthlySum, locale)} ${unit}`;
19419
- progressTexts[1].textContent = `${formatNumber2(annualTotal, locale)} ${unit}`;
19631
+ progressTexts[0].textContent = `${formatNumber3(monthlySum, locale)} ${unit}`;
19632
+ progressTexts[1].textContent = `${formatNumber3(annualTotal, locale)} ${unit}`;
19420
19633
  }
19421
19634
  }
19422
19635
  function updateMonthlyUnits(unit) {
@@ -19514,7 +19727,7 @@ function openGoalsPanel(params) {
19514
19727
  modal.addEventListener("keydown", handleTab);
19515
19728
  firstElement.focus();
19516
19729
  }
19517
- function formatNumber2(value, locale2) {
19730
+ function formatNumber3(value, locale2) {
19518
19731
  return new Intl.NumberFormat(locale2, {
19519
19732
  minimumFractionDigits: 0,
19520
19733
  maximumFractionDigits: 2
@@ -22464,12 +22677,1743 @@ function openTemperatureSettingsModal(params) {
22464
22677
  });
22465
22678
  return { destroy };
22466
22679
  }
22680
+
22681
+ // src/components/ModalHeader/index.ts
22682
+ var DEFAULT_BG_COLOR = "#3e1a7d";
22683
+ var DEFAULT_TEXT_COLOR = "white";
22684
+ var DEFAULT_BORDER_RADIUS = "10px 10px 0 0";
22685
+ var EXPORT_FORMAT_LABELS = {
22686
+ csv: "CSV",
22687
+ xls: "Excel (XLS)",
22688
+ pdf: "PDF"
22689
+ };
22690
+ var EXPORT_FORMAT_ICONS = {
22691
+ csv: "\u{1F4C4}",
22692
+ xls: "\u{1F4CA}",
22693
+ pdf: "\u{1F4D1}"
22694
+ };
22695
+ function createModalHeader(config) {
22696
+ let currentTheme = config.theme || "light";
22697
+ let currentIsMaximized = config.isMaximized || false;
22698
+ let currentTitle = config.title;
22699
+ let themeBtn = null;
22700
+ let maximizeBtn = null;
22701
+ let closeBtn = null;
22702
+ let exportBtn = null;
22703
+ let exportDropdown = null;
22704
+ const cleanupHandlers = [];
22705
+ const handleThemeClick = () => {
22706
+ currentTheme = currentTheme === "light" ? "dark" : "light";
22707
+ config.onThemeToggle?.(currentTheme);
22708
+ updateButtonIcons();
22709
+ };
22710
+ const handleMaximizeClick = () => {
22711
+ currentIsMaximized = !currentIsMaximized;
22712
+ config.onMaximize?.(currentIsMaximized);
22713
+ updateButtonIcons();
22714
+ };
22715
+ const handleCloseClick = () => {
22716
+ config.onClose?.();
22717
+ };
22718
+ const handleExportClick = (format) => {
22719
+ config.onExport?.(format);
22720
+ if (exportDropdown) {
22721
+ exportDropdown.style.display = "none";
22722
+ }
22723
+ };
22724
+ function updateButtonIcons() {
22725
+ if (themeBtn) {
22726
+ themeBtn.textContent = currentTheme === "dark" ? "\u2600\uFE0F" : "\u{1F319}";
22727
+ themeBtn.title = currentTheme === "dark" ? "Modo claro" : "Modo escuro";
22728
+ }
22729
+ if (maximizeBtn) {
22730
+ maximizeBtn.textContent = currentIsMaximized ? "\u{1F5D7}" : "\u{1F5D6}";
22731
+ maximizeBtn.title = currentIsMaximized ? "Restaurar" : "Maximizar";
22732
+ }
22733
+ }
22734
+ function getButtonStyle() {
22735
+ return `
22736
+ background: none;
22737
+ border: none;
22738
+ font-size: 16px;
22739
+ cursor: pointer;
22740
+ padding: 4px 8px;
22741
+ border-radius: 6px;
22742
+ color: rgba(255, 255, 255, 0.8);
22743
+ transition: background-color 0.2s, color 0.2s;
22744
+ `.replace(/\s+/g, " ").trim();
22745
+ }
22746
+ function renderExportDropdown() {
22747
+ const formats = config.exportFormats || [];
22748
+ if (formats.length === 0) return "";
22749
+ if (formats.length === 1) {
22750
+ return `
22751
+ <button id="${config.id}-export" title="Exportar ${EXPORT_FORMAT_LABELS[formats[0]]}" style="${getButtonStyle()}">
22752
+ \u{1F4E5}
22753
+ </button>
22754
+ `;
22755
+ }
22756
+ return `
22757
+ <div style="position: relative; display: inline-block;">
22758
+ <button id="${config.id}-export-btn" title="Exportar" style="${getButtonStyle()}">
22759
+ \u{1F4E5}
22760
+ </button>
22761
+ <div id="${config.id}-export-dropdown" style="
22762
+ display: none;
22763
+ position: absolute;
22764
+ top: 100%;
22765
+ right: 0;
22766
+ background: white;
22767
+ border-radius: 6px;
22768
+ box-shadow: 0 4px 12px rgba(0,0,0,0.15);
22769
+ min-width: 140px;
22770
+ z-index: 10001;
22771
+ margin-top: 4px;
22772
+ overflow: hidden;
22773
+ ">
22774
+ ${formats.map((format) => `
22775
+ <button
22776
+ id="${config.id}-export-${format}"
22777
+ class="myio-export-option"
22778
+ data-format="${format}"
22779
+ style="
22780
+ display: flex;
22781
+ align-items: center;
22782
+ gap: 8px;
22783
+ width: 100%;
22784
+ padding: 10px 14px;
22785
+ border: none;
22786
+ background: white;
22787
+ cursor: pointer;
22788
+ font-size: 13px;
22789
+ color: #333;
22790
+ text-align: left;
22791
+ transition: background-color 0.2s;
22792
+ "
22793
+ >
22794
+ ${EXPORT_FORMAT_ICONS[format]} ${EXPORT_FORMAT_LABELS[format]}
22795
+ </button>
22796
+ `).join("")}
22797
+ </div>
22798
+ </div>
22799
+ `;
22800
+ }
22801
+ const instance = {
22802
+ render() {
22803
+ const bgColor = config.backgroundColor || DEFAULT_BG_COLOR;
22804
+ const textColor = config.textColor || DEFAULT_TEXT_COLOR;
22805
+ const borderRadius = currentIsMaximized ? "0" : config.borderRadius || DEFAULT_BORDER_RADIUS;
22806
+ const showTheme = config.showThemeToggle !== false;
22807
+ const showMax = config.showMaximize !== false;
22808
+ const showClose = config.showClose !== false;
22809
+ const showExport = config.exportFormats && config.exportFormats.length > 0;
22810
+ const iconHtml = config.icon ? `<span style="margin-right: 8px;">${config.icon}</span>` : "";
22811
+ const buttonStyle = getButtonStyle();
22812
+ return `
22813
+ <div class="myio-modal-header" style="
22814
+ padding: 4px 8px;
22815
+ display: flex;
22816
+ align-items: center;
22817
+ justify-content: space-between;
22818
+ background: ${bgColor};
22819
+ color: ${textColor};
22820
+ border-radius: ${borderRadius};
22821
+ min-height: 20px;
22822
+ font-family: 'Roboto', Arial, sans-serif;
22823
+ ">
22824
+ <h2 id="${config.id}-header-title" style="
22825
+ margin: 6px;
22826
+ font-size: 18px;
22827
+ font-weight: 600;
22828
+ color: ${textColor};
22829
+ line-height: 2;
22830
+ display: flex;
22831
+ align-items: center;
22832
+ ">
22833
+ ${iconHtml}${currentTitle}
22834
+ </h2>
22835
+ <div style="display: flex; gap: 4px; align-items: center;">
22836
+ ${showExport ? renderExportDropdown() : ""}
22837
+ ${showTheme ? `
22838
+ <button id="${config.id}-theme-toggle" title="${currentTheme === "dark" ? "Modo claro" : "Modo escuro"}" style="${buttonStyle}">
22839
+ ${currentTheme === "dark" ? "\u2600\uFE0F" : "\u{1F319}"}
22840
+ </button>
22841
+ ` : ""}
22842
+ ${showMax ? `
22843
+ <button id="${config.id}-maximize" title="${currentIsMaximized ? "Restaurar" : "Maximizar"}" style="${buttonStyle}">
22844
+ ${currentIsMaximized ? "\u{1F5D7}" : "\u{1F5D6}"}
22845
+ </button>
22846
+ ` : ""}
22847
+ ${showClose ? `
22848
+ <button id="${config.id}-close" title="Fechar" style="${buttonStyle}; font-size: 20px;">
22849
+ \xD7
22850
+ </button>
22851
+ ` : ""}
22852
+ </div>
22853
+ </div>
22854
+ `;
22855
+ },
22856
+ attachListeners() {
22857
+ themeBtn = document.getElementById(`${config.id}-theme-toggle`);
22858
+ maximizeBtn = document.getElementById(`${config.id}-maximize`);
22859
+ closeBtn = document.getElementById(`${config.id}-close`);
22860
+ const singleExportBtn = document.getElementById(`${config.id}-export`);
22861
+ if (singleExportBtn && config.exportFormats?.length === 1) {
22862
+ exportBtn = singleExportBtn;
22863
+ const format = config.exportFormats[0];
22864
+ const clickHandler = () => handleExportClick(format);
22865
+ exportBtn.addEventListener("click", clickHandler);
22866
+ cleanupHandlers.push(() => exportBtn?.removeEventListener("click", clickHandler));
22867
+ const enterHandler = () => {
22868
+ exportBtn.style.backgroundColor = "rgba(255, 255, 255, 0.2)";
22869
+ };
22870
+ const leaveHandler = () => {
22871
+ exportBtn.style.backgroundColor = "transparent";
22872
+ };
22873
+ exportBtn.addEventListener("mouseenter", enterHandler);
22874
+ exportBtn.addEventListener("mouseleave", leaveHandler);
22875
+ cleanupHandlers.push(() => {
22876
+ exportBtn?.removeEventListener("mouseenter", enterHandler);
22877
+ exportBtn?.removeEventListener("mouseleave", leaveHandler);
22878
+ });
22879
+ }
22880
+ const exportDropdownBtn = document.getElementById(`${config.id}-export-btn`);
22881
+ exportDropdown = document.getElementById(`${config.id}-export-dropdown`);
22882
+ if (exportDropdownBtn && exportDropdown) {
22883
+ exportBtn = exportDropdownBtn;
22884
+ const toggleHandler = (e) => {
22885
+ e.stopPropagation();
22886
+ if (exportDropdown) {
22887
+ exportDropdown.style.display = exportDropdown.style.display === "none" ? "block" : "none";
22888
+ }
22889
+ };
22890
+ exportDropdownBtn.addEventListener("click", toggleHandler);
22891
+ cleanupHandlers.push(() => exportDropdownBtn.removeEventListener("click", toggleHandler));
22892
+ const outsideClickHandler = (e) => {
22893
+ if (exportDropdown && !exportDropdown.contains(e.target) && e.target !== exportDropdownBtn) {
22894
+ exportDropdown.style.display = "none";
22895
+ }
22896
+ };
22897
+ document.addEventListener("click", outsideClickHandler);
22898
+ cleanupHandlers.push(() => document.removeEventListener("click", outsideClickHandler));
22899
+ config.exportFormats?.forEach((format) => {
22900
+ const btn = document.getElementById(`${config.id}-export-${format}`);
22901
+ if (btn) {
22902
+ const clickHandler = () => handleExportClick(format);
22903
+ btn.addEventListener("click", clickHandler);
22904
+ cleanupHandlers.push(() => btn.removeEventListener("click", clickHandler));
22905
+ const enterHandler2 = () => {
22906
+ btn.style.backgroundColor = "#f0f0f0";
22907
+ };
22908
+ const leaveHandler2 = () => {
22909
+ btn.style.backgroundColor = "white";
22910
+ };
22911
+ btn.addEventListener("mouseenter", enterHandler2);
22912
+ btn.addEventListener("mouseleave", leaveHandler2);
22913
+ cleanupHandlers.push(() => {
22914
+ btn.removeEventListener("mouseenter", enterHandler2);
22915
+ btn.removeEventListener("mouseleave", leaveHandler2);
22916
+ });
22917
+ }
22918
+ });
22919
+ const enterHandler = () => {
22920
+ exportDropdownBtn.style.backgroundColor = "rgba(255, 255, 255, 0.2)";
22921
+ };
22922
+ const leaveHandler = () => {
22923
+ exportDropdownBtn.style.backgroundColor = "transparent";
22924
+ };
22925
+ exportDropdownBtn.addEventListener("mouseenter", enterHandler);
22926
+ exportDropdownBtn.addEventListener("mouseleave", leaveHandler);
22927
+ cleanupHandlers.push(() => {
22928
+ exportDropdownBtn.removeEventListener("mouseenter", enterHandler);
22929
+ exportDropdownBtn.removeEventListener("mouseleave", leaveHandler);
22930
+ });
22931
+ }
22932
+ if (themeBtn && config.showThemeToggle !== false) {
22933
+ themeBtn.addEventListener("click", handleThemeClick);
22934
+ cleanupHandlers.push(() => themeBtn?.removeEventListener("click", handleThemeClick));
22935
+ const enterHandler = () => {
22936
+ themeBtn.style.backgroundColor = "rgba(255, 255, 255, 0.2)";
22937
+ };
22938
+ const leaveHandler = () => {
22939
+ themeBtn.style.backgroundColor = "transparent";
22940
+ };
22941
+ themeBtn.addEventListener("mouseenter", enterHandler);
22942
+ themeBtn.addEventListener("mouseleave", leaveHandler);
22943
+ cleanupHandlers.push(() => {
22944
+ themeBtn?.removeEventListener("mouseenter", enterHandler);
22945
+ themeBtn?.removeEventListener("mouseleave", leaveHandler);
22946
+ });
22947
+ }
22948
+ if (maximizeBtn && config.showMaximize !== false) {
22949
+ maximizeBtn.addEventListener("click", handleMaximizeClick);
22950
+ cleanupHandlers.push(() => maximizeBtn?.removeEventListener("click", handleMaximizeClick));
22951
+ const enterHandler = () => {
22952
+ maximizeBtn.style.backgroundColor = "rgba(255, 255, 255, 0.2)";
22953
+ };
22954
+ const leaveHandler = () => {
22955
+ maximizeBtn.style.backgroundColor = "transparent";
22956
+ };
22957
+ maximizeBtn.addEventListener("mouseenter", enterHandler);
22958
+ maximizeBtn.addEventListener("mouseleave", leaveHandler);
22959
+ cleanupHandlers.push(() => {
22960
+ maximizeBtn?.removeEventListener("mouseenter", enterHandler);
22961
+ maximizeBtn?.removeEventListener("mouseleave", leaveHandler);
22962
+ });
22963
+ }
22964
+ if (closeBtn && config.showClose !== false) {
22965
+ closeBtn.addEventListener("click", handleCloseClick);
22966
+ cleanupHandlers.push(() => closeBtn?.removeEventListener("click", handleCloseClick));
22967
+ const enterHandler = () => {
22968
+ closeBtn.style.backgroundColor = "rgba(255, 255, 255, 0.2)";
22969
+ };
22970
+ const leaveHandler = () => {
22971
+ closeBtn.style.backgroundColor = "transparent";
22972
+ };
22973
+ closeBtn.addEventListener("mouseenter", enterHandler);
22974
+ closeBtn.addEventListener("mouseleave", leaveHandler);
22975
+ cleanupHandlers.push(() => {
22976
+ closeBtn?.removeEventListener("mouseenter", enterHandler);
22977
+ closeBtn?.removeEventListener("mouseleave", leaveHandler);
22978
+ });
22979
+ }
22980
+ },
22981
+ update(updates) {
22982
+ if (updates.theme !== void 0) {
22983
+ currentTheme = updates.theme;
22984
+ updateButtonIcons();
22985
+ }
22986
+ if (updates.isMaximized !== void 0) {
22987
+ currentIsMaximized = updates.isMaximized;
22988
+ updateButtonIcons();
22989
+ }
22990
+ if (updates.title !== void 0) {
22991
+ currentTitle = updates.title;
22992
+ const titleEl = document.getElementById(`${config.id}-header-title`);
22993
+ if (titleEl) {
22994
+ const iconHtml = config.icon ? `<span style="margin-right: 8px;">${config.icon}</span>` : "";
22995
+ titleEl.innerHTML = `${iconHtml}${currentTitle}`;
22996
+ }
22997
+ }
22998
+ },
22999
+ getState() {
23000
+ return {
23001
+ theme: currentTheme,
23002
+ isMaximized: currentIsMaximized
23003
+ };
23004
+ },
23005
+ destroy() {
23006
+ cleanupHandlers.forEach((handler) => handler());
23007
+ cleanupHandlers.length = 0;
23008
+ themeBtn = null;
23009
+ maximizeBtn = null;
23010
+ closeBtn = null;
23011
+ exportBtn = null;
23012
+ exportDropdown = null;
23013
+ }
23014
+ };
23015
+ return instance;
23016
+ }
23017
+ function getModalHeaderStyles() {
23018
+ return `
23019
+ .myio-modal-header button:hover {
23020
+ background-color: rgba(255, 255, 255, 0.2) !important;
23021
+ }
23022
+ .myio-modal-header button:active {
23023
+ background-color: rgba(255, 255, 255, 0.3) !important;
23024
+ }
23025
+ .myio-export-option:hover {
23026
+ background-color: #f0f0f0 !important;
23027
+ }
23028
+ `;
23029
+ }
23030
+
23031
+ // src/components/Consumption7DaysChart/types.ts
23032
+ var DEFAULT_COLORS = {
23033
+ energy: {
23034
+ primary: "#2563eb",
23035
+ background: "rgba(37, 99, 235, 0.1)",
23036
+ gradient: ["#f0fdf4", "#dcfce7"],
23037
+ pointBackground: "#2563eb",
23038
+ pointBorder: "#ffffff"
23039
+ },
23040
+ water: {
23041
+ primary: "#0288d1",
23042
+ background: "rgba(2, 136, 209, 0.1)",
23043
+ gradient: ["#f0f9ff", "#bae6fd"],
23044
+ pointBackground: "#0288d1",
23045
+ pointBorder: "#ffffff"
23046
+ },
23047
+ gas: {
23048
+ primary: "#ea580c",
23049
+ background: "rgba(234, 88, 12, 0.1)",
23050
+ gradient: ["#fff7ed", "#fed7aa"],
23051
+ pointBackground: "#ea580c",
23052
+ pointBorder: "#ffffff"
23053
+ },
23054
+ temperature: {
23055
+ primary: "#dc2626",
23056
+ background: "rgba(220, 38, 38, 0.1)",
23057
+ gradient: ["#fef2f2", "#fecaca"],
23058
+ pointBackground: "#dc2626",
23059
+ pointBorder: "#ffffff"
23060
+ }
23061
+ };
23062
+ var THEME_COLORS = {
23063
+ light: {
23064
+ chartBackground: "#ffffff",
23065
+ text: "#1f2937",
23066
+ textMuted: "#6b7280",
23067
+ grid: "rgba(0, 0, 0, 0.1)",
23068
+ border: "#e5e7eb",
23069
+ tooltipBackground: "#ffffff",
23070
+ tooltipText: "#1f2937"
23071
+ },
23072
+ dark: {
23073
+ chartBackground: "#1f2937",
23074
+ text: "#f9fafb",
23075
+ textMuted: "#9ca3af",
23076
+ grid: "rgba(255, 255, 255, 0.1)",
23077
+ border: "#374151",
23078
+ tooltipBackground: "#374151",
23079
+ tooltipText: "#f9fafb"
23080
+ }
23081
+ };
23082
+ var DEFAULT_CONFIG = {
23083
+ defaultPeriod: 7,
23084
+ defaultChartType: "line",
23085
+ defaultVizMode: "total",
23086
+ defaultTheme: "light",
23087
+ cacheTTL: 3e5,
23088
+ // 5 minutes
23089
+ decimalPlaces: 1,
23090
+ lineTension: 0.4,
23091
+ pointRadius: 4,
23092
+ borderWidth: 2,
23093
+ fill: true,
23094
+ showLegend: false,
23095
+ enableExport: true
23096
+ };
23097
+
23098
+ // src/components/Consumption7DaysChart/createConsumption7DaysChart.ts
23099
+ function createConsumption7DaysChart(config) {
23100
+ let chartInstance = null;
23101
+ let cachedData = null;
23102
+ let currentPeriod = config.defaultPeriod ?? DEFAULT_CONFIG.defaultPeriod;
23103
+ let currentChartType = config.defaultChartType ?? DEFAULT_CONFIG.defaultChartType;
23104
+ let currentVizMode = config.defaultVizMode ?? DEFAULT_CONFIG.defaultVizMode;
23105
+ let currentTheme = config.theme ?? DEFAULT_CONFIG.defaultTheme;
23106
+ let currentIdealRange = config.idealRange ?? null;
23107
+ let isRendered = false;
23108
+ let autoRefreshTimer = null;
23109
+ const colors = {
23110
+ ...DEFAULT_COLORS[config.domain] ?? DEFAULT_COLORS.energy,
23111
+ ...config.colors
23112
+ };
23113
+ function $id(id) {
23114
+ if (config.$container && config.$container[0]) {
23115
+ return config.$container[0].querySelector(`#${id}`);
23116
+ }
23117
+ return document.getElementById(id);
23118
+ }
23119
+ function log(level, ...args) {
23120
+ const prefix = `[${config.domain.toUpperCase()}]`;
23121
+ console[level](prefix, ...args);
23122
+ }
23123
+ function calculateYAxisMax(values) {
23124
+ const maxValue = Math.max(...values, 0);
23125
+ if (config.domain === "temperature") {
23126
+ const tempConfig = config.temperatureConfig;
23127
+ if (tempConfig?.clampRange) {
23128
+ return tempConfig.clampRange.max;
23129
+ }
23130
+ const maxWithThreshold = Math.max(
23131
+ maxValue,
23132
+ tempConfig?.maxThreshold?.value ?? 0,
23133
+ tempConfig?.idealRange?.max ?? 0
23134
+ );
23135
+ return Math.ceil(maxWithThreshold + 5);
23136
+ }
23137
+ if (maxValue === 0) {
23138
+ return config.thresholdForLargeUnit ? config.thresholdForLargeUnit / 2 : 500;
23139
+ }
23140
+ let roundTo;
23141
+ if (config.thresholdForLargeUnit && maxValue >= config.thresholdForLargeUnit) {
23142
+ roundTo = config.thresholdForLargeUnit / 10;
23143
+ } else if (maxValue >= 1e3) {
23144
+ roundTo = 100;
23145
+ } else if (maxValue >= 100) {
23146
+ roundTo = 50;
23147
+ } else if (maxValue >= 10) {
23148
+ roundTo = 10;
23149
+ } else {
23150
+ roundTo = 5;
23151
+ }
23152
+ return Math.ceil(maxValue * 1.1 / roundTo) * roundTo;
23153
+ }
23154
+ function calculateYAxisMin(values) {
23155
+ if (config.domain !== "temperature") {
23156
+ return 0;
23157
+ }
23158
+ const tempConfig = config.temperatureConfig;
23159
+ if (tempConfig?.clampRange) {
23160
+ return tempConfig.clampRange.min;
23161
+ }
23162
+ const minValue = Math.min(...values);
23163
+ const minWithThreshold = Math.min(
23164
+ minValue,
23165
+ tempConfig?.minThreshold?.value ?? minValue,
23166
+ tempConfig?.idealRange?.min ?? minValue
23167
+ );
23168
+ return Math.floor(minWithThreshold - 5);
23169
+ }
23170
+ function buildTemperatureAnnotations() {
23171
+ const tempConfig = config.temperatureConfig;
23172
+ if (!tempConfig || config.domain !== "temperature") {
23173
+ return {};
23174
+ }
23175
+ const annotations = {};
23176
+ const createLineAnnotation = (line, id) => {
23177
+ const borderDash = line.lineStyle === "dashed" ? [6, 6] : line.lineStyle === "dotted" ? [2, 2] : [];
23178
+ return {
23179
+ type: "line",
23180
+ yMin: line.value,
23181
+ yMax: line.value,
23182
+ borderColor: line.color,
23183
+ borderWidth: line.lineWidth ?? 2,
23184
+ borderDash,
23185
+ label: {
23186
+ display: true,
23187
+ content: line.label,
23188
+ position: "end",
23189
+ backgroundColor: line.color,
23190
+ color: "#fff",
23191
+ font: { size: 10, weight: "bold" },
23192
+ padding: { x: 4, y: 2 }
23193
+ }
23194
+ };
23195
+ };
23196
+ if (tempConfig.minThreshold) {
23197
+ annotations["minThreshold"] = createLineAnnotation(tempConfig.minThreshold, "minThreshold");
23198
+ }
23199
+ if (tempConfig.maxThreshold) {
23200
+ annotations["maxThreshold"] = createLineAnnotation(tempConfig.maxThreshold, "maxThreshold");
23201
+ }
23202
+ if (tempConfig.idealRange) {
23203
+ annotations["idealRange"] = {
23204
+ type: "box",
23205
+ yMin: tempConfig.idealRange.min,
23206
+ yMax: tempConfig.idealRange.max,
23207
+ backgroundColor: tempConfig.idealRange.color,
23208
+ borderWidth: 0,
23209
+ label: tempConfig.idealRange.label ? {
23210
+ display: true,
23211
+ content: tempConfig.idealRange.label,
23212
+ position: { x: "start", y: "center" },
23213
+ color: "#666",
23214
+ font: { size: 10 }
23215
+ } : void 0
23216
+ };
23217
+ }
23218
+ return annotations;
23219
+ }
23220
+ function buildIdealRangeAnnotation() {
23221
+ if (!currentIdealRange) {
23222
+ return {};
23223
+ }
23224
+ const { min, max, enabled = true } = currentIdealRange;
23225
+ if (!enabled || min === 0 && max === 0 || min >= max) {
23226
+ return {};
23227
+ }
23228
+ const defaultColors = {
23229
+ temperature: { bg: "rgba(34, 197, 94, 0.15)", border: "rgba(34, 197, 94, 0.4)" },
23230
+ energy: { bg: "rgba(37, 99, 235, 0.1)", border: "rgba(37, 99, 235, 0.3)" },
23231
+ water: { bg: "rgba(2, 136, 209, 0.1)", border: "rgba(2, 136, 209, 0.3)" },
23232
+ gas: { bg: "rgba(234, 88, 12, 0.1)", border: "rgba(234, 88, 12, 0.3)" }
23233
+ };
23234
+ const domainDefaults = defaultColors[config.domain] || defaultColors.energy;
23235
+ return {
23236
+ idealRangeBox: {
23237
+ type: "box",
23238
+ yMin: min,
23239
+ yMax: max,
23240
+ backgroundColor: currentIdealRange.color || domainDefaults.bg,
23241
+ borderColor: currentIdealRange.borderColor || domainDefaults.border,
23242
+ borderWidth: 1,
23243
+ label: currentIdealRange.label ? {
23244
+ display: true,
23245
+ content: currentIdealRange.label,
23246
+ position: { x: "start", y: "center" },
23247
+ color: "#666",
23248
+ font: { size: 10, style: "italic" },
23249
+ backgroundColor: "rgba(255, 255, 255, 0.8)",
23250
+ padding: { x: 4, y: 2 }
23251
+ } : void 0
23252
+ }
23253
+ };
23254
+ }
23255
+ function formatValue(value, includeUnit = true) {
23256
+ const decimals = config.decimalPlaces ?? DEFAULT_CONFIG.decimalPlaces;
23257
+ if (config.unitLarge && config.thresholdForLargeUnit && value >= config.thresholdForLargeUnit) {
23258
+ const converted = value / config.thresholdForLargeUnit;
23259
+ return includeUnit ? `${converted.toFixed(decimals)} ${config.unitLarge}` : converted.toFixed(decimals);
23260
+ }
23261
+ return includeUnit ? `${value.toFixed(decimals)} ${config.unit}` : value.toFixed(decimals);
23262
+ }
23263
+ function formatTickValue(value) {
23264
+ if (config.unitLarge && config.thresholdForLargeUnit && value >= config.thresholdForLargeUnit) {
23265
+ return `${(value / config.thresholdForLargeUnit).toFixed(1)}`;
23266
+ }
23267
+ return value.toFixed(0);
23268
+ }
23269
+ function buildChartConfig(data) {
23270
+ const yAxisMax = calculateYAxisMax(data.dailyTotals);
23271
+ const yAxisMin = calculateYAxisMin(data.dailyTotals);
23272
+ const tension = config.lineTension ?? DEFAULT_CONFIG.lineTension;
23273
+ const pointRadius = config.pointRadius ?? DEFAULT_CONFIG.pointRadius;
23274
+ const borderWidth = config.borderWidth ?? DEFAULT_CONFIG.borderWidth;
23275
+ const fill = config.fill ?? DEFAULT_CONFIG.fill;
23276
+ const showLegend = config.showLegend ?? DEFAULT_CONFIG.showLegend;
23277
+ const themeColors = THEME_COLORS[currentTheme];
23278
+ const isTemperature = config.domain === "temperature";
23279
+ let datasets;
23280
+ if (currentVizMode === "separate" && data.shoppingData && data.shoppingNames) {
23281
+ const shoppingColors = [
23282
+ "#2563eb",
23283
+ "#16a34a",
23284
+ "#ea580c",
23285
+ "#dc2626",
23286
+ "#8b5cf6",
23287
+ "#0891b2",
23288
+ "#65a30d",
23289
+ "#d97706",
23290
+ "#be185d",
23291
+ "#0d9488"
23292
+ ];
23293
+ datasets = Object.entries(data.shoppingData).map(([shoppingId, values], index) => ({
23294
+ label: data.shoppingNames?.[shoppingId] || shoppingId,
23295
+ data: values,
23296
+ borderColor: shoppingColors[index % shoppingColors.length],
23297
+ backgroundColor: currentChartType === "line" ? `${shoppingColors[index % shoppingColors.length]}20` : shoppingColors[index % shoppingColors.length],
23298
+ fill: currentChartType === "line" && fill,
23299
+ tension,
23300
+ borderWidth,
23301
+ pointRadius: currentChartType === "line" ? pointRadius : 0,
23302
+ pointBackgroundColor: shoppingColors[index % shoppingColors.length],
23303
+ pointBorderColor: "#fff",
23304
+ pointBorderWidth: 2
23305
+ }));
23306
+ } else {
23307
+ const datasetLabel = isTemperature ? `Temperatura (${config.unit})` : `Consumo (${config.unit})`;
23308
+ datasets = [
23309
+ {
23310
+ label: datasetLabel,
23311
+ data: data.dailyTotals,
23312
+ borderColor: colors.primary,
23313
+ backgroundColor: currentChartType === "line" ? colors.background : colors.primary,
23314
+ fill: currentChartType === "line" && fill,
23315
+ tension,
23316
+ borderWidth,
23317
+ pointRadius: currentChartType === "line" ? pointRadius : 0,
23318
+ pointBackgroundColor: colors.pointBackground || colors.primary,
23319
+ pointBorderColor: colors.pointBorder || "#fff",
23320
+ pointBorderWidth: 2,
23321
+ borderRadius: currentChartType === "bar" ? 4 : 0
23322
+ }
23323
+ ];
23324
+ }
23325
+ const temperatureAnnotations = buildTemperatureAnnotations();
23326
+ const idealRangeAnnotations = buildIdealRangeAnnotation();
23327
+ const allAnnotations = { ...temperatureAnnotations, ...idealRangeAnnotations };
23328
+ const yAxisLabel = config.unitLarge && config.thresholdForLargeUnit && yAxisMax >= config.thresholdForLargeUnit ? config.unitLarge : config.unit;
23329
+ return {
23330
+ type: currentChartType,
23331
+ data: {
23332
+ labels: data.labels,
23333
+ datasets
23334
+ },
23335
+ options: {
23336
+ responsive: true,
23337
+ maintainAspectRatio: false,
23338
+ animation: false,
23339
+ // CRITICAL: Prevents infinite growth bug
23340
+ plugins: {
23341
+ legend: {
23342
+ display: showLegend || currentVizMode === "separate",
23343
+ position: "bottom",
23344
+ labels: {
23345
+ color: themeColors.text
23346
+ }
23347
+ },
23348
+ tooltip: {
23349
+ backgroundColor: themeColors.tooltipBackground,
23350
+ titleColor: themeColors.tooltipText,
23351
+ bodyColor: themeColors.tooltipText,
23352
+ borderColor: themeColors.border,
23353
+ borderWidth: 1,
23354
+ callbacks: {
23355
+ label: function(context) {
23356
+ const value = context.parsed.y || 0;
23357
+ const label = context.dataset.label || "";
23358
+ return `${label}: ${formatValue(value)}`;
23359
+ }
23360
+ }
23361
+ },
23362
+ // Reference lines and ideal range (requires chartjs-plugin-annotation)
23363
+ annotation: Object.keys(allAnnotations).length > 0 ? {
23364
+ annotations: allAnnotations
23365
+ } : void 0
23366
+ },
23367
+ scales: {
23368
+ y: {
23369
+ beginAtZero: !isTemperature,
23370
+ // Temperature can have negative values
23371
+ min: yAxisMin,
23372
+ max: yAxisMax,
23373
+ // CRITICAL: Fixed max prevents animation loop
23374
+ grid: {
23375
+ color: themeColors.grid
23376
+ },
23377
+ title: {
23378
+ display: true,
23379
+ text: yAxisLabel,
23380
+ font: { size: 12 },
23381
+ color: themeColors.text
23382
+ },
23383
+ ticks: {
23384
+ font: { size: 11 },
23385
+ color: themeColors.textMuted,
23386
+ callback: function(value) {
23387
+ return formatTickValue(value);
23388
+ }
23389
+ }
23390
+ },
23391
+ x: {
23392
+ grid: {
23393
+ color: themeColors.grid
23394
+ },
23395
+ ticks: {
23396
+ font: { size: 11 },
23397
+ color: themeColors.textMuted
23398
+ }
23399
+ }
23400
+ }
23401
+ }
23402
+ };
23403
+ }
23404
+ function validateChartJs() {
23405
+ if (typeof Chart === "undefined") {
23406
+ log("error", "Chart.js not loaded. Cannot initialize chart.");
23407
+ config.onError?.(new Error("Chart.js not loaded"));
23408
+ return false;
23409
+ }
23410
+ return true;
23411
+ }
23412
+ function validateCanvas() {
23413
+ const canvas = $id(config.containerId);
23414
+ if (!canvas) {
23415
+ log("error", `Canvas #${config.containerId} not found`);
23416
+ config.onError?.(new Error(`Canvas #${config.containerId} not found`));
23417
+ return null;
23418
+ }
23419
+ return canvas;
23420
+ }
23421
+ function setupAutoRefresh() {
23422
+ if (config.autoRefreshInterval && config.autoRefreshInterval > 0) {
23423
+ if (autoRefreshTimer) {
23424
+ clearInterval(autoRefreshTimer);
23425
+ }
23426
+ autoRefreshTimer = setInterval(async () => {
23427
+ log("log", "Auto-refreshing data...");
23428
+ await instance.refresh(true);
23429
+ }, config.autoRefreshInterval);
23430
+ }
23431
+ }
23432
+ function cleanupAutoRefresh() {
23433
+ if (autoRefreshTimer) {
23434
+ clearInterval(autoRefreshTimer);
23435
+ autoRefreshTimer = null;
23436
+ }
23437
+ }
23438
+ function setupButtonHandlers() {
23439
+ if (config.settingsButtonId && config.onSettingsClick) {
23440
+ const settingsBtn = $id(config.settingsButtonId);
23441
+ if (settingsBtn) {
23442
+ settingsBtn.addEventListener("click", () => {
23443
+ log("log", "Settings button clicked");
23444
+ config.onSettingsClick?.();
23445
+ });
23446
+ log("log", "Settings button handler attached");
23447
+ }
23448
+ }
23449
+ if (config.maximizeButtonId && config.onMaximizeClick) {
23450
+ const maximizeBtn = $id(config.maximizeButtonId);
23451
+ if (maximizeBtn) {
23452
+ maximizeBtn.addEventListener("click", () => {
23453
+ log("log", "Maximize button clicked");
23454
+ config.onMaximizeClick?.();
23455
+ });
23456
+ log("log", "Maximize button handler attached");
23457
+ }
23458
+ }
23459
+ const enableExport = config.enableExport ?? DEFAULT_CONFIG.enableExport;
23460
+ if (enableExport && config.exportButtonId) {
23461
+ const exportBtn = $id(config.exportButtonId);
23462
+ if (exportBtn) {
23463
+ exportBtn.addEventListener("click", () => {
23464
+ log("log", "Export button clicked");
23465
+ if (config.onExportCSV && cachedData) {
23466
+ config.onExportCSV(cachedData);
23467
+ } else {
23468
+ instance.exportCSV();
23469
+ }
23470
+ });
23471
+ log("log", "Export button handler attached");
23472
+ }
23473
+ }
23474
+ }
23475
+ function generateCSVContent(data) {
23476
+ const rows = [];
23477
+ const decimals = config.decimalPlaces ?? DEFAULT_CONFIG.decimalPlaces;
23478
+ if (currentVizMode === "separate" && data.shoppingData && data.shoppingNames) {
23479
+ const shoppingHeaders = Object.keys(data.shoppingData).map(
23480
+ (id) => data.shoppingNames?.[id] || id
23481
+ );
23482
+ rows.push(["Data", ...shoppingHeaders, "Total"].join(";"));
23483
+ data.labels.forEach((label, index) => {
23484
+ const shoppingValues = Object.keys(data.shoppingData).map(
23485
+ (id) => data.shoppingData[id][index].toFixed(decimals)
23486
+ );
23487
+ rows.push([label, ...shoppingValues, data.dailyTotals[index].toFixed(decimals)].join(";"));
23488
+ });
23489
+ } else {
23490
+ rows.push(["Data", `Consumo (${config.unit})`].join(";"));
23491
+ data.labels.forEach((label, index) => {
23492
+ rows.push([label, data.dailyTotals[index].toFixed(decimals)].join(";"));
23493
+ });
23494
+ }
23495
+ const total = data.dailyTotals.reduce((sum, v) => sum + v, 0);
23496
+ const avg = total / data.dailyTotals.length;
23497
+ rows.push("");
23498
+ rows.push(["Total", total.toFixed(decimals)].join(";"));
23499
+ rows.push(["M\xE9dia", avg.toFixed(decimals)].join(";"));
23500
+ return rows.join("\n");
23501
+ }
23502
+ function downloadCSV(content, filename) {
23503
+ const BOM = "\uFEFF";
23504
+ const blob = new Blob([BOM + content], { type: "text/csv;charset=utf-8" });
23505
+ const url = URL.createObjectURL(blob);
23506
+ const link = document.createElement("a");
23507
+ link.href = url;
23508
+ link.download = `${filename}.csv`;
23509
+ document.body.appendChild(link);
23510
+ link.click();
23511
+ document.body.removeChild(link);
23512
+ URL.revokeObjectURL(url);
23513
+ log("log", `CSV exported: ${filename}.csv`);
23514
+ }
23515
+ function updateTitle() {
23516
+ if (config.titleElementId) {
23517
+ const titleEl = $id(config.titleElementId);
23518
+ if (titleEl) {
23519
+ if (currentPeriod === 0) {
23520
+ titleEl.textContent = `Consumo - Per\xEDodo Personalizado`;
23521
+ } else {
23522
+ titleEl.textContent = `Consumo dos \xFAltimos ${currentPeriod} dias`;
23523
+ }
23524
+ }
23525
+ }
23526
+ }
23527
+ const instance = {
23528
+ async render() {
23529
+ log("log", "Rendering chart...");
23530
+ if (!validateChartJs()) return;
23531
+ const canvas = validateCanvas();
23532
+ if (!canvas) return;
23533
+ try {
23534
+ log("log", `Fetching ${currentPeriod} days of data...`);
23535
+ cachedData = await config.fetchData(currentPeriod);
23536
+ cachedData.fetchTimestamp = Date.now();
23537
+ if (config.onBeforeRender) {
23538
+ cachedData = config.onBeforeRender(cachedData);
23539
+ }
23540
+ if (chartInstance) {
23541
+ chartInstance.destroy();
23542
+ chartInstance = null;
23543
+ }
23544
+ const ctx = canvas.getContext("2d");
23545
+ const chartConfig = buildChartConfig(cachedData);
23546
+ chartInstance = new Chart(ctx, chartConfig);
23547
+ isRendered = true;
23548
+ const yAxisMax = calculateYAxisMax(cachedData.dailyTotals);
23549
+ log("log", `Chart initialized with yAxisMax: ${yAxisMax}`);
23550
+ config.onDataLoaded?.(cachedData);
23551
+ config.onAfterRender?.(chartInstance);
23552
+ setupButtonHandlers();
23553
+ updateTitle();
23554
+ setupAutoRefresh();
23555
+ } catch (error) {
23556
+ log("error", "Failed to render chart:", error);
23557
+ config.onError?.(error instanceof Error ? error : new Error(String(error)));
23558
+ }
23559
+ },
23560
+ async update(data) {
23561
+ if (data) {
23562
+ cachedData = data;
23563
+ cachedData.fetchTimestamp = Date.now();
23564
+ }
23565
+ if (!chartInstance || !cachedData) {
23566
+ log("warn", "Cannot update: chart not initialized or no data");
23567
+ return;
23568
+ }
23569
+ let renderData = cachedData;
23570
+ if (config.onBeforeRender) {
23571
+ renderData = config.onBeforeRender(cachedData);
23572
+ }
23573
+ const chartConfig = buildChartConfig(renderData);
23574
+ chartInstance.data = chartConfig.data;
23575
+ chartInstance.options = chartConfig.options;
23576
+ chartInstance.update("none");
23577
+ log("log", "Chart updated");
23578
+ },
23579
+ setChartType(type) {
23580
+ if (currentChartType === type) return;
23581
+ log("log", `Changing chart type to: ${type}`);
23582
+ currentChartType = type;
23583
+ if (cachedData && chartInstance) {
23584
+ const canvas = validateCanvas();
23585
+ if (canvas) {
23586
+ chartInstance.destroy();
23587
+ const ctx = canvas.getContext("2d");
23588
+ chartInstance = new Chart(ctx, buildChartConfig(cachedData));
23589
+ }
23590
+ }
23591
+ },
23592
+ setVizMode(mode) {
23593
+ if (currentVizMode === mode) return;
23594
+ log("log", `Changing viz mode to: ${mode}`);
23595
+ currentVizMode = mode;
23596
+ if (cachedData) {
23597
+ this.update();
23598
+ }
23599
+ },
23600
+ async setPeriod(days) {
23601
+ if (currentPeriod === days) return;
23602
+ log("log", `Changing period to: ${days} days`);
23603
+ currentPeriod = days;
23604
+ updateTitle();
23605
+ await this.refresh(true);
23606
+ },
23607
+ async refresh(forceRefresh = false) {
23608
+ if (!forceRefresh && cachedData?.fetchTimestamp) {
23609
+ const age = Date.now() - cachedData.fetchTimestamp;
23610
+ const ttl = config.cacheTTL ?? DEFAULT_CONFIG.cacheTTL;
23611
+ if (age < ttl) {
23612
+ log("log", `Using cached data (age: ${Math.round(age / 1e3)}s)`);
23613
+ return;
23614
+ }
23615
+ }
23616
+ log("log", "Refreshing data...");
23617
+ await this.render();
23618
+ },
23619
+ destroy() {
23620
+ log("log", "Destroying chart...");
23621
+ cleanupAutoRefresh();
23622
+ if (chartInstance) {
23623
+ chartInstance.destroy();
23624
+ chartInstance = null;
23625
+ }
23626
+ cachedData = null;
23627
+ isRendered = false;
23628
+ },
23629
+ getChartInstance() {
23630
+ return chartInstance;
23631
+ },
23632
+ getCachedData() {
23633
+ return cachedData;
23634
+ },
23635
+ getState() {
23636
+ return {
23637
+ period: currentPeriod,
23638
+ chartType: currentChartType,
23639
+ vizMode: currentVizMode,
23640
+ theme: currentTheme,
23641
+ isRendered
23642
+ };
23643
+ },
23644
+ exportCSV(filename) {
23645
+ if (!cachedData) {
23646
+ log("warn", "Cannot export: no data available");
23647
+ return;
23648
+ }
23649
+ const defaultFilename = config.exportFilename || `${config.domain}-consumo-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`;
23650
+ const csvContent = generateCSVContent(cachedData);
23651
+ downloadCSV(csvContent, filename || defaultFilename);
23652
+ },
23653
+ setTheme(theme) {
23654
+ if (currentTheme === theme) return;
23655
+ log("log", `Changing theme to: ${theme}`);
23656
+ currentTheme = theme;
23657
+ if (cachedData && chartInstance) {
23658
+ const canvas = validateCanvas();
23659
+ if (canvas) {
23660
+ chartInstance.destroy();
23661
+ const ctx = canvas.getContext("2d");
23662
+ chartInstance = new Chart(ctx, buildChartConfig(cachedData));
23663
+ }
23664
+ }
23665
+ },
23666
+ setIdealRange(range) {
23667
+ const rangeChanged = JSON.stringify(currentIdealRange) !== JSON.stringify(range);
23668
+ if (!rangeChanged) return;
23669
+ if (range) {
23670
+ log("log", `Setting ideal range: ${range.min} - ${range.max}`);
23671
+ } else {
23672
+ log("log", "Clearing ideal range");
23673
+ }
23674
+ currentIdealRange = range;
23675
+ if (cachedData && chartInstance) {
23676
+ const canvas = validateCanvas();
23677
+ if (canvas) {
23678
+ chartInstance.destroy();
23679
+ const ctx = canvas.getContext("2d");
23680
+ chartInstance = new Chart(ctx, buildChartConfig(cachedData));
23681
+ }
23682
+ }
23683
+ },
23684
+ getIdealRange() {
23685
+ return currentIdealRange;
23686
+ }
23687
+ };
23688
+ return instance;
23689
+ }
23690
+
23691
+ // src/components/Consumption7DaysChart/createConsumptionModal.ts
23692
+ var DOMAIN_CONFIG3 = {
23693
+ energy: { name: "Energia", icon: "\u26A1" },
23694
+ water: { name: "\xC1gua", icon: "\u{1F4A7}" },
23695
+ gas: { name: "G\xE1s", icon: "\u{1F525}" },
23696
+ temperature: { name: "Temperatura", icon: "\u{1F321}\uFE0F" }
23697
+ };
23698
+ function createConsumptionModal(config) {
23699
+ const modalId = `myio-consumption-modal-${Date.now()}`;
23700
+ let modalElement = null;
23701
+ let chartInstance = null;
23702
+ let headerInstance = null;
23703
+ let currentTheme = config.theme ?? "light";
23704
+ let currentChartType = config.defaultChartType ?? "line";
23705
+ let currentVizMode = config.defaultVizMode ?? "total";
23706
+ let isMaximized = false;
23707
+ const domainCfg = DOMAIN_CONFIG3[config.domain] || { name: config.domain, icon: "\u{1F4CA}" };
23708
+ const title = config.title || `${domainCfg.name} - Hist\xF3rico de Consumo`;
23709
+ function getThemeColors2() {
23710
+ return THEME_COLORS[currentTheme];
23711
+ }
23712
+ function renderModal4() {
23713
+ const colors = getThemeColors2();
23714
+ const exportFormats = config.exportFormats || ["csv"];
23715
+ headerInstance = createModalHeader({
23716
+ id: modalId,
23717
+ title,
23718
+ icon: domainCfg.icon,
23719
+ theme: currentTheme,
23720
+ isMaximized,
23721
+ exportFormats,
23722
+ onExport: (format) => {
23723
+ if (config.onExport) {
23724
+ config.onExport(format);
23725
+ } else {
23726
+ if (format === "csv") {
23727
+ chartInstance?.exportCSV();
23728
+ } else {
23729
+ console.warn(`[ConsumptionModal] Export format "${format}" requires custom onExport handler`);
23730
+ }
23731
+ }
23732
+ },
23733
+ onThemeToggle: (theme) => {
23734
+ currentTheme = theme;
23735
+ chartInstance?.setTheme(currentTheme);
23736
+ updateModal();
23737
+ },
23738
+ onMaximize: (maximized) => {
23739
+ isMaximized = maximized;
23740
+ updateModal();
23741
+ },
23742
+ onClose: () => {
23743
+ instance.close();
23744
+ }
23745
+ });
23746
+ return `
23747
+ <div class="myio-consumption-modal-overlay" style="
23748
+ position: fixed;
23749
+ top: 0;
23750
+ left: 0;
23751
+ width: 100%;
23752
+ height: 100%;
23753
+ background: rgba(0, 0, 0, 0.5);
23754
+ backdrop-filter: blur(2px);
23755
+ z-index: 99998;
23756
+ display: flex;
23757
+ justify-content: center;
23758
+ align-items: center;
23759
+ ">
23760
+ <div class="myio-consumption-modal-content" style="
23761
+ background: ${colors.chartBackground};
23762
+ border-radius: ${isMaximized ? "0" : "10px"};
23763
+ width: ${isMaximized ? "100%" : "95%"};
23764
+ max-width: ${isMaximized ? "100%" : "1200px"};
23765
+ height: ${isMaximized ? "100%" : "85vh"};
23766
+ display: flex;
23767
+ flex-direction: column;
23768
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
23769
+ overflow: hidden;
23770
+ ">
23771
+ <!-- MyIO Premium Header (using ModalHeader component) -->
23772
+ ${headerInstance.render()}
23773
+
23774
+ <!-- Controls Bar -->
23775
+ <div class="myio-consumption-modal-controls" style="
23776
+ display: flex;
23777
+ gap: 16px;
23778
+ padding: 12px 16px;
23779
+ background: ${currentTheme === "dark" ? "#374151" : "#f7f7f7"};
23780
+ border-bottom: 1px solid ${colors.border};
23781
+ align-items: center;
23782
+ flex-wrap: wrap;
23783
+ ">
23784
+ <!-- Viz Mode Tabs -->
23785
+ <div style="display: flex; gap: 2px; background: ${currentTheme === "dark" ? "#4b5563" : "#e5e7eb"}; border-radius: 8px; padding: 2px;">
23786
+ <button id="${modalId}-viz-total" style="
23787
+ padding: 6px 12px;
23788
+ border: none;
23789
+ border-radius: 6px;
23790
+ font-size: 13px;
23791
+ cursor: pointer;
23792
+ transition: all 0.2s;
23793
+ background: ${currentVizMode === "total" ? "#3e1a7d" : "transparent"};
23794
+ color: ${currentVizMode === "total" ? "white" : colors.text};
23795
+ ">Consolidado</button>
23796
+ <button id="${modalId}-viz-separate" style="
23797
+ padding: 6px 12px;
23798
+ border: none;
23799
+ border-radius: 6px;
23800
+ font-size: 13px;
23801
+ cursor: pointer;
23802
+ transition: all 0.2s;
23803
+ background: ${currentVizMode === "separate" ? "#3e1a7d" : "transparent"};
23804
+ color: ${currentVizMode === "separate" ? "white" : colors.text};
23805
+ ">Por Shopping</button>
23806
+ </div>
23807
+
23808
+ <!-- Chart Type Tabs -->
23809
+ <div style="display: flex; gap: 2px; background: ${currentTheme === "dark" ? "#4b5563" : "#e5e7eb"}; border-radius: 8px; padding: 2px;">
23810
+ <button id="${modalId}-type-line" style="
23811
+ padding: 6px 12px;
23812
+ border: none;
23813
+ border-radius: 6px;
23814
+ font-size: 13px;
23815
+ cursor: pointer;
23816
+ transition: all 0.2s;
23817
+ background: ${currentChartType === "line" ? "#3e1a7d" : "transparent"};
23818
+ color: ${currentChartType === "line" ? "white" : colors.text};
23819
+ ">Linhas</button>
23820
+ <button id="${modalId}-type-bar" style="
23821
+ padding: 6px 12px;
23822
+ border: none;
23823
+ border-radius: 6px;
23824
+ font-size: 13px;
23825
+ cursor: pointer;
23826
+ transition: all 0.2s;
23827
+ background: ${currentChartType === "bar" ? "#3e1a7d" : "transparent"};
23828
+ color: ${currentChartType === "bar" ? "white" : colors.text};
23829
+ ">Barras</button>
23830
+ </div>
23831
+ </div>
23832
+
23833
+ <!-- Chart Container -->
23834
+ <div style="
23835
+ flex: 1;
23836
+ padding: 16px;
23837
+ min-height: 0;
23838
+ position: relative;
23839
+ background: ${colors.chartBackground};
23840
+ ">
23841
+ <canvas id="${modalId}-chart" style="width: 100%; height: 100%;"></canvas>
23842
+ </div>
23843
+ </div>
23844
+ </div>
23845
+ `;
23846
+ }
23847
+ function setupListeners() {
23848
+ if (!modalElement) return;
23849
+ headerInstance?.attachListeners();
23850
+ document.getElementById(`${modalId}-viz-total`)?.addEventListener("click", () => {
23851
+ currentVizMode = "total";
23852
+ chartInstance?.setVizMode("total");
23853
+ updateControlStyles();
23854
+ });
23855
+ document.getElementById(`${modalId}-viz-separate`)?.addEventListener("click", () => {
23856
+ currentVizMode = "separate";
23857
+ chartInstance?.setVizMode("separate");
23858
+ updateControlStyles();
23859
+ });
23860
+ document.getElementById(`${modalId}-type-line`)?.addEventListener("click", () => {
23861
+ currentChartType = "line";
23862
+ chartInstance?.setChartType("line");
23863
+ updateControlStyles();
23864
+ });
23865
+ document.getElementById(`${modalId}-type-bar`)?.addEventListener("click", () => {
23866
+ currentChartType = "bar";
23867
+ chartInstance?.setChartType("bar");
23868
+ updateControlStyles();
23869
+ });
23870
+ modalElement.querySelector(".myio-consumption-modal-overlay")?.addEventListener("click", (e) => {
23871
+ if (e.target.classList.contains("myio-consumption-modal-overlay")) {
23872
+ instance.close();
23873
+ }
23874
+ });
23875
+ const handleKeydown = (e) => {
23876
+ if (e.key === "Escape") {
23877
+ instance.close();
23878
+ }
23879
+ };
23880
+ document.addEventListener("keydown", handleKeydown);
23881
+ modalElement.__handleKeydown = handleKeydown;
23882
+ }
23883
+ function updateControlStyles() {
23884
+ const colors = getThemeColors2();
23885
+ const vizTotalBtn = document.getElementById(`${modalId}-viz-total`);
23886
+ const vizSeparateBtn = document.getElementById(`${modalId}-viz-separate`);
23887
+ if (vizTotalBtn) {
23888
+ vizTotalBtn.style.background = currentVizMode === "total" ? "#3e1a7d" : "transparent";
23889
+ vizTotalBtn.style.color = currentVizMode === "total" ? "white" : colors.text;
23890
+ }
23891
+ if (vizSeparateBtn) {
23892
+ vizSeparateBtn.style.background = currentVizMode === "separate" ? "#3e1a7d" : "transparent";
23893
+ vizSeparateBtn.style.color = currentVizMode === "separate" ? "white" : colors.text;
23894
+ }
23895
+ const typeLineBtn = document.getElementById(`${modalId}-type-line`);
23896
+ const typeBarBtn = document.getElementById(`${modalId}-type-bar`);
23897
+ if (typeLineBtn) {
23898
+ typeLineBtn.style.background = currentChartType === "line" ? "#3e1a7d" : "transparent";
23899
+ typeLineBtn.style.color = currentChartType === "line" ? "white" : colors.text;
23900
+ }
23901
+ if (typeBarBtn) {
23902
+ typeBarBtn.style.background = currentChartType === "bar" ? "#3e1a7d" : "transparent";
23903
+ typeBarBtn.style.color = currentChartType === "bar" ? "white" : colors.text;
23904
+ }
23905
+ }
23906
+ function updateModal() {
23907
+ if (!modalElement) return;
23908
+ const cachedData = chartInstance?.getCachedData();
23909
+ headerInstance?.destroy();
23910
+ chartInstance?.destroy();
23911
+ modalElement.innerHTML = renderModal4();
23912
+ setupListeners();
23913
+ if (cachedData) {
23914
+ chartInstance = createConsumption7DaysChart({
23915
+ ...config,
23916
+ containerId: `${modalId}-chart`,
23917
+ theme: currentTheme,
23918
+ defaultChartType: currentChartType,
23919
+ defaultVizMode: currentVizMode
23920
+ });
23921
+ chartInstance.update(cachedData);
23922
+ }
23923
+ }
23924
+ const instance = {
23925
+ async open() {
23926
+ modalElement = document.createElement("div");
23927
+ modalElement.id = modalId;
23928
+ modalElement.innerHTML = renderModal4();
23929
+ const container = config.container || document.body;
23930
+ container.appendChild(modalElement);
23931
+ setupListeners();
23932
+ chartInstance = createConsumption7DaysChart({
23933
+ ...config,
23934
+ containerId: `${modalId}-chart`,
23935
+ theme: currentTheme,
23936
+ defaultChartType: currentChartType,
23937
+ defaultVizMode: currentVizMode
23938
+ });
23939
+ await chartInstance.render();
23940
+ },
23941
+ close() {
23942
+ if (modalElement) {
23943
+ const handleKeydown = modalElement.__handleKeydown;
23944
+ if (handleKeydown) {
23945
+ document.removeEventListener("keydown", handleKeydown);
23946
+ }
23947
+ headerInstance?.destroy();
23948
+ headerInstance = null;
23949
+ chartInstance?.destroy();
23950
+ chartInstance = null;
23951
+ modalElement.remove();
23952
+ modalElement = null;
23953
+ config.onClose?.();
23954
+ }
23955
+ },
23956
+ getChart() {
23957
+ return chartInstance;
23958
+ },
23959
+ destroy() {
23960
+ instance.close();
23961
+ }
23962
+ };
23963
+ return instance;
23964
+ }
23965
+
23966
+ // src/components/ExportData/index.ts
23967
+ var DEFAULT_COLORS3 = {
23968
+ primary: "#3e1a7d",
23969
+ // MyIO purple
23970
+ secondary: "#6b4c9a",
23971
+ // Light purple
23972
+ accent: "#00bcd4",
23973
+ // Cyan accent
23974
+ background: "#ffffff",
23975
+ // White
23976
+ text: "#333333",
23977
+ // Dark gray
23978
+ chartColors: ["#3e1a7d", "#00bcd4", "#4caf50", "#ff9800", "#e91e63", "#9c27b0"]
23979
+ };
23980
+ var DOMAIN_ICONS = {
23981
+ energy: "\u26A1",
23982
+ // Lightning bolt
23983
+ water: "\u{1F4A7}",
23984
+ // Water drop
23985
+ temperature: "\u{1F321}\uFE0F"
23986
+ // Thermometer
23987
+ };
23988
+ var DOMAIN_LABELS = {
23989
+ energy: "Energia",
23990
+ water: "\xC1gua",
23991
+ temperature: "Temperatura"
23992
+ };
23993
+ var DOMAIN_LABELS_EN = {
23994
+ energy: "Energy",
23995
+ water: "Water",
23996
+ temperature: "Temperature"
23997
+ };
23998
+ var DOMAIN_UNITS = {
23999
+ energy: "kWh",
24000
+ water: "m\xB3",
24001
+ temperature: "\xB0C"
24002
+ };
24003
+ var CSV_SEPARATORS = {
24004
+ "pt-BR": ";",
24005
+ "en-US": ",",
24006
+ "default": ";"
24007
+ };
24008
+ function formatDateForFilename(date) {
24009
+ const pad = (n) => n.toString().padStart(2, "0");
24010
+ return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}-${pad(date.getHours())}-${pad(date.getMinutes())}-${pad(date.getSeconds())}`;
24011
+ }
24012
+ function sanitizeFilename(str) {
24013
+ return str.replace(/[<>:"/\\|?*]/g, "").replace(/\s+/g, "_").substring(0, 50);
24014
+ }
24015
+ function generateFilename(data, config) {
24016
+ const timestamp = formatDateForFilename(/* @__PURE__ */ new Date());
24017
+ const domainLabel = DOMAIN_LABELS_EN[config.domain].toUpperCase();
24018
+ const ext = config.formatExport;
24019
+ let baseName = "export";
24020
+ if ("device" in data && data.device) {
24021
+ const device = data.device;
24022
+ const label = device.label || device.name || "device";
24023
+ const identifier = device.identifier ? `-${device.identifier}` : "";
24024
+ baseName = `${sanitizeFilename(label)}${identifier}`;
24025
+ } else if ("customer" in data && data.customer?.customerName) {
24026
+ baseName = sanitizeFilename(data.customer.customerName);
24027
+ } else if ("groupName" in data) {
24028
+ baseName = sanitizeFilename(data.groupName);
24029
+ }
24030
+ return `${baseName}-${domainLabel}-${timestamp}.${ext}`;
24031
+ }
24032
+ function normalizeTimestamp(ts) {
24033
+ if (ts instanceof Date) return ts;
24034
+ if (typeof ts === "number") return new Date(ts);
24035
+ return new Date(ts);
24036
+ }
24037
+ function calculateStats2(dataPoints) {
24038
+ if (dataPoints.length === 0) {
24039
+ return { min: 0, max: 0, average: 0, sum: 0, count: 0 };
24040
+ }
24041
+ const values = dataPoints.map((d) => d.value);
24042
+ const sum = values.reduce((a, b) => a + b, 0);
24043
+ return {
24044
+ min: Math.min(...values),
24045
+ max: Math.max(...values),
24046
+ average: sum / values.length,
24047
+ sum,
24048
+ count: values.length
24049
+ };
24050
+ }
24051
+ function formatNumber2(value, locale, decimals = 2) {
24052
+ return new Intl.NumberFormat(locale, {
24053
+ minimumFractionDigits: decimals,
24054
+ maximumFractionDigits: decimals
24055
+ }).format(value);
24056
+ }
24057
+ function formatDate3(date, locale) {
24058
+ return new Intl.DateTimeFormat(locale, {
24059
+ year: "numeric",
24060
+ month: "2-digit",
24061
+ day: "2-digit",
24062
+ hour: "2-digit",
24063
+ minute: "2-digit"
24064
+ }).format(date);
24065
+ }
24066
+ function generateCSV(data, config) {
24067
+ const sep = CSV_SEPARATORS[config.locale] || CSV_SEPARATORS["default"];
24068
+ const rows = [];
24069
+ const escapeCSV = (val) => {
24070
+ const str = String(val ?? "");
24071
+ if (str.includes(sep) || str.includes('"') || str.includes("\n")) {
24072
+ return `"${str.replace(/"/g, '""')}"`;
24073
+ }
24074
+ return str;
24075
+ };
24076
+ const formatNumCSV = (val) => {
24077
+ return formatNumber2(val, config.locale);
24078
+ };
24079
+ if ("device" in data && "data" in data && Array.isArray(data.data)) {
24080
+ const deviceData = data;
24081
+ rows.push(["Timestamp", config.domainLabel, `Unit (${config.domainUnit})`]);
24082
+ for (const point of deviceData.data) {
24083
+ const ts = normalizeTimestamp(point.timestamp);
24084
+ rows.push([
24085
+ formatDate3(ts, config.locale),
24086
+ formatNumCSV(point.value),
24087
+ point.unit || config.domainUnit
24088
+ ]);
24089
+ }
24090
+ if (config.includeStats) {
24091
+ const stats = calculateStats2(deviceData.data);
24092
+ rows.push([]);
24093
+ rows.push(["Statistics", "", ""]);
24094
+ rows.push(["Minimum", formatNumCSV(stats.min), config.domainUnit]);
24095
+ rows.push(["Maximum", formatNumCSV(stats.max), config.domainUnit]);
24096
+ rows.push(["Average", formatNumCSV(stats.average), config.domainUnit]);
24097
+ rows.push(["Total", formatNumCSV(stats.sum), config.domainUnit]);
24098
+ rows.push(["Count", String(stats.count), "points"]);
24099
+ }
24100
+ } else if ("devices" in data && Array.isArray(data.devices)) {
24101
+ const compData = data;
24102
+ const deviceHeaders = compData.devices.map(
24103
+ (d) => d.device.label || d.device.name || "Device"
24104
+ );
24105
+ rows.push(["Timestamp", ...deviceHeaders]);
24106
+ const allTimestamps = /* @__PURE__ */ new Set();
24107
+ compData.devices.forEach((d) => {
24108
+ d.data.forEach((point) => {
24109
+ allTimestamps.add(normalizeTimestamp(point.timestamp).getTime());
24110
+ });
24111
+ });
24112
+ const sortedTimestamps = Array.from(allTimestamps).sort((a, b) => a - b);
24113
+ for (const ts of sortedTimestamps) {
24114
+ const row = [formatDate3(new Date(ts), config.locale)];
24115
+ for (const device of compData.devices) {
24116
+ const point = device.data.find(
24117
+ (p) => normalizeTimestamp(p.timestamp).getTime() === ts
24118
+ );
24119
+ row.push(point ? formatNumCSV(point.value) : "");
24120
+ }
24121
+ rows.push(row);
24122
+ }
24123
+ }
24124
+ return rows.map((row) => row.map(escapeCSV).join(sep)).join("\r\n");
24125
+ }
24126
+ function generateXLSX(data, config) {
24127
+ return generateCSV(data, config);
24128
+ }
24129
+ function generatePDFContent(data, config) {
24130
+ const { colors, domainIcon, domainLabel, domainUnit, locale, includeStats, includeChart } = config;
24131
+ let deviceLabel = "Export";
24132
+ let customerName = "";
24133
+ let identifier = "";
24134
+ let dataPoints = [];
24135
+ if ("device" in data && "data" in data) {
24136
+ const deviceData = data;
24137
+ deviceLabel = deviceData.device.label || deviceData.device.name || "Device";
24138
+ identifier = deviceData.device.identifier || "";
24139
+ customerName = deviceData.customer?.customerName || "";
24140
+ dataPoints = deviceData.data;
24141
+ }
24142
+ const stats = calculateStats2(dataPoints);
24143
+ const tableRows = dataPoints.slice(0, 100).map((point) => {
24144
+ const ts = normalizeTimestamp(point.timestamp);
24145
+ return `
24146
+ <tr>
24147
+ <td style="padding: 8px; border-bottom: 1px solid #eee;">${formatDate3(ts, locale)}</td>
24148
+ <td style="padding: 8px; border-bottom: 1px solid #eee; text-align: right;">${formatNumber2(point.value, locale)}</td>
24149
+ <td style="padding: 8px; border-bottom: 1px solid #eee;">${point.unit || domainUnit}</td>
24150
+ </tr>
24151
+ `;
24152
+ }).join("");
24153
+ const statsSection = includeStats ? `
24154
+ <div style="margin-top: 24px; padding: 16px; background: #f5f5f5; border-radius: 8px;">
24155
+ <h3 style="margin: 0 0 12px 0; color: ${colors.primary};">Statistics</h3>
24156
+ <table style="width: 100%;">
24157
+ <tr>
24158
+ <td><strong>Minimum:</strong></td>
24159
+ <td>${formatNumber2(stats.min, locale)} ${domainUnit}</td>
24160
+ <td><strong>Maximum:</strong></td>
24161
+ <td>${formatNumber2(stats.max, locale)} ${domainUnit}</td>
24162
+ </tr>
24163
+ <tr>
24164
+ <td><strong>Average:</strong></td>
24165
+ <td>${formatNumber2(stats.average, locale)} ${domainUnit}</td>
24166
+ <td><strong>Total:</strong></td>
24167
+ <td>${formatNumber2(stats.sum, locale)} ${domainUnit}</td>
24168
+ </tr>
24169
+ </table>
24170
+ </div>
24171
+ ` : "";
24172
+ return `
24173
+ <!DOCTYPE html>
24174
+ <html>
24175
+ <head>
24176
+ <meta charset="UTF-8">
24177
+ <title>${deviceLabel} - ${domainLabel} Report</title>
24178
+ <style>
24179
+ body {
24180
+ font-family: 'Roboto', Arial, sans-serif;
24181
+ margin: 0;
24182
+ padding: 24px;
24183
+ color: ${colors.text};
24184
+ background: ${colors.background};
24185
+ }
24186
+ .header {
24187
+ background: ${colors.primary};
24188
+ color: white;
24189
+ padding: 20px;
24190
+ border-radius: 8px;
24191
+ margin-bottom: 24px;
24192
+ }
24193
+ .header h1 {
24194
+ margin: 0;
24195
+ font-size: 24px;
24196
+ }
24197
+ .header .subtitle {
24198
+ opacity: 0.9;
24199
+ margin-top: 8px;
24200
+ }
24201
+ .device-info {
24202
+ display: flex;
24203
+ gap: 16px;
24204
+ margin-bottom: 16px;
24205
+ padding: 12px;
24206
+ background: #f5f5f5;
24207
+ border-radius: 8px;
24208
+ }
24209
+ .device-info span {
24210
+ padding: 4px 12px;
24211
+ background: ${colors.secondary};
24212
+ color: white;
24213
+ border-radius: 4px;
24214
+ font-size: 14px;
24215
+ }
24216
+ table {
24217
+ width: 100%;
24218
+ border-collapse: collapse;
24219
+ }
24220
+ th {
24221
+ background: ${colors.primary};
24222
+ color: white;
24223
+ padding: 12px 8px;
24224
+ text-align: left;
24225
+ }
24226
+ th:nth-child(2) {
24227
+ text-align: right;
24228
+ }
24229
+ .footer {
24230
+ margin-top: 24px;
24231
+ padding-top: 16px;
24232
+ border-top: 1px solid #eee;
24233
+ text-align: center;
24234
+ font-size: 12px;
24235
+ color: #999;
24236
+ }
24237
+ @media print {
24238
+ body { padding: 0; }
24239
+ .header { border-radius: 0; }
24240
+ }
24241
+ </style>
24242
+ </head>
24243
+ <body>
24244
+ <div class="header">
24245
+ <h1>${domainIcon} ${deviceLabel}</h1>
24246
+ <div class="subtitle">${domainLabel} Report - Generated ${formatDate3(/* @__PURE__ */ new Date(), locale)}</div>
24247
+ </div>
24248
+
24249
+ ${customerName ? `<div class="customer-name" style="margin-bottom: 16px; font-size: 18px;"><strong>Customer:</strong> ${customerName}</div>` : ""}
24250
+
24251
+ ${identifier ? `
24252
+ <div class="device-info">
24253
+ <span>ID: ${identifier}</span>
24254
+ <span>Domain: ${domainLabel}</span>
24255
+ <span>Unit: ${domainUnit}</span>
24256
+ </div>
24257
+ ` : ""}
24258
+
24259
+ <table>
24260
+ <thead>
24261
+ <tr>
24262
+ <th>Timestamp</th>
24263
+ <th>${domainLabel} (${domainUnit})</th>
24264
+ <th>Unit</th>
24265
+ </tr>
24266
+ </thead>
24267
+ <tbody>
24268
+ ${tableRows}
24269
+ ${dataPoints.length > 100 ? `<tr><td colspan="3" style="text-align: center; padding: 16px; color: #999;">... and ${dataPoints.length - 100} more rows</td></tr>` : ""}
24270
+ </tbody>
24271
+ </table>
24272
+
24273
+ ${statsSection}
24274
+
24275
+ <div class="footer">
24276
+ <p>${config.footerText || "Generated by MyIO Platform"}</p>
24277
+ </div>
24278
+ </body>
24279
+ </html>
24280
+ `;
24281
+ }
24282
+ function buildTemplateExport(params) {
24283
+ const {
24284
+ domain,
24285
+ formatExport,
24286
+ typeExport,
24287
+ colorsPallet,
24288
+ locale = "pt-BR",
24289
+ includeChart = formatExport === "pdf",
24290
+ includeStats = true,
24291
+ headerText,
24292
+ footerText
24293
+ } = params;
24294
+ const colors = {
24295
+ ...DEFAULT_COLORS3,
24296
+ ...colorsPallet,
24297
+ chartColors: colorsPallet?.chartColors || DEFAULT_COLORS3.chartColors
24298
+ };
24299
+ return {
24300
+ domain,
24301
+ formatExport,
24302
+ typeExport,
24303
+ colors,
24304
+ locale,
24305
+ includeChart,
24306
+ includeStats,
24307
+ headerText: headerText || `${DOMAIN_LABELS[domain]} Report`,
24308
+ footerText: footerText || "Generated by MyIO Platform",
24309
+ domainIcon: DOMAIN_ICONS[domain],
24310
+ domainLabel: DOMAIN_LABELS[domain],
24311
+ domainUnit: DOMAIN_UNITS[domain]
24312
+ };
24313
+ }
24314
+ function myioExportData(data, config, options) {
24315
+ const filename = generateFilename(data, config);
24316
+ let allDataPoints = [];
24317
+ if ("data" in data && Array.isArray(data.data)) {
24318
+ allDataPoints = data.data;
24319
+ } else if ("devices" in data && Array.isArray(data.devices)) {
24320
+ allDataPoints = data.devices.flatMap((d) => d.data);
24321
+ }
24322
+ const stats = calculateStats2(allDataPoints);
24323
+ const instance = {
24324
+ async export() {
24325
+ try {
24326
+ options?.onProgress?.(10, "Generating content...");
24327
+ let content;
24328
+ let mimeType;
24329
+ let finalFilename = filename;
24330
+ switch (config.formatExport) {
24331
+ case "csv":
24332
+ content = generateCSV(data, config);
24333
+ mimeType = "text/csv;charset=utf-8;";
24334
+ break;
24335
+ case "xlsx":
24336
+ content = generateXLSX(data, config);
24337
+ mimeType = "text/csv;charset=utf-8;";
24338
+ finalFilename = filename.replace(".xlsx", ".csv");
24339
+ break;
24340
+ case "pdf":
24341
+ content = generatePDFContent(data, config);
24342
+ mimeType = "text/html;charset=utf-8;";
24343
+ finalFilename = filename.replace(".pdf", ".html");
24344
+ break;
24345
+ default:
24346
+ throw new Error(`Unsupported format: ${config.formatExport}`);
24347
+ }
24348
+ options?.onProgress?.(80, "Creating file...");
24349
+ const bom = config.formatExport === "csv" ? "\uFEFF" : "";
24350
+ const blob = new Blob([bom + content], { type: mimeType });
24351
+ options?.onProgress?.(100, "Export complete");
24352
+ return {
24353
+ success: true,
24354
+ filename: finalFilename,
24355
+ blob,
24356
+ dataUrl: URL.createObjectURL(blob)
24357
+ };
24358
+ } catch (error) {
24359
+ return {
24360
+ success: false,
24361
+ filename,
24362
+ error: error instanceof Error ? error.message : "Unknown error"
24363
+ };
24364
+ }
24365
+ },
24366
+ async download() {
24367
+ const result = await this.export();
24368
+ if (!result.success || !result.blob) {
24369
+ console.error("Export failed:", result.error);
24370
+ return;
24371
+ }
24372
+ const link = document.createElement("a");
24373
+ link.href = URL.createObjectURL(result.blob);
24374
+ link.download = result.filename;
24375
+ link.style.display = "none";
24376
+ document.body.appendChild(link);
24377
+ link.click();
24378
+ document.body.removeChild(link);
24379
+ URL.revokeObjectURL(link.href);
24380
+ },
24381
+ async preview() {
24382
+ if (config.formatExport !== "pdf") {
24383
+ return null;
24384
+ }
24385
+ const result = await this.export();
24386
+ return result.dataUrl || null;
24387
+ },
24388
+ getStats() {
24389
+ return stats;
24390
+ },
24391
+ getFilename() {
24392
+ return filename;
24393
+ }
24394
+ };
24395
+ if (options?.autoDownload) {
24396
+ instance.download();
24397
+ }
24398
+ return instance;
24399
+ }
24400
+ var EXPORT_DEFAULT_COLORS = DEFAULT_COLORS3;
24401
+ var EXPORT_DOMAIN_ICONS = DOMAIN_ICONS;
24402
+ var EXPORT_DOMAIN_LABELS = DOMAIN_LABELS;
24403
+ var EXPORT_DOMAIN_UNITS = DOMAIN_UNITS;
22467
24404
  // Annotate the CommonJS export names for ESM import in node:
22468
24405
  0 && (module.exports = {
22469
24406
  CHART_COLORS,
24407
+ CONSUMPTION_CHART_COLORS,
24408
+ CONSUMPTION_CHART_DEFAULTS,
24409
+ CONSUMPTION_THEME_COLORS,
22470
24410
  ConnectionStatusType,
22471
24411
  DEFAULT_CLAMP_RANGE,
22472
24412
  DeviceStatusType,
24413
+ EXPORT_DEFAULT_COLORS,
24414
+ EXPORT_DOMAIN_ICONS,
24415
+ EXPORT_DOMAIN_LABELS,
24416
+ EXPORT_DOMAIN_UNITS,
22473
24417
  MyIOChartModal,
22474
24418
  MyIODraggableCard,
22475
24419
  MyIOSelectionStore,
@@ -22481,11 +24425,13 @@ function openTemperatureSettingsModal(params) {
22481
24425
  averageByDay,
22482
24426
  buildListItemsThingsboardByUniqueDatasource,
22483
24427
  buildMyioIngestionAuth,
24428
+ buildTemplateExport,
22484
24429
  buildWaterReportCSV,
22485
24430
  buildWaterStoresCSV,
22486
24431
  calcDeltaPercent,
22487
24432
  calculateDeviceStatus,
22488
24433
  calculateDeviceStatusWithRanges,
24434
+ calculateExportStats,
22489
24435
  calculateStats,
22490
24436
  clampTemperature,
22491
24437
  classify,
@@ -22493,8 +24439,11 @@ function openTemperatureSettingsModal(params) {
22493
24439
  classifyWaterLabels,
22494
24440
  clearAllAuthCaches,
22495
24441
  connectionStatusIcons,
24442
+ createConsumption7DaysChart,
24443
+ createConsumptionModal,
22496
24444
  createDateRangePicker,
22497
24445
  createInputDateRangePickerInsideDIV,
24446
+ createModalHeader,
22498
24447
  decodePayload,
22499
24448
  decodePayloadBase64Xor,
22500
24449
  detectDeviceType,
@@ -22508,6 +24457,7 @@ function openTemperatureSettingsModal(params) {
22508
24457
  fetchThingsboardCustomerAttrsFromStorage,
22509
24458
  fetchThingsboardCustomerServerScopeAttrs,
22510
24459
  findValue,
24460
+ findValueWithDefault,
22511
24461
  fmtPerc,
22512
24462
  fmtPercLegacy,
22513
24463
  formatAllInSameUnit,
@@ -22515,18 +24465,24 @@ function openTemperatureSettingsModal(params) {
22515
24465
  formatDateForInput,
22516
24466
  formatDateToYMD,
22517
24467
  formatDateWithTimezoneOffset,
24468
+ formatDuration,
22518
24469
  formatEnergy,
22519
24470
  formatNumberReadable,
24471
+ formatRelativeTime,
22520
24472
  formatTankHeadFromCm,
22521
24473
  formatTemperature,
24474
+ formatWater,
22522
24475
  formatWaterByGroup,
22523
24476
  formatWaterVolumeM3,
24477
+ formatarDuracao,
24478
+ generateExportFilename,
22524
24479
  getAuthCacheStats,
22525
24480
  getAvailableContexts,
22526
24481
  getConnectionStatusIcon,
22527
24482
  getDateRangeArray,
22528
24483
  getDeviceStatusIcon,
22529
24484
  getDeviceStatusInfo,
24485
+ getModalHeaderStyles,
22530
24486
  getSaoPauloISOString,
22531
24487
  getSaoPauloISOStringFixed,
22532
24488
  getValueByDatakey,
@@ -22538,8 +24494,10 @@ function openTemperatureSettingsModal(params) {
22538
24494
  isValidConnectionStatus,
22539
24495
  isValidDeviceStatus,
22540
24496
  isWaterCategory,
24497
+ mapConnectionStatus,
22541
24498
  mapDeviceStatusToCardStatus,
22542
24499
  mapDeviceToConnectionStatus,
24500
+ myioExportData,
22543
24501
  normalizeRecipients,
22544
24502
  numbers,
22545
24503
  openDashboardPopup,