myio-js-library 0.1.63 → 0.1.64

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
@@ -5171,7 +5171,7 @@ var MODAL_STYLES = `
5171
5171
  }
5172
5172
 
5173
5173
  .myio-modal-header {
5174
- padding: var(--myio-spacing);
5174
+ padding: 4px;
5175
5175
  border-bottom: none;
5176
5176
  display: flex;
5177
5177
  align-items: center;
@@ -5179,13 +5179,15 @@ var MODAL_STYLES = `
5179
5179
  background: var(--myio-brand-700);
5180
5180
  color: white;
5181
5181
  border-radius: var(--myio-radius) var(--myio-radius) 0 0;
5182
+ min-height: 20px;
5182
5183
  }
5183
5184
 
5184
5185
  .myio-modal-title {
5185
- font-size: var(--myio-font-size-lg);
5186
+ font-size: 18px !important;
5186
5187
  font-weight: 600;
5187
- margin: 0;
5188
+ margin: 6px !important;
5188
5189
  color: white;
5190
+ line-height: 2 !important;
5189
5191
  }
5190
5192
 
5191
5193
  .myio-modal-close {
@@ -6408,15 +6410,11 @@ var EnergyModalView = class {
6408
6410
  this.setupEventListeners();
6409
6411
  }
6410
6412
  /**
6411
- * Creates the main modal content structure - CLEAN LAYOUT, NO BLUR ON MODAL
6413
+ * Creates the main modal content structure
6412
6414
  */
6413
6415
  createModalContent() {
6414
6416
  const container = document.createElement("div");
6415
- container.id = "dashboard-popup";
6416
- container.className = "myio-modal-overlay";
6417
- container.setAttribute("role", "dialog");
6418
- container.setAttribute("aria-modal", "true");
6419
- container.setAttribute("aria-labelledby", "modal-title");
6417
+ container.className = "myio-energy-modal-scope";
6420
6418
  const { device } = this.config.context;
6421
6419
  const identifier = device.attributes.identifier || "SEM IDENTIFICADOR";
6422
6420
  const label = device.label || "SEM ETIQUETA";
@@ -6424,48 +6422,48 @@ var EnergyModalView = class {
6424
6422
  <style>
6425
6423
  ${this.getModalStyles()}
6426
6424
  </style>
6427
-
6428
- <div class="myio-modal energy-report-modal">
6429
- <!-- SINGLE HEADER ONLY -->
6430
- <header class="myio-modal__header">
6431
- <h2 id="modal-title">Relat\xF3rio de Energia \u2013 ${identifier} \u2013 ${label}</h2>
6432
- <button id="close-btn" class="myio-modal__close" aria-label="Fechar">\xD7</button>
6433
- </header>
6434
-
6435
- <!-- TOOLBAR -->
6436
- <div class="myio-modal__toolbar">
6437
- <div class="tb-start">
6438
- <input type="text" id="energy-date-range" readonly placeholder="Selecione o per\xEDodo">
6439
- <button id="btn-load" class="btn btn-ghost">
6425
+ <div class="myio-modal-scope" style="height: 100%; display: flex; flex-direction: column;">
6426
+ <!-- Controls Section -->
6427
+ <div style="margin-bottom: 16px; flex-shrink: 0;">
6428
+ <div style="display: flex; gap: 16px; align-items: end; margin-bottom: 16px;">
6429
+ <div class="myio-form-group" style="margin-bottom: 0;">
6430
+ <label class="myio-label" for="date-range">Per\xEDodo</label>
6431
+ <input type="text" id="date-range" class="myio-input" readonly placeholder="Selecione o per\xEDodo" style="width: 300px;">
6432
+ </div>
6433
+ <button id="load-btn" class="myio-btn myio-btn-primary">
6440
6434
  <span class="myio-spinner" id="load-spinner" style="display: none;"></span>
6441
6435
  Carregar
6442
6436
  </button>
6443
- </div>
6444
- <div class="tb-end">
6445
- <button id="btn-csv" class="btn btn-ghost" disabled>Exportar CSV</button>
6446
- <button id="btn-pdf" class="btn btn-premium">Exportar PDF</button>
6447
- <button id="btn-close" class="btn btn-ghost">Fechar</button>
6437
+ <button id="export-csv-btn" class="myio-btn myio-btn-secondary" disabled>
6438
+ Exportar CSV
6439
+ </button>
6440
+ <button id="close-btn" class="myio-btn myio-btn-secondary">
6441
+ Fechar
6442
+ </button>
6448
6443
  </div>
6449
6444
  </div>
6450
-
6451
- <!-- ERROR CONTAINER -->
6452
- <div id="energy-error" class="myio-energy-error" style="display: none;">
6445
+
6446
+ <!-- Error Container -->
6447
+ <div id="energy-error" class="myio-energy-error" style="display: none; flex-shrink: 0;">
6453
6448
  <!-- Error messages will be displayed here -->
6454
6449
  </div>
6455
-
6456
- <!-- MAIN CHART CONTAINER - FULL HEIGHT -->
6457
- <main class="myio-modal__body">
6458
- <div class="chart-iframe-wrap">
6459
- <iframe id="energy-report-iframe" src="" loading="lazy" title="Energy Chart" style="display: none;">
6460
- </iframe>
6461
- <div id="energy-chart-container" class="myio-energy-chart-container">
6462
- <div class="myio-loading-state">
6463
- <div class="myio-spinner"></div>
6464
- <p>${this.getI18nText("loading")}</p>
6465
- </div>
6466
- </div>
6450
+
6451
+ <!-- Main Chart Container - Full Width -->
6452
+ <div id="energy-chart-container" class="myio-energy-chart-container" style="width: 100%; flex: 1; box-sizing: border-box;">
6453
+ <div class="myio-loading-state">
6454
+ <div class="myio-spinner"></div>
6455
+ <p>${this.getI18nText("loading")}</p>
6467
6456
  </div>
6468
- </main>
6457
+ </div>
6458
+
6459
+ <!-- KPI Button Section -->
6460
+ <div id="energy-kpi-btn" style="display: none; margin-top: 16px; text-align: center; flex-shrink: 0;">
6461
+ <button id="show-kpis-btn" class="myio-btn myio-btn-secondary" title="Show detailed metrics" style="display: none;">
6462
+ <span style="font-size: 16px; font-weight: bold;">+</span>
6463
+ <span style="margin-left: 8px;">Show Metrics</span>
6464
+ </button>
6465
+ </div>
6466
+
6469
6467
  </div>
6470
6468
  `;
6471
6469
  this.container = container;
@@ -6483,13 +6481,9 @@ var EnergyModalView = class {
6483
6481
  <p>${this.getI18nText("loading")}</p>
6484
6482
  </div>
6485
6483
  `;
6486
- this.chartContainer.style.display = "flex";
6487
- }
6488
- const iframe = document.getElementById("energy-report-iframe");
6489
- if (iframe) {
6490
- iframe.style.display = "none";
6491
6484
  }
6492
6485
  this.hideError();
6486
+ this.hideKPIButton();
6493
6487
  }
6494
6488
  /**
6495
6489
  * Shows error message
@@ -6508,20 +6502,21 @@ var EnergyModalView = class {
6508
6502
  this.hideLoadingState();
6509
6503
  }
6510
6504
  /**
6511
- * Renders energy data (charts only - NO KPIs, NO TABLE)
6505
+ * Renders energy data (charts, KPIs, table)
6512
6506
  */
6513
6507
  renderEnergyData(energyData) {
6514
6508
  this.currentEnergyData = energyData;
6515
6509
  this.hideLoadingState();
6516
6510
  this.hideError();
6517
6511
  this.renderChart(energyData);
6518
- const exportCsvBtn = document.getElementById("btn-csv");
6519
- const exportPdfBtn = document.getElementById("btn-pdf");
6520
- if (exportCsvBtn) exportCsvBtn.disabled = false;
6521
- if (exportPdfBtn) exportPdfBtn.disabled = false;
6512
+ this.showKPIButton();
6513
+ const exportBtn = document.getElementById("export-csv-btn");
6514
+ if (exportBtn) {
6515
+ exportBtn.disabled = false;
6516
+ }
6522
6517
  }
6523
6518
  /**
6524
- * Renders the energy chart with full height
6519
+ * Renders the energy chart
6525
6520
  */
6526
6521
  renderChart(energyData) {
6527
6522
  if (!this.chartContainer) return;
@@ -6535,44 +6530,66 @@ var EnergyModalView = class {
6535
6530
  */
6536
6531
  tryRenderWithSDK(energyData) {
6537
6532
  try {
6538
- if (typeof window !== "undefined" && window.EnergyChartSDK) {
6539
- let startISO, endISO;
6540
- if (this.dateRangePicker) {
6541
- const dates = this.dateRangePicker.getDates();
6542
- startISO = dates.startISO;
6543
- endISO = dates.endISO;
6544
- } else {
6545
- startISO = this.normalizeToSaoPauloISO(this.config.params.startDate, false);
6546
- endISO = this.normalizeToSaoPauloISO(this.config.params.endDate, true);
6533
+ if (this.chartInstance && typeof this.chartInstance.destroy === "function") {
6534
+ this.chartInstance.destroy();
6535
+ this.chartInstance = null;
6536
+ }
6537
+ if (this.chartContainer) {
6538
+ this.chartContainer.innerHTML = "";
6539
+ }
6540
+ let renderTelemetryChart;
6541
+ if (typeof window !== "undefined" && window.EnergyChartSDK && typeof window.EnergyChartSDK.renderTelemetryChart === "function") {
6542
+ renderTelemetryChart = window.EnergyChartSDK.renderTelemetryChart;
6543
+ } else {
6544
+ console.error("[EnergyModalView] EnergyChartSDK v2 (renderTelemetryChart) not loaded!");
6545
+ if (this.chartContainer) {
6546
+ this.chartContainer.innerHTML = '<div style="padding: 20px; text-align: center; color: red;">EnergyChartSDK v2 (renderTelemetryChart) not loaded. Check widget configuration and browser console.</div>';
6547
6547
  }
6548
- const chartConfig = {
6549
- version: "v2",
6550
- clientId: this.config.params.clientId || "ADMIN_DASHBOARD_CLIENT",
6551
- clientSecret: this.config.params.clientSecret || "admin_dashboard_secret_2025",
6552
- deviceId: this.config.context.resolved.ingestionId,
6553
- readingType: "energy",
6554
- startDate: startISO,
6555
- endDate: endISO,
6556
- granularity: this.config.params.granularity || "1d",
6557
- theme: this.config.params.theme || "light",
6558
- timezone: this.config.params.timezone || "America/Sao_Paulo",
6559
- iframeBaseUrl: this.config.params.chartsBaseUrl || "https://graphs.apps.myio-bas.com",
6560
- apiBaseUrl: this.config.params.dataApiHost || "https://api.data.apps.myio-bas.com"
6561
- };
6562
- console.log("[EnergyModalView] Rendering chart with SDK config:", {
6563
- deviceId: chartConfig.deviceId,
6564
- startDate: chartConfig.startDate,
6565
- endDate: chartConfig.endDate,
6566
- granularity: chartConfig.granularity
6548
+ return false;
6549
+ }
6550
+ let startISO, endISO;
6551
+ if (this.dateRangePicker) {
6552
+ const dates = this.dateRangePicker.getDates();
6553
+ startISO = dates.startISO;
6554
+ endISO = dates.endISO;
6555
+ } else {
6556
+ startISO = this.normalizeToSaoPauloISO(this.config.params.startDate, false);
6557
+ endISO = this.normalizeToSaoPauloISO(this.config.params.endDate, true);
6558
+ }
6559
+ const tzIdentifier = this.config.params.timezone || "America/Sao_Paulo";
6560
+ const granularity = this.config.params.granularity || "1d";
6561
+ const theme = this.config.params.theme || "light";
6562
+ const ingestionId = this.config.context.resolved.ingestionId;
6563
+ console.log(`[EnergyModalView] Initializing v2 chart with: deviceId=${ingestionId}, startDate=${startISO}, endDate=${endISO}, granularity=${granularity}, theme=${theme}, timezone=${tzIdentifier}`);
6564
+ const chartConfig = {
6565
+ version: "v2",
6566
+ clientId: this.config.params.clientId || "ADMIN_DASHBOARD_CLIENT",
6567
+ clientSecret: this.config.params.clientSecret || "admin_dashboard_secret_2025",
6568
+ deviceId: ingestionId,
6569
+ readingType: "energy",
6570
+ startDate: startISO,
6571
+ endDate: endISO,
6572
+ granularity,
6573
+ theme,
6574
+ timezone: tzIdentifier,
6575
+ iframeBaseUrl: this.config.params.chartsBaseUrl || "https://graphs.apps.myio-bas.com",
6576
+ apiBaseUrl: this.config.params.dataApiHost || "https://api.data.apps.myio-bas.com"
6577
+ };
6578
+ this.chartInstance = renderTelemetryChart(this.chartContainer, chartConfig);
6579
+ if (this.chartInstance && typeof this.chartInstance.on === "function") {
6580
+ this.chartInstance.on("drilldown", (data) => {
6581
+ console.log("[EnergyModalView] v2 SDK Drilldown Event:", data);
6567
6582
  });
6568
- this.chartContainer.style.display = "none";
6569
- const iframe = document.getElementById("energy-report-iframe");
6570
- if (iframe) {
6571
- iframe.style.display = "block";
6572
- window.EnergyChartSDK.renderTelemetryChart(iframe.parentElement, chartConfig);
6573
- }
6574
- return true;
6583
+ this.chartInstance.on("error", (errorData) => {
6584
+ console.error("[EnergyModalView] v2 SDK Error Event:", errorData);
6585
+ if (this.chartContainer) {
6586
+ this.chartContainer.innerHTML = `<div style="padding: 20px; text-align: center; color: red;">v2 Chart Error: ${errorData.message || "Unknown error"}</div>`;
6587
+ }
6588
+ });
6589
+ } else if (this.chartInstance) {
6590
+ console.warn("[EnergyModalView] EnergyChartSDK v2 instance does not have an 'on' method for event listeners.");
6575
6591
  }
6592
+ return true;
6576
6593
  } catch (error) {
6577
6594
  console.warn("[EnergyModalView] EnergyChartSDK failed, using fallback:", error);
6578
6595
  }
@@ -6604,26 +6621,51 @@ var EnergyModalView = class {
6604
6621
  */
6605
6622
  renderFallbackChart(energyData) {
6606
6623
  if (!this.chartContainer) return;
6607
- this.chartContainer.style.display = "flex";
6608
- const iframe = document.getElementById("energy-report-iframe");
6609
- if (iframe) {
6610
- iframe.style.display = "none";
6611
- }
6624
+ const maxValue = Math.max(...energyData.consumption.map((p) => p.value));
6625
+ const chartHeight = 300;
6612
6626
  const chartHTML = `
6613
- <div class="myio-fallback-chart" style="height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center;">
6614
- <div style="text-align: center; color: #666;">
6615
- <div style="font-size: 48px; margin-bottom: 16px;">\u{1F4CA}</div>
6616
- <h3 style="margin: 0 0 8px 0; color: #2D1458;">Full-Height Energy Chart</h3>
6617
- <p style="margin: 0; color: #6b7280; line-height: 1.5;">
6618
- EnergyChartSDK iframe renders here at 100% height<br>
6619
- Data points: ${energyData.consumption.length}<br>
6620
- <strong style="color: #2D1458;">Chart takes all available space</strong>
6621
- </p>
6627
+ <div class="myio-fallback-chart">
6628
+ <h4>${this.getI18nText("energyChart")}</h4>
6629
+ <div class="myio-chart-container" style="height: ${chartHeight}px;">
6630
+ <svg width="100%" height="100%" viewBox="0 0 800 ${chartHeight}">
6631
+ ${energyData.consumption.map((point, index) => {
6632
+ const x = index / (energyData.consumption.length - 1) * 750 + 25;
6633
+ const y = chartHeight - 50 - point.value / maxValue * (chartHeight - 100);
6634
+ const barWidth = Math.max(2, 750 / energyData.consumption.length - 2);
6635
+ return `
6636
+ <rect x="${x - barWidth / 2}" y="${y}" width="${barWidth}" height="${chartHeight - 50 - y}"
6637
+ fill="var(--myio-energy-primary, #6366f1)" opacity="0.7">
6638
+ <title>${formatDate(point.timestamp)}: ${formatNumber(point.value)} kWh</title>
6639
+ </rect>
6640
+ `;
6641
+ }).join("")}
6642
+
6643
+ <!-- Y-axis -->
6644
+ <line x1="25" y1="25" x2="25" y2="${chartHeight - 25}" stroke="#ccc" stroke-width="1"/>
6645
+ <!-- X-axis -->
6646
+ <line x1="25" y1="${chartHeight - 25}" x2="775" y2="${chartHeight - 25}" stroke="#ccc" stroke-width="1"/>
6647
+
6648
+ <!-- Y-axis labels -->
6649
+ <text x="15" y="30" text-anchor="end" font-size="12" fill="#666">${formatNumber(maxValue)}</text>
6650
+ <text x="15" y="${chartHeight - 30}" text-anchor="end" font-size="12" fill="#666">0</text>
6651
+ </svg>
6622
6652
  </div>
6653
+ <p class="myio-chart-note">
6654
+ ${this.getI18nText("kwhUnit")} consumption over time. Hover over bars for details.
6655
+ </p>
6623
6656
  </div>
6624
6657
  `;
6625
6658
  this.chartContainer.innerHTML = chartHTML;
6626
6659
  }
6660
+ /**
6661
+ * Shows the KPI button
6662
+ */
6663
+ showKPIButton() {
6664
+ const kpiButtonContainer = document.getElementById("energy-kpi-btn");
6665
+ if (kpiButtonContainer) {
6666
+ kpiButtonContainer.style.display = "block";
6667
+ }
6668
+ }
6627
6669
  /**
6628
6670
  * Exports data to CSV
6629
6671
  */
@@ -6652,116 +6694,6 @@ var EnergyModalView = class {
6652
6694
  const csvContent = toCsv(csvData);
6653
6695
  this.downloadCSV(csvContent, `energy-report-${device.id}-${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.csv`);
6654
6696
  }
6655
- /**
6656
- * Robust PDF export using UMD libraries and iframe handshake
6657
- */
6658
- async exportToPdf() {
6659
- try {
6660
- await this.ensurePdfLibs();
6661
- const iframe = document.getElementById("energy-report-iframe");
6662
- const requestId = "energy-pdf:" + Date.now();
6663
- const png = await this.requestIframePng(iframe, requestId, 1400).catch(() => null);
6664
- const { jsPDF } = window.jspdf;
6665
- const pdf = new jsPDF({ orientation: "landscape", unit: "px", format: "a4" });
6666
- const W = pdf.internal.pageSize.getWidth();
6667
- pdf.setFillColor(45, 20, 88);
6668
- pdf.rect(0, 0, W, 56, "F");
6669
- pdf.setTextColor(255, 255, 255);
6670
- pdf.setFontSize(18);
6671
- const title = document.getElementById("modal-title")?.textContent || "Relat\xF3rio de Energia";
6672
- pdf.text(title, 20, 34);
6673
- if (png) {
6674
- const img = new Image();
6675
- img.src = png;
6676
- await img.decode();
6677
- const maxW = W - 40;
6678
- const ratio = img.height / img.width;
6679
- const drawW = maxW;
6680
- const drawH = drawW * ratio;
6681
- pdf.addImage(png, "PNG", 20, 76, drawW, drawH, void 0, "FAST");
6682
- } else {
6683
- const toolbar = document.querySelector(".myio-modal__toolbar");
6684
- if (toolbar) {
6685
- const cap = await window.html2canvas(toolbar, {
6686
- backgroundColor: "#ffffff",
6687
- scale: Math.min(window.devicePixelRatio || 1, 2)
6688
- });
6689
- const dataUrl = cap.toDataURL("image/png", 0.92);
6690
- pdf.addImage(dataUrl, "PNG", 20, 76, W - 40, (W - 40) * (cap.height / cap.width));
6691
- }
6692
- pdf.setTextColor(85, 85, 85);
6693
- pdf.setFontSize(12);
6694
- pdf.text("Nota: o gr\xE1fico est\xE1 hospedado em iframe cross-origin; use a vers\xE3o integrada/same-origin para exportar o gr\xE1fico completo.", 20, 70);
6695
- }
6696
- const fname = this.buildEnergyFileName();
6697
- pdf.save(fname);
6698
- } catch (error) {
6699
- console.error("[EnergyModalView] PDF export error:", error);
6700
- throw new Error("Failed to export PDF. Please try again.");
6701
- }
6702
- }
6703
- /**
6704
- * Build energy filename
6705
- */
6706
- buildEnergyFileName() {
6707
- const { device } = this.config.context;
6708
- const identifier = device.attributes.identifier || "SEM_IDENTIFICADOR";
6709
- const startDate = this.config.params.startDate instanceof Date ? this.config.params.startDate.toISOString().split("T")[0] : this.config.params.startDate;
6710
- const endDate = this.config.params.endDate instanceof Date ? this.config.params.endDate.toISOString().split("T")[0] : this.config.params.endDate;
6711
- return `EnergyReport_${identifier}_${startDate}_to_${endDate}.pdf`;
6712
- }
6713
- /**
6714
- * Ensure PDF libraries are loaded from CDN (UMD)
6715
- */
6716
- async ensurePdfLibs() {
6717
- const tasks = [];
6718
- if (!window.html2canvas) {
6719
- tasks.push(this.loadScript("https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js"));
6720
- }
6721
- if (!(window.jspdf && window.jspdf.jsPDF)) {
6722
- tasks.push(this.loadScript("https://cdn.jsdelivr.net/npm/jspdf@2.5.1/dist/jspdf.umd.min.js"));
6723
- }
6724
- await Promise.all(tasks);
6725
- }
6726
- /**
6727
- * Load script from CDN
6728
- */
6729
- loadScript(src) {
6730
- return new Promise((resolve, reject) => {
6731
- const script = document.createElement("script");
6732
- script.src = src;
6733
- script.async = true;
6734
- script.onload = () => resolve();
6735
- script.onerror = reject;
6736
- document.head.appendChild(script);
6737
- });
6738
- }
6739
- /**
6740
- * Request PNG from iframe via postMessage (same-origin)
6741
- */
6742
- requestIframePng(iframe, requestId, targetWidth = 1400) {
6743
- return new Promise((resolve, reject) => {
6744
- const timeout = setTimeout(() => {
6745
- cleanup();
6746
- reject(new Error("iframe timeout"));
6747
- }, 6e3);
6748
- function cleanup() {
6749
- clearTimeout(timeout);
6750
- window.removeEventListener("message", onMessage);
6751
- }
6752
- function onMessage(ev) {
6753
- if (!ev.data || ev.data.type !== "energy-report:png" || ev.data.requestId !== requestId) return;
6754
- cleanup();
6755
- resolve(ev.data.dataUrl);
6756
- }
6757
- window.addEventListener("message", onMessage);
6758
- iframe?.contentWindow?.postMessage({
6759
- type: "energy-report:render-png",
6760
- requestId,
6761
- targetWidth
6762
- }, "*");
6763
- });
6764
- }
6765
6697
  /**
6766
6698
  * Downloads CSV file
6767
6699
  */
@@ -6779,14 +6711,27 @@ var EnergyModalView = class {
6779
6711
  document.body.removeChild(link);
6780
6712
  URL.revokeObjectURL(url);
6781
6713
  }
6714
+ /**
6715
+ * Gets default start date (first day of current month)
6716
+ */
6717
+ getDefaultStartDate() {
6718
+ const date = /* @__PURE__ */ new Date();
6719
+ date.setDate(1);
6720
+ return date.toISOString().split("T")[0];
6721
+ }
6722
+ /**
6723
+ * Gets default end date (today)
6724
+ */
6725
+ getDefaultEndDate() {
6726
+ return (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
6727
+ }
6782
6728
  /**
6783
6729
  * Loads data with new date range
6784
6730
  */
6785
6731
  async loadData() {
6786
6732
  if (this.isLoading) return;
6787
- const loadBtn = document.getElementById("btn-load");
6788
- const exportCsvBtn = document.getElementById("btn-csv");
6789
- const exportPdfBtn = document.getElementById("btn-pdf");
6733
+ const loadBtn = document.getElementById("load-btn");
6734
+ const exportBtn = document.getElementById("export-csv-btn");
6790
6735
  const spinner = document.getElementById("load-spinner");
6791
6736
  if (!this.dateRangePicker) {
6792
6737
  this.showError("Seletor de data n\xE3o inicializado");
@@ -6794,8 +6739,7 @@ var EnergyModalView = class {
6794
6739
  }
6795
6740
  this.isLoading = true;
6796
6741
  loadBtn.disabled = true;
6797
- exportCsvBtn.disabled = true;
6798
- exportPdfBtn.disabled = true;
6742
+ exportBtn.disabled = true;
6799
6743
  spinner.style.display = "inline-block";
6800
6744
  try {
6801
6745
  const { startISO, endISO } = this.dateRangePicker.getDates();
@@ -6820,35 +6764,20 @@ var EnergyModalView = class {
6820
6764
  * Sets up event listeners
6821
6765
  */
6822
6766
  async setupEventListeners() {
6823
- const exportCsvBtn = document.getElementById("btn-csv");
6824
- const exportPdfBtn = document.getElementById("btn-pdf");
6767
+ const exportBtn = document.getElementById("export-csv-btn");
6825
6768
  const closeBtn = document.getElementById("close-btn");
6826
- const closeBtnToolbar = document.getElementById("btn-close");
6827
- const loadBtn = document.getElementById("btn-load");
6828
- const dateRangeInput = document.getElementById("energy-date-range");
6829
- if (exportCsvBtn) {
6830
- exportCsvBtn.addEventListener("click", () => {
6769
+ const loadBtn = document.getElementById("load-btn");
6770
+ const showKpisBtn = document.getElementById("show-kpis-btn");
6771
+ const dateRangeInput = document.getElementById("date-range");
6772
+ if (exportBtn) {
6773
+ exportBtn.addEventListener("click", () => {
6831
6774
  try {
6832
6775
  this.exportToCsv();
6833
6776
  } catch (error) {
6834
- console.error("[EnergyModalView] CSV export error:", error);
6835
- this.config.onError({
6836
- code: "UNKNOWN_ERROR",
6837
- message: "Failed to export CSV",
6838
- cause: error
6839
- });
6840
- }
6841
- });
6842
- }
6843
- if (exportPdfBtn) {
6844
- exportPdfBtn.addEventListener("click", async () => {
6845
- try {
6846
- await this.exportToPdf();
6847
- } catch (error) {
6848
- console.error("[EnergyModalView] PDF export error:", error);
6777
+ console.error("[EnergyModalView] Export error:", error);
6849
6778
  this.config.onError({
6850
6779
  code: "UNKNOWN_ERROR",
6851
- message: "Failed to export PDF",
6780
+ message: "Failed to export data",
6852
6781
  cause: error
6853
6782
  });
6854
6783
  }
@@ -6859,14 +6788,15 @@ var EnergyModalView = class {
6859
6788
  this.modal.close();
6860
6789
  });
6861
6790
  }
6862
- if (closeBtnToolbar) {
6863
- closeBtnToolbar.addEventListener("click", () => {
6864
- this.modal.close();
6865
- });
6866
- }
6867
6791
  if (loadBtn) {
6868
6792
  loadBtn.addEventListener("click", () => this.loadData());
6869
6793
  }
6794
+ if (showKpisBtn) {
6795
+ showKpisBtn.addEventListener("click", () => {
6796
+ console.log("[EnergyModalView] Show KPIs modal clicked");
6797
+ alert("KPI modal functionality to be implemented");
6798
+ });
6799
+ }
6870
6800
  try {
6871
6801
  this.dateRangePicker = await attach(dateRangeInput, {
6872
6802
  presetStart: this.config.params.startDate instanceof Date ? this.config.params.startDate.toISOString().split("T")[0] : this.config.params.startDate,
@@ -6881,23 +6811,14 @@ var EnergyModalView = class {
6881
6811
  } catch (error) {
6882
6812
  console.warn("DateRangePicker initialization failed, using fallback:", error);
6883
6813
  }
6884
- this.applyDynamicHeights();
6885
- }
6886
- /**
6887
- * Apply dynamic heights to prevent iframe squashing
6888
- */
6889
- applyDynamicHeights() {
6890
- const modal = document.querySelector(".energy-report-modal");
6891
- const body = document.querySelector(".myio-modal__body");
6892
- if (!modal || !body) return;
6893
- body.style.minHeight = "0px";
6894
6814
  }
6895
6815
  /**
6896
6816
  * Helper methods for hiding/showing sections
6897
6817
  */
6898
6818
  hideLoadingState() {
6899
- if (this.chartContainer) {
6900
- this.chartContainer.style.display = "none";
6819
+ const loadingState = this.chartContainer?.querySelector(".myio-loading-state");
6820
+ if (loadingState) {
6821
+ loadingState.remove();
6901
6822
  }
6902
6823
  }
6903
6824
  hideError() {
@@ -6906,6 +6827,12 @@ var EnergyModalView = class {
6906
6827
  errorContainer.style.display = "none";
6907
6828
  }
6908
6829
  }
6830
+ hideKPIButton() {
6831
+ const kpiButtonContainer = document.getElementById("energy-kpi-btn");
6832
+ if (kpiButtonContainer) {
6833
+ kpiButtonContainer.style.display = "none";
6834
+ }
6835
+ }
6909
6836
  /**
6910
6837
  * Gets internationalized text
6911
6838
  */
@@ -6914,202 +6841,112 @@ var EnergyModalView = class {
6914
6841
  return i18n[key] || DEFAULT_I18N2[key];
6915
6842
  }
6916
6843
  /**
6917
- * Gets modal styles - FIXED BLUR ISSUE + PROPER LAYOUT
6844
+ * Gets modal styles with header color matching openDashboardPopupEnergy
6918
6845
  */
6919
6846
  getModalStyles() {
6847
+ const styles = this.config.params.styles || {};
6920
6848
  return `
6921
- /* === Overlay: blur ONLY the background, NOT the modal === */
6922
- .myio-modal-overlay {
6923
- position: fixed;
6924
- inset: 0;
6925
- z-index: 9998;
6926
- /* Use the requested brand tint but semi-transparent */
6927
- background: rgba(150,132,181,0.35); /* #9684B5 @ 35% */
6928
- backdrop-filter: blur(6px); /* blur the page behind */
6929
- display: grid;
6930
- place-items: center;
6931
- }
6932
-
6933
- /* Modal sits ABOVE overlay */
6934
- .myio-modal {
6935
- position: relative;
6936
- z-index: 9999; /* ensure not blurred */
6937
- background: #fff;
6938
- color: #111;
6939
- border-radius: 16px;
6940
- overflow: hidden;
6941
- box-shadow: 0 24px 64px rgba(0,0,0,.25);
6942
- }
6943
-
6944
- /* Fullscreen-ish layout */
6945
- .energy-report-modal {
6946
- width: 96vw;
6947
- max-width: 96vw;
6948
- max-height: calc(100vh - 48px);
6949
- display: flex;
6950
- flex-direction: column;
6951
- }
6952
-
6953
- /* Header */
6954
- .myio-modal__header {
6955
- background: #2D1458;
6956
- color: #fff;
6957
- padding: 16px 20px;
6958
- display: flex;
6959
- align-items: center;
6960
- justify-content: space-between;
6961
- flex-shrink: 0;
6962
- }
6963
-
6964
- .myio-modal__header h2 {
6965
- margin: 0;
6966
- font-size: 18px;
6967
- font-weight: 600;
6849
+ .myio-energy-modal-scope {
6850
+ --myio-energy-primary: ${styles.primaryColor || "#4A148C"};
6851
+ --myio-energy-bg: ${styles.backgroundColor || "#ffffff"};
6852
+ --myio-energy-text: ${styles.textColor || "#1f2937"};
6853
+ --myio-energy-border: ${styles.borderColor || "#e5e7eb"};
6854
+ --myio-energy-radius: ${styles.borderRadius || "8px"};
6855
+ --myio-energy-font: ${styles.fontFamily || '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'};
6856
+
6857
+ font-family: var(--myio-energy-font);
6858
+ color: var(--myio-energy-text);
6968
6859
  }
6969
6860
 
6970
- .myio-modal__close {
6971
- background: none;
6972
- border: none;
6973
- color: #fff;
6974
- font-size: 24px;
6861
+ .myio-btn {
6862
+ padding: 8px 16px;
6863
+ border-radius: var(--myio-energy-radius);
6864
+ border: 1px solid var(--myio-energy-border);
6865
+ background: var(--myio-energy-bg);
6866
+ color: var(--myio-energy-text);
6975
6867
  cursor: pointer;
6976
- padding: 4px;
6977
- border-radius: 4px;
6978
- transition: background-color 0.2s;
6979
- }
6980
-
6981
- .myio-modal__close:hover {
6982
- background: rgba(255,255,255,0.1);
6983
- }
6984
-
6985
- /* Toolbar */
6986
- .myio-modal__toolbar {
6987
- display: flex;
6988
- align-items: center;
6989
- justify-content: space-between;
6990
- gap: 12px;
6991
- padding: 12px 16px;
6992
- border-bottom: 1px solid rgba(45,20,88,.12);
6993
- flex-shrink: 0;
6994
- }
6995
-
6996
- .tb-start, .tb-end {
6997
- display: flex;
6998
- align-items: center;
6999
- gap: 12px;
7000
- }
7001
-
7002
- /* Body must grow; enable internal scroll only */
7003
- .myio-modal__body {
7004
- flex: 1 1 auto;
7005
- min-height: 0; /* critical to avoid squashing */
7006
- padding: 12px 16px 16px;
7007
- overflow: auto;
7008
- }
7009
-
7010
- /* Chart area fills the remaining height */
7011
- .chart-iframe-wrap {
7012
- flex: 1 1 auto;
7013
- min-height: 0;
7014
- height: 100%;
7015
- display: flex;
6868
+ font-size: 14px;
6869
+ transition: all 0.2s;
7016
6870
  }
7017
6871
 
7018
- .chart-iframe-wrap iframe,
7019
- .myio-energy-chart-container {
7020
- flex: 1 1 auto;
7021
- min-height: 0;
7022
- width: 100%;
7023
- height: 100%;
7024
- border: 0;
7025
- border-radius: 12px;
7026
- background: #f8f8fb;
6872
+ .myio-btn:hover:not(:disabled) {
6873
+ background: #f3f4f6;
7027
6874
  }
7028
6875
 
7029
- .myio-energy-chart-container {
7030
- display: flex;
7031
- align-items: center;
7032
- justify-content: center;
6876
+ .myio-btn:disabled {
6877
+ opacity: 0.5;
6878
+ cursor: not-allowed;
7033
6879
  }
7034
6880
 
7035
- /* Premium buttons */
7036
- .btn {
7037
- padding: 8px 12px;
7038
- border-radius: 10px;
7039
- font-size: 14px;
7040
- font-weight: 500;
7041
- cursor: pointer;
7042
- transition: all 0.2s;
7043
- border: 1px solid transparent;
7044
- display: flex;
7045
- align-items: center;
7046
- gap: 4px;
7047
- background: transparent;
6881
+ .myio-btn-primary {
6882
+ background: var(--myio-energy-primary);
6883
+ color: white;
6884
+ border-color: var(--myio-energy-primary);
7048
6885
  }
7049
6886
 
7050
- .btn:disabled {
7051
- opacity: 0.5;
7052
- cursor: not-allowed;
6887
+ .myio-btn-primary:hover:not(:disabled) {
6888
+ background: #6A1B9A;
7053
6889
  }
7054
6890
 
7055
- .btn-premium {
7056
- background: #2D1458;
7057
- color: #fff;
7058
- border: 1px solid rgba(255,255,255,.08);
6891
+ .myio-btn-secondary {
6892
+ background: #f3f4f6;
6893
+ color: var(--myio-energy-text);
6894
+ border-color: var(--myio-energy-border);
7059
6895
  }
7060
6896
 
7061
- .btn-premium:hover:not(:disabled) {
7062
- background: #3A1A6F;
6897
+ .myio-btn-secondary:hover:not(:disabled) {
6898
+ background: #e5e7eb;
7063
6899
  }
7064
6900
 
7065
- .btn-premium:focus {
7066
- outline: 3px solid rgba(45,20,88,.35);
7067
- outline-offset: 2px;
6901
+ .myio-modal-scope {
6902
+ height: 100% !important;
6903
+ display: flex !important;
6904
+ flex-direction: column !important;
7068
6905
  }
7069
6906
 
7070
- .btn-ghost {
7071
- background: transparent;
7072
- color: #2D1458;
7073
- border: 1px solid rgba(45,20,88,0.12);
6907
+ .myio-energy-chart-container {
6908
+ flex: 1 !important;
6909
+ min-height: 353px !important;
6910
+ height: 353px !important;
6911
+ background: var(--myio-energy-bg);
6912
+ border-radius: var(--myio-energy-radius);
6913
+ border: 1px solid var(--myio-energy-border);
6914
+ padding: 10px !important;
6915
+ display: block !important;
6916
+ overflow: hidden !important;
7074
6917
  }
7075
6918
 
7076
- .btn-ghost:hover:not(:disabled) {
7077
- background: rgba(45,20,88,0.05);
6919
+ .myio-energy-chart-container > iframe {
6920
+ width: 100% !important;
6921
+ height: 100% !important;
6922
+ min-height: 408px !important;
6923
+ border: none !important;
7078
6924
  }
7079
6925
 
7080
- /* Input styling */
7081
- #energy-date-range {
7082
- padding: 8px 12px;
7083
- border: 1px solid rgba(45,20,88,0.12);
7084
- border-radius: 8px;
7085
- font-size: 14px;
7086
- background: #fff;
7087
- color: #111;
7088
- width: 300px;
6926
+ .myio-energy-chart-container iframe,
6927
+ .myio-energy-chart-container iframe body,
6928
+ .myio-energy-chart-container iframe html {
6929
+ height: 100% !important;
6930
+ min-height: 408px !important;
7089
6931
  }
7090
6932
 
7091
- #energy-date-range:focus {
7092
- outline: none;
7093
- border-color: #2D1458;
7094
- box-shadow: 0 0 0 3px rgba(45,20,88,0.1);
6933
+ .myio-energy-chart-container .chart-wrapper,
6934
+ .myio-energy-chart-container .chart-container,
6935
+ .myio-energy-chart-container canvas,
6936
+ .myio-energy-chart-container svg {
6937
+ height: 408px !important;
6938
+ min-height: 408px !important;
7095
6939
  }
7096
6940
 
7097
- /* Loading and error states */
7098
6941
  .myio-loading-state {
7099
6942
  text-align: center;
7100
- padding: 40px;
7101
- height: 100%;
7102
- display: flex;
7103
- flex-direction: column;
7104
- align-items: center;
7105
- justify-content: center;
7106
6943
  }
7107
6944
 
7108
6945
  .myio-spinner {
7109
6946
  width: 40px;
7110
6947
  height: 40px;
7111
- border: 4px solid rgba(45,20,88,0.12);
7112
- border-top: 4px solid #2D1458;
6948
+ border: 4px solid var(--myio-energy-border);
6949
+ border-top: 4px solid var(--myio-energy-primary);
7113
6950
  border-radius: 50%;
7114
6951
  animation: spin 1s linear infinite;
7115
6952
  margin: 0 auto 16px;
@@ -7123,9 +6960,8 @@ var EnergyModalView = class {
7123
6960
  .myio-energy-error {
7124
6961
  background: #fef2f2;
7125
6962
  border: 1px solid #fecaca;
7126
- border-radius: 8px;
6963
+ border-radius: var(--myio-energy-radius);
7127
6964
  padding: 16px;
7128
- margin: 8px 16px;
7129
6965
  }
7130
6966
 
7131
6967
  .myio-error-content {
@@ -7143,20 +6979,62 @@ var EnergyModalView = class {
7143
6979
  font-weight: 500;
7144
6980
  }
7145
6981
 
7146
- /* Responsive */
6982
+
6983
+
6984
+ .myio-fallback-chart h4 {
6985
+ margin: 0 0 16px 0;
6986
+ text-align: center;
6987
+ }
6988
+
6989
+ .myio-chart-container {
6990
+ border: 1px solid var(--myio-energy-border);
6991
+ border-radius: var(--myio-energy-radius);
6992
+ overflow: hidden;
6993
+ }
6994
+
6995
+ .myio-chart-note {
6996
+ margin: 12px 0 0 0;
6997
+ font-size: 12px;
6998
+ color: #666;
6999
+ text-align: center;
7000
+ }
7001
+
7002
+ .myio-form-group {
7003
+ display: flex;
7004
+ flex-direction: column;
7005
+ }
7006
+
7007
+ .myio-label {
7008
+ font-weight: 500;
7009
+ margin-bottom: 5px;
7010
+ color: var(--myio-energy-text);
7011
+ }
7012
+
7013
+ .myio-input {
7014
+ padding: 8px 12px;
7015
+ border: 1px solid var(--myio-energy-border);
7016
+ border-radius: var(--myio-energy-radius);
7017
+ font-size: 14px;
7018
+ background: var(--myio-energy-bg);
7019
+ color: var(--myio-energy-text);
7020
+ }
7021
+
7022
+ .myio-input:focus {
7023
+ outline: none;
7024
+ border-color: var(--myio-energy-primary);
7025
+ box-shadow: 0 0 0 3px rgba(74, 20, 140, 0.1);
7026
+ }
7027
+
7147
7028
  @media (max-width: 768px) {
7148
- .myio-modal__toolbar {
7149
- flex-direction: column;
7150
- align-items: stretch;
7151
- gap: 8px;
7029
+ .myio-energy-modal-layout {
7030
+ grid-template-columns: 1fr;
7031
+ grid-template-rows: auto 1fr;
7152
7032
  }
7153
7033
 
7154
- .tb-start, .tb-end {
7155
- justify-content: center;
7156
- }
7157
-
7158
- #energy-date-range {
7159
- width: 100%;
7034
+ .myio-energy-header {
7035
+ flex-direction: column;
7036
+ gap: 12px;
7037
+ align-items: stretch;
7160
7038
  }
7161
7039
  }
7162
7040
  `;
@@ -7165,10 +7043,17 @@ var EnergyModalView = class {
7165
7043
  * Destroys the view and cleans up resources
7166
7044
  */
7167
7045
  destroy() {
7046
+ if (this.chartInstance && typeof this.chartInstance.destroy === "function") {
7047
+ this.chartInstance.destroy();
7048
+ this.chartInstance = null;
7049
+ }
7168
7050
  if (this.dateRangePicker) {
7169
7051
  this.dateRangePicker.destroy();
7170
7052
  this.dateRangePicker = null;
7171
7053
  }
7054
+ if (this.chartContainer) {
7055
+ this.chartContainer.innerHTML = "";
7056
+ }
7172
7057
  this.currentEnergyData = null;
7173
7058
  this.container = null;
7174
7059
  this.chartContainer = null;
@@ -7212,8 +7097,8 @@ var EnergyModal = class {
7212
7097
  const identifier = this.context.device.attributes.identifier || "SEM IDENTIFICADOR";
7213
7098
  const label = this.context.device.label || "SEM ETIQUETA";
7214
7099
  this.modal = createModal({
7215
- title: `Relat\xF3rio de Energia - ${identifier} - ${label}`,
7216
- width: "96vw",
7100
+ title: `Energy Report - ${identifier} - ${label}`,
7101
+ width: "80vw",
7217
7102
  height: "90vh",
7218
7103
  theme: this.params.theme === "dark" ? "dark" : "light"
7219
7104
  });
@@ -7221,7 +7106,7 @@ var EnergyModal = class {
7221
7106
  context: this.context,
7222
7107
  params: this.params,
7223
7108
  onExport: () => this.handleExport(),
7224
- onError: (error) => this.handleError(error),
7109
+ onError: (error) => this.handleEnergyModalError(error),
7225
7110
  onDateRangeChange: (startISO, endISO) => this.handleDateRangeChange(startISO, endISO)
7226
7111
  });
7227
7112
  this.setupModalEventHandlers();
@@ -7454,6 +7339,23 @@ var EnergyModal = class {
7454
7339
  this.handleError(new Error("Failed to export data to CSV"));
7455
7340
  }
7456
7341
  }
7342
+ /**
7343
+ * Handles EnergyModalError types from the view
7344
+ */
7345
+ handleEnergyModalError(error) {
7346
+ console.error("[EnergyModal] EnergyModalError occurred:", error);
7347
+ if (this.view) {
7348
+ this.view.showError(error.message);
7349
+ }
7350
+ if (this.params.onError) {
7351
+ try {
7352
+ this.params.onError(error);
7353
+ } catch (callbackError) {
7354
+ console.warn("[EnergyModal] onError callback error:", callbackError);
7355
+ }
7356
+ }
7357
+ this.emit("error", { message: error.message, error });
7358
+ }
7457
7359
  /**
7458
7360
  * Handles errors with user feedback
7459
7361
  */