myio-js-library 0.1.213 → 0.1.215
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 +479 -68
- package/dist/index.js +479 -68
- package/dist/myio-js-library.umd.js +479 -68
- package/dist/myio-js-library.umd.min.js +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -21299,26 +21299,26 @@ var WaterTankModalView = class {
|
|
|
21299
21299
|
*/
|
|
21300
21300
|
getI18n() {
|
|
21301
21301
|
const defaults = {
|
|
21302
|
-
title: "
|
|
21303
|
-
loading: "
|
|
21304
|
-
error: "
|
|
21305
|
-
noData: "
|
|
21306
|
-
exportCsv: "
|
|
21307
|
-
close: "
|
|
21308
|
-
currentLevel: "
|
|
21309
|
-
averageLevel: "
|
|
21310
|
-
minLevel: "
|
|
21311
|
-
maxLevel: "
|
|
21312
|
-
dateRange: "
|
|
21313
|
-
deviceInfo: "
|
|
21314
|
-
levelChart: "
|
|
21302
|
+
title: "Caixa d'\xC1gua",
|
|
21303
|
+
loading: "Carregando...",
|
|
21304
|
+
error: "Erro ao carregar dados",
|
|
21305
|
+
noData: "Nenhum dado dispon\xEDvel",
|
|
21306
|
+
exportCsv: "Exportar CSV",
|
|
21307
|
+
close: "Fechar",
|
|
21308
|
+
currentLevel: "N\xEDvel Atual",
|
|
21309
|
+
averageLevel: "N\xEDvel M\xE9dio",
|
|
21310
|
+
minLevel: "N\xEDvel M\xEDnimo",
|
|
21311
|
+
maxLevel: "N\xEDvel M\xE1ximo",
|
|
21312
|
+
dateRange: "Per\xEDodo",
|
|
21313
|
+
deviceInfo: "Informa\xE7\xF5es do Dispositivo",
|
|
21314
|
+
levelChart: "Hist\xF3rico de N\xEDvel (m.c.a)",
|
|
21315
21315
|
percentUnit: "%",
|
|
21316
21316
|
status: {
|
|
21317
|
-
critical: "
|
|
21318
|
-
low: "
|
|
21319
|
-
medium: "
|
|
21320
|
-
good: "
|
|
21321
|
-
full: "
|
|
21317
|
+
critical: "Cr\xEDtico",
|
|
21318
|
+
low: "Baixo",
|
|
21319
|
+
medium: "M\xE9dio",
|
|
21320
|
+
good: "Bom",
|
|
21321
|
+
full: "Cheio"
|
|
21322
21322
|
}
|
|
21323
21323
|
};
|
|
21324
21324
|
return {
|
|
@@ -21419,62 +21419,327 @@ var WaterTankModalView = class {
|
|
|
21419
21419
|
this.attachEventListeners();
|
|
21420
21420
|
}
|
|
21421
21421
|
/**
|
|
21422
|
-
* Render modal header
|
|
21422
|
+
* Render modal header - MyIO Premium Style
|
|
21423
21423
|
*/
|
|
21424
21424
|
renderHeader() {
|
|
21425
21425
|
const { context, params } = this.config;
|
|
21426
21426
|
const title = params.ui?.title || `${this.i18n.title} - ${context.device.label}`;
|
|
21427
21427
|
return `
|
|
21428
21428
|
<div class="myio-water-tank-modal-header" style="
|
|
21429
|
-
padding:
|
|
21430
|
-
border-bottom: 1px solid #e0e0e0;
|
|
21429
|
+
padding: 4px 8px;
|
|
21431
21430
|
display: flex;
|
|
21432
21431
|
align-items: center;
|
|
21433
21432
|
justify-content: space-between;
|
|
21433
|
+
background: #3e1a7d;
|
|
21434
|
+
color: white;
|
|
21435
|
+
border-radius: 12px 12px 0 0;
|
|
21436
|
+
min-height: 20px;
|
|
21434
21437
|
">
|
|
21435
21438
|
<h2 style="
|
|
21436
|
-
margin:
|
|
21437
|
-
font-size:
|
|
21439
|
+
margin: 6px;
|
|
21440
|
+
font-size: 18px;
|
|
21438
21441
|
font-weight: 600;
|
|
21439
|
-
color:
|
|
21440
|
-
|
|
21441
|
-
|
|
21442
|
-
|
|
21443
|
-
|
|
21444
|
-
|
|
21445
|
-
|
|
21446
|
-
|
|
21447
|
-
|
|
21448
|
-
|
|
21449
|
-
|
|
21450
|
-
|
|
21451
|
-
|
|
21452
|
-
|
|
21453
|
-
|
|
21454
|
-
transition: background 0.2s ease;
|
|
21455
|
-
" title="${this.i18n.close}">
|
|
21456
|
-
\xD7
|
|
21457
|
-
</button>
|
|
21442
|
+
color: white;
|
|
21443
|
+
line-height: 2;
|
|
21444
|
+
">\u{1F4A7} ${title}</h2>
|
|
21445
|
+
<div style="display: flex; gap: 4px; align-items: center;">
|
|
21446
|
+
<button class="myio-water-tank-modal-close" title="${this.i18n.close}" style="
|
|
21447
|
+
background: none;
|
|
21448
|
+
border: none;
|
|
21449
|
+
font-size: 20px;
|
|
21450
|
+
cursor: pointer;
|
|
21451
|
+
padding: 4px 8px;
|
|
21452
|
+
border-radius: 6px;
|
|
21453
|
+
color: rgba(255,255,255,0.8);
|
|
21454
|
+
transition: background-color 0.2s;
|
|
21455
|
+
">\xD7</button>
|
|
21456
|
+
</div>
|
|
21458
21457
|
</div>
|
|
21459
21458
|
`;
|
|
21460
21459
|
}
|
|
21461
21460
|
/**
|
|
21462
|
-
* Render modal body
|
|
21461
|
+
* RFC-0107: Render modal body with new layout
|
|
21462
|
+
* Left side: Tank visualization with percentage
|
|
21463
|
+
* Right side: Chart with controls (larger area)
|
|
21463
21464
|
*/
|
|
21464
21465
|
renderBody() {
|
|
21465
21466
|
return `
|
|
21466
21467
|
<div class="myio-water-tank-modal-body" style="
|
|
21467
|
-
padding:
|
|
21468
|
+
padding: 20px;
|
|
21468
21469
|
overflow-y: auto;
|
|
21469
21470
|
flex: 1;
|
|
21471
|
+
display: flex;
|
|
21472
|
+
flex-direction: column;
|
|
21473
|
+
gap: 16px;
|
|
21470
21474
|
">
|
|
21471
|
-
${this.
|
|
21472
|
-
|
|
21473
|
-
|
|
21475
|
+
${this.renderControlsBar()}
|
|
21476
|
+
<div style="
|
|
21477
|
+
display: flex;
|
|
21478
|
+
gap: 20px;
|
|
21479
|
+
flex: 1;
|
|
21480
|
+
min-height: 400px;
|
|
21481
|
+
">
|
|
21482
|
+
${this.renderTankPanel()}
|
|
21483
|
+
${this.renderChartPanel()}
|
|
21484
|
+
</div>
|
|
21474
21485
|
</div>
|
|
21475
21486
|
${this.renderFooter()}
|
|
21476
21487
|
`;
|
|
21477
21488
|
}
|
|
21489
|
+
/**
|
|
21490
|
+
* RFC-0107: Render controls bar with date range, aggregation, and limit
|
|
21491
|
+
*/
|
|
21492
|
+
renderControlsBar() {
|
|
21493
|
+
const { params } = this.config;
|
|
21494
|
+
const startDate = this.formatDateForInput(params.startTs);
|
|
21495
|
+
const endDate = this.formatDateForInput(params.endTs);
|
|
21496
|
+
const currentAggregation = params.aggregation || "NONE";
|
|
21497
|
+
const currentLimit = params.limit || 1e3;
|
|
21498
|
+
return `
|
|
21499
|
+
<div style="
|
|
21500
|
+
background: #f8f9fa;
|
|
21501
|
+
border: 1px solid #e0e0e0;
|
|
21502
|
+
border-radius: 8px;
|
|
21503
|
+
padding: 12px 16px;
|
|
21504
|
+
display: flex;
|
|
21505
|
+
align-items: center;
|
|
21506
|
+
gap: 16px;
|
|
21507
|
+
flex-wrap: wrap;
|
|
21508
|
+
">
|
|
21509
|
+
<div style="display: flex; align-items: center; gap: 8px;">
|
|
21510
|
+
<label style="font-size: 13px; font-weight: 500; color: #2c3e50;">De:</label>
|
|
21511
|
+
<input type="date" id="myio-water-tank-start-date" value="${startDate}" style="
|
|
21512
|
+
padding: 6px 10px;
|
|
21513
|
+
border: 1px solid #ddd;
|
|
21514
|
+
border-radius: 6px;
|
|
21515
|
+
font-size: 13px;
|
|
21516
|
+
color: #2c3e50;
|
|
21517
|
+
cursor: pointer;
|
|
21518
|
+
"/>
|
|
21519
|
+
</div>
|
|
21520
|
+
<div style="display: flex; align-items: center; gap: 8px;">
|
|
21521
|
+
<label style="font-size: 13px; font-weight: 500; color: #2c3e50;">At\xE9:</label>
|
|
21522
|
+
<input type="date" id="myio-water-tank-end-date" value="${endDate}" style="
|
|
21523
|
+
padding: 6px 10px;
|
|
21524
|
+
border: 1px solid #ddd;
|
|
21525
|
+
border-radius: 6px;
|
|
21526
|
+
font-size: 13px;
|
|
21527
|
+
color: #2c3e50;
|
|
21528
|
+
cursor: pointer;
|
|
21529
|
+
"/>
|
|
21530
|
+
</div>
|
|
21531
|
+
<div style="display: flex; align-items: center; gap: 8px;">
|
|
21532
|
+
<label style="font-size: 13px; font-weight: 500; color: #2c3e50;">Agrega\xE7\xE3o:</label>
|
|
21533
|
+
<select id="myio-water-tank-aggregation" style="
|
|
21534
|
+
padding: 6px 10px;
|
|
21535
|
+
border: 1px solid #ddd;
|
|
21536
|
+
border-radius: 6px;
|
|
21537
|
+
font-size: 13px;
|
|
21538
|
+
color: #2c3e50;
|
|
21539
|
+
background: white;
|
|
21540
|
+
cursor: pointer;
|
|
21541
|
+
">
|
|
21542
|
+
<option value="NONE" ${currentAggregation === "NONE" ? "selected" : ""}>Nenhuma</option>
|
|
21543
|
+
<option value="AVG" ${currentAggregation === "AVG" ? "selected" : ""}>M\xE9dia</option>
|
|
21544
|
+
<option value="MIN" ${currentAggregation === "MIN" ? "selected" : ""}>M\xEDnimo</option>
|
|
21545
|
+
<option value="MAX" ${currentAggregation === "MAX" ? "selected" : ""}>M\xE1ximo</option>
|
|
21546
|
+
<option value="SUM" ${currentAggregation === "SUM" ? "selected" : ""}>Soma</option>
|
|
21547
|
+
<option value="COUNT" ${currentAggregation === "COUNT" ? "selected" : ""}>Contagem</option>
|
|
21548
|
+
</select>
|
|
21549
|
+
</div>
|
|
21550
|
+
<div style="display: flex; align-items: center; gap: 8px;">
|
|
21551
|
+
<label style="font-size: 13px; font-weight: 500; color: #2c3e50;">Limite:</label>
|
|
21552
|
+
<select id="myio-water-tank-limit" style="
|
|
21553
|
+
padding: 6px 10px;
|
|
21554
|
+
border: 1px solid #ddd;
|
|
21555
|
+
border-radius: 6px;
|
|
21556
|
+
font-size: 13px;
|
|
21557
|
+
color: #2c3e50;
|
|
21558
|
+
background: white;
|
|
21559
|
+
cursor: pointer;
|
|
21560
|
+
">
|
|
21561
|
+
<option value="100" ${currentLimit === 100 ? "selected" : ""}>100</option>
|
|
21562
|
+
<option value="500" ${currentLimit === 500 ? "selected" : ""}>500</option>
|
|
21563
|
+
<option value="1000" ${currentLimit === 1e3 ? "selected" : ""}>1000</option>
|
|
21564
|
+
<option value="2000" ${currentLimit === 2e3 ? "selected" : ""}>2000</option>
|
|
21565
|
+
<option value="5000" ${currentLimit === 5e3 ? "selected" : ""}>5000</option>
|
|
21566
|
+
</select>
|
|
21567
|
+
</div>
|
|
21568
|
+
<button id="myio-water-tank-apply-dates" style="
|
|
21569
|
+
background: linear-gradient(135deg, #3498db 0%, #2980b9 100%);
|
|
21570
|
+
color: white;
|
|
21571
|
+
border: none;
|
|
21572
|
+
padding: 6px 16px;
|
|
21573
|
+
border-radius: 6px;
|
|
21574
|
+
font-size: 13px;
|
|
21575
|
+
font-weight: 500;
|
|
21576
|
+
cursor: pointer;
|
|
21577
|
+
transition: all 0.2s ease;
|
|
21578
|
+
">
|
|
21579
|
+
Aplicar
|
|
21580
|
+
</button>
|
|
21581
|
+
</div>
|
|
21582
|
+
`;
|
|
21583
|
+
}
|
|
21584
|
+
/**
|
|
21585
|
+
* RFC-0107: Render tank panel (left side)
|
|
21586
|
+
*/
|
|
21587
|
+
renderTankPanel() {
|
|
21588
|
+
const { data, context } = this.config;
|
|
21589
|
+
let percentage = 0;
|
|
21590
|
+
const percentagePoints = data.telemetry.filter((p) => p.key === "water_percentage");
|
|
21591
|
+
if (percentagePoints.length > 0) {
|
|
21592
|
+
const latestPercentage = percentagePoints[percentagePoints.length - 1].value;
|
|
21593
|
+
percentage = latestPercentage <= 1.5 ? latestPercentage * 100 : latestPercentage;
|
|
21594
|
+
} else if (context.device.currentLevel !== void 0) {
|
|
21595
|
+
const level = context.device.currentLevel;
|
|
21596
|
+
percentage = level <= 1.5 ? level * 100 : level;
|
|
21597
|
+
}
|
|
21598
|
+
const levelStatus = this.getLevelStatus(Math.min(percentage, 100));
|
|
21599
|
+
const tankImageUrl = this.getTankImageUrl(Math.min(percentage, 100));
|
|
21600
|
+
const displayPercentage = percentage.toFixed(1);
|
|
21601
|
+
return `
|
|
21602
|
+
<div style="
|
|
21603
|
+
width: 200px;
|
|
21604
|
+
min-width: 200px;
|
|
21605
|
+
background: linear-gradient(135deg, ${levelStatus.color}10 0%, ${levelStatus.color}05 100%);
|
|
21606
|
+
border: 1px solid ${levelStatus.color}30;
|
|
21607
|
+
border-radius: 12px;
|
|
21608
|
+
padding: 24px 16px;
|
|
21609
|
+
display: flex;
|
|
21610
|
+
flex-direction: column;
|
|
21611
|
+
align-items: center;
|
|
21612
|
+
justify-content: center;
|
|
21613
|
+
gap: 16px;
|
|
21614
|
+
">
|
|
21615
|
+
<img src="${tankImageUrl}" alt="Water Tank" style="
|
|
21616
|
+
width: 100px;
|
|
21617
|
+
height: auto;
|
|
21618
|
+
filter: drop-shadow(0 4px 8px rgba(0,0,0,0.1));
|
|
21619
|
+
"/>
|
|
21620
|
+
<div style="
|
|
21621
|
+
font-size: 42px;
|
|
21622
|
+
font-weight: 700;
|
|
21623
|
+
color: ${levelStatus.color};
|
|
21624
|
+
line-height: 1;
|
|
21625
|
+
">${displayPercentage}%</div>
|
|
21626
|
+
<div style="
|
|
21627
|
+
background: ${levelStatus.color};
|
|
21628
|
+
color: white;
|
|
21629
|
+
padding: 4px 12px;
|
|
21630
|
+
border-radius: 20px;
|
|
21631
|
+
font-size: 11px;
|
|
21632
|
+
font-weight: 600;
|
|
21633
|
+
text-transform: uppercase;
|
|
21634
|
+
">${levelStatus.label}</div>
|
|
21635
|
+
<div style="
|
|
21636
|
+
font-size: 12px;
|
|
21637
|
+
color: #7f8c8d;
|
|
21638
|
+
text-align: center;
|
|
21639
|
+
">${this.i18n.currentLevel}</div>
|
|
21640
|
+
</div>
|
|
21641
|
+
`;
|
|
21642
|
+
}
|
|
21643
|
+
/**
|
|
21644
|
+
* RFC-0107: Render chart panel (right side) with maximize button
|
|
21645
|
+
*/
|
|
21646
|
+
renderChartPanel() {
|
|
21647
|
+
const chartPoints = this.getChartDataPoints();
|
|
21648
|
+
const chartTitle = this.chartDisplayMode === "water_percentage" ? "Hist\xF3rico de N\xEDvel (%)" : this.i18n.levelChart;
|
|
21649
|
+
if (chartPoints.length === 0) {
|
|
21650
|
+
const displayLabel = this.chartDisplayMode === "water_percentage" ? "%" : "m.c.a";
|
|
21651
|
+
return `
|
|
21652
|
+
<div style="
|
|
21653
|
+
flex: 1;
|
|
21654
|
+
background: #f8f9fa;
|
|
21655
|
+
border: 1px solid #e0e0e0;
|
|
21656
|
+
border-radius: 12px;
|
|
21657
|
+
display: flex;
|
|
21658
|
+
flex-direction: column;
|
|
21659
|
+
align-items: center;
|
|
21660
|
+
justify-content: center;
|
|
21661
|
+
padding: 24px;
|
|
21662
|
+
">
|
|
21663
|
+
<div style="font-size: 48px; margin-bottom: 16px; opacity: 0.3;">\u{1F4CA}</div>
|
|
21664
|
+
<div style="color: #7f8c8d; font-size: 16px;">${this.i18n.noData}</div>
|
|
21665
|
+
<div style="color: #bdc3c7; font-size: 13px; margin-top: 8px;">
|
|
21666
|
+
Sem dados de ${this.chartDisplayMode === "water_percentage" ? "percentual" : "n\xEDvel"} (${displayLabel}) dispon\xEDveis
|
|
21667
|
+
</div>
|
|
21668
|
+
</div>
|
|
21669
|
+
`;
|
|
21670
|
+
}
|
|
21671
|
+
const firstTs = chartPoints[0]?.ts;
|
|
21672
|
+
const lastTs = chartPoints[chartPoints.length - 1]?.ts;
|
|
21673
|
+
return `
|
|
21674
|
+
<div id="myio-water-tank-chart-panel" style="
|
|
21675
|
+
flex: 1;
|
|
21676
|
+
background: white;
|
|
21677
|
+
border: 1px solid #e0e0e0;
|
|
21678
|
+
border-radius: 12px;
|
|
21679
|
+
padding: 16px;
|
|
21680
|
+
display: flex;
|
|
21681
|
+
flex-direction: column;
|
|
21682
|
+
position: relative;
|
|
21683
|
+
">
|
|
21684
|
+
<div style="
|
|
21685
|
+
display: flex;
|
|
21686
|
+
align-items: center;
|
|
21687
|
+
justify-content: space-between;
|
|
21688
|
+
margin-bottom: 12px;
|
|
21689
|
+
">
|
|
21690
|
+
<h3 style="
|
|
21691
|
+
margin: 0;
|
|
21692
|
+
font-size: 15px;
|
|
21693
|
+
font-weight: 600;
|
|
21694
|
+
color: #2c3e50;
|
|
21695
|
+
">${chartTitle}</h3>
|
|
21696
|
+
<div style="
|
|
21697
|
+
display: flex;
|
|
21698
|
+
align-items: center;
|
|
21699
|
+
gap: 8px;
|
|
21700
|
+
">
|
|
21701
|
+
<select id="myio-water-tank-display-mode" style="
|
|
21702
|
+
padding: 4px 8px;
|
|
21703
|
+
border: 1px solid #ddd;
|
|
21704
|
+
border-radius: 4px;
|
|
21705
|
+
font-size: 12px;
|
|
21706
|
+
color: #2c3e50;
|
|
21707
|
+
background: white;
|
|
21708
|
+
cursor: pointer;
|
|
21709
|
+
">
|
|
21710
|
+
<option value="water_level" ${this.chartDisplayMode === "water_level" ? "selected" : ""}>N\xEDvel (m.c.a)</option>
|
|
21711
|
+
<option value="water_percentage" ${this.chartDisplayMode === "water_percentage" ? "selected" : ""}>Percentual (%)</option>
|
|
21712
|
+
</select>
|
|
21713
|
+
<button id="myio-water-tank-maximize" title="Maximizar gr\xE1fico" style="
|
|
21714
|
+
background: #f0f0f0;
|
|
21715
|
+
border: 1px solid #ddd;
|
|
21716
|
+
border-radius: 4px;
|
|
21717
|
+
padding: 4px 8px;
|
|
21718
|
+
cursor: pointer;
|
|
21719
|
+
font-size: 14px;
|
|
21720
|
+
display: flex;
|
|
21721
|
+
align-items: center;
|
|
21722
|
+
justify-content: center;
|
|
21723
|
+
">\u26F6</button>
|
|
21724
|
+
</div>
|
|
21725
|
+
</div>
|
|
21726
|
+
<div style="flex: 1; min-height: 300px;">
|
|
21727
|
+
<canvas id="myio-water-tank-chart" style="width: 100%; height: 100%;"></canvas>
|
|
21728
|
+
</div>
|
|
21729
|
+
${firstTs && lastTs ? `
|
|
21730
|
+
<div style="
|
|
21731
|
+
margin-top: 8px;
|
|
21732
|
+
font-size: 11px;
|
|
21733
|
+
color: #7f8c8d;
|
|
21734
|
+
text-align: center;
|
|
21735
|
+
">
|
|
21736
|
+
${this.formatDate(firstTs, false)} \u2014 ${this.formatDate(lastTs, false)}
|
|
21737
|
+
(${chartPoints.length} leituras)
|
|
21738
|
+
</div>
|
|
21739
|
+
` : ""}
|
|
21740
|
+
</div>
|
|
21741
|
+
`;
|
|
21742
|
+
}
|
|
21478
21743
|
/**
|
|
21479
21744
|
* Render date range picker
|
|
21480
21745
|
*/
|
|
@@ -21495,7 +21760,7 @@ var WaterTankModalView = class {
|
|
|
21495
21760
|
flex-wrap: wrap;
|
|
21496
21761
|
">
|
|
21497
21762
|
<div style="display: flex; align-items: center; gap: 8px;">
|
|
21498
|
-
<label style="font-size: 14px; font-weight: 500; color: #2c3e50;">
|
|
21763
|
+
<label style="font-size: 14px; font-weight: 500; color: #2c3e50;">De:</label>
|
|
21499
21764
|
<input type="date" id="myio-water-tank-start-date" value="${startDate}" style="
|
|
21500
21765
|
padding: 8px 12px;
|
|
21501
21766
|
border: 1px solid #ddd;
|
|
@@ -21506,7 +21771,7 @@ var WaterTankModalView = class {
|
|
|
21506
21771
|
"/>
|
|
21507
21772
|
</div>
|
|
21508
21773
|
<div style="display: flex; align-items: center; gap: 8px;">
|
|
21509
|
-
<label style="font-size: 14px; font-weight: 500; color: #2c3e50;">
|
|
21774
|
+
<label style="font-size: 14px; font-weight: 500; color: #2c3e50;">At\xE9:</label>
|
|
21510
21775
|
<input type="date" id="myio-water-tank-end-date" value="${endDate}" style="
|
|
21511
21776
|
padding: 8px 12px;
|
|
21512
21777
|
border: 1px solid #ddd;
|
|
@@ -21527,7 +21792,7 @@ var WaterTankModalView = class {
|
|
|
21527
21792
|
cursor: pointer;
|
|
21528
21793
|
transition: all 0.2s ease;
|
|
21529
21794
|
">
|
|
21530
|
-
|
|
21795
|
+
Aplicar
|
|
21531
21796
|
</button>
|
|
21532
21797
|
</div>
|
|
21533
21798
|
`;
|
|
@@ -21749,7 +22014,7 @@ var WaterTankModalView = class {
|
|
|
21749
22014
|
}
|
|
21750
22015
|
const applyDatesBtn = this.modal.querySelector("#myio-water-tank-apply-dates");
|
|
21751
22016
|
if (applyDatesBtn) {
|
|
21752
|
-
applyDatesBtn.addEventListener("click", () => this.
|
|
22017
|
+
applyDatesBtn.addEventListener("click", () => this.handleApplyParams());
|
|
21753
22018
|
}
|
|
21754
22019
|
const displayModeSelect = this.modal.querySelector("#myio-water-tank-display-mode");
|
|
21755
22020
|
if (displayModeSelect) {
|
|
@@ -21758,6 +22023,10 @@ var WaterTankModalView = class {
|
|
|
21758
22023
|
this.refreshChart();
|
|
21759
22024
|
});
|
|
21760
22025
|
}
|
|
22026
|
+
const maximizeBtn = this.modal.querySelector("#myio-water-tank-maximize");
|
|
22027
|
+
if (maximizeBtn) {
|
|
22028
|
+
maximizeBtn.addEventListener("click", () => this.handleMaximize());
|
|
22029
|
+
}
|
|
21761
22030
|
this.overlay.addEventListener("click", (e) => {
|
|
21762
22031
|
if (e.target === this.overlay) {
|
|
21763
22032
|
this.config.onClose();
|
|
@@ -21772,12 +22041,20 @@ var WaterTankModalView = class {
|
|
|
21772
22041
|
});
|
|
21773
22042
|
}
|
|
21774
22043
|
/**
|
|
21775
|
-
* Handle date range change
|
|
22044
|
+
* Handle date range change (legacy - kept for compatibility)
|
|
21776
22045
|
*/
|
|
21777
22046
|
handleDateRangeChange() {
|
|
22047
|
+
this.handleApplyParams();
|
|
22048
|
+
}
|
|
22049
|
+
/**
|
|
22050
|
+
* RFC-0107: Handle apply params (date range, aggregation, limit)
|
|
22051
|
+
*/
|
|
22052
|
+
handleApplyParams() {
|
|
21778
22053
|
if (!this.modal) return;
|
|
21779
22054
|
const startInput = this.modal.querySelector("#myio-water-tank-start-date");
|
|
21780
22055
|
const endInput = this.modal.querySelector("#myio-water-tank-end-date");
|
|
22056
|
+
const aggregationSelect = this.modal.querySelector("#myio-water-tank-aggregation");
|
|
22057
|
+
const limitSelect = this.modal.querySelector("#myio-water-tank-limit");
|
|
21781
22058
|
if (startInput && endInput) {
|
|
21782
22059
|
const startTs = new Date(startInput.value).setHours(0, 0, 0, 0);
|
|
21783
22060
|
const endTs = new Date(endInput.value).setHours(23, 59, 59, 999);
|
|
@@ -21785,17 +22062,77 @@ var WaterTankModalView = class {
|
|
|
21785
22062
|
alert("Start date must be before end date");
|
|
21786
22063
|
return;
|
|
21787
22064
|
}
|
|
21788
|
-
|
|
22065
|
+
const aggregation = aggregationSelect?.value || "NONE";
|
|
22066
|
+
const limit = parseInt(limitSelect?.value || "1000", 10);
|
|
22067
|
+
console.log("[WaterTankModalView] Params changed:", {
|
|
21789
22068
|
startTs,
|
|
21790
22069
|
endTs,
|
|
22070
|
+
aggregation,
|
|
22071
|
+
limit,
|
|
21791
22072
|
startDate: new Date(startTs).toISOString(),
|
|
21792
22073
|
endDate: new Date(endTs).toISOString()
|
|
21793
22074
|
});
|
|
21794
|
-
|
|
22075
|
+
this.config.params.startTs = startTs;
|
|
22076
|
+
this.config.params.endTs = endTs;
|
|
22077
|
+
this.config.params.aggregation = aggregation;
|
|
22078
|
+
this.config.params.limit = limit;
|
|
22079
|
+
if (this.config.onParamsChange) {
|
|
22080
|
+
this.config.onParamsChange({ startTs, endTs, aggregation, limit });
|
|
22081
|
+
} else if (this.config.onDateRangeChange) {
|
|
21795
22082
|
this.config.onDateRangeChange(startTs, endTs);
|
|
21796
22083
|
}
|
|
21797
22084
|
}
|
|
21798
22085
|
}
|
|
22086
|
+
/**
|
|
22087
|
+
* RFC-0107: Handle maximize/restore chart
|
|
22088
|
+
*/
|
|
22089
|
+
isMaximized = false;
|
|
22090
|
+
originalModalStyle = "";
|
|
22091
|
+
handleMaximize() {
|
|
22092
|
+
if (!this.modal) return;
|
|
22093
|
+
const chartPanel = this.modal.querySelector("#myio-water-tank-chart-panel");
|
|
22094
|
+
const tankPanel = chartPanel?.previousElementSibling;
|
|
22095
|
+
const maximizeBtn = this.modal.querySelector("#myio-water-tank-maximize");
|
|
22096
|
+
if (!chartPanel) return;
|
|
22097
|
+
if (this.isMaximized) {
|
|
22098
|
+
this.modal.style.cssText = this.originalModalStyle;
|
|
22099
|
+
if (tankPanel) tankPanel.style.display = "";
|
|
22100
|
+
chartPanel.style.cssText = `
|
|
22101
|
+
flex: 1;
|
|
22102
|
+
background: white;
|
|
22103
|
+
border: 1px solid #e0e0e0;
|
|
22104
|
+
border-radius: 12px;
|
|
22105
|
+
padding: 16px;
|
|
22106
|
+
display: flex;
|
|
22107
|
+
flex-direction: column;
|
|
22108
|
+
position: relative;
|
|
22109
|
+
`;
|
|
22110
|
+
if (maximizeBtn) maximizeBtn.textContent = "\u26F6";
|
|
22111
|
+
this.isMaximized = false;
|
|
22112
|
+
} else {
|
|
22113
|
+
this.originalModalStyle = this.modal.style.cssText;
|
|
22114
|
+
this.modal.style.width = "95vw";
|
|
22115
|
+
this.modal.style.height = "90vh";
|
|
22116
|
+
this.modal.style.maxWidth = "95vw";
|
|
22117
|
+
if (tankPanel) tankPanel.style.display = "none";
|
|
22118
|
+
chartPanel.style.cssText = `
|
|
22119
|
+
flex: 1;
|
|
22120
|
+
background: white;
|
|
22121
|
+
border: 1px solid #e0e0e0;
|
|
22122
|
+
border-radius: 12px;
|
|
22123
|
+
padding: 16px;
|
|
22124
|
+
display: flex;
|
|
22125
|
+
flex-direction: column;
|
|
22126
|
+
position: relative;
|
|
22127
|
+
min-height: 100%;
|
|
22128
|
+
`;
|
|
22129
|
+
if (maximizeBtn) maximizeBtn.textContent = "\u26F6";
|
|
22130
|
+
this.isMaximized = true;
|
|
22131
|
+
}
|
|
22132
|
+
requestAnimationFrame(() => {
|
|
22133
|
+
this.renderCanvasChart();
|
|
22134
|
+
});
|
|
22135
|
+
}
|
|
21799
22136
|
handleEscapeKey(e) {
|
|
21800
22137
|
if (e.key === "Escape") {
|
|
21801
22138
|
this.config.onClose();
|
|
@@ -21934,7 +22271,7 @@ var WaterTankModalView = class {
|
|
|
21934
22271
|
}
|
|
21935
22272
|
}
|
|
21936
22273
|
/**
|
|
21937
|
-
* Update data and re-render chart
|
|
22274
|
+
* Update data and re-render chart with new layout
|
|
21938
22275
|
*/
|
|
21939
22276
|
updateData(data) {
|
|
21940
22277
|
this.config.data = data;
|
|
@@ -21942,13 +22279,20 @@ var WaterTankModalView = class {
|
|
|
21942
22279
|
const bodyEl = this.modal.querySelector(".myio-water-tank-modal-body");
|
|
21943
22280
|
if (bodyEl) {
|
|
21944
22281
|
bodyEl.innerHTML = `
|
|
21945
|
-
${this.
|
|
21946
|
-
|
|
21947
|
-
|
|
22282
|
+
${this.renderControlsBar()}
|
|
22283
|
+
<div style="
|
|
22284
|
+
display: flex;
|
|
22285
|
+
gap: 20px;
|
|
22286
|
+
flex: 1;
|
|
22287
|
+
min-height: 400px;
|
|
22288
|
+
">
|
|
22289
|
+
${this.renderTankPanel()}
|
|
22290
|
+
${this.renderChartPanel()}
|
|
22291
|
+
</div>
|
|
21948
22292
|
`;
|
|
21949
22293
|
const applyDatesBtn = this.modal.querySelector("#myio-water-tank-apply-dates");
|
|
21950
22294
|
if (applyDatesBtn) {
|
|
21951
|
-
applyDatesBtn.addEventListener("click", () => this.
|
|
22295
|
+
applyDatesBtn.addEventListener("click", () => this.handleApplyParams());
|
|
21952
22296
|
}
|
|
21953
22297
|
const displayModeSelect = this.modal.querySelector("#myio-water-tank-display-mode");
|
|
21954
22298
|
if (displayModeSelect) {
|
|
@@ -21957,6 +22301,10 @@ var WaterTankModalView = class {
|
|
|
21957
22301
|
this.refreshChart();
|
|
21958
22302
|
});
|
|
21959
22303
|
}
|
|
22304
|
+
const maximizeBtn = this.modal.querySelector("#myio-water-tank-maximize");
|
|
22305
|
+
if (maximizeBtn) {
|
|
22306
|
+
maximizeBtn.addEventListener("click", () => this.handleMaximize());
|
|
22307
|
+
}
|
|
21960
22308
|
requestAnimationFrame(() => {
|
|
21961
22309
|
this.renderCanvasChart();
|
|
21962
22310
|
});
|
|
@@ -22236,7 +22584,8 @@ var WaterTankModal = class {
|
|
|
22236
22584
|
onError: (error) => this.handleError(error),
|
|
22237
22585
|
onClose: () => this.close(),
|
|
22238
22586
|
// Call close() to destroy view and trigger user callback
|
|
22239
|
-
onDateRangeChange: (startTs, endTs) => this.handleDateRangeChange(startTs, endTs)
|
|
22587
|
+
onDateRangeChange: (startTs, endTs) => this.handleDateRangeChange(startTs, endTs),
|
|
22588
|
+
onParamsChange: (params) => this.handleParamsChange(params)
|
|
22240
22589
|
});
|
|
22241
22590
|
this.view.render();
|
|
22242
22591
|
this.view.show();
|
|
@@ -22301,6 +22650,43 @@ var WaterTankModal = class {
|
|
|
22301
22650
|
this.handleError(error);
|
|
22302
22651
|
}
|
|
22303
22652
|
}
|
|
22653
|
+
/**
|
|
22654
|
+
* RFC-0107: Handle params change (date range, aggregation, limit)
|
|
22655
|
+
*/
|
|
22656
|
+
async handleParamsChange(params) {
|
|
22657
|
+
console.log("[WaterTankModal] Params changed:", {
|
|
22658
|
+
startTs: params.startTs,
|
|
22659
|
+
endTs: params.endTs,
|
|
22660
|
+
aggregation: params.aggregation,
|
|
22661
|
+
limit: params.limit,
|
|
22662
|
+
startDate: new Date(params.startTs).toISOString(),
|
|
22663
|
+
endDate: new Date(params.endTs).toISOString()
|
|
22664
|
+
});
|
|
22665
|
+
this.options.startTs = params.startTs;
|
|
22666
|
+
this.options.endTs = params.endTs;
|
|
22667
|
+
this.options.aggregation = params.aggregation;
|
|
22668
|
+
this.options.limit = params.limit;
|
|
22669
|
+
this.context.timeRange.startTs = params.startTs;
|
|
22670
|
+
this.context.timeRange.endTs = params.endTs;
|
|
22671
|
+
try {
|
|
22672
|
+
console.log("[WaterTankModal] Fetching data with new params...");
|
|
22673
|
+
this.data = await this.fetchTelemetryData();
|
|
22674
|
+
if (this.view) {
|
|
22675
|
+
this.view.updateData(this.data);
|
|
22676
|
+
}
|
|
22677
|
+
if (this.options.onDataLoaded) {
|
|
22678
|
+
try {
|
|
22679
|
+
this.options.onDataLoaded(this.data);
|
|
22680
|
+
} catch (callbackError) {
|
|
22681
|
+
console.warn("[WaterTankModal] onDataLoaded callback error:", callbackError);
|
|
22682
|
+
}
|
|
22683
|
+
}
|
|
22684
|
+
console.log("[WaterTankModal] Data refreshed with new params successfully");
|
|
22685
|
+
} catch (error) {
|
|
22686
|
+
console.error("[WaterTankModal] Failed to fetch data with new params:", error);
|
|
22687
|
+
this.handleError(error);
|
|
22688
|
+
}
|
|
22689
|
+
}
|
|
22304
22690
|
/**
|
|
22305
22691
|
* Handle export functionality
|
|
22306
22692
|
*/
|
|
@@ -22422,8 +22808,8 @@ function validateOptions2(options) {
|
|
|
22422
22808
|
}
|
|
22423
22809
|
if (options.currentLevel !== void 0) {
|
|
22424
22810
|
const level = Number(options.currentLevel);
|
|
22425
|
-
if (isNaN(level) || level < 0
|
|
22426
|
-
errors.push("currentLevel must be a number
|
|
22811
|
+
if (isNaN(level) || level < 0) {
|
|
22812
|
+
errors.push("currentLevel must be a non-negative number");
|
|
22427
22813
|
}
|
|
22428
22814
|
}
|
|
22429
22815
|
if (options.limit !== void 0) {
|
|
@@ -28648,13 +29034,31 @@ var DefaultSettingsPersister = class {
|
|
|
28648
29034
|
};
|
|
28649
29035
|
|
|
28650
29036
|
// src/components/premium-modals/settings/SettingsFetcher.ts
|
|
28651
|
-
var DefaultSettingsFetcher = class {
|
|
29037
|
+
var DefaultSettingsFetcher = class _DefaultSettingsFetcher {
|
|
28652
29038
|
jwtToken;
|
|
28653
29039
|
tbBaseUrl;
|
|
29040
|
+
static FETCH_TIMEOUT_MS = 8e3;
|
|
29041
|
+
// 8 second timeout
|
|
28654
29042
|
constructor(jwtToken, apiConfig) {
|
|
28655
29043
|
this.jwtToken = jwtToken;
|
|
28656
29044
|
this.tbBaseUrl = apiConfig?.tbBaseUrl || window.location.origin;
|
|
28657
29045
|
}
|
|
29046
|
+
/**
|
|
29047
|
+
* Fetch with timeout to prevent hanging requests from blocking modal render
|
|
29048
|
+
*/
|
|
29049
|
+
async fetchWithTimeout(url, options, timeoutMs = _DefaultSettingsFetcher.FETCH_TIMEOUT_MS) {
|
|
29050
|
+
const controller = new AbortController();
|
|
29051
|
+
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
29052
|
+
try {
|
|
29053
|
+
const response = await fetch(url, {
|
|
29054
|
+
...options,
|
|
29055
|
+
signal: controller.signal
|
|
29056
|
+
});
|
|
29057
|
+
return response;
|
|
29058
|
+
} finally {
|
|
29059
|
+
clearTimeout(timeoutId);
|
|
29060
|
+
}
|
|
29061
|
+
}
|
|
28658
29062
|
async fetchCurrentSettings(deviceId, jwtToken, scope = "SERVER_SCOPE") {
|
|
28659
29063
|
try {
|
|
28660
29064
|
const [entityResult, attributesResult] = await Promise.allSettled([
|
|
@@ -28688,9 +29092,12 @@ var DefaultSettingsFetcher = class {
|
|
|
28688
29092
|
}
|
|
28689
29093
|
}
|
|
28690
29094
|
async fetchDeviceEntity(deviceId) {
|
|
28691
|
-
const response = await
|
|
28692
|
-
|
|
28693
|
-
|
|
29095
|
+
const response = await this.fetchWithTimeout(
|
|
29096
|
+
`${this.tbBaseUrl}/api/device/${deviceId}`,
|
|
29097
|
+
{
|
|
29098
|
+
headers: { "X-Authorization": `Bearer ${this.jwtToken}` }
|
|
29099
|
+
}
|
|
29100
|
+
);
|
|
28694
29101
|
if (!response.ok) {
|
|
28695
29102
|
throw new Error(
|
|
28696
29103
|
`Failed to fetch device entity: ${response.status} ${response.statusText}`
|
|
@@ -28702,7 +29109,7 @@ var DefaultSettingsFetcher = class {
|
|
|
28702
29109
|
};
|
|
28703
29110
|
}
|
|
28704
29111
|
async fetchDeviceAttributes(deviceId, scope) {
|
|
28705
|
-
const response = await
|
|
29112
|
+
const response = await this.fetchWithTimeout(
|
|
28706
29113
|
`${this.tbBaseUrl}/api/plugins/telemetry/DEVICE/${deviceId}/values/attributes/${scope}`,
|
|
28707
29114
|
{
|
|
28708
29115
|
headers: { "X-Authorization": `Bearer ${this.jwtToken}` }
|
|
@@ -28911,12 +29318,16 @@ var SettingsController = class {
|
|
|
28911
29318
|
const tbBaseUrl = this.params.api?.tbBaseUrl || window.location.origin;
|
|
28912
29319
|
const url = `${tbBaseUrl}/api/plugins/telemetry/CUSTOMER/${customerId}/values/attributes/SERVER_SCOPE?keys=mapInstantaneousPower`;
|
|
28913
29320
|
console.log("[SettingsModal] RFC-0080: Fetching GLOBAL from:", url);
|
|
29321
|
+
const controller = new AbortController();
|
|
29322
|
+
const timeoutId = setTimeout(() => controller.abort(), 8e3);
|
|
28914
29323
|
const response = await fetch(url, {
|
|
28915
29324
|
headers: {
|
|
28916
29325
|
"X-Authorization": `Bearer ${jwtToken}`,
|
|
28917
29326
|
"Content-Type": "application/json"
|
|
28918
|
-
}
|
|
29327
|
+
},
|
|
29328
|
+
signal: controller.signal
|
|
28919
29329
|
});
|
|
29330
|
+
clearTimeout(timeoutId);
|
|
28920
29331
|
if (!response.ok) {
|
|
28921
29332
|
throw new Error(`HTTP ${response.status}`);
|
|
28922
29333
|
}
|