myio-js-library 0.1.213 → 0.1.214

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
@@ -21471,26 +21471,26 @@ var WaterTankModalView = class {
21471
21471
  */
21472
21472
  getI18n() {
21473
21473
  const defaults = {
21474
- title: "Water Tank",
21475
- loading: "Loading...",
21476
- error: "Error loading data",
21477
- noData: "No data available",
21478
- exportCsv: "Export CSV",
21479
- close: "Close",
21480
- currentLevel: "Current Level",
21481
- averageLevel: "Average Level",
21482
- minLevel: "Minimum Level",
21483
- maxLevel: "Maximum Level",
21484
- dateRange: "Date Range",
21485
- deviceInfo: "Device Information",
21486
- levelChart: "Water Level History (m.c.a)",
21474
+ title: "Caixa d'\xC1gua",
21475
+ loading: "Carregando...",
21476
+ error: "Erro ao carregar dados",
21477
+ noData: "Nenhum dado dispon\xEDvel",
21478
+ exportCsv: "Exportar CSV",
21479
+ close: "Fechar",
21480
+ currentLevel: "N\xEDvel Atual",
21481
+ averageLevel: "N\xEDvel M\xE9dio",
21482
+ minLevel: "N\xEDvel M\xEDnimo",
21483
+ maxLevel: "N\xEDvel M\xE1ximo",
21484
+ dateRange: "Per\xEDodo",
21485
+ deviceInfo: "Informa\xE7\xF5es do Dispositivo",
21486
+ levelChart: "Hist\xF3rico de N\xEDvel (m.c.a)",
21487
21487
  percentUnit: "%",
21488
21488
  status: {
21489
- critical: "Critical",
21490
- low: "Low",
21491
- medium: "Medium",
21492
- good: "Good",
21493
- full: "Full"
21489
+ critical: "Cr\xEDtico",
21490
+ low: "Baixo",
21491
+ medium: "M\xE9dio",
21492
+ good: "Bom",
21493
+ full: "Cheio"
21494
21494
  }
21495
21495
  };
21496
21496
  return {
@@ -21591,62 +21591,327 @@ var WaterTankModalView = class {
21591
21591
  this.attachEventListeners();
21592
21592
  }
21593
21593
  /**
21594
- * Render modal header
21594
+ * Render modal header - MyIO Premium Style
21595
21595
  */
21596
21596
  renderHeader() {
21597
21597
  const { context, params } = this.config;
21598
21598
  const title = params.ui?.title || `${this.i18n.title} - ${context.device.label}`;
21599
21599
  return `
21600
21600
  <div class="myio-water-tank-modal-header" style="
21601
- padding: 20px 24px;
21602
- border-bottom: 1px solid #e0e0e0;
21601
+ padding: 4px 8px;
21603
21602
  display: flex;
21604
21603
  align-items: center;
21605
21604
  justify-content: space-between;
21605
+ background: #3e1a7d;
21606
+ color: white;
21607
+ border-radius: 12px 12px 0 0;
21608
+ min-height: 20px;
21606
21609
  ">
21607
21610
  <h2 style="
21608
- margin: 0;
21609
- font-size: 20px;
21611
+ margin: 6px;
21612
+ font-size: 18px;
21610
21613
  font-weight: 600;
21611
- color: #2c3e50;
21612
- ">${title}</h2>
21613
- <button class="myio-water-tank-modal-close" style="
21614
- background: none;
21615
- border: none;
21616
- font-size: 24px;
21617
- color: #7f8c8d;
21618
- cursor: pointer;
21619
- padding: 0;
21620
- width: 32px;
21621
- height: 32px;
21622
- display: flex;
21623
- align-items: center;
21624
- justify-content: center;
21625
- border-radius: 4px;
21626
- transition: background 0.2s ease;
21627
- " title="${this.i18n.close}">
21628
- \xD7
21629
- </button>
21614
+ color: white;
21615
+ line-height: 2;
21616
+ ">\u{1F4A7} ${title}</h2>
21617
+ <div style="display: flex; gap: 4px; align-items: center;">
21618
+ <button class="myio-water-tank-modal-close" title="${this.i18n.close}" style="
21619
+ background: none;
21620
+ border: none;
21621
+ font-size: 20px;
21622
+ cursor: pointer;
21623
+ padding: 4px 8px;
21624
+ border-radius: 6px;
21625
+ color: rgba(255,255,255,0.8);
21626
+ transition: background-color 0.2s;
21627
+ ">\xD7</button>
21628
+ </div>
21630
21629
  </div>
21631
21630
  `;
21632
21631
  }
21633
21632
  /**
21634
- * Render modal body
21633
+ * RFC-0107: Render modal body with new layout
21634
+ * Left side: Tank visualization with percentage
21635
+ * Right side: Chart with controls (larger area)
21635
21636
  */
21636
21637
  renderBody() {
21637
21638
  return `
21638
21639
  <div class="myio-water-tank-modal-body" style="
21639
- padding: 24px;
21640
+ padding: 20px;
21640
21641
  overflow-y: auto;
21641
21642
  flex: 1;
21643
+ display: flex;
21644
+ flex-direction: column;
21645
+ gap: 16px;
21642
21646
  ">
21643
- ${this.renderDateRangePicker()}
21644
- ${this.renderTankVisualization()}
21645
- ${this.renderChart()}
21647
+ ${this.renderControlsBar()}
21648
+ <div style="
21649
+ display: flex;
21650
+ gap: 20px;
21651
+ flex: 1;
21652
+ min-height: 400px;
21653
+ ">
21654
+ ${this.renderTankPanel()}
21655
+ ${this.renderChartPanel()}
21656
+ </div>
21646
21657
  </div>
21647
21658
  ${this.renderFooter()}
21648
21659
  `;
21649
21660
  }
21661
+ /**
21662
+ * RFC-0107: Render controls bar with date range, aggregation, and limit
21663
+ */
21664
+ renderControlsBar() {
21665
+ const { params } = this.config;
21666
+ const startDate = this.formatDateForInput(params.startTs);
21667
+ const endDate = this.formatDateForInput(params.endTs);
21668
+ const currentAggregation = params.aggregation || "NONE";
21669
+ const currentLimit = params.limit || 1e3;
21670
+ return `
21671
+ <div style="
21672
+ background: #f8f9fa;
21673
+ border: 1px solid #e0e0e0;
21674
+ border-radius: 8px;
21675
+ padding: 12px 16px;
21676
+ display: flex;
21677
+ align-items: center;
21678
+ gap: 16px;
21679
+ flex-wrap: wrap;
21680
+ ">
21681
+ <div style="display: flex; align-items: center; gap: 8px;">
21682
+ <label style="font-size: 13px; font-weight: 500; color: #2c3e50;">De:</label>
21683
+ <input type="date" id="myio-water-tank-start-date" value="${startDate}" style="
21684
+ padding: 6px 10px;
21685
+ border: 1px solid #ddd;
21686
+ border-radius: 6px;
21687
+ font-size: 13px;
21688
+ color: #2c3e50;
21689
+ cursor: pointer;
21690
+ "/>
21691
+ </div>
21692
+ <div style="display: flex; align-items: center; gap: 8px;">
21693
+ <label style="font-size: 13px; font-weight: 500; color: #2c3e50;">At\xE9:</label>
21694
+ <input type="date" id="myio-water-tank-end-date" value="${endDate}" style="
21695
+ padding: 6px 10px;
21696
+ border: 1px solid #ddd;
21697
+ border-radius: 6px;
21698
+ font-size: 13px;
21699
+ color: #2c3e50;
21700
+ cursor: pointer;
21701
+ "/>
21702
+ </div>
21703
+ <div style="display: flex; align-items: center; gap: 8px;">
21704
+ <label style="font-size: 13px; font-weight: 500; color: #2c3e50;">Agrega\xE7\xE3o:</label>
21705
+ <select id="myio-water-tank-aggregation" style="
21706
+ padding: 6px 10px;
21707
+ border: 1px solid #ddd;
21708
+ border-radius: 6px;
21709
+ font-size: 13px;
21710
+ color: #2c3e50;
21711
+ background: white;
21712
+ cursor: pointer;
21713
+ ">
21714
+ <option value="NONE" ${currentAggregation === "NONE" ? "selected" : ""}>Nenhuma</option>
21715
+ <option value="AVG" ${currentAggregation === "AVG" ? "selected" : ""}>M\xE9dia</option>
21716
+ <option value="MIN" ${currentAggregation === "MIN" ? "selected" : ""}>M\xEDnimo</option>
21717
+ <option value="MAX" ${currentAggregation === "MAX" ? "selected" : ""}>M\xE1ximo</option>
21718
+ <option value="SUM" ${currentAggregation === "SUM" ? "selected" : ""}>Soma</option>
21719
+ <option value="COUNT" ${currentAggregation === "COUNT" ? "selected" : ""}>Contagem</option>
21720
+ </select>
21721
+ </div>
21722
+ <div style="display: flex; align-items: center; gap: 8px;">
21723
+ <label style="font-size: 13px; font-weight: 500; color: #2c3e50;">Limite:</label>
21724
+ <select id="myio-water-tank-limit" style="
21725
+ padding: 6px 10px;
21726
+ border: 1px solid #ddd;
21727
+ border-radius: 6px;
21728
+ font-size: 13px;
21729
+ color: #2c3e50;
21730
+ background: white;
21731
+ cursor: pointer;
21732
+ ">
21733
+ <option value="100" ${currentLimit === 100 ? "selected" : ""}>100</option>
21734
+ <option value="500" ${currentLimit === 500 ? "selected" : ""}>500</option>
21735
+ <option value="1000" ${currentLimit === 1e3 ? "selected" : ""}>1000</option>
21736
+ <option value="2000" ${currentLimit === 2e3 ? "selected" : ""}>2000</option>
21737
+ <option value="5000" ${currentLimit === 5e3 ? "selected" : ""}>5000</option>
21738
+ </select>
21739
+ </div>
21740
+ <button id="myio-water-tank-apply-dates" style="
21741
+ background: linear-gradient(135deg, #3498db 0%, #2980b9 100%);
21742
+ color: white;
21743
+ border: none;
21744
+ padding: 6px 16px;
21745
+ border-radius: 6px;
21746
+ font-size: 13px;
21747
+ font-weight: 500;
21748
+ cursor: pointer;
21749
+ transition: all 0.2s ease;
21750
+ ">
21751
+ Aplicar
21752
+ </button>
21753
+ </div>
21754
+ `;
21755
+ }
21756
+ /**
21757
+ * RFC-0107: Render tank panel (left side)
21758
+ */
21759
+ renderTankPanel() {
21760
+ const { data, context } = this.config;
21761
+ let percentage = 0;
21762
+ const percentagePoints = data.telemetry.filter((p) => p.key === "water_percentage");
21763
+ if (percentagePoints.length > 0) {
21764
+ const latestPercentage = percentagePoints[percentagePoints.length - 1].value;
21765
+ percentage = latestPercentage <= 1.5 ? latestPercentage * 100 : latestPercentage;
21766
+ } else if (context.device.currentLevel !== void 0) {
21767
+ const level = context.device.currentLevel;
21768
+ percentage = level <= 1.5 ? level * 100 : level;
21769
+ }
21770
+ const levelStatus = this.getLevelStatus(Math.min(percentage, 100));
21771
+ const tankImageUrl = this.getTankImageUrl(Math.min(percentage, 100));
21772
+ const displayPercentage = percentage.toFixed(1);
21773
+ return `
21774
+ <div style="
21775
+ width: 200px;
21776
+ min-width: 200px;
21777
+ background: linear-gradient(135deg, ${levelStatus.color}10 0%, ${levelStatus.color}05 100%);
21778
+ border: 1px solid ${levelStatus.color}30;
21779
+ border-radius: 12px;
21780
+ padding: 24px 16px;
21781
+ display: flex;
21782
+ flex-direction: column;
21783
+ align-items: center;
21784
+ justify-content: center;
21785
+ gap: 16px;
21786
+ ">
21787
+ <img src="${tankImageUrl}" alt="Water Tank" style="
21788
+ width: 100px;
21789
+ height: auto;
21790
+ filter: drop-shadow(0 4px 8px rgba(0,0,0,0.1));
21791
+ "/>
21792
+ <div style="
21793
+ font-size: 42px;
21794
+ font-weight: 700;
21795
+ color: ${levelStatus.color};
21796
+ line-height: 1;
21797
+ ">${displayPercentage}%</div>
21798
+ <div style="
21799
+ background: ${levelStatus.color};
21800
+ color: white;
21801
+ padding: 4px 12px;
21802
+ border-radius: 20px;
21803
+ font-size: 11px;
21804
+ font-weight: 600;
21805
+ text-transform: uppercase;
21806
+ ">${levelStatus.label}</div>
21807
+ <div style="
21808
+ font-size: 12px;
21809
+ color: #7f8c8d;
21810
+ text-align: center;
21811
+ ">${this.i18n.currentLevel}</div>
21812
+ </div>
21813
+ `;
21814
+ }
21815
+ /**
21816
+ * RFC-0107: Render chart panel (right side) with maximize button
21817
+ */
21818
+ renderChartPanel() {
21819
+ const chartPoints = this.getChartDataPoints();
21820
+ const chartTitle = this.chartDisplayMode === "water_percentage" ? "Hist\xF3rico de N\xEDvel (%)" : this.i18n.levelChart;
21821
+ if (chartPoints.length === 0) {
21822
+ const displayLabel = this.chartDisplayMode === "water_percentage" ? "%" : "m.c.a";
21823
+ return `
21824
+ <div style="
21825
+ flex: 1;
21826
+ background: #f8f9fa;
21827
+ border: 1px solid #e0e0e0;
21828
+ border-radius: 12px;
21829
+ display: flex;
21830
+ flex-direction: column;
21831
+ align-items: center;
21832
+ justify-content: center;
21833
+ padding: 24px;
21834
+ ">
21835
+ <div style="font-size: 48px; margin-bottom: 16px; opacity: 0.3;">\u{1F4CA}</div>
21836
+ <div style="color: #7f8c8d; font-size: 16px;">${this.i18n.noData}</div>
21837
+ <div style="color: #bdc3c7; font-size: 13px; margin-top: 8px;">
21838
+ Sem dados de ${this.chartDisplayMode === "water_percentage" ? "percentual" : "n\xEDvel"} (${displayLabel}) dispon\xEDveis
21839
+ </div>
21840
+ </div>
21841
+ `;
21842
+ }
21843
+ const firstTs = chartPoints[0]?.ts;
21844
+ const lastTs = chartPoints[chartPoints.length - 1]?.ts;
21845
+ return `
21846
+ <div id="myio-water-tank-chart-panel" style="
21847
+ flex: 1;
21848
+ background: white;
21849
+ border: 1px solid #e0e0e0;
21850
+ border-radius: 12px;
21851
+ padding: 16px;
21852
+ display: flex;
21853
+ flex-direction: column;
21854
+ position: relative;
21855
+ ">
21856
+ <div style="
21857
+ display: flex;
21858
+ align-items: center;
21859
+ justify-content: space-between;
21860
+ margin-bottom: 12px;
21861
+ ">
21862
+ <h3 style="
21863
+ margin: 0;
21864
+ font-size: 15px;
21865
+ font-weight: 600;
21866
+ color: #2c3e50;
21867
+ ">${chartTitle}</h3>
21868
+ <div style="
21869
+ display: flex;
21870
+ align-items: center;
21871
+ gap: 8px;
21872
+ ">
21873
+ <select id="myio-water-tank-display-mode" style="
21874
+ padding: 4px 8px;
21875
+ border: 1px solid #ddd;
21876
+ border-radius: 4px;
21877
+ font-size: 12px;
21878
+ color: #2c3e50;
21879
+ background: white;
21880
+ cursor: pointer;
21881
+ ">
21882
+ <option value="water_level" ${this.chartDisplayMode === "water_level" ? "selected" : ""}>N\xEDvel (m.c.a)</option>
21883
+ <option value="water_percentage" ${this.chartDisplayMode === "water_percentage" ? "selected" : ""}>Percentual (%)</option>
21884
+ </select>
21885
+ <button id="myio-water-tank-maximize" title="Maximizar gr\xE1fico" style="
21886
+ background: #f0f0f0;
21887
+ border: 1px solid #ddd;
21888
+ border-radius: 4px;
21889
+ padding: 4px 8px;
21890
+ cursor: pointer;
21891
+ font-size: 14px;
21892
+ display: flex;
21893
+ align-items: center;
21894
+ justify-content: center;
21895
+ ">\u26F6</button>
21896
+ </div>
21897
+ </div>
21898
+ <div style="flex: 1; min-height: 300px;">
21899
+ <canvas id="myio-water-tank-chart" style="width: 100%; height: 100%;"></canvas>
21900
+ </div>
21901
+ ${firstTs && lastTs ? `
21902
+ <div style="
21903
+ margin-top: 8px;
21904
+ font-size: 11px;
21905
+ color: #7f8c8d;
21906
+ text-align: center;
21907
+ ">
21908
+ ${this.formatDate(firstTs, false)} \u2014 ${this.formatDate(lastTs, false)}
21909
+ (${chartPoints.length} leituras)
21910
+ </div>
21911
+ ` : ""}
21912
+ </div>
21913
+ `;
21914
+ }
21650
21915
  /**
21651
21916
  * Render date range picker
21652
21917
  */
@@ -21667,7 +21932,7 @@ var WaterTankModalView = class {
21667
21932
  flex-wrap: wrap;
21668
21933
  ">
21669
21934
  <div style="display: flex; align-items: center; gap: 8px;">
21670
- <label style="font-size: 14px; font-weight: 500; color: #2c3e50;">From:</label>
21935
+ <label style="font-size: 14px; font-weight: 500; color: #2c3e50;">De:</label>
21671
21936
  <input type="date" id="myio-water-tank-start-date" value="${startDate}" style="
21672
21937
  padding: 8px 12px;
21673
21938
  border: 1px solid #ddd;
@@ -21678,7 +21943,7 @@ var WaterTankModalView = class {
21678
21943
  "/>
21679
21944
  </div>
21680
21945
  <div style="display: flex; align-items: center; gap: 8px;">
21681
- <label style="font-size: 14px; font-weight: 500; color: #2c3e50;">To:</label>
21946
+ <label style="font-size: 14px; font-weight: 500; color: #2c3e50;">At\xE9:</label>
21682
21947
  <input type="date" id="myio-water-tank-end-date" value="${endDate}" style="
21683
21948
  padding: 8px 12px;
21684
21949
  border: 1px solid #ddd;
@@ -21699,7 +21964,7 @@ var WaterTankModalView = class {
21699
21964
  cursor: pointer;
21700
21965
  transition: all 0.2s ease;
21701
21966
  ">
21702
- Apply
21967
+ Aplicar
21703
21968
  </button>
21704
21969
  </div>
21705
21970
  `;
@@ -21921,7 +22186,7 @@ var WaterTankModalView = class {
21921
22186
  }
21922
22187
  const applyDatesBtn = this.modal.querySelector("#myio-water-tank-apply-dates");
21923
22188
  if (applyDatesBtn) {
21924
- applyDatesBtn.addEventListener("click", () => this.handleDateRangeChange());
22189
+ applyDatesBtn.addEventListener("click", () => this.handleApplyParams());
21925
22190
  }
21926
22191
  const displayModeSelect = this.modal.querySelector("#myio-water-tank-display-mode");
21927
22192
  if (displayModeSelect) {
@@ -21930,6 +22195,10 @@ var WaterTankModalView = class {
21930
22195
  this.refreshChart();
21931
22196
  });
21932
22197
  }
22198
+ const maximizeBtn = this.modal.querySelector("#myio-water-tank-maximize");
22199
+ if (maximizeBtn) {
22200
+ maximizeBtn.addEventListener("click", () => this.handleMaximize());
22201
+ }
21933
22202
  this.overlay.addEventListener("click", (e) => {
21934
22203
  if (e.target === this.overlay) {
21935
22204
  this.config.onClose();
@@ -21944,12 +22213,20 @@ var WaterTankModalView = class {
21944
22213
  });
21945
22214
  }
21946
22215
  /**
21947
- * Handle date range change
22216
+ * Handle date range change (legacy - kept for compatibility)
21948
22217
  */
21949
22218
  handleDateRangeChange() {
22219
+ this.handleApplyParams();
22220
+ }
22221
+ /**
22222
+ * RFC-0107: Handle apply params (date range, aggregation, limit)
22223
+ */
22224
+ handleApplyParams() {
21950
22225
  if (!this.modal) return;
21951
22226
  const startInput = this.modal.querySelector("#myio-water-tank-start-date");
21952
22227
  const endInput = this.modal.querySelector("#myio-water-tank-end-date");
22228
+ const aggregationSelect = this.modal.querySelector("#myio-water-tank-aggregation");
22229
+ const limitSelect = this.modal.querySelector("#myio-water-tank-limit");
21953
22230
  if (startInput && endInput) {
21954
22231
  const startTs = new Date(startInput.value).setHours(0, 0, 0, 0);
21955
22232
  const endTs = new Date(endInput.value).setHours(23, 59, 59, 999);
@@ -21957,17 +22234,77 @@ var WaterTankModalView = class {
21957
22234
  alert("Start date must be before end date");
21958
22235
  return;
21959
22236
  }
21960
- console.log("[WaterTankModalView] Date range changed:", {
22237
+ const aggregation = aggregationSelect?.value || "NONE";
22238
+ const limit = parseInt(limitSelect?.value || "1000", 10);
22239
+ console.log("[WaterTankModalView] Params changed:", {
21961
22240
  startTs,
21962
22241
  endTs,
22242
+ aggregation,
22243
+ limit,
21963
22244
  startDate: new Date(startTs).toISOString(),
21964
22245
  endDate: new Date(endTs).toISOString()
21965
22246
  });
21966
- if (this.config.onDateRangeChange) {
22247
+ this.config.params.startTs = startTs;
22248
+ this.config.params.endTs = endTs;
22249
+ this.config.params.aggregation = aggregation;
22250
+ this.config.params.limit = limit;
22251
+ if (this.config.onParamsChange) {
22252
+ this.config.onParamsChange({ startTs, endTs, aggregation, limit });
22253
+ } else if (this.config.onDateRangeChange) {
21967
22254
  this.config.onDateRangeChange(startTs, endTs);
21968
22255
  }
21969
22256
  }
21970
22257
  }
22258
+ /**
22259
+ * RFC-0107: Handle maximize/restore chart
22260
+ */
22261
+ isMaximized = false;
22262
+ originalModalStyle = "";
22263
+ handleMaximize() {
22264
+ if (!this.modal) return;
22265
+ const chartPanel = this.modal.querySelector("#myio-water-tank-chart-panel");
22266
+ const tankPanel = chartPanel?.previousElementSibling;
22267
+ const maximizeBtn = this.modal.querySelector("#myio-water-tank-maximize");
22268
+ if (!chartPanel) return;
22269
+ if (this.isMaximized) {
22270
+ this.modal.style.cssText = this.originalModalStyle;
22271
+ if (tankPanel) tankPanel.style.display = "";
22272
+ chartPanel.style.cssText = `
22273
+ flex: 1;
22274
+ background: white;
22275
+ border: 1px solid #e0e0e0;
22276
+ border-radius: 12px;
22277
+ padding: 16px;
22278
+ display: flex;
22279
+ flex-direction: column;
22280
+ position: relative;
22281
+ `;
22282
+ if (maximizeBtn) maximizeBtn.textContent = "\u26F6";
22283
+ this.isMaximized = false;
22284
+ } else {
22285
+ this.originalModalStyle = this.modal.style.cssText;
22286
+ this.modal.style.width = "95vw";
22287
+ this.modal.style.height = "90vh";
22288
+ this.modal.style.maxWidth = "95vw";
22289
+ if (tankPanel) tankPanel.style.display = "none";
22290
+ chartPanel.style.cssText = `
22291
+ flex: 1;
22292
+ background: white;
22293
+ border: 1px solid #e0e0e0;
22294
+ border-radius: 12px;
22295
+ padding: 16px;
22296
+ display: flex;
22297
+ flex-direction: column;
22298
+ position: relative;
22299
+ min-height: 100%;
22300
+ `;
22301
+ if (maximizeBtn) maximizeBtn.textContent = "\u26F6";
22302
+ this.isMaximized = true;
22303
+ }
22304
+ requestAnimationFrame(() => {
22305
+ this.renderCanvasChart();
22306
+ });
22307
+ }
21971
22308
  handleEscapeKey(e) {
21972
22309
  if (e.key === "Escape") {
21973
22310
  this.config.onClose();
@@ -22106,7 +22443,7 @@ var WaterTankModalView = class {
22106
22443
  }
22107
22444
  }
22108
22445
  /**
22109
- * Update data and re-render chart
22446
+ * Update data and re-render chart with new layout
22110
22447
  */
22111
22448
  updateData(data) {
22112
22449
  this.config.data = data;
@@ -22114,13 +22451,20 @@ var WaterTankModalView = class {
22114
22451
  const bodyEl = this.modal.querySelector(".myio-water-tank-modal-body");
22115
22452
  if (bodyEl) {
22116
22453
  bodyEl.innerHTML = `
22117
- ${this.renderDateRangePicker()}
22118
- ${this.renderTankVisualization()}
22119
- ${this.renderChart()}
22454
+ ${this.renderControlsBar()}
22455
+ <div style="
22456
+ display: flex;
22457
+ gap: 20px;
22458
+ flex: 1;
22459
+ min-height: 400px;
22460
+ ">
22461
+ ${this.renderTankPanel()}
22462
+ ${this.renderChartPanel()}
22463
+ </div>
22120
22464
  `;
22121
22465
  const applyDatesBtn = this.modal.querySelector("#myio-water-tank-apply-dates");
22122
22466
  if (applyDatesBtn) {
22123
- applyDatesBtn.addEventListener("click", () => this.handleDateRangeChange());
22467
+ applyDatesBtn.addEventListener("click", () => this.handleApplyParams());
22124
22468
  }
22125
22469
  const displayModeSelect = this.modal.querySelector("#myio-water-tank-display-mode");
22126
22470
  if (displayModeSelect) {
@@ -22129,6 +22473,10 @@ var WaterTankModalView = class {
22129
22473
  this.refreshChart();
22130
22474
  });
22131
22475
  }
22476
+ const maximizeBtn = this.modal.querySelector("#myio-water-tank-maximize");
22477
+ if (maximizeBtn) {
22478
+ maximizeBtn.addEventListener("click", () => this.handleMaximize());
22479
+ }
22132
22480
  requestAnimationFrame(() => {
22133
22481
  this.renderCanvasChart();
22134
22482
  });
@@ -22408,7 +22756,8 @@ var WaterTankModal = class {
22408
22756
  onError: (error) => this.handleError(error),
22409
22757
  onClose: () => this.close(),
22410
22758
  // Call close() to destroy view and trigger user callback
22411
- onDateRangeChange: (startTs, endTs) => this.handleDateRangeChange(startTs, endTs)
22759
+ onDateRangeChange: (startTs, endTs) => this.handleDateRangeChange(startTs, endTs),
22760
+ onParamsChange: (params) => this.handleParamsChange(params)
22412
22761
  });
22413
22762
  this.view.render();
22414
22763
  this.view.show();
@@ -22473,6 +22822,43 @@ var WaterTankModal = class {
22473
22822
  this.handleError(error);
22474
22823
  }
22475
22824
  }
22825
+ /**
22826
+ * RFC-0107: Handle params change (date range, aggregation, limit)
22827
+ */
22828
+ async handleParamsChange(params) {
22829
+ console.log("[WaterTankModal] Params changed:", {
22830
+ startTs: params.startTs,
22831
+ endTs: params.endTs,
22832
+ aggregation: params.aggregation,
22833
+ limit: params.limit,
22834
+ startDate: new Date(params.startTs).toISOString(),
22835
+ endDate: new Date(params.endTs).toISOString()
22836
+ });
22837
+ this.options.startTs = params.startTs;
22838
+ this.options.endTs = params.endTs;
22839
+ this.options.aggregation = params.aggregation;
22840
+ this.options.limit = params.limit;
22841
+ this.context.timeRange.startTs = params.startTs;
22842
+ this.context.timeRange.endTs = params.endTs;
22843
+ try {
22844
+ console.log("[WaterTankModal] Fetching data with new params...");
22845
+ this.data = await this.fetchTelemetryData();
22846
+ if (this.view) {
22847
+ this.view.updateData(this.data);
22848
+ }
22849
+ if (this.options.onDataLoaded) {
22850
+ try {
22851
+ this.options.onDataLoaded(this.data);
22852
+ } catch (callbackError) {
22853
+ console.warn("[WaterTankModal] onDataLoaded callback error:", callbackError);
22854
+ }
22855
+ }
22856
+ console.log("[WaterTankModal] Data refreshed with new params successfully");
22857
+ } catch (error) {
22858
+ console.error("[WaterTankModal] Failed to fetch data with new params:", error);
22859
+ this.handleError(error);
22860
+ }
22861
+ }
22476
22862
  /**
22477
22863
  * Handle export functionality
22478
22864
  */
@@ -22594,8 +22980,8 @@ function validateOptions2(options) {
22594
22980
  }
22595
22981
  if (options.currentLevel !== void 0) {
22596
22982
  const level = Number(options.currentLevel);
22597
- if (isNaN(level) || level < 0 || level > 100) {
22598
- errors.push("currentLevel must be a number between 0 and 100");
22983
+ if (isNaN(level) || level < 0) {
22984
+ errors.push("currentLevel must be a non-negative number");
22599
22985
  }
22600
22986
  }
22601
22987
  if (options.limit !== void 0) {