myio-js-library 0.1.69 → 0.1.70

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
@@ -8860,11 +8860,11 @@ var AllReportModal = class {
8860
8860
  parentEl: this.modal.element,
8861
8861
  onApply: ({ startISO, endISO }) => {
8862
8862
  this.hideError();
8863
- console.log("Date range selected:", { startISO, endISO });
8863
+ this.debugLog("Date range selected:", { startISO, endISO });
8864
8864
  }
8865
8865
  });
8866
8866
  } catch (error) {
8867
- console.warn("DateRangePicker initialization failed, using fallback:", error);
8867
+ this.debugLog("DateRangePicker initialization failed, using fallback:", error);
8868
8868
  }
8869
8869
  }
8870
8870
  async loadData() {
@@ -8922,7 +8922,7 @@ var AllReportModal = class {
8922
8922
  } catch (error) {
8923
8923
  this.debugLog("\u274C Error in loadData", error);
8924
8924
  this.showError("Erro ao carregar dados: " + error.message);
8925
- console.error("Error loading data:", error);
8925
+ this.debugLog("Error loading data:", error);
8926
8926
  this.emit("error", { message: error.message, context: "loadData" });
8927
8927
  } finally {
8928
8928
  this.isLoading = false;
@@ -9245,7 +9245,7 @@ var AllReportModal = class {
9245
9245
  const endTime = encodeURIComponent(endISO);
9246
9246
  const endpoint = this.domainConfig.endpoint;
9247
9247
  const url = `${baseUrl}/api/v1/telemetry/customers/${this.params.customerId}/${endpoint}/devices/totals?startTime=${startTime}&endTime=${endTime}`;
9248
- console.log("[AllReportModal] Fetching customer totals:", { url, customerId: this.params.customerId, domain: this.params.domain || "energy" });
9248
+ this.debugLog("[AllReportModal] Fetching customer totals:", { url, customerId: this.params.customerId, domain: this.params.domain || "energy" });
9249
9249
  const response = await fetch(url, {
9250
9250
  method: "GET",
9251
9251
  headers: {
@@ -9258,14 +9258,14 @@ var AllReportModal = class {
9258
9258
  throw new Error(`Erro na API: ${response.status} ${response.statusText}`);
9259
9259
  }
9260
9260
  const data = await response.json();
9261
- console.log("[AllReportModal] Customer totals response:", data);
9261
+ this.debugLog("[AllReportModal] Customer totals response:", data);
9262
9262
  return data;
9263
9263
  }
9264
9264
  mapCustomerTotalsResponse(apiResponse) {
9265
9265
  this.debugLog("\u{1F50D} Starting mapCustomerTotalsResponse", { apiResponse });
9266
9266
  const apiArray = Array.isArray(apiResponse?.data) ? apiResponse.data : Array.isArray(apiResponse) ? apiResponse : [];
9267
- console.log("[AllReportModal] NEW MAPPING - API array length:", apiArray.length);
9268
- console.log("[AllReportModal] NEW MAPPING - ItemsList length:", this.params.itemsList.length);
9267
+ this.debugLog("[AllReportModal] NEW MAPPING - API array length:", apiArray.length);
9268
+ this.debugLog("[AllReportModal] NEW MAPPING - ItemsList length:", this.params.itemsList.length);
9269
9269
  this.debugLog("\u{1F4CB} API data array extracted", {
9270
9270
  isDataProperty: !!apiResponse?.data,
9271
9271
  isDirectArray: Array.isArray(apiResponse),
@@ -9274,7 +9274,7 @@ var AllReportModal = class {
9274
9274
  });
9275
9275
  if (!apiArray.length) {
9276
9276
  this.debugLog("\u26A0\uFE0F Empty API array, returning empty result");
9277
- console.warn("[AllReportModal] Empty/invalid API response:", apiResponse);
9277
+ this.debugLog("[AllReportModal] Empty/invalid API response:", apiResponse);
9278
9278
  return [];
9279
9279
  }
9280
9280
  const sumByApiId = /* @__PURE__ */ new Map();
@@ -9285,7 +9285,7 @@ var AllReportModal = class {
9285
9285
  const consumption = this.pickConsumption(item);
9286
9286
  totalApiConsumption += consumption;
9287
9287
  if (index < 3) {
9288
- console.log(`[AllReportModal] NEW MAPPING - API item ${index}:`, {
9288
+ this.debugLog(`[AllReportModal] NEW MAPPING - API item ${index}:`, {
9289
9289
  id: item?.id,
9290
9290
  name: item?.name,
9291
9291
  assetName: item?.assetName,
@@ -9308,8 +9308,8 @@ var AllReportModal = class {
9308
9308
  this.debugLog(`\u274C API item without ID:`, item);
9309
9309
  }
9310
9310
  }
9311
- console.log("[AllReportModal] NEW MAPPING - Total API consumption:", totalApiConsumption);
9312
- console.log("[AllReportModal] NEW MAPPING - Unique API IDs:", sumByApiId.size);
9311
+ this.debugLog("[AllReportModal] NEW MAPPING - Total API consumption:", totalApiConsumption);
9312
+ this.debugLog("[AllReportModal] NEW MAPPING - Unique API IDs:", sumByApiId.size);
9313
9313
  this.debugLog("\u{1F4CA} ID index built", {
9314
9314
  sumByApiIdSize: sumByApiId.size,
9315
9315
  sumByApiIdEntries: Array.from(sumByApiId.entries()),
@@ -9323,7 +9323,7 @@ var AllReportModal = class {
9323
9323
  let consumption = sumByApiId.get(listItem.id) ?? 0;
9324
9324
  this.debugLog(`\u{1F3AF} Primary ID match for ${listItem.id}: ${consumption}`);
9325
9325
  if (index < 3) {
9326
- console.log(`[AllReportModal] NEW MAPPING - ItemsList item ${index}:`, {
9326
+ this.debugLog(`[AllReportModal] NEW MAPPING - ItemsList item ${index}:`, {
9327
9327
  id: listItem.id,
9328
9328
  identifier: listItem.identifier,
9329
9329
  label: listItem.label,
@@ -9344,7 +9344,7 @@ var AllReportModal = class {
9344
9344
  const itemConsumption = this.pickConsumption(apiItem);
9345
9345
  consumption += itemConsumption;
9346
9346
  if (index < 3) {
9347
- console.log(`[AllReportModal] NEW MAPPING - Substring match for ${listItem.identifier}:`, {
9347
+ this.debugLog(`[AllReportModal] NEW MAPPING - Substring match for ${listItem.identifier}:`, {
9348
9348
  apiItemName: name,
9349
9349
  apiItemAssetName: assetName,
9350
9350
  itemConsumption,
@@ -9389,9 +9389,9 @@ var AllReportModal = class {
9389
9389
  totalApiConsumption,
9390
9390
  totalMappedConsumption
9391
9391
  };
9392
- console.log("[AllReportModal] NEW MAPPING - Final stats:", stats);
9392
+ this.debugLog("[AllReportModal] NEW MAPPING - Final stats:", stats);
9393
9393
  this.debugLog("\u{1F4CA} Final mapping stats:", stats);
9394
- console.log("[AllReportModal] Mapping stats:", stats);
9394
+ this.debugLog("[AllReportModal] Mapping stats:", stats);
9395
9395
  return rows;
9396
9396
  }
9397
9397
  parseConsumptionValue(item) {
@@ -9412,7 +9412,7 @@ var AllReportModal = class {
9412
9412
  }
9413
9413
  }
9414
9414
  }
9415
- console.warn("[AllReportModal] No valid consumption value found in item:", item);
9415
+ this.debugLog("[AllReportModal] No valid consumption value found in item:", item);
9416
9416
  return 0;
9417
9417
  }
9418
9418
  downloadCSV(content, filename) {
@@ -10891,16 +10891,22 @@ var STRINGS = {
10891
10891
  atTime: "\xE1s",
10892
10892
  timeUnit: "hs",
10893
10893
  exportPdf: "Exportar PDF",
10894
+ exportCsv: "Exportar CSV",
10894
10895
  fullscreen: "Tela cheia",
10895
10896
  close: "Fechar",
10896
10897
  resetZoom: "Reset Zoom",
10897
10898
  loading: "Carregando dados...",
10898
10899
  noData: "Sem pontos de demanda no per\xEDodo selecionado",
10899
10900
  error: "Erro ao carregar dados",
10900
- zoomHelp: "Scroll: zoom | Drag: selecionar | Ctrl+Drag: mover",
10901
+ zoomHelp: "Scroll: zoom | Arraste: mover | Ctrl+Arraste: selecionar",
10901
10902
  demand: "Demanda (kW)",
10902
10903
  reportTitle: "Relat\xF3rio de Demanda",
10903
- reportFooter: "MyIO Energy Management System"
10904
+ reportFooter: "MyIO Energy Management System",
10905
+ startDate: "Data Inicial",
10906
+ endDate: "Data Final",
10907
+ updatePeriod: "Atualizar",
10908
+ invalidDateRange: "Data final deve ser maior que data inicial",
10909
+ maxRangeExceeded: "Per\xEDodo m\xE1ximo de 30 dias"
10904
10910
  },
10905
10911
  "en-US": {
10906
10912
  title: "Demand",
@@ -10910,16 +10916,22 @@ var STRINGS = {
10910
10916
  atTime: "at",
10911
10917
  timeUnit: "",
10912
10918
  exportPdf: "Export PDF",
10919
+ exportCsv: "Export CSV",
10913
10920
  fullscreen: "Fullscreen",
10914
10921
  close: "Close",
10915
10922
  resetZoom: "Reset Zoom",
10916
10923
  loading: "Loading data...",
10917
10924
  noData: "No demand points in the selected period",
10918
10925
  error: "Error loading data",
10919
- zoomHelp: "Scroll: zoom | Drag: select | Ctrl+Drag: pan",
10926
+ zoomHelp: "Scroll: zoom | Drag: pan | Ctrl+Drag: select",
10920
10927
  demand: "Demand (kW)",
10921
10928
  reportTitle: "Demand Report",
10922
- reportFooter: "MyIO Energy Management System"
10929
+ reportFooter: "MyIO Energy Management System",
10930
+ startDate: "Start Date",
10931
+ endDate: "End Date",
10932
+ updatePeriod: "Update",
10933
+ invalidDateRange: "End date must be greater than start date",
10934
+ maxRangeExceeded: "Maximum range of 30 days"
10923
10935
  }
10924
10936
  };
10925
10937
  async function loadScript(url, checkGlobal) {
@@ -11156,6 +11168,69 @@ function injectCSS(styles) {
11156
11168
  border-bottom: 1px solid #e0e0e0;
11157
11169
  }
11158
11170
 
11171
+ .myio-demand-modal-period-selector {
11172
+ display: flex;
11173
+ align-items: center;
11174
+ gap: ${styles.spacingMd};
11175
+ padding: ${styles.spacingMd} ${styles.spacingLg};
11176
+ background: #f9f9f9;
11177
+ border-bottom: 1px solid #e0e0e0;
11178
+ flex-wrap: wrap;
11179
+ }
11180
+
11181
+ .myio-demand-modal-period-selector label {
11182
+ display: flex;
11183
+ flex-direction: column;
11184
+ gap: ${styles.spacingXs};
11185
+ font-size: ${styles.fontSizeSm};
11186
+ color: ${styles.textPrimary};
11187
+ font-weight: ${styles.fontWeightBold};
11188
+ }
11189
+
11190
+ .myio-demand-modal-date-input {
11191
+ padding: ${styles.spacingSm};
11192
+ border: 1px solid #ccc;
11193
+ border-radius: ${styles.buttonRadius};
11194
+ font-size: ${styles.fontSizeSm};
11195
+ font-family: inherit;
11196
+ min-width: 150px;
11197
+ }
11198
+
11199
+ .myio-demand-modal-date-input:focus {
11200
+ outline: 2px solid ${styles.primaryColor};
11201
+ outline-offset: 1px;
11202
+ }
11203
+
11204
+ .myio-demand-modal-btn-update {
11205
+ background: ${styles.primaryColor};
11206
+ border: none;
11207
+ color: white;
11208
+ padding: ${styles.spacingSm} ${styles.spacingMd};
11209
+ border-radius: ${styles.buttonRadius};
11210
+ cursor: pointer;
11211
+ font-size: ${styles.fontSizeSm};
11212
+ font-family: inherit;
11213
+ font-weight: ${styles.fontWeightBold};
11214
+ align-self: flex-end;
11215
+ margin-bottom: 2px;
11216
+ }
11217
+
11218
+ .myio-demand-modal-btn-update:hover {
11219
+ background: #6A1B9A;
11220
+ }
11221
+
11222
+ .myio-demand-modal-btn-update:disabled {
11223
+ background: #ccc;
11224
+ cursor: not-allowed;
11225
+ }
11226
+
11227
+ .myio-demand-modal-period-error {
11228
+ flex-basis: 100%;
11229
+ color: ${styles.dangerColor};
11230
+ font-size: ${styles.fontSizeXs};
11231
+ margin-top: -${styles.spacingSm};
11232
+ }
11233
+
11159
11234
  .myio-demand-modal-peak {
11160
11235
  margin: ${styles.spacingMd} ${styles.spacingLg} 0;
11161
11236
  padding: ${styles.spacingSm} ${styles.spacingMd};
@@ -11306,12 +11381,14 @@ async function fetchTelemetryData(token, deviceId, startDate, endDate, queryPara
11306
11381
  const startTs = new Date(startDate).getTime();
11307
11382
  const endTs = new Date(endDate).getTime();
11308
11383
  const keys = queryParams?.keys || "consumption";
11309
- const limit = queryParams?.limit || 5e4;
11310
11384
  const intervalType = queryParams?.intervalType || "MILLISECONDS";
11311
- const interval = queryParams?.interval || 54e6;
11312
- const agg = queryParams?.agg || "SUM";
11385
+ const interval = queryParams?.interval || 864e5;
11386
+ const agg = queryParams?.agg || "MAX";
11313
11387
  const orderBy = queryParams?.orderBy || "ASC";
11314
- const url = `/api/plugins/telemetry/DEVICE/${deviceId}/values/timeseries?keys=${keys}&startTs=${startTs}&endTs=${endTs}&limit=${limit}&intervalType=${intervalType}&interval=${interval}&agg=${agg}&orderBy=${orderBy}`;
11388
+ let url = `/api/plugins/telemetry/DEVICE/${deviceId}/values/timeseries?keys=${keys}&startTs=${startTs}&endTs=${endTs}&intervalType=${intervalType}&interval=${interval}&agg=${agg}&orderBy=${orderBy}`;
11389
+ if (agg === "NONE" && queryParams?.limit) {
11390
+ url += `&limit=${queryParams.limit}`;
11391
+ }
11315
11392
  const response = await fetch(url, {
11316
11393
  headers: {
11317
11394
  "X-Authorization": `Bearer ${token}`,
@@ -11445,9 +11522,12 @@ async function openDemandModal(params) {
11445
11522
  <h2 id="modal-title" class="myio-demand-modal-title">${strings.title} \u2013 ${label}</h2>
11446
11523
  </div>
11447
11524
  <div class="myio-demand-modal-actions">
11448
- <button class="myio-demand-modal-btn myio-demand-modal-btn-pdf" type="button">
11525
+ <button class="myio-demand-modal-btn myio-demand-modal-btn-pdf" type="button" style="display: none;">
11449
11526
  ${strings.exportPdf}
11450
11527
  </button>
11528
+ <button class="myio-demand-modal-btn myio-demand-modal-btn-csv" type="button">
11529
+ ${strings.exportCsv}
11530
+ </button>
11451
11531
  <button class="myio-demand-modal-btn myio-demand-modal-btn-fullscreen" type="button" aria-label="${strings.fullscreen}">
11452
11532
  \u26F6
11453
11533
  </button>
@@ -11460,7 +11540,22 @@ async function openDemandModal(params) {
11460
11540
  <div class="myio-demand-modal-period">
11461
11541
  ${strings.period}: ${startDateFormatted} \u2192 ${endDateFormatted}
11462
11542
  </div>
11463
-
11543
+
11544
+ <div class="myio-demand-modal-period-selector">
11545
+ <label>
11546
+ ${strings.startDate}:
11547
+ <input type="date" class="myio-demand-modal-date-input myio-demand-modal-date-start" />
11548
+ </label>
11549
+ <label>
11550
+ ${strings.endDate}:
11551
+ <input type="date" class="myio-demand-modal-date-input myio-demand-modal-date-end" />
11552
+ </label>
11553
+ <button class="myio-demand-modal-btn-update" type="button">
11554
+ ${strings.updatePeriod}
11555
+ </button>
11556
+ <div class="myio-demand-modal-period-error" style="display: none;"></div>
11557
+ </div>
11558
+
11464
11559
  <div class="myio-demand-modal-peak" style="display: none;"></div>
11465
11560
 
11466
11561
  <div class="myio-demand-modal-content">
@@ -11494,6 +11589,7 @@ async function openDemandModal(params) {
11494
11589
  const closeBtn = overlay.querySelector(".myio-demand-modal-btn-close");
11495
11590
  const fullscreenBtn = overlay.querySelector(".myio-demand-modal-btn-fullscreen");
11496
11591
  const pdfBtn = overlay.querySelector(".myio-demand-modal-btn-pdf");
11592
+ const csvBtn = overlay.querySelector(".myio-demand-modal-btn-csv");
11497
11593
  const resetBtn = overlay.querySelector(".myio-demand-modal-btn-reset");
11498
11594
  const chartCanvas = overlay.querySelector(".myio-demand-modal-chart");
11499
11595
  const loadingEl = overlay.querySelector(".myio-demand-modal-loading");
@@ -11501,9 +11597,16 @@ async function openDemandModal(params) {
11501
11597
  const errorText = overlay.querySelector(".myio-demand-modal-error-text");
11502
11598
  const peakEl = overlay.querySelector(".myio-demand-modal-peak");
11503
11599
  const contentEl = overlay.querySelector(".myio-demand-modal-content");
11600
+ const periodDisplayEl = overlay.querySelector(".myio-demand-modal-period");
11601
+ const dateStartInput = overlay.querySelector(".myio-demand-modal-date-start");
11602
+ const dateEndInput = overlay.querySelector(".myio-demand-modal-date-end");
11603
+ const updateBtn = overlay.querySelector(".myio-demand-modal-btn-update");
11604
+ const periodErrorEl = overlay.querySelector(".myio-demand-modal-period-error");
11504
11605
  let chart = null;
11505
11606
  let chartData = null;
11506
11607
  let isFullscreen = false;
11608
+ let currentStartDate = params.startDate;
11609
+ let currentEndDate = params.endDate;
11507
11610
  const originalOverflow = document.body.style.overflow;
11508
11611
  document.body.style.overflow = "hidden";
11509
11612
  const releaseFocusTrap = createFocusTrap(overlay);
@@ -11528,6 +11631,45 @@ async function openDemandModal(params) {
11528
11631
  chart.resetZoom();
11529
11632
  }
11530
11633
  }
11634
+ function exportCsv() {
11635
+ if (!chartData) {
11636
+ alert("Nenhum dado dispon\xEDvel para exportar");
11637
+ return;
11638
+ }
11639
+ const btn = csvBtn;
11640
+ const originalHtml = btn.innerHTML;
11641
+ btn.disabled = true;
11642
+ btn.innerHTML = "<span>\u23F3</span> Gerando CSV...";
11643
+ try {
11644
+ const BOM = "\uFEFF";
11645
+ let csv = BOM + "Data/Hora,S\xE9rie,Valor (kW)\n";
11646
+ chartData.series.forEach((series) => {
11647
+ series.points.forEach((point) => {
11648
+ const dateStr2 = formatDateTime(new Date(point.x), locale);
11649
+ const value = point.y.toFixed(2);
11650
+ csv += `${dateStr2},${series.label},${value}
11651
+ `;
11652
+ });
11653
+ });
11654
+ const blob = new Blob([csv], { type: "text/csv;charset=utf-8;" });
11655
+ const url = URL.createObjectURL(blob);
11656
+ const link = document.createElement("a");
11657
+ link.href = url;
11658
+ const labelSafe = label.replace(/\s+/g, "_");
11659
+ const dateStr = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
11660
+ link.download = `demanda_${labelSafe}_${dateStr}.csv`;
11661
+ document.body.appendChild(link);
11662
+ link.click();
11663
+ document.body.removeChild(link);
11664
+ URL.revokeObjectURL(url);
11665
+ } catch (error) {
11666
+ console.error("[CSV Export] Error:", error);
11667
+ alert("Erro ao gerar CSV. Por favor, tente novamente.");
11668
+ } finally {
11669
+ btn.disabled = false;
11670
+ btn.innerHTML = originalHtml;
11671
+ }
11672
+ }
11531
11673
  async function exportPdf() {
11532
11674
  if (!chartData || !chart) {
11533
11675
  alert("Nenhum dado dispon\xEDvel para exportar");
@@ -11601,10 +11743,45 @@ async function openDemandModal(params) {
11601
11743
  btn.innerHTML = originalHtml;
11602
11744
  }
11603
11745
  }
11746
+ function initializeDateInputs() {
11747
+ const startDate = new Date(currentStartDate);
11748
+ const endDate = new Date(currentEndDate);
11749
+ dateStartInput.value = startDate.toISOString().split("T")[0];
11750
+ dateEndInput.value = endDate.toISOString().split("T")[0];
11751
+ }
11752
+ async function updatePeriod() {
11753
+ periodErrorEl.style.display = "none";
11754
+ const newStartDate = dateStartInput.value;
11755
+ const newEndDate = dateEndInput.value;
11756
+ if (!newStartDate || !newEndDate) {
11757
+ periodErrorEl.textContent = strings.error;
11758
+ periodErrorEl.style.display = "block";
11759
+ return;
11760
+ }
11761
+ const startDateObj = new Date(newStartDate);
11762
+ const endDateObj = new Date(newEndDate);
11763
+ if (endDateObj < startDateObj) {
11764
+ periodErrorEl.textContent = strings.invalidDateRange;
11765
+ periodErrorEl.style.display = "block";
11766
+ return;
11767
+ }
11768
+ const diffTime = Math.abs(endDateObj.getTime() - startDateObj.getTime());
11769
+ const diffDays = Math.ceil(diffTime / (1e3 * 60 * 60 * 24));
11770
+ if (diffDays > 30) {
11771
+ periodErrorEl.textContent = strings.maxRangeExceeded;
11772
+ periodErrorEl.style.display = "block";
11773
+ return;
11774
+ }
11775
+ currentStartDate = startDateObj.toISOString();
11776
+ currentEndDate = endDateObj.toISOString();
11777
+ await loadData();
11778
+ }
11604
11779
  closeBtn.addEventListener("click", closeModal);
11605
11780
  fullscreenBtn.addEventListener("click", toggleFullscreen);
11606
11781
  resetBtn.addEventListener("click", resetZoom);
11607
11782
  pdfBtn.addEventListener("click", exportPdf);
11783
+ csvBtn.addEventListener("click", exportCsv);
11784
+ updateBtn.addEventListener("click", updatePeriod);
11608
11785
  overlay.addEventListener("click", (e) => {
11609
11786
  if (e.target === overlay) {
11610
11787
  closeModal();
@@ -11620,8 +11797,9 @@ async function openDemandModal(params) {
11620
11797
  loadingEl.style.display = "flex";
11621
11798
  contentEl.style.display = "none";
11622
11799
  errorEl.style.display = "none";
11623
- const startDateObj = new Date(params.startDate);
11624
- const endDateObj = new Date(params.endDate);
11800
+ peakEl.style.display = "none";
11801
+ const startDateObj = new Date(currentStartDate);
11802
+ const endDateObj = new Date(currentEndDate);
11625
11803
  const diffTime = Math.abs(endDateObj.getTime() - startDateObj.getTime());
11626
11804
  const diffDays = Math.ceil(diffTime / (1e3 * 60 * 60 * 24));
11627
11805
  if (diffDays > 30) {
@@ -11630,7 +11808,10 @@ async function openDemandModal(params) {
11630
11808
  errorText.textContent = "O Limite de busca \xE9 de 30 dias de intervalo.";
11631
11809
  return;
11632
11810
  }
11633
- const rawData = params.fetcher ? await params.fetcher({ token: params.token, deviceId: params.deviceId, startDate: params.startDate, endDate: params.endDate, telemetryQuery: params.telemetryQuery }) : await fetchTelemetryData(params.token, params.deviceId, params.startDate, params.endDate, params.telemetryQuery);
11811
+ const startDateFormatted2 = formatDate2(startDateObj, locale);
11812
+ const endDateFormatted2 = formatDate2(endDateObj, locale);
11813
+ periodDisplayEl.textContent = `${strings.period}: ${startDateFormatted2} \u2192 ${endDateFormatted2}`;
11814
+ const rawData = params.fetcher ? await params.fetcher({ token: params.token, deviceId: params.deviceId, startDate: currentStartDate, endDate: currentEndDate, telemetryQuery: params.telemetryQuery }) : await fetchTelemetryData(params.token, params.deviceId, currentStartDate, currentEndDate, params.telemetryQuery);
11634
11815
  chartData = processMultiSeriesChartData(
11635
11816
  rawData,
11636
11817
  params.telemetryQuery?.keys || "consumption",
@@ -11659,79 +11840,88 @@ async function openDemandModal(params) {
11659
11840
  }
11660
11841
  const Chart = window.Chart;
11661
11842
  Chart.register(window.ChartZoom);
11662
- chart = new Chart(chartCanvas, {
11663
- type: "line",
11664
- data: {
11665
- datasets: chartData.series.map((series) => ({
11666
- label: series.label,
11667
- data: series.points,
11668
- borderColor: series.color,
11669
- backgroundColor: series.color + "20",
11670
- fill: false,
11671
- // No fill for multi-series
11672
- tension: 0.4,
11673
- pointRadius: 2,
11674
- pointHoverRadius: 6
11675
- }))
11676
- },
11677
- options: {
11678
- responsive: true,
11679
- maintainAspectRatio: false,
11680
- plugins: {
11681
- legend: {
11682
- display: chartData.series.length > 1,
11683
- // Show legend only if multiple series
11684
- position: "top"
11685
- },
11686
- zoom: {
11687
- zoom: {
11688
- wheel: { enabled: true },
11689
- pinch: { enabled: true },
11690
- drag: { enabled: true, modifierKey: "ctrl" },
11691
- mode: "x"
11692
- },
11693
- pan: {
11694
- enabled: true,
11695
- mode: "x",
11696
- modifierKey: "ctrl"
11697
- }
11698
- }
11843
+ if (chart) {
11844
+ chart.data.datasets = chartData.series.map((series) => ({
11845
+ label: series.label,
11846
+ data: series.points,
11847
+ borderColor: series.color,
11848
+ backgroundColor: series.color + "CC",
11849
+ borderWidth: 1
11850
+ }));
11851
+ chart.options.plugins.legend.display = chartData.series.length > 1;
11852
+ chart.update();
11853
+ } else {
11854
+ chart = new Chart(chartCanvas, {
11855
+ type: "bar",
11856
+ data: {
11857
+ datasets: chartData.series.map((series) => ({
11858
+ label: series.label,
11859
+ data: series.points,
11860
+ borderColor: series.color,
11861
+ backgroundColor: series.color + "CC",
11862
+ // More opaque for bars
11863
+ borderWidth: 1
11864
+ }))
11699
11865
  },
11700
- scales: {
11701
- x: {
11702
- type: "linear",
11703
- position: "bottom",
11704
- title: {
11705
- display: true,
11706
- text: "Tempo"
11866
+ options: {
11867
+ responsive: true,
11868
+ maintainAspectRatio: false,
11869
+ plugins: {
11870
+ legend: {
11871
+ display: chartData.series.length > 1,
11872
+ // Show legend only if multiple series
11873
+ position: "top"
11707
11874
  },
11708
- ticks: {
11709
- callback: function(value) {
11710
- const date = new Date(value);
11711
- return date.toLocaleDateString(locale, {
11712
- month: "2-digit",
11713
- day: "2-digit",
11714
- hour: "2-digit",
11715
- minute: "2-digit"
11716
- });
11875
+ zoom: {
11876
+ zoom: {
11877
+ wheel: { enabled: true },
11878
+ pinch: { enabled: true },
11879
+ drag: { enabled: true, modifierKey: "ctrl" },
11880
+ mode: "x"
11881
+ },
11882
+ pan: {
11883
+ enabled: true,
11884
+ mode: "x"
11885
+ // No modifierKey - pan is free after zoom
11717
11886
  }
11718
11887
  }
11719
11888
  },
11720
- y: {
11721
- title: {
11722
- display: true,
11723
- text: params.yAxisLabel || strings.demand
11724
- // Use configurable Y-axis label
11889
+ scales: {
11890
+ x: {
11891
+ type: "linear",
11892
+ position: "bottom",
11893
+ title: {
11894
+ display: true,
11895
+ text: "Tempo"
11896
+ },
11897
+ ticks: {
11898
+ callback: function(value) {
11899
+ const date = new Date(value);
11900
+ return date.toLocaleDateString(locale, {
11901
+ month: "2-digit",
11902
+ day: "2-digit",
11903
+ hour: "2-digit",
11904
+ minute: "2-digit"
11905
+ });
11906
+ }
11907
+ }
11725
11908
  },
11726
- beginAtZero: true
11909
+ y: {
11910
+ title: {
11911
+ display: true,
11912
+ text: params.yAxisLabel || strings.demand
11913
+ // Use configurable Y-axis label
11914
+ },
11915
+ beginAtZero: true
11916
+ }
11917
+ },
11918
+ interaction: {
11919
+ intersect: false,
11920
+ mode: "index"
11727
11921
  }
11728
- },
11729
- interaction: {
11730
- intersect: false,
11731
- mode: "index"
11732
11922
  }
11733
- }
11734
- });
11923
+ });
11924
+ }
11735
11925
  loadingEl.style.display = "none";
11736
11926
  contentEl.style.display = "flex";
11737
11927
  } catch (error) {
@@ -11741,6 +11931,7 @@ async function openDemandModal(params) {
11741
11931
  errorText.textContent = `${strings.error}: ${error instanceof Error ? error.message : "Unknown error"}`;
11742
11932
  }
11743
11933
  }
11934
+ initializeDateInputs();
11744
11935
  loadData();
11745
11936
  return {
11746
11937
  destroy: closeModal