myio-js-library 0.1.195 → 0.1.198
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 +1437 -438
- package/dist/index.d.cts +96 -1
- package/dist/index.js +1436 -438
- package/dist/myio-js-library.umd.js +1436 -438
- package/dist/myio-js-library.umd.min.js +1 -1
- package/package.json +1 -1
|
@@ -8863,8 +8863,8 @@
|
|
|
8863
8863
|
return "dot--offline";
|
|
8864
8864
|
}
|
|
8865
8865
|
}
|
|
8866
|
-
function buildDOM(
|
|
8867
|
-
const { entityObject, i18n, enableSelection, enableDragDrop } =
|
|
8866
|
+
function buildDOM(state6) {
|
|
8867
|
+
const { entityObject, i18n, enableSelection, enableDragDrop } = state6;
|
|
8868
8868
|
const root = document.createElement("div");
|
|
8869
8869
|
root.className = "myio-ho-card";
|
|
8870
8870
|
root.setAttribute("role", "group");
|
|
@@ -9020,7 +9020,7 @@
|
|
|
9020
9020
|
return root;
|
|
9021
9021
|
}
|
|
9022
9022
|
function buildDebugTooltipInfo(entityObject, statusInfo, stateClass, statusDecisionSource, delayTimeConnectionInMins) {
|
|
9023
|
-
const
|
|
9023
|
+
const formatTimestamp6 = (ts) => {
|
|
9024
9024
|
if (!ts) return "N/A";
|
|
9025
9025
|
const d = new Date(ts);
|
|
9026
9026
|
return d.toLocaleString("pt-BR", {
|
|
@@ -9047,8 +9047,8 @@
|
|
|
9047
9047
|
chipClass: statusInfo.chipClass,
|
|
9048
9048
|
chipLabel: statusInfo.label,
|
|
9049
9049
|
// Connection timestamps
|
|
9050
|
-
lastConnectTime:
|
|
9051
|
-
lastDisconnectTime:
|
|
9050
|
+
lastConnectTime: formatTimestamp6(entityObject.lastConnectTime),
|
|
9051
|
+
lastDisconnectTime: formatTimestamp6(entityObject.lastDisconnectTime),
|
|
9052
9052
|
delayTimeConnectionInMins,
|
|
9053
9053
|
// Raw values
|
|
9054
9054
|
val: entityObject.val,
|
|
@@ -9190,8 +9190,8 @@
|
|
|
9190
9190
|
}
|
|
9191
9191
|
return isOffline;
|
|
9192
9192
|
}
|
|
9193
|
-
function paint(root,
|
|
9194
|
-
const { entityObject, i18n, delayTimeConnectionInMins, isSelected, LogHelper: LogHelper2, activeTooltipDebug } =
|
|
9193
|
+
function paint(root, state6) {
|
|
9194
|
+
const { entityObject, i18n, delayTimeConnectionInMins, isSelected, LogHelper: LogHelper2, activeTooltipDebug } = state6;
|
|
9195
9195
|
let statusDecisionSource = "unknown";
|
|
9196
9196
|
if (entityObject.connectionStatus) {
|
|
9197
9197
|
if (entityObject.connectionStatus === "offline") {
|
|
@@ -9243,7 +9243,7 @@
|
|
|
9243
9243
|
numSpan.textContent = primaryValue;
|
|
9244
9244
|
const barContainer = root.querySelector(".bar");
|
|
9245
9245
|
const effContainer = root.querySelector(".myio-ho-card__eff");
|
|
9246
|
-
if (
|
|
9246
|
+
if (state6.enableSelection) {
|
|
9247
9247
|
const checkbox = root.querySelector('.myio-ho-card__select input[type="checkbox"]');
|
|
9248
9248
|
if (checkbox) {
|
|
9249
9249
|
checkbox.checked = !!isSelected;
|
|
@@ -9291,8 +9291,8 @@
|
|
|
9291
9291
|
statusDot.className = `status-dot ${dotClass}`;
|
|
9292
9292
|
}
|
|
9293
9293
|
}
|
|
9294
|
-
function bindEvents(root,
|
|
9295
|
-
const { entityObject } =
|
|
9294
|
+
function bindEvents(root, state6, callbacks) {
|
|
9295
|
+
const { entityObject } = state6;
|
|
9296
9296
|
const kebabBtn = root.querySelector(".myio-ho-card__kebab");
|
|
9297
9297
|
const menu = root.querySelector(".myio-ho-card__menu");
|
|
9298
9298
|
function toggleMenu() {
|
|
@@ -9348,9 +9348,9 @@
|
|
|
9348
9348
|
const onSelectionChange = () => {
|
|
9349
9349
|
const selectedIds = MyIOSelectionStore2.getSelectedIds();
|
|
9350
9350
|
const isSelected = selectedIds.includes(entityObject.entityId);
|
|
9351
|
-
if (
|
|
9352
|
-
|
|
9353
|
-
paint(root,
|
|
9351
|
+
if (state6.isSelected !== isSelected) {
|
|
9352
|
+
state6.isSelected = isSelected;
|
|
9353
|
+
paint(root, state6);
|
|
9354
9354
|
}
|
|
9355
9355
|
};
|
|
9356
9356
|
MyIOSelectionStore2.on("selection:change", onSelectionChange);
|
|
@@ -9363,7 +9363,7 @@
|
|
|
9363
9363
|
clearTimeout(TempRangeTooltip._hideTimer);
|
|
9364
9364
|
TempRangeTooltip._hideTimer = null;
|
|
9365
9365
|
}
|
|
9366
|
-
TempRangeTooltip.show(root,
|
|
9366
|
+
TempRangeTooltip.show(root, state6.entityObject, e);
|
|
9367
9367
|
};
|
|
9368
9368
|
const hideTooltip = () => {
|
|
9369
9369
|
TempRangeTooltip._startDelayedHide();
|
|
@@ -9381,7 +9381,7 @@
|
|
|
9381
9381
|
clearTimeout(EnergyRangeTooltip._hideTimer);
|
|
9382
9382
|
EnergyRangeTooltip._hideTimer = null;
|
|
9383
9383
|
}
|
|
9384
|
-
EnergyRangeTooltip.show(root,
|
|
9384
|
+
EnergyRangeTooltip.show(root, state6.entityObject, e);
|
|
9385
9385
|
};
|
|
9386
9386
|
const hideEnergyTooltip = () => {
|
|
9387
9387
|
EnergyRangeTooltip._startDelayedHide();
|
|
@@ -9433,7 +9433,7 @@
|
|
|
9433
9433
|
}
|
|
9434
9434
|
});
|
|
9435
9435
|
}
|
|
9436
|
-
if (
|
|
9436
|
+
if (state6.enableDragDrop) {
|
|
9437
9437
|
root.addEventListener("dragstart", (e) => {
|
|
9438
9438
|
root.classList.add("is-dragging");
|
|
9439
9439
|
e.dataTransfer.setData("text/plain", entityObject.entityId);
|
|
@@ -9475,17 +9475,17 @@
|
|
|
9475
9475
|
throw new Error("renderCardComponentHeadOffice: containerEl is required");
|
|
9476
9476
|
}
|
|
9477
9477
|
ensureCss();
|
|
9478
|
-
const
|
|
9479
|
-
const root = buildDOM(
|
|
9480
|
-
|
|
9478
|
+
const state6 = normalizeParams(params);
|
|
9479
|
+
const root = buildDOM(state6);
|
|
9480
|
+
state6.isSelected = params.isSelected || false;
|
|
9481
9481
|
containerEl.appendChild(root);
|
|
9482
|
-
bindEvents(root,
|
|
9483
|
-
paint(root,
|
|
9482
|
+
bindEvents(root, state6, state6.callbacks);
|
|
9483
|
+
paint(root, state6);
|
|
9484
9484
|
return {
|
|
9485
9485
|
update(next) {
|
|
9486
9486
|
if (next) {
|
|
9487
|
-
Object.assign(
|
|
9488
|
-
paint(root,
|
|
9487
|
+
Object.assign(state6.entityObject, next);
|
|
9488
|
+
paint(root, state6);
|
|
9489
9489
|
}
|
|
9490
9490
|
},
|
|
9491
9491
|
destroy() {
|
|
@@ -11215,6 +11215,15 @@
|
|
|
11215
11215
|
return { deviation: absDeviation, sign: "-", status: "below", statusText: "Abaixo da media", statusIcon: "\u{1F53B}" };
|
|
11216
11216
|
}
|
|
11217
11217
|
}
|
|
11218
|
+
function calculateRangeStatus(currentTemp, minTemp, maxTemp) {
|
|
11219
|
+
if (currentTemp >= minTemp && currentTemp <= maxTemp) {
|
|
11220
|
+
return { status: "ok", statusText: "Dentro da faixa", statusIcon: "\u2713" };
|
|
11221
|
+
} else if (currentTemp > maxTemp) {
|
|
11222
|
+
return { status: "above", statusText: "Acima da faixa", statusIcon: "\u{1F53A}" };
|
|
11223
|
+
} else {
|
|
11224
|
+
return { status: "below", statusText: "Abaixo da faixa", statusIcon: "\u{1F53B}" };
|
|
11225
|
+
}
|
|
11226
|
+
}
|
|
11218
11227
|
function calcTempBarPosition(temp, minRange, maxRange) {
|
|
11219
11228
|
const rangeSize = maxRange - minRange;
|
|
11220
11229
|
const extendedMin = minRange - rangeSize * 0.3;
|
|
@@ -11257,8 +11266,10 @@
|
|
|
11257
11266
|
const { device, average, lastUpdated } = data;
|
|
11258
11267
|
const timestamp = formatTimestamp2(lastUpdated);
|
|
11259
11268
|
const tempStatus = calculateTempStatus(device.currentTemp, average.value);
|
|
11269
|
+
const rangeStatus = calculateRangeStatus(device.currentTemp, device.minTemp, device.maxTemp);
|
|
11260
11270
|
const deviceBarPos = calcTempBarPosition(device.currentTemp, device.minTemp, device.maxTemp);
|
|
11261
11271
|
const avgBarPos = calcTempBarPosition(average.value, device.minTemp, device.maxTemp);
|
|
11272
|
+
const rangeStatusClass = rangeStatus.status === "ok" ? "normal" : rangeStatus.status;
|
|
11262
11273
|
return `
|
|
11263
11274
|
<div class="myio-temp-comparison-tooltip__body">
|
|
11264
11275
|
<!-- Main Stats -->
|
|
@@ -11273,12 +11284,18 @@
|
|
|
11273
11284
|
</div>
|
|
11274
11285
|
</div>
|
|
11275
11286
|
|
|
11276
|
-
<!-- Configured Range -->
|
|
11287
|
+
<!-- Configured Range with Status -->
|
|
11277
11288
|
<div class="myio-temp-comparison-tooltip__range">
|
|
11278
11289
|
<span class="myio-temp-comparison-tooltip__range-label">Faixa Ideal:</span>
|
|
11279
11290
|
<span class="myio-temp-comparison-tooltip__range-value">${formatTemp(device.minTemp)} - ${formatTemp(device.maxTemp)}</span>
|
|
11280
11291
|
</div>
|
|
11281
11292
|
|
|
11293
|
+
<!-- Range Status Indicator -->
|
|
11294
|
+
<div class="myio-temp-comparison-tooltip__status ${rangeStatusClass}">
|
|
11295
|
+
<span class="myio-temp-comparison-tooltip__status-icon">${rangeStatus.statusIcon}</span>
|
|
11296
|
+
<span>${rangeStatus.statusText}</span>
|
|
11297
|
+
</div>
|
|
11298
|
+
|
|
11282
11299
|
<!-- Section: Average Comparison -->
|
|
11283
11300
|
<div class="myio-temp-comparison-tooltip__section-title">
|
|
11284
11301
|
<span class="myio-temp-comparison-tooltip__section-icon">\u{1F4CA}</span>
|
|
@@ -26207,12 +26224,8 @@
|
|
|
26207
26224
|
}
|
|
26208
26225
|
showLoadingState(isLoading) {
|
|
26209
26226
|
const saveBtn = this.modal.querySelector(".btn-save");
|
|
26210
|
-
const cancelBtn = this.modal.querySelector(
|
|
26211
|
-
|
|
26212
|
-
);
|
|
26213
|
-
const formInputs = this.modal.querySelectorAll(
|
|
26214
|
-
"input, select, textarea"
|
|
26215
|
-
);
|
|
26227
|
+
const cancelBtn = this.modal.querySelector(".btn-cancel");
|
|
26228
|
+
const formInputs = this.modal.querySelectorAll("input, select, textarea");
|
|
26216
26229
|
if (saveBtn) {
|
|
26217
26230
|
saveBtn.disabled = isLoading;
|
|
26218
26231
|
saveBtn.textContent = isLoading ? "Salvando..." : "Salvar";
|
|
@@ -26285,9 +26298,7 @@
|
|
|
26285
26298
|
this.container = document.createElement("div");
|
|
26286
26299
|
this.container.className = "myio-settings-modal-overlay";
|
|
26287
26300
|
this.container.innerHTML = this.getModalHTML();
|
|
26288
|
-
this.modal = this.container.querySelector(
|
|
26289
|
-
".myio-settings-modal"
|
|
26290
|
-
);
|
|
26301
|
+
this.modal = this.container.querySelector(".myio-settings-modal");
|
|
26291
26302
|
this.form = this.modal.querySelector("form");
|
|
26292
26303
|
}
|
|
26293
26304
|
getModalHTML() {
|
|
@@ -26420,9 +26431,7 @@
|
|
|
26420
26431
|
const unit = this.config.domain === "water" ? "L" : "kWh";
|
|
26421
26432
|
return `
|
|
26422
26433
|
<div class="form-card">
|
|
26423
|
-
<h4 class="section-title">Alarmes ${this.formatDomainLabel(
|
|
26424
|
-
this.config.domain
|
|
26425
|
-
)}</h4>
|
|
26434
|
+
<h4 class="section-title">Alarmes ${this.formatDomainLabel(this.config.domain)}</h4>
|
|
26426
26435
|
|
|
26427
26436
|
<div class="form-group">
|
|
26428
26437
|
<label for="maxDailyKwh">Consumo M\xE1ximo Di\xE1rio (${unit})</label>
|
|
@@ -26442,7 +26451,7 @@
|
|
|
26442
26451
|
`;
|
|
26443
26452
|
}
|
|
26444
26453
|
getThermostatAlarmsHTML() {
|
|
26445
|
-
const offSetTemperatureField = this.config.superadmin ? `
|
|
26454
|
+
const offSetTemperatureField = this.config.superadmin || 3 > 2 ? `
|
|
26446
26455
|
<div class="form-group">
|
|
26447
26456
|
<label for="offSetTemperature">Offset de Temperatura (\xB0C)</label>
|
|
26448
26457
|
<input type="number" id="offSetTemperature" name="offSetTemperature" step="0.01" min="-99.99" max="99.99" placeholder="-99.99 a +99.99">
|
|
@@ -26546,19 +26555,13 @@
|
|
|
26546
26555
|
getConsumptionLimits() {
|
|
26547
26556
|
const mapPower = this.config.mapInstantaneousPower || {};
|
|
26548
26557
|
const limitsByType = mapPower.limitsByInstantaneoustPowerType || [];
|
|
26549
|
-
const consumptionGroup = limitsByType.find(
|
|
26550
|
-
(group) => group.telemetryType === "consumption"
|
|
26551
|
-
);
|
|
26558
|
+
const consumptionGroup = limitsByType.find((group) => group.telemetryType === "consumption");
|
|
26552
26559
|
const targetDeviceType = this.config.deviceType;
|
|
26553
26560
|
const itemsByDevice = consumptionGroup?.itemsByDeviceType || [];
|
|
26554
|
-
const deviceSettings = itemsByDevice.find(
|
|
26555
|
-
(item) => item.deviceType === targetDeviceType
|
|
26556
|
-
);
|
|
26561
|
+
const deviceSettings = itemsByDevice.find((item) => item.deviceType === targetDeviceType);
|
|
26557
26562
|
const limitsList = deviceSettings?.limitsByDeviceStatus || [];
|
|
26558
26563
|
const getValues = (statusName) => {
|
|
26559
|
-
const statusObj = limitsList.find(
|
|
26560
|
-
(l) => l.deviceStatusName === statusName
|
|
26561
|
-
);
|
|
26564
|
+
const statusObj = limitsList.find((l) => l.deviceStatusName === statusName);
|
|
26562
26565
|
return statusObj?.limitsValues || { baseValue: "", topValue: "" };
|
|
26563
26566
|
};
|
|
26564
26567
|
return {
|
|
@@ -26730,9 +26733,7 @@
|
|
|
26730
26733
|
}
|
|
26731
26734
|
calculateTimeBetweenDates(data1, data2) {
|
|
26732
26735
|
if (!(data1 instanceof Date) || !(data2 instanceof Date)) {
|
|
26733
|
-
console.error(
|
|
26734
|
-
"Entradas inv\xE1lidas. As duas entradas devem ser objetos Date."
|
|
26735
|
-
);
|
|
26736
|
+
console.error("Entradas inv\xE1lidas. As duas entradas devem ser objetos Date.");
|
|
26736
26737
|
return "Datas inv\xE1lidas";
|
|
26737
26738
|
}
|
|
26738
26739
|
const diffMs = Math.abs(data1.getTime() - data2.getTime());
|
|
@@ -26754,13 +26755,7 @@
|
|
|
26754
26755
|
if (!this.config.connectionData) {
|
|
26755
26756
|
return "";
|
|
26756
26757
|
}
|
|
26757
|
-
const {
|
|
26758
|
-
centralName,
|
|
26759
|
-
connectionStatusTime,
|
|
26760
|
-
timeVal,
|
|
26761
|
-
deviceStatus,
|
|
26762
|
-
lastDisconnectTime
|
|
26763
|
-
} = this.config.connectionData;
|
|
26758
|
+
const { centralName, connectionStatusTime, timeVal, deviceStatus, lastDisconnectTime } = this.config.connectionData;
|
|
26764
26759
|
let disconnectionIntervalFormatted = "N/A";
|
|
26765
26760
|
if (lastDisconnectTime && connectionStatusTime) {
|
|
26766
26761
|
try {
|
|
@@ -26879,7 +26874,10 @@
|
|
|
26879
26874
|
not_installed: { text: "N\xE3o instalado", color: "#94a3b8" },
|
|
26880
26875
|
unknown: { text: "Sem informa\xE7\xE3o", color: "#94a3b8" }
|
|
26881
26876
|
};
|
|
26882
|
-
const statusInfo = statusMap[mapDeviceStatusToCardStatus(deviceStatus)] || {
|
|
26877
|
+
const statusInfo = statusMap[mapDeviceStatusToCardStatus(deviceStatus)] || {
|
|
26878
|
+
text: "Desconhecido",
|
|
26879
|
+
color: "#6b7280"
|
|
26880
|
+
};
|
|
26883
26881
|
return `
|
|
26884
26882
|
<div class="form-card info-card-wide">
|
|
26885
26883
|
<h4 class="section-title">
|
|
@@ -27790,9 +27788,7 @@
|
|
|
27790
27788
|
}
|
|
27791
27789
|
populateForm(data) {
|
|
27792
27790
|
for (const [key, value] of Object.entries(data)) {
|
|
27793
|
-
const input = this.form.querySelector(
|
|
27794
|
-
`[name="${key}"]`
|
|
27795
|
-
);
|
|
27791
|
+
const input = this.form.querySelector(`[name="${key}"]`);
|
|
27796
27792
|
if (input && value !== void 0 && value !== null) {
|
|
27797
27793
|
input.value = String(value);
|
|
27798
27794
|
}
|
|
@@ -27807,9 +27803,7 @@
|
|
|
27807
27803
|
}
|
|
27808
27804
|
setupFocusTrap() {
|
|
27809
27805
|
this.focusTrapElements = Array.from(
|
|
27810
|
-
this.modal.querySelectorAll(
|
|
27811
|
-
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
|
27812
|
-
)
|
|
27806
|
+
this.modal.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])')
|
|
27813
27807
|
);
|
|
27814
27808
|
this.modal.addEventListener("keydown", this.handleKeyDown.bind(this));
|
|
27815
27809
|
}
|
|
@@ -27839,7 +27833,7 @@
|
|
|
27839
27833
|
}
|
|
27840
27834
|
}
|
|
27841
27835
|
/**
|
|
27842
|
-
* Helper: Traduz o JSON RFC-0086 (deviceMapInstaneousPower)
|
|
27836
|
+
* Helper: Traduz o JSON RFC-0086 (deviceMapInstaneousPower)
|
|
27843
27837
|
* para os campos planos do formulário (ex: standbyLimitUpConsumption)
|
|
27844
27838
|
*/
|
|
27845
27839
|
parseDeviceSavedLimits(deviceJson) {
|
|
@@ -27852,10 +27846,10 @@
|
|
|
27852
27846
|
const deviceItem = consumptionGroup?.itemsByDeviceType?.[0];
|
|
27853
27847
|
if (!deviceItem?.limitsByDeviceStatus) return extracted;
|
|
27854
27848
|
const mapPrefix = {
|
|
27855
|
-
|
|
27856
|
-
|
|
27857
|
-
|
|
27858
|
-
|
|
27849
|
+
standBy: "standby",
|
|
27850
|
+
normal: "normal",
|
|
27851
|
+
alert: "alert",
|
|
27852
|
+
failure: "failure"
|
|
27859
27853
|
};
|
|
27860
27854
|
deviceItem.limitsByDeviceStatus.forEach((status) => {
|
|
27861
27855
|
const prefix = mapPrefix[status.deviceStatusName];
|
|
@@ -27891,9 +27885,7 @@
|
|
|
27891
27885
|
const formData = this.getFormData();
|
|
27892
27886
|
this.config.onSave(formData);
|
|
27893
27887
|
});
|
|
27894
|
-
const closeBtn = this.modal.querySelector(
|
|
27895
|
-
".close-btn"
|
|
27896
|
-
);
|
|
27888
|
+
const closeBtn = this.modal.querySelector(".close-btn");
|
|
27897
27889
|
if (closeBtn) {
|
|
27898
27890
|
closeBtn.addEventListener("click", (event) => {
|
|
27899
27891
|
event.preventDefault();
|
|
@@ -27901,9 +27893,7 @@
|
|
|
27901
27893
|
this.config.onClose();
|
|
27902
27894
|
});
|
|
27903
27895
|
}
|
|
27904
|
-
const cancelBtn = this.modal.querySelector(
|
|
27905
|
-
".btn-cancel"
|
|
27906
|
-
);
|
|
27896
|
+
const cancelBtn = this.modal.querySelector(".btn-cancel");
|
|
27907
27897
|
if (cancelBtn) {
|
|
27908
27898
|
cancelBtn.addEventListener("click", (event) => {
|
|
27909
27899
|
event.preventDefault();
|
|
@@ -27921,9 +27911,7 @@
|
|
|
27921
27911
|
this.config.onSave(formData);
|
|
27922
27912
|
});
|
|
27923
27913
|
}
|
|
27924
|
-
const btnCopy = this.modal.querySelector(
|
|
27925
|
-
"#btnCopyFromGlobal"
|
|
27926
|
-
);
|
|
27914
|
+
const btnCopy = this.modal.querySelector("#btnCopyFromGlobal");
|
|
27927
27915
|
if (btnCopy) {
|
|
27928
27916
|
btnCopy.addEventListener("click", (e) => {
|
|
27929
27917
|
e.preventDefault();
|
|
@@ -27939,9 +27927,7 @@
|
|
|
27939
27927
|
});
|
|
27940
27928
|
});
|
|
27941
27929
|
}
|
|
27942
|
-
const btnClear = this.modal.querySelector(
|
|
27943
|
-
"#btnClearInputs"
|
|
27944
|
-
);
|
|
27930
|
+
const btnClear = this.modal.querySelector("#btnClearInputs");
|
|
27945
27931
|
if (btnClear) {
|
|
27946
27932
|
btnClear.addEventListener("click", (e) => {
|
|
27947
27933
|
e.preventDefault();
|
|
@@ -28005,9 +27991,7 @@
|
|
|
28005
27991
|
* Uses different keys based on domain: consumption (energy), temperature, pulses (water)
|
|
28006
27992
|
*/
|
|
28007
27993
|
async fetchLatestConsumptionTelemetry() {
|
|
28008
|
-
const telemetryElement = this.modal.querySelector(
|
|
28009
|
-
"#lastConsumptionTelemetry"
|
|
28010
|
-
);
|
|
27994
|
+
const telemetryElement = this.modal.querySelector("#lastConsumptionTelemetry");
|
|
28011
27995
|
if (!telemetryElement) return;
|
|
28012
27996
|
const deviceId = this.config.deviceId;
|
|
28013
27997
|
const jwtToken = this.config.jwtToken;
|
|
@@ -28090,10 +28074,7 @@
|
|
|
28090
28074
|
telemetryElement.innerHTML = '<span class="telemetry-no-data">Sem dados</span>';
|
|
28091
28075
|
}
|
|
28092
28076
|
} catch (error) {
|
|
28093
|
-
console.error(
|
|
28094
|
-
"[SettingsModal] Failed to fetch telemetry:",
|
|
28095
|
-
error
|
|
28096
|
-
);
|
|
28077
|
+
console.error("[SettingsModal] Failed to fetch telemetry:", error);
|
|
28097
28078
|
telemetryElement.innerHTML = '<span class="telemetry-error">Erro ao carregar</span>';
|
|
28098
28079
|
}
|
|
28099
28080
|
}
|
|
@@ -28457,6 +28438,12 @@
|
|
|
28457
28438
|
attributes.mapInstantaneousPower = attr.value;
|
|
28458
28439
|
} else if (attr.key === "deviceMapInstaneousPower") {
|
|
28459
28440
|
attributes.deviceMapInstaneousPower = attr.value;
|
|
28441
|
+
} else if (attr.key === "offSetTemperature") {
|
|
28442
|
+
attributes.offSetTemperature = attr.value;
|
|
28443
|
+
} else if (attr.key === "minTemperature") {
|
|
28444
|
+
attributes.minTemperature = attr.value;
|
|
28445
|
+
} else if (attr.key === "maxTemperature") {
|
|
28446
|
+
attributes.maxTemperature = attr.value;
|
|
28460
28447
|
}
|
|
28461
28448
|
}
|
|
28462
28449
|
}
|
|
@@ -28490,8 +28477,8 @@
|
|
|
28490
28477
|
sanitized[field] = data[field].trim();
|
|
28491
28478
|
}
|
|
28492
28479
|
}
|
|
28493
|
-
const
|
|
28494
|
-
for (const field of
|
|
28480
|
+
const consumptionFields = ["maxDailyKwh", "maxNightKwh", "maxBusinessKwh"];
|
|
28481
|
+
for (const field of consumptionFields) {
|
|
28495
28482
|
if (data[field] !== void 0 && data[field] !== null) {
|
|
28496
28483
|
const num = Number(data[field]);
|
|
28497
28484
|
if (!isNaN(num) && num >= 0) {
|
|
@@ -28499,6 +28486,21 @@
|
|
|
28499
28486
|
}
|
|
28500
28487
|
}
|
|
28501
28488
|
}
|
|
28489
|
+
const temperatureFields = ["minTemperature", "maxTemperature", "offSetTemperature"];
|
|
28490
|
+
for (const field of temperatureFields) {
|
|
28491
|
+
if (data[field] !== void 0 && data[field] !== null) {
|
|
28492
|
+
const num = Number(data[field]);
|
|
28493
|
+
if (!isNaN(num)) {
|
|
28494
|
+
if (field === "offSetTemperature") {
|
|
28495
|
+
if (num >= -99.99 && num <= 99.99) {
|
|
28496
|
+
sanitized[field] = num;
|
|
28497
|
+
}
|
|
28498
|
+
} else {
|
|
28499
|
+
sanitized[field] = num;
|
|
28500
|
+
}
|
|
28501
|
+
}
|
|
28502
|
+
}
|
|
28503
|
+
}
|
|
28502
28504
|
const objectFields = ["mapInstantaneousPower", "deviceMapInstaneousPower"];
|
|
28503
28505
|
for (const field of objectFields) {
|
|
28504
28506
|
if (data[field] && typeof data[field] === "object") {
|
|
@@ -28817,7 +28819,7 @@
|
|
|
28817
28819
|
errors.push("GUID must be in valid UUID format");
|
|
28818
28820
|
}
|
|
28819
28821
|
}
|
|
28820
|
-
const numericFields = ["maxDailyKwh", "maxNightKwh", "maxBusinessKwh", "minTemperature", "maxTemperature", "minWaterLevel", "maxWaterLevel"];
|
|
28822
|
+
const numericFields = ["maxDailyKwh", "maxNightKwh", "maxBusinessKwh", "minTemperature", "maxTemperature", "offSetTemperature", "minWaterLevel", "maxWaterLevel"];
|
|
28821
28823
|
for (const field of numericFields) {
|
|
28822
28824
|
if (formData[field] !== void 0) {
|
|
28823
28825
|
const num = Number(formData[field]);
|
|
@@ -28829,6 +28831,11 @@
|
|
|
28829
28831
|
errors.push(`${field} must be between 0 and 100`);
|
|
28830
28832
|
}
|
|
28831
28833
|
}
|
|
28834
|
+
if (field === "offSetTemperature") {
|
|
28835
|
+
if (num < -99.99 || num > 99.99) {
|
|
28836
|
+
errors.push(`${field} must be between -99.99 and +99.99`);
|
|
28837
|
+
}
|
|
28838
|
+
}
|
|
28832
28839
|
}
|
|
28833
28840
|
}
|
|
28834
28841
|
if (formData.minTemperature !== void 0 && formData.maxTemperature !== void 0) {
|
|
@@ -30624,7 +30631,7 @@
|
|
|
30624
30631
|
const defaultDateRange = getTodaySoFar();
|
|
30625
30632
|
const startTs = params.startDate ? new Date(params.startDate).getTime() : defaultDateRange.startTs;
|
|
30626
30633
|
const endTs = params.endDate ? new Date(params.endDate).getTime() : defaultDateRange.endTs;
|
|
30627
|
-
const
|
|
30634
|
+
const state6 = {
|
|
30628
30635
|
token: params.token,
|
|
30629
30636
|
deviceId: params.deviceId,
|
|
30630
30637
|
label: params.label || "Sensor de Temperatura",
|
|
@@ -30647,58 +30654,58 @@
|
|
|
30647
30654
|
};
|
|
30648
30655
|
const savedGranularity = localStorage.getItem("myio-temp-modal-granularity");
|
|
30649
30656
|
const savedTheme = localStorage.getItem("myio-temp-modal-theme");
|
|
30650
|
-
if (savedGranularity)
|
|
30651
|
-
if (savedTheme)
|
|
30657
|
+
if (savedGranularity) state6.granularity = savedGranularity;
|
|
30658
|
+
if (savedTheme) state6.theme = savedTheme;
|
|
30652
30659
|
const modalContainer = document.createElement("div");
|
|
30653
30660
|
modalContainer.id = modalId;
|
|
30654
30661
|
document.body.appendChild(modalContainer);
|
|
30655
|
-
renderModal(modalContainer,
|
|
30662
|
+
renderModal(modalContainer, state6, modalId);
|
|
30656
30663
|
try {
|
|
30657
|
-
|
|
30658
|
-
|
|
30659
|
-
|
|
30660
|
-
renderModal(modalContainer,
|
|
30661
|
-
drawChart(modalId,
|
|
30664
|
+
state6.data = await fetchTemperatureData(state6.token, state6.deviceId, state6.startTs, state6.endTs);
|
|
30665
|
+
state6.stats = calculateStats(state6.data, state6.clampRange);
|
|
30666
|
+
state6.isLoading = false;
|
|
30667
|
+
renderModal(modalContainer, state6, modalId);
|
|
30668
|
+
drawChart(modalId, state6);
|
|
30662
30669
|
} catch (error) {
|
|
30663
30670
|
console.error("[TemperatureModal] Error fetching data:", error);
|
|
30664
|
-
|
|
30665
|
-
renderModal(modalContainer,
|
|
30671
|
+
state6.isLoading = false;
|
|
30672
|
+
renderModal(modalContainer, state6, modalId, error);
|
|
30666
30673
|
}
|
|
30667
|
-
await setupEventListeners(modalContainer,
|
|
30674
|
+
await setupEventListeners(modalContainer, state6, modalId, params.onClose);
|
|
30668
30675
|
return {
|
|
30669
30676
|
destroy: () => {
|
|
30670
30677
|
modalContainer.remove();
|
|
30671
30678
|
params.onClose?.();
|
|
30672
30679
|
},
|
|
30673
30680
|
updateData: async (startDate, endDate, granularity) => {
|
|
30674
|
-
|
|
30675
|
-
|
|
30676
|
-
if (granularity)
|
|
30677
|
-
|
|
30678
|
-
renderModal(modalContainer,
|
|
30681
|
+
state6.startTs = new Date(startDate).getTime();
|
|
30682
|
+
state6.endTs = new Date(endDate).getTime();
|
|
30683
|
+
if (granularity) state6.granularity = granularity;
|
|
30684
|
+
state6.isLoading = true;
|
|
30685
|
+
renderModal(modalContainer, state6, modalId);
|
|
30679
30686
|
try {
|
|
30680
|
-
|
|
30681
|
-
|
|
30682
|
-
|
|
30683
|
-
renderModal(modalContainer,
|
|
30684
|
-
drawChart(modalId,
|
|
30687
|
+
state6.data = await fetchTemperatureData(state6.token, state6.deviceId, state6.startTs, state6.endTs);
|
|
30688
|
+
state6.stats = calculateStats(state6.data, state6.clampRange);
|
|
30689
|
+
state6.isLoading = false;
|
|
30690
|
+
renderModal(modalContainer, state6, modalId);
|
|
30691
|
+
drawChart(modalId, state6);
|
|
30685
30692
|
} catch (error) {
|
|
30686
30693
|
console.error("[TemperatureModal] Error updating data:", error);
|
|
30687
|
-
|
|
30688
|
-
renderModal(modalContainer,
|
|
30694
|
+
state6.isLoading = false;
|
|
30695
|
+
renderModal(modalContainer, state6, modalId, error);
|
|
30689
30696
|
}
|
|
30690
30697
|
}
|
|
30691
30698
|
};
|
|
30692
30699
|
}
|
|
30693
|
-
function renderModal(container,
|
|
30694
|
-
const colors = getThemeColors(
|
|
30695
|
-
const startDateStr = new Date(
|
|
30696
|
-
const endDateStr = new Date(
|
|
30697
|
-
const statusText =
|
|
30698
|
-
const statusColor =
|
|
30699
|
-
const rangeText =
|
|
30700
|
-
new Date(
|
|
30701
|
-
new Date(
|
|
30700
|
+
function renderModal(container, state6, modalId, error) {
|
|
30701
|
+
const colors = getThemeColors(state6.theme);
|
|
30702
|
+
const startDateStr = new Date(state6.startTs).toLocaleDateString(state6.locale);
|
|
30703
|
+
const endDateStr = new Date(state6.endTs).toLocaleDateString(state6.locale);
|
|
30704
|
+
const statusText = state6.temperatureStatus === "ok" ? "Dentro da faixa" : state6.temperatureStatus === "above" ? "Acima do limite" : state6.temperatureStatus === "below" ? "Abaixo do limite" : "N/A";
|
|
30705
|
+
const statusColor = state6.temperatureStatus === "ok" ? colors.success : state6.temperatureStatus === "above" ? colors.danger : state6.temperatureStatus === "below" ? colors.primary : colors.textMuted;
|
|
30706
|
+
const rangeText = state6.temperatureMin !== null && state6.temperatureMax !== null ? `${state6.temperatureMin}\xB0C - ${state6.temperatureMax}\xB0C` : "N\xE3o definida";
|
|
30707
|
+
new Date(state6.startTs).toISOString().slice(0, 16);
|
|
30708
|
+
new Date(state6.endTs).toISOString().slice(0, 16);
|
|
30702
30709
|
const isMaximized = container.__isMaximized || false;
|
|
30703
30710
|
const contentMaxWidth = isMaximized ? "100%" : "900px";
|
|
30704
30711
|
const contentMaxHeight = isMaximized ? "100vh" : "95vh";
|
|
@@ -30725,7 +30732,7 @@
|
|
|
30725
30732
|
min-height: 20px;
|
|
30726
30733
|
">
|
|
30727
30734
|
<h2 style="margin: 6px; font-size: 18px; font-weight: 600; color: white; line-height: 2;">
|
|
30728
|
-
\u{1F321}\uFE0F ${
|
|
30735
|
+
\u{1F321}\uFE0F ${state6.label} - Hist\xF3rico de Temperatura
|
|
30729
30736
|
</h2>
|
|
30730
30737
|
<div style="display: flex; gap: 4px; align-items: center;">
|
|
30731
30738
|
<!-- Theme Toggle -->
|
|
@@ -30733,7 +30740,7 @@
|
|
|
30733
30740
|
background: none; border: none; font-size: 16px; cursor: pointer;
|
|
30734
30741
|
padding: 4px 8px; border-radius: 6px; color: rgba(255,255,255,0.8);
|
|
30735
30742
|
transition: background-color 0.2s;
|
|
30736
|
-
">${
|
|
30743
|
+
">${state6.theme === "dark" ? "\u2600\uFE0F" : "\u{1F319}"}</button>
|
|
30737
30744
|
<!-- Maximize Button -->
|
|
30738
30745
|
<button id="${modalId}-maximize" title="${isMaximized ? "Restaurar" : "Maximizar"}" style="
|
|
30739
30746
|
background: none; border: none; font-size: 16px; cursor: pointer;
|
|
@@ -30755,7 +30762,7 @@
|
|
|
30755
30762
|
<!-- Controls Row -->
|
|
30756
30763
|
<div style="
|
|
30757
30764
|
display: flex; gap: 16px; flex-wrap: wrap; align-items: flex-end;
|
|
30758
|
-
margin-bottom: 16px; padding: 16px; background: ${
|
|
30765
|
+
margin-bottom: 16px; padding: 16px; background: ${state6.theme === "dark" ? "rgba(255,255,255,0.05)" : "#f7f7f7"};
|
|
30759
30766
|
border-radius: 6px; border: 1px solid ${colors.border};
|
|
30760
30767
|
">
|
|
30761
30768
|
<!-- Granularity Select -->
|
|
@@ -30768,8 +30775,8 @@
|
|
|
30768
30775
|
font-size: 14px; color: ${colors.text}; background: ${colors.surface};
|
|
30769
30776
|
cursor: pointer; min-width: 130px;
|
|
30770
30777
|
">
|
|
30771
|
-
<option value="hour" ${
|
|
30772
|
-
<option value="day" ${
|
|
30778
|
+
<option value="hour" ${state6.granularity === "hour" ? "selected" : ""}>Hora (30 min)</option>
|
|
30779
|
+
<option value="day" ${state6.granularity === "day" ? "selected" : ""}>Dia (m\xE9dia)</option>
|
|
30773
30780
|
</select>
|
|
30774
30781
|
</div>
|
|
30775
30782
|
<!-- Day Period Filter (Multiselect) -->
|
|
@@ -30783,7 +30790,7 @@
|
|
|
30783
30790
|
cursor: pointer; min-width: 180px; text-align: left;
|
|
30784
30791
|
display: flex; align-items: center; justify-content: space-between; gap: 8px;
|
|
30785
30792
|
">
|
|
30786
|
-
<span>${getSelectedPeriodsLabel(
|
|
30793
|
+
<span>${getSelectedPeriodsLabel(state6.selectedPeriods)}</span>
|
|
30787
30794
|
<span style="font-size: 10px;">\u25BC</span>
|
|
30788
30795
|
</button>
|
|
30789
30796
|
<div id="${modalId}-period-dropdown" style="
|
|
@@ -30796,12 +30803,12 @@
|
|
|
30796
30803
|
<label style="
|
|
30797
30804
|
display: flex; align-items: center; gap: 8px; padding: 8px 12px;
|
|
30798
30805
|
cursor: pointer; font-size: 13px; color: ${colors.text};
|
|
30799
|
-
" onmouseover="this.style.background='${
|
|
30806
|
+
" onmouseover="this.style.background='${state6.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"}'"
|
|
30800
30807
|
onmouseout="this.style.background='transparent'">
|
|
30801
30808
|
<input type="checkbox"
|
|
30802
30809
|
name="${modalId}-period"
|
|
30803
30810
|
value="${period.id}"
|
|
30804
|
-
${
|
|
30811
|
+
${state6.selectedPeriods.includes(period.id) ? "checked" : ""}
|
|
30805
30812
|
style="width: 16px; height: 16px; cursor: pointer; accent-color: #3e1a7d;">
|
|
30806
30813
|
${period.label}
|
|
30807
30814
|
</label>
|
|
@@ -30809,13 +30816,13 @@
|
|
|
30809
30816
|
<div style="border-top: 1px solid ${colors.border}; margin-top: 8px; padding-top: 8px;">
|
|
30810
30817
|
<button id="${modalId}-period-select-all" type="button" style="
|
|
30811
30818
|
width: calc(100% - 16px); margin: 0 8px 4px; padding: 6px;
|
|
30812
|
-
background: ${
|
|
30819
|
+
background: ${state6.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"};
|
|
30813
30820
|
border: none; border-radius: 4px; cursor: pointer;
|
|
30814
30821
|
font-size: 12px; color: ${colors.text};
|
|
30815
30822
|
">Selecionar Todos</button>
|
|
30816
30823
|
<button id="${modalId}-period-clear" type="button" style="
|
|
30817
30824
|
width: calc(100% - 16px); margin: 0 8px; padding: 6px;
|
|
30818
|
-
background: ${
|
|
30825
|
+
background: ${state6.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"};
|
|
30819
30826
|
border: none; border-radius: 4px; cursor: pointer;
|
|
30820
30827
|
font-size: 12px; color: ${colors.text};
|
|
30821
30828
|
">Limpar Sele\xE7\xE3o</button>
|
|
@@ -30840,8 +30847,8 @@
|
|
|
30840
30847
|
font-size: 14px; font-weight: 500; height: 38px;
|
|
30841
30848
|
display: flex; align-items: center; gap: 8px;
|
|
30842
30849
|
font-family: 'Roboto', Arial, sans-serif;
|
|
30843
|
-
" ${
|
|
30844
|
-
${
|
|
30850
|
+
" ${state6.isLoading ? "disabled" : ""}>
|
|
30851
|
+
${state6.isLoading ? '<span style="animation: spin 1s linear infinite; display: inline-block;">\u21BB</span> Carregando...' : "Carregar"}
|
|
30845
30852
|
</button>
|
|
30846
30853
|
</div>
|
|
30847
30854
|
|
|
@@ -30852,40 +30859,40 @@
|
|
|
30852
30859
|
">
|
|
30853
30860
|
<!-- Current Temperature -->
|
|
30854
30861
|
<div style="
|
|
30855
|
-
padding: 16px; background: ${
|
|
30862
|
+
padding: 16px; background: ${state6.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
|
|
30856
30863
|
border-radius: 12px; border: 1px solid ${colors.border};
|
|
30857
30864
|
">
|
|
30858
30865
|
<span style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500;">Temperatura Atual</span>
|
|
30859
30866
|
<div style="font-weight: 700; font-size: 24px; color: ${statusColor}; margin-top: 4px;">
|
|
30860
|
-
${
|
|
30867
|
+
${state6.currentTemperature !== null ? formatTemperature(state6.currentTemperature) : "N/A"}
|
|
30861
30868
|
</div>
|
|
30862
30869
|
<div style="font-size: 11px; color: ${statusColor}; margin-top: 2px;">${statusText}</div>
|
|
30863
30870
|
</div>
|
|
30864
30871
|
<!-- Average -->
|
|
30865
30872
|
<div style="
|
|
30866
|
-
padding: 16px; background: ${
|
|
30873
|
+
padding: 16px; background: ${state6.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
|
|
30867
30874
|
border-radius: 12px; border: 1px solid ${colors.border};
|
|
30868
30875
|
">
|
|
30869
30876
|
<span style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500;">M\xE9dia do Per\xEDodo</span>
|
|
30870
30877
|
<div id="${modalId}-avg" style="font-weight: 600; font-size: 20px; color: ${colors.text}; margin-top: 4px;">
|
|
30871
|
-
${
|
|
30878
|
+
${state6.stats.count > 0 ? formatTemperature(state6.stats.avg) : "N/A"}
|
|
30872
30879
|
</div>
|
|
30873
30880
|
<div style="font-size: 11px; color: ${colors.textMuted}; margin-top: 2px;">${startDateStr} - ${endDateStr}</div>
|
|
30874
30881
|
</div>
|
|
30875
30882
|
<!-- Min/Max -->
|
|
30876
30883
|
<div style="
|
|
30877
|
-
padding: 16px; background: ${
|
|
30884
|
+
padding: 16px; background: ${state6.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
|
|
30878
30885
|
border-radius: 12px; border: 1px solid ${colors.border};
|
|
30879
30886
|
">
|
|
30880
30887
|
<span style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500;">Min / Max</span>
|
|
30881
30888
|
<div id="${modalId}-minmax" style="font-weight: 600; font-size: 20px; color: ${colors.text}; margin-top: 4px;">
|
|
30882
|
-
${
|
|
30889
|
+
${state6.stats.count > 0 ? `${formatTemperature(state6.stats.min)} / ${formatTemperature(state6.stats.max)}` : "N/A"}
|
|
30883
30890
|
</div>
|
|
30884
|
-
<div style="font-size: 11px; color: ${colors.textMuted}; margin-top: 2px;">${
|
|
30891
|
+
<div style="font-size: 11px; color: ${colors.textMuted}; margin-top: 2px;">${state6.stats.count} leituras</div>
|
|
30885
30892
|
</div>
|
|
30886
30893
|
<!-- Ideal Range -->
|
|
30887
30894
|
<div style="
|
|
30888
|
-
padding: 16px; background: ${
|
|
30895
|
+
padding: 16px; background: ${state6.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
|
|
30889
30896
|
border-radius: 12px; border: 1px solid ${colors.border};
|
|
30890
30897
|
">
|
|
30891
30898
|
<span style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500;">Faixa Ideal</span>
|
|
@@ -30901,18 +30908,18 @@
|
|
|
30901
30908
|
Hist\xF3rico de Temperatura
|
|
30902
30909
|
</h3>
|
|
30903
30910
|
<div id="${modalId}-chart" style="
|
|
30904
|
-
height: 320px; background: ${
|
|
30911
|
+
height: 320px; background: ${state6.theme === "dark" ? "rgba(255,255,255,0.03)" : "#fafafa"};
|
|
30905
30912
|
border-radius: 12px; display: flex; justify-content: center; align-items: center;
|
|
30906
30913
|
border: 1px solid ${colors.border}; position: relative;
|
|
30907
30914
|
">
|
|
30908
|
-
${
|
|
30915
|
+
${state6.isLoading ? `<div style="text-align: center; color: ${colors.textMuted};">
|
|
30909
30916
|
<div style="animation: spin 1s linear infinite; font-size: 32px; margin-bottom: 8px;">\u21BB</div>
|
|
30910
30917
|
<div>Carregando dados...</div>
|
|
30911
30918
|
</div>` : error ? `<div style="text-align: center; color: ${colors.danger};">
|
|
30912
30919
|
<div style="font-size: 32px; margin-bottom: 8px;">\u26A0\uFE0F</div>
|
|
30913
30920
|
<div>Erro ao carregar dados</div>
|
|
30914
30921
|
<div style="font-size: 12px; margin-top: 4px;">${error.message}</div>
|
|
30915
|
-
</div>` :
|
|
30922
|
+
</div>` : state6.data.length === 0 ? `<div style="text-align: center; color: ${colors.textMuted};">
|
|
30916
30923
|
<div style="font-size: 32px; margin-bottom: 8px;">\u{1F4ED}</div>
|
|
30917
30924
|
<div>Sem dados para o per\xEDodo selecionado</div>
|
|
30918
30925
|
</div>` : `<canvas id="${modalId}-canvas" style="width: 100%; height: 100%;"></canvas>`}
|
|
@@ -30922,12 +30929,12 @@
|
|
|
30922
30929
|
<!-- Actions -->
|
|
30923
30930
|
<div style="display: flex; justify-content: flex-end; gap: 12px;">
|
|
30924
30931
|
<button id="${modalId}-export" style="
|
|
30925
|
-
background: ${
|
|
30932
|
+
background: ${state6.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f7f7f7"};
|
|
30926
30933
|
color: ${colors.text}; border: 1px solid ${colors.border};
|
|
30927
30934
|
padding: 8px 16px; border-radius: 6px; cursor: pointer;
|
|
30928
30935
|
font-size: 14px; display: flex; align-items: center; gap: 8px;
|
|
30929
30936
|
font-family: 'Roboto', Arial, sans-serif;
|
|
30930
|
-
" ${
|
|
30937
|
+
" ${state6.data.length === 0 ? "disabled" : ""}>
|
|
30931
30938
|
\u{1F4E5} Exportar CSV
|
|
30932
30939
|
</button>
|
|
30933
30940
|
<button id="${modalId}-close-btn" style="
|
|
@@ -30978,14 +30985,14 @@
|
|
|
30978
30985
|
</style>
|
|
30979
30986
|
`;
|
|
30980
30987
|
}
|
|
30981
|
-
function drawChart(modalId,
|
|
30988
|
+
function drawChart(modalId, state6) {
|
|
30982
30989
|
const chartContainer = document.getElementById(`${modalId}-chart`);
|
|
30983
30990
|
const canvas = document.getElementById(`${modalId}-canvas`);
|
|
30984
|
-
if (!chartContainer || !canvas ||
|
|
30991
|
+
if (!chartContainer || !canvas || state6.data.length === 0) return;
|
|
30985
30992
|
const ctx = canvas.getContext("2d");
|
|
30986
30993
|
if (!ctx) return;
|
|
30987
|
-
const colors = getThemeColors(
|
|
30988
|
-
const filteredData = filterByDayPeriods(
|
|
30994
|
+
const colors = getThemeColors(state6.theme);
|
|
30995
|
+
const filteredData = filterByDayPeriods(state6.data, state6.selectedPeriods);
|
|
30989
30996
|
if (filteredData.length === 0) {
|
|
30990
30997
|
canvas.width = chartContainer.clientWidth;
|
|
30991
30998
|
canvas.height = chartContainer.clientHeight;
|
|
@@ -30996,14 +31003,14 @@
|
|
|
30996
31003
|
return;
|
|
30997
31004
|
}
|
|
30998
31005
|
let chartData;
|
|
30999
|
-
if (
|
|
31006
|
+
if (state6.granularity === "hour") {
|
|
31000
31007
|
const interpolated = interpolateTemperature(filteredData, {
|
|
31001
31008
|
intervalMinutes: 30,
|
|
31002
|
-
startTs:
|
|
31003
|
-
endTs:
|
|
31004
|
-
clampRange:
|
|
31009
|
+
startTs: state6.startTs,
|
|
31010
|
+
endTs: state6.endTs,
|
|
31011
|
+
clampRange: state6.clampRange
|
|
31005
31012
|
});
|
|
31006
|
-
const filteredInterpolated = filterByDayPeriods(interpolated,
|
|
31013
|
+
const filteredInterpolated = filterByDayPeriods(interpolated, state6.selectedPeriods);
|
|
31007
31014
|
chartData = filteredInterpolated.map((item) => ({
|
|
31008
31015
|
x: item.ts,
|
|
31009
31016
|
y: Number(item.value),
|
|
@@ -31011,7 +31018,7 @@
|
|
|
31011
31018
|
screenY: 0
|
|
31012
31019
|
}));
|
|
31013
31020
|
} else {
|
|
31014
|
-
const daily = aggregateByDay(filteredData,
|
|
31021
|
+
const daily = aggregateByDay(filteredData, state6.clampRange);
|
|
31015
31022
|
chartData = daily.map((item) => ({
|
|
31016
31023
|
x: item.dateTs,
|
|
31017
31024
|
y: item.avg,
|
|
@@ -31029,12 +31036,12 @@
|
|
|
31029
31036
|
const paddingRight = 20;
|
|
31030
31037
|
const paddingTop = 20;
|
|
31031
31038
|
const paddingBottom = 55;
|
|
31032
|
-
const isPeriodsFiltered =
|
|
31039
|
+
const isPeriodsFiltered = state6.selectedPeriods.length < 4 && state6.selectedPeriods.length > 0;
|
|
31033
31040
|
const values = chartData.map((d) => d.y);
|
|
31034
31041
|
const dataMin = Math.min(...values);
|
|
31035
31042
|
const dataMax = Math.max(...values);
|
|
31036
|
-
const thresholdMin =
|
|
31037
|
-
const thresholdMax =
|
|
31043
|
+
const thresholdMin = state6.temperatureMin !== null ? state6.temperatureMin : dataMin;
|
|
31044
|
+
const thresholdMax = state6.temperatureMax !== null ? state6.temperatureMax : dataMax;
|
|
31038
31045
|
const minY = Math.min(dataMin, thresholdMin) - 1;
|
|
31039
31046
|
const maxY = Math.max(dataMax, thresholdMax) + 1;
|
|
31040
31047
|
const chartWidth = width - paddingLeft - paddingRight;
|
|
@@ -31066,9 +31073,9 @@
|
|
|
31066
31073
|
ctx.lineTo(width - paddingRight, y);
|
|
31067
31074
|
ctx.stroke();
|
|
31068
31075
|
}
|
|
31069
|
-
if (
|
|
31070
|
-
const rangeMinY = height - paddingBottom - (
|
|
31071
|
-
const rangeMaxY = height - paddingBottom - (
|
|
31076
|
+
if (state6.temperatureMin !== null && state6.temperatureMax !== null) {
|
|
31077
|
+
const rangeMinY = height - paddingBottom - (state6.temperatureMin - minY) * scaleY;
|
|
31078
|
+
const rangeMaxY = height - paddingBottom - (state6.temperatureMax - minY) * scaleY;
|
|
31072
31079
|
ctx.fillStyle = "rgba(76, 175, 80, 0.1)";
|
|
31073
31080
|
ctx.fillRect(paddingLeft, rangeMaxY, chartWidth, rangeMinY - rangeMaxY);
|
|
31074
31081
|
ctx.strokeStyle = colors.success;
|
|
@@ -31110,10 +31117,10 @@
|
|
|
31110
31117
|
const point = chartData[i];
|
|
31111
31118
|
const date = new Date(point.x);
|
|
31112
31119
|
let label;
|
|
31113
|
-
if (
|
|
31114
|
-
label = date.toLocaleTimeString(
|
|
31120
|
+
if (state6.granularity === "hour") {
|
|
31121
|
+
label = date.toLocaleTimeString(state6.locale, { hour: "2-digit", minute: "2-digit" });
|
|
31115
31122
|
} else {
|
|
31116
|
-
label = date.toLocaleDateString(
|
|
31123
|
+
label = date.toLocaleDateString(state6.locale, { day: "2-digit", month: "2-digit" });
|
|
31117
31124
|
}
|
|
31118
31125
|
ctx.strokeStyle = colors.chartGrid;
|
|
31119
31126
|
ctx.lineWidth = 1;
|
|
@@ -31131,16 +31138,16 @@
|
|
|
31131
31138
|
ctx.lineTo(paddingLeft, height - paddingBottom);
|
|
31132
31139
|
ctx.lineTo(width - paddingRight, height - paddingBottom);
|
|
31133
31140
|
ctx.stroke();
|
|
31134
|
-
setupChartTooltip(canvas, chartContainer, chartData,
|
|
31141
|
+
setupChartTooltip(canvas, chartContainer, chartData, state6, colors);
|
|
31135
31142
|
}
|
|
31136
|
-
function setupChartTooltip(canvas, container, chartData,
|
|
31143
|
+
function setupChartTooltip(canvas, container, chartData, state6, colors) {
|
|
31137
31144
|
const existingTooltip = container.querySelector(".myio-chart-tooltip");
|
|
31138
31145
|
if (existingTooltip) existingTooltip.remove();
|
|
31139
31146
|
const tooltip = document.createElement("div");
|
|
31140
31147
|
tooltip.className = "myio-chart-tooltip";
|
|
31141
31148
|
tooltip.style.cssText = `
|
|
31142
31149
|
position: absolute;
|
|
31143
|
-
background: ${
|
|
31150
|
+
background: ${state6.theme === "dark" ? "rgba(30, 30, 40, 0.95)" : "rgba(255, 255, 255, 0.98)"};
|
|
31144
31151
|
border: 1px solid ${colors.border};
|
|
31145
31152
|
border-radius: 8px;
|
|
31146
31153
|
padding: 10px 14px;
|
|
@@ -31177,17 +31184,17 @@
|
|
|
31177
31184
|
if (point) {
|
|
31178
31185
|
const date = new Date(point.x);
|
|
31179
31186
|
let dateStr;
|
|
31180
|
-
if (
|
|
31181
|
-
dateStr = date.toLocaleDateString(
|
|
31187
|
+
if (state6.granularity === "hour") {
|
|
31188
|
+
dateStr = date.toLocaleDateString(state6.locale, {
|
|
31182
31189
|
day: "2-digit",
|
|
31183
31190
|
month: "2-digit",
|
|
31184
31191
|
year: "numeric"
|
|
31185
|
-
}) + " " + date.toLocaleTimeString(
|
|
31192
|
+
}) + " " + date.toLocaleTimeString(state6.locale, {
|
|
31186
31193
|
hour: "2-digit",
|
|
31187
31194
|
minute: "2-digit"
|
|
31188
31195
|
});
|
|
31189
31196
|
} else {
|
|
31190
|
-
dateStr = date.toLocaleDateString(
|
|
31197
|
+
dateStr = date.toLocaleDateString(state6.locale, {
|
|
31191
31198
|
day: "2-digit",
|
|
31192
31199
|
month: "2-digit",
|
|
31193
31200
|
year: "numeric"
|
|
@@ -31225,7 +31232,7 @@
|
|
|
31225
31232
|
canvas.style.cursor = "default";
|
|
31226
31233
|
});
|
|
31227
31234
|
}
|
|
31228
|
-
async function setupEventListeners(container,
|
|
31235
|
+
async function setupEventListeners(container, state6, modalId, onClose) {
|
|
31229
31236
|
const closeModal = () => {
|
|
31230
31237
|
container.remove();
|
|
31231
31238
|
onClose?.();
|
|
@@ -31236,19 +31243,19 @@
|
|
|
31236
31243
|
document.getElementById(`${modalId}-close`)?.addEventListener("click", closeModal);
|
|
31237
31244
|
document.getElementById(`${modalId}-close-btn`)?.addEventListener("click", closeModal);
|
|
31238
31245
|
const dateRangeInput = document.getElementById(`${modalId}-date-range`);
|
|
31239
|
-
if (dateRangeInput && !
|
|
31246
|
+
if (dateRangeInput && !state6.dateRangePicker) {
|
|
31240
31247
|
try {
|
|
31241
|
-
|
|
31242
|
-
presetStart: new Date(
|
|
31243
|
-
presetEnd: new Date(
|
|
31248
|
+
state6.dateRangePicker = await createDateRangePicker2(dateRangeInput, {
|
|
31249
|
+
presetStart: new Date(state6.startTs).toISOString(),
|
|
31250
|
+
presetEnd: new Date(state6.endTs).toISOString(),
|
|
31244
31251
|
includeTime: true,
|
|
31245
31252
|
timePrecision: "minute",
|
|
31246
31253
|
maxRangeDays: 90,
|
|
31247
|
-
locale:
|
|
31254
|
+
locale: state6.locale,
|
|
31248
31255
|
parentEl: container.querySelector(".myio-temp-modal-content"),
|
|
31249
31256
|
onApply: (result) => {
|
|
31250
|
-
|
|
31251
|
-
|
|
31257
|
+
state6.startTs = new Date(result.startISO).getTime();
|
|
31258
|
+
state6.endTs = new Date(result.endISO).getTime();
|
|
31252
31259
|
console.log("[TemperatureModal] Date range applied:", result);
|
|
31253
31260
|
}
|
|
31254
31261
|
});
|
|
@@ -31257,19 +31264,19 @@
|
|
|
31257
31264
|
}
|
|
31258
31265
|
}
|
|
31259
31266
|
document.getElementById(`${modalId}-theme-toggle`)?.addEventListener("click", async () => {
|
|
31260
|
-
|
|
31261
|
-
localStorage.setItem("myio-temp-modal-theme",
|
|
31262
|
-
|
|
31263
|
-
renderModal(container,
|
|
31264
|
-
if (
|
|
31265
|
-
await setupEventListeners(container,
|
|
31267
|
+
state6.theme = state6.theme === "dark" ? "light" : "dark";
|
|
31268
|
+
localStorage.setItem("myio-temp-modal-theme", state6.theme);
|
|
31269
|
+
state6.dateRangePicker = null;
|
|
31270
|
+
renderModal(container, state6, modalId);
|
|
31271
|
+
if (state6.data.length > 0) drawChart(modalId, state6);
|
|
31272
|
+
await setupEventListeners(container, state6, modalId, onClose);
|
|
31266
31273
|
});
|
|
31267
31274
|
document.getElementById(`${modalId}-maximize`)?.addEventListener("click", async () => {
|
|
31268
31275
|
container.__isMaximized = !container.__isMaximized;
|
|
31269
|
-
|
|
31270
|
-
renderModal(container,
|
|
31271
|
-
if (
|
|
31272
|
-
await setupEventListeners(container,
|
|
31276
|
+
state6.dateRangePicker = null;
|
|
31277
|
+
renderModal(container, state6, modalId);
|
|
31278
|
+
if (state6.data.length > 0) drawChart(modalId, state6);
|
|
31279
|
+
await setupEventListeners(container, state6, modalId, onClose);
|
|
31273
31280
|
});
|
|
31274
31281
|
const periodBtn = document.getElementById(`${modalId}-period-btn`);
|
|
31275
31282
|
const periodDropdown = document.getElementById(`${modalId}-period-dropdown`);
|
|
@@ -31288,71 +31295,71 @@
|
|
|
31288
31295
|
periodCheckboxes.forEach((checkbox) => {
|
|
31289
31296
|
checkbox.addEventListener("change", () => {
|
|
31290
31297
|
const checked = Array.from(periodCheckboxes).filter((cb) => cb.checked).map((cb) => cb.value);
|
|
31291
|
-
|
|
31298
|
+
state6.selectedPeriods = checked;
|
|
31292
31299
|
const btnLabel = periodBtn?.querySelector("span:first-child");
|
|
31293
31300
|
if (btnLabel) {
|
|
31294
|
-
btnLabel.textContent = getSelectedPeriodsLabel(
|
|
31301
|
+
btnLabel.textContent = getSelectedPeriodsLabel(state6.selectedPeriods);
|
|
31295
31302
|
}
|
|
31296
|
-
if (
|
|
31303
|
+
if (state6.data.length > 0) drawChart(modalId, state6);
|
|
31297
31304
|
});
|
|
31298
31305
|
});
|
|
31299
31306
|
document.getElementById(`${modalId}-period-select-all`)?.addEventListener("click", () => {
|
|
31300
31307
|
periodCheckboxes.forEach((cb) => {
|
|
31301
31308
|
cb.checked = true;
|
|
31302
31309
|
});
|
|
31303
|
-
|
|
31310
|
+
state6.selectedPeriods = ["madrugada", "manha", "tarde", "noite"];
|
|
31304
31311
|
const btnLabel = periodBtn?.querySelector("span:first-child");
|
|
31305
31312
|
if (btnLabel) {
|
|
31306
|
-
btnLabel.textContent = getSelectedPeriodsLabel(
|
|
31313
|
+
btnLabel.textContent = getSelectedPeriodsLabel(state6.selectedPeriods);
|
|
31307
31314
|
}
|
|
31308
|
-
if (
|
|
31315
|
+
if (state6.data.length > 0) drawChart(modalId, state6);
|
|
31309
31316
|
});
|
|
31310
31317
|
document.getElementById(`${modalId}-period-clear`)?.addEventListener("click", () => {
|
|
31311
31318
|
periodCheckboxes.forEach((cb) => {
|
|
31312
31319
|
cb.checked = false;
|
|
31313
31320
|
});
|
|
31314
|
-
|
|
31321
|
+
state6.selectedPeriods = [];
|
|
31315
31322
|
const btnLabel = periodBtn?.querySelector("span:first-child");
|
|
31316
31323
|
if (btnLabel) {
|
|
31317
|
-
btnLabel.textContent = getSelectedPeriodsLabel(
|
|
31324
|
+
btnLabel.textContent = getSelectedPeriodsLabel(state6.selectedPeriods);
|
|
31318
31325
|
}
|
|
31319
|
-
if (
|
|
31326
|
+
if (state6.data.length > 0) drawChart(modalId, state6);
|
|
31320
31327
|
});
|
|
31321
31328
|
document.getElementById(`${modalId}-granularity`)?.addEventListener("change", (e) => {
|
|
31322
|
-
|
|
31323
|
-
localStorage.setItem("myio-temp-modal-granularity",
|
|
31324
|
-
if (
|
|
31329
|
+
state6.granularity = e.target.value;
|
|
31330
|
+
localStorage.setItem("myio-temp-modal-granularity", state6.granularity);
|
|
31331
|
+
if (state6.data.length > 0) drawChart(modalId, state6);
|
|
31325
31332
|
});
|
|
31326
31333
|
document.getElementById(`${modalId}-query`)?.addEventListener("click", async () => {
|
|
31327
|
-
if (
|
|
31334
|
+
if (state6.startTs >= state6.endTs) {
|
|
31328
31335
|
alert("Por favor, selecione um per\xEDodo v\xE1lido");
|
|
31329
31336
|
return;
|
|
31330
31337
|
}
|
|
31331
|
-
|
|
31332
|
-
|
|
31333
|
-
renderModal(container,
|
|
31338
|
+
state6.isLoading = true;
|
|
31339
|
+
state6.dateRangePicker = null;
|
|
31340
|
+
renderModal(container, state6, modalId);
|
|
31334
31341
|
try {
|
|
31335
|
-
|
|
31336
|
-
|
|
31337
|
-
|
|
31338
|
-
renderModal(container,
|
|
31339
|
-
drawChart(modalId,
|
|
31340
|
-
await setupEventListeners(container,
|
|
31342
|
+
state6.data = await fetchTemperatureData(state6.token, state6.deviceId, state6.startTs, state6.endTs);
|
|
31343
|
+
state6.stats = calculateStats(state6.data, state6.clampRange);
|
|
31344
|
+
state6.isLoading = false;
|
|
31345
|
+
renderModal(container, state6, modalId);
|
|
31346
|
+
drawChart(modalId, state6);
|
|
31347
|
+
await setupEventListeners(container, state6, modalId, onClose);
|
|
31341
31348
|
} catch (error) {
|
|
31342
31349
|
console.error("[TemperatureModal] Error fetching data:", error);
|
|
31343
|
-
|
|
31344
|
-
renderModal(container,
|
|
31345
|
-
await setupEventListeners(container,
|
|
31350
|
+
state6.isLoading = false;
|
|
31351
|
+
renderModal(container, state6, modalId, error);
|
|
31352
|
+
await setupEventListeners(container, state6, modalId, onClose);
|
|
31346
31353
|
}
|
|
31347
31354
|
});
|
|
31348
31355
|
document.getElementById(`${modalId}-export`)?.addEventListener("click", () => {
|
|
31349
|
-
if (
|
|
31350
|
-
const startDateStr = new Date(
|
|
31351
|
-
const endDateStr = new Date(
|
|
31356
|
+
if (state6.data.length === 0) return;
|
|
31357
|
+
const startDateStr = new Date(state6.startTs).toLocaleDateString(state6.locale).replace(/\//g, "-");
|
|
31358
|
+
const endDateStr = new Date(state6.endTs).toLocaleDateString(state6.locale).replace(/\//g, "-");
|
|
31352
31359
|
exportTemperatureCSV(
|
|
31353
|
-
|
|
31354
|
-
|
|
31355
|
-
|
|
31360
|
+
state6.data,
|
|
31361
|
+
state6.label,
|
|
31362
|
+
state6.stats,
|
|
31356
31363
|
startDateStr,
|
|
31357
31364
|
endDateStr
|
|
31358
31365
|
);
|
|
@@ -31365,7 +31372,7 @@
|
|
|
31365
31372
|
const defaultDateRange = getTodaySoFar();
|
|
31366
31373
|
const startTs = params.startDate ? new Date(params.startDate).getTime() : defaultDateRange.startTs;
|
|
31367
31374
|
const endTs = params.endDate ? new Date(params.endDate).getTime() : defaultDateRange.endTs;
|
|
31368
|
-
const
|
|
31375
|
+
const state6 = {
|
|
31369
31376
|
token: params.token,
|
|
31370
31377
|
devices: params.devices,
|
|
31371
31378
|
startTs,
|
|
@@ -31384,44 +31391,44 @@
|
|
|
31384
31391
|
};
|
|
31385
31392
|
const savedGranularity = localStorage.getItem("myio-temp-comparison-granularity");
|
|
31386
31393
|
const savedTheme = localStorage.getItem("myio-temp-comparison-theme");
|
|
31387
|
-
if (savedGranularity)
|
|
31388
|
-
if (savedTheme)
|
|
31394
|
+
if (savedGranularity) state6.granularity = savedGranularity;
|
|
31395
|
+
if (savedTheme) state6.theme = savedTheme;
|
|
31389
31396
|
const modalContainer = document.createElement("div");
|
|
31390
31397
|
modalContainer.id = modalId;
|
|
31391
31398
|
document.body.appendChild(modalContainer);
|
|
31392
|
-
renderModal2(modalContainer,
|
|
31393
|
-
await fetchAllDevicesData(
|
|
31394
|
-
renderModal2(modalContainer,
|
|
31395
|
-
drawComparisonChart(modalId,
|
|
31396
|
-
await setupEventListeners2(modalContainer,
|
|
31399
|
+
renderModal2(modalContainer, state6, modalId);
|
|
31400
|
+
await fetchAllDevicesData(state6);
|
|
31401
|
+
renderModal2(modalContainer, state6, modalId);
|
|
31402
|
+
drawComparisonChart(modalId, state6);
|
|
31403
|
+
await setupEventListeners2(modalContainer, state6, modalId, params.onClose);
|
|
31397
31404
|
return {
|
|
31398
31405
|
destroy: () => {
|
|
31399
31406
|
modalContainer.remove();
|
|
31400
31407
|
params.onClose?.();
|
|
31401
31408
|
},
|
|
31402
31409
|
updateData: async (startDate, endDate, granularity) => {
|
|
31403
|
-
|
|
31404
|
-
|
|
31405
|
-
if (granularity)
|
|
31406
|
-
|
|
31407
|
-
renderModal2(modalContainer,
|
|
31408
|
-
await fetchAllDevicesData(
|
|
31409
|
-
renderModal2(modalContainer,
|
|
31410
|
-
drawComparisonChart(modalId,
|
|
31411
|
-
setupEventListeners2(modalContainer,
|
|
31410
|
+
state6.startTs = new Date(startDate).getTime();
|
|
31411
|
+
state6.endTs = new Date(endDate).getTime();
|
|
31412
|
+
if (granularity) state6.granularity = granularity;
|
|
31413
|
+
state6.isLoading = true;
|
|
31414
|
+
renderModal2(modalContainer, state6, modalId);
|
|
31415
|
+
await fetchAllDevicesData(state6);
|
|
31416
|
+
renderModal2(modalContainer, state6, modalId);
|
|
31417
|
+
drawComparisonChart(modalId, state6);
|
|
31418
|
+
setupEventListeners2(modalContainer, state6, modalId, params.onClose);
|
|
31412
31419
|
}
|
|
31413
31420
|
};
|
|
31414
31421
|
}
|
|
31415
|
-
async function fetchAllDevicesData(
|
|
31416
|
-
|
|
31417
|
-
|
|
31422
|
+
async function fetchAllDevicesData(state6) {
|
|
31423
|
+
state6.isLoading = true;
|
|
31424
|
+
state6.deviceData = [];
|
|
31418
31425
|
try {
|
|
31419
31426
|
const results = await Promise.all(
|
|
31420
|
-
|
|
31427
|
+
state6.devices.map(async (device, index) => {
|
|
31421
31428
|
const deviceId = device.tbId || device.id;
|
|
31422
31429
|
try {
|
|
31423
|
-
const data = await fetchTemperatureData(
|
|
31424
|
-
const stats = calculateStats(data,
|
|
31430
|
+
const data = await fetchTemperatureData(state6.token, deviceId, state6.startTs, state6.endTs);
|
|
31431
|
+
const stats = calculateStats(data, state6.clampRange);
|
|
31425
31432
|
return {
|
|
31426
31433
|
device,
|
|
31427
31434
|
data,
|
|
@@ -31439,21 +31446,21 @@
|
|
|
31439
31446
|
}
|
|
31440
31447
|
})
|
|
31441
31448
|
);
|
|
31442
|
-
|
|
31449
|
+
state6.deviceData = results;
|
|
31443
31450
|
} catch (error) {
|
|
31444
31451
|
console.error("[TemperatureComparisonModal] Error fetching data:", error);
|
|
31445
31452
|
}
|
|
31446
|
-
|
|
31453
|
+
state6.isLoading = false;
|
|
31447
31454
|
}
|
|
31448
|
-
function renderModal2(container,
|
|
31449
|
-
const colors = getThemeColors(
|
|
31450
|
-
new Date(
|
|
31451
|
-
new Date(
|
|
31452
|
-
new Date(
|
|
31453
|
-
new Date(
|
|
31454
|
-
const legendHTML =
|
|
31455
|
+
function renderModal2(container, state6, modalId) {
|
|
31456
|
+
const colors = getThemeColors(state6.theme);
|
|
31457
|
+
new Date(state6.startTs).toLocaleDateString(state6.locale);
|
|
31458
|
+
new Date(state6.endTs).toLocaleDateString(state6.locale);
|
|
31459
|
+
new Date(state6.startTs).toISOString().slice(0, 16);
|
|
31460
|
+
new Date(state6.endTs).toISOString().slice(0, 16);
|
|
31461
|
+
const legendHTML = state6.deviceData.map((dd) => `
|
|
31455
31462
|
<div style="display: flex; align-items: center; gap: 8px; padding: 8px 12px;
|
|
31456
|
-
background: ${
|
|
31463
|
+
background: ${state6.theme === "dark" ? "rgba(255,255,255,0.05)" : "rgba(0,0,0,0.03)"};
|
|
31457
31464
|
border-radius: 8px;">
|
|
31458
31465
|
<span style="width: 12px; height: 12px; border-radius: 50%; background: ${dd.color};"></span>
|
|
31459
31466
|
<span style="color: ${colors.text}; font-size: 13px;">${dd.device.label}</span>
|
|
@@ -31462,9 +31469,9 @@
|
|
|
31462
31469
|
</span>
|
|
31463
31470
|
</div>
|
|
31464
31471
|
`).join("");
|
|
31465
|
-
const statsHTML =
|
|
31472
|
+
const statsHTML = state6.deviceData.map((dd) => `
|
|
31466
31473
|
<div style="
|
|
31467
|
-
padding: 12px; background: ${
|
|
31474
|
+
padding: 12px; background: ${state6.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
|
|
31468
31475
|
border-radius: 10px; border-left: 4px solid ${dd.color};
|
|
31469
31476
|
min-width: 150px;
|
|
31470
31477
|
">
|
|
@@ -31515,7 +31522,7 @@
|
|
|
31515
31522
|
min-height: 20px;
|
|
31516
31523
|
">
|
|
31517
31524
|
<h2 style="margin: 6px; font-size: 18px; font-weight: 600; color: white; line-height: 2;">
|
|
31518
|
-
\u{1F321}\uFE0F Compara\xE7\xE3o de Temperatura - ${
|
|
31525
|
+
\u{1F321}\uFE0F Compara\xE7\xE3o de Temperatura - ${state6.devices.length} sensores
|
|
31519
31526
|
</h2>
|
|
31520
31527
|
<div style="display: flex; gap: 4px; align-items: center;">
|
|
31521
31528
|
<!-- Theme Toggle -->
|
|
@@ -31523,7 +31530,7 @@
|
|
|
31523
31530
|
background: none; border: none; font-size: 16px; cursor: pointer;
|
|
31524
31531
|
padding: 4px 8px; border-radius: 6px; color: rgba(255,255,255,0.8);
|
|
31525
31532
|
transition: background-color 0.2s;
|
|
31526
|
-
">${
|
|
31533
|
+
">${state6.theme === "dark" ? "\u2600\uFE0F" : "\u{1F319}"}</button>
|
|
31527
31534
|
<!-- Maximize Button -->
|
|
31528
31535
|
<button id="${modalId}-maximize" title="${isMaximized ? "Restaurar" : "Maximizar"}" style="
|
|
31529
31536
|
background: none; border: none; font-size: 16px; cursor: pointer;
|
|
@@ -31546,7 +31553,7 @@
|
|
|
31546
31553
|
<div style="
|
|
31547
31554
|
display: flex; gap: 16px; flex-wrap: wrap; align-items: flex-end;
|
|
31548
31555
|
margin-bottom: 16px; padding: 16px;
|
|
31549
|
-
background: ${
|
|
31556
|
+
background: ${state6.theme === "dark" ? "rgba(255,255,255,0.05)" : "#f7f7f7"};
|
|
31550
31557
|
border-radius: 6px; border: 1px solid ${colors.border};
|
|
31551
31558
|
">
|
|
31552
31559
|
<!-- Granularity Select -->
|
|
@@ -31559,8 +31566,8 @@
|
|
|
31559
31566
|
font-size: 14px; color: ${colors.text}; background: ${colors.surface};
|
|
31560
31567
|
cursor: pointer; min-width: 130px;
|
|
31561
31568
|
">
|
|
31562
|
-
<option value="hour" ${
|
|
31563
|
-
<option value="day" ${
|
|
31569
|
+
<option value="hour" ${state6.granularity === "hour" ? "selected" : ""}>Hora (30 min)</option>
|
|
31570
|
+
<option value="day" ${state6.granularity === "day" ? "selected" : ""}>Dia (m\xE9dia)</option>
|
|
31564
31571
|
</select>
|
|
31565
31572
|
</div>
|
|
31566
31573
|
<!-- Day Period Filter (Multiselect) -->
|
|
@@ -31574,7 +31581,7 @@
|
|
|
31574
31581
|
cursor: pointer; min-width: 180px; text-align: left;
|
|
31575
31582
|
display: flex; align-items: center; justify-content: space-between; gap: 8px;
|
|
31576
31583
|
">
|
|
31577
|
-
<span>${getSelectedPeriodsLabel(
|
|
31584
|
+
<span>${getSelectedPeriodsLabel(state6.selectedPeriods)}</span>
|
|
31578
31585
|
<span style="font-size: 10px;">\u25BC</span>
|
|
31579
31586
|
</button>
|
|
31580
31587
|
<div id="${modalId}-period-dropdown" style="
|
|
@@ -31587,12 +31594,12 @@
|
|
|
31587
31594
|
<label style="
|
|
31588
31595
|
display: flex; align-items: center; gap: 8px; padding: 8px 12px;
|
|
31589
31596
|
cursor: pointer; font-size: 13px; color: ${colors.text};
|
|
31590
|
-
" onmouseover="this.style.background='${
|
|
31597
|
+
" onmouseover="this.style.background='${state6.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"}'"
|
|
31591
31598
|
onmouseout="this.style.background='transparent'">
|
|
31592
31599
|
<input type="checkbox"
|
|
31593
31600
|
name="${modalId}-period"
|
|
31594
31601
|
value="${period.id}"
|
|
31595
|
-
${
|
|
31602
|
+
${state6.selectedPeriods.includes(period.id) ? "checked" : ""}
|
|
31596
31603
|
style="width: 16px; height: 16px; cursor: pointer; accent-color: #3e1a7d;">
|
|
31597
31604
|
${period.label}
|
|
31598
31605
|
</label>
|
|
@@ -31600,13 +31607,13 @@
|
|
|
31600
31607
|
<div style="border-top: 1px solid ${colors.border}; margin-top: 8px; padding-top: 8px;">
|
|
31601
31608
|
<button id="${modalId}-period-select-all" type="button" style="
|
|
31602
31609
|
width: calc(100% - 16px); margin: 0 8px 4px; padding: 6px;
|
|
31603
|
-
background: ${
|
|
31610
|
+
background: ${state6.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"};
|
|
31604
31611
|
border: none; border-radius: 4px; cursor: pointer;
|
|
31605
31612
|
font-size: 12px; color: ${colors.text};
|
|
31606
31613
|
">Selecionar Todos</button>
|
|
31607
31614
|
<button id="${modalId}-period-clear" type="button" style="
|
|
31608
31615
|
width: calc(100% - 16px); margin: 0 8px; padding: 6px;
|
|
31609
|
-
background: ${
|
|
31616
|
+
background: ${state6.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"};
|
|
31610
31617
|
border: none; border-radius: 4px; cursor: pointer;
|
|
31611
31618
|
font-size: 12px; color: ${colors.text};
|
|
31612
31619
|
">Limpar Sele\xE7\xE3o</button>
|
|
@@ -31631,8 +31638,8 @@
|
|
|
31631
31638
|
font-size: 14px; font-weight: 500; height: 38px;
|
|
31632
31639
|
display: flex; align-items: center; gap: 8px;
|
|
31633
31640
|
font-family: 'Roboto', Arial, sans-serif;
|
|
31634
|
-
" ${
|
|
31635
|
-
${
|
|
31641
|
+
" ${state6.isLoading ? "disabled" : ""}>
|
|
31642
|
+
${state6.isLoading ? '<span style="animation: spin 1s linear infinite; display: inline-block;">\u21BB</span> Carregando...' : "Carregar"}
|
|
31636
31643
|
</button>
|
|
31637
31644
|
</div>
|
|
31638
31645
|
|
|
@@ -31648,14 +31655,14 @@
|
|
|
31648
31655
|
<div style="margin-bottom: 24px;">
|
|
31649
31656
|
<div id="${modalId}-chart" style="
|
|
31650
31657
|
height: 380px;
|
|
31651
|
-
background: ${
|
|
31658
|
+
background: ${state6.theme === "dark" ? "rgba(255,255,255,0.03)" : "#fafafa"};
|
|
31652
31659
|
border-radius: 14px; display: flex; justify-content: center; align-items: center;
|
|
31653
31660
|
border: 1px solid ${colors.border}; position: relative;
|
|
31654
31661
|
">
|
|
31655
|
-
${
|
|
31662
|
+
${state6.isLoading ? `<div style="text-align: center; color: ${colors.textMuted};">
|
|
31656
31663
|
<div style="animation: spin 1s linear infinite; font-size: 36px; margin-bottom: 12px;">\u21BB</div>
|
|
31657
|
-
<div style="font-size: 15px;">Carregando dados de ${
|
|
31658
|
-
</div>` :
|
|
31664
|
+
<div style="font-size: 15px;">Carregando dados de ${state6.devices.length} sensores...</div>
|
|
31665
|
+
</div>` : state6.deviceData.every((dd) => dd.data.length === 0) ? `<div style="text-align: center; color: ${colors.textMuted};">
|
|
31659
31666
|
<div style="font-size: 48px; margin-bottom: 12px;">\u{1F4ED}</div>
|
|
31660
31667
|
<div style="font-size: 16px;">Sem dados para o per\xEDodo selecionado</div>
|
|
31661
31668
|
</div>` : `<canvas id="${modalId}-canvas" style="width: 100%; height: 100%;"></canvas>`}
|
|
@@ -31673,12 +31680,12 @@
|
|
|
31673
31680
|
<!-- Actions -->
|
|
31674
31681
|
<div style="display: flex; justify-content: flex-end; gap: 12px;">
|
|
31675
31682
|
<button id="${modalId}-export" style="
|
|
31676
|
-
background: ${
|
|
31683
|
+
background: ${state6.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f7f7f7"};
|
|
31677
31684
|
color: ${colors.text}; border: 1px solid ${colors.border};
|
|
31678
31685
|
padding: 8px 16px; border-radius: 6px; cursor: pointer;
|
|
31679
31686
|
font-size: 14px; display: flex; align-items: center; gap: 8px;
|
|
31680
31687
|
font-family: 'Roboto', Arial, sans-serif;
|
|
31681
|
-
" ${
|
|
31688
|
+
" ${state6.deviceData.every((dd) => dd.data.length === 0) ? "disabled" : ""}>
|
|
31682
31689
|
\u{1F4E5} Exportar CSV
|
|
31683
31690
|
</button>
|
|
31684
31691
|
<button id="${modalId}-close-btn" style="
|
|
@@ -31713,15 +31720,15 @@
|
|
|
31713
31720
|
</style>
|
|
31714
31721
|
`;
|
|
31715
31722
|
}
|
|
31716
|
-
function drawComparisonChart(modalId,
|
|
31723
|
+
function drawComparisonChart(modalId, state6) {
|
|
31717
31724
|
const chartContainer = document.getElementById(`${modalId}-chart`);
|
|
31718
31725
|
const canvas = document.getElementById(`${modalId}-canvas`);
|
|
31719
31726
|
if (!chartContainer || !canvas) return;
|
|
31720
|
-
const hasData =
|
|
31727
|
+
const hasData = state6.deviceData.some((dd) => dd.data.length > 0);
|
|
31721
31728
|
if (!hasData) return;
|
|
31722
31729
|
const ctx = canvas.getContext("2d");
|
|
31723
31730
|
if (!ctx) return;
|
|
31724
|
-
const colors = getThemeColors(
|
|
31731
|
+
const colors = getThemeColors(state6.theme);
|
|
31725
31732
|
const width = chartContainer.clientWidth - 2;
|
|
31726
31733
|
const height = 380;
|
|
31727
31734
|
canvas.width = width;
|
|
@@ -31732,19 +31739,19 @@
|
|
|
31732
31739
|
const paddingBottom = 55;
|
|
31733
31740
|
ctx.clearRect(0, 0, width, height);
|
|
31734
31741
|
const processedData = [];
|
|
31735
|
-
|
|
31742
|
+
state6.deviceData.forEach((dd) => {
|
|
31736
31743
|
if (dd.data.length === 0) return;
|
|
31737
|
-
const filteredData = filterByDayPeriods(dd.data,
|
|
31744
|
+
const filteredData = filterByDayPeriods(dd.data, state6.selectedPeriods);
|
|
31738
31745
|
if (filteredData.length === 0) return;
|
|
31739
31746
|
let points;
|
|
31740
|
-
if (
|
|
31747
|
+
if (state6.granularity === "hour") {
|
|
31741
31748
|
const interpolated = interpolateTemperature(filteredData, {
|
|
31742
31749
|
intervalMinutes: 30,
|
|
31743
|
-
startTs:
|
|
31744
|
-
endTs:
|
|
31745
|
-
clampRange:
|
|
31750
|
+
startTs: state6.startTs,
|
|
31751
|
+
endTs: state6.endTs,
|
|
31752
|
+
clampRange: state6.clampRange
|
|
31746
31753
|
});
|
|
31747
|
-
const filteredInterpolated = filterByDayPeriods(interpolated,
|
|
31754
|
+
const filteredInterpolated = filterByDayPeriods(interpolated, state6.selectedPeriods);
|
|
31748
31755
|
points = filteredInterpolated.map((item) => ({
|
|
31749
31756
|
x: item.ts,
|
|
31750
31757
|
y: Number(item.value),
|
|
@@ -31754,7 +31761,7 @@
|
|
|
31754
31761
|
deviceColor: dd.color
|
|
31755
31762
|
}));
|
|
31756
31763
|
} else {
|
|
31757
|
-
const daily = aggregateByDay(filteredData,
|
|
31764
|
+
const daily = aggregateByDay(filteredData, state6.clampRange);
|
|
31758
31765
|
points = daily.map((item) => ({
|
|
31759
31766
|
x: item.dateTs,
|
|
31760
31767
|
y: item.avg,
|
|
@@ -31775,7 +31782,7 @@
|
|
|
31775
31782
|
ctx.fillText("Nenhum dado para os per\xEDodos selecionados", width / 2, height / 2);
|
|
31776
31783
|
return;
|
|
31777
31784
|
}
|
|
31778
|
-
const isPeriodsFiltered =
|
|
31785
|
+
const isPeriodsFiltered = state6.selectedPeriods.length < 4 && state6.selectedPeriods.length > 0;
|
|
31779
31786
|
let dataMinY = Infinity;
|
|
31780
31787
|
let dataMaxY = -Infinity;
|
|
31781
31788
|
processedData.forEach(({ points }) => {
|
|
@@ -31785,7 +31792,7 @@
|
|
|
31785
31792
|
});
|
|
31786
31793
|
});
|
|
31787
31794
|
const rangeMap = /* @__PURE__ */ new Map();
|
|
31788
|
-
|
|
31795
|
+
state6.deviceData.forEach((dd, index) => {
|
|
31789
31796
|
const device = dd.device;
|
|
31790
31797
|
const min = device.temperatureMin;
|
|
31791
31798
|
const max = device.temperatureMax;
|
|
@@ -31804,10 +31811,10 @@
|
|
|
31804
31811
|
}
|
|
31805
31812
|
}
|
|
31806
31813
|
});
|
|
31807
|
-
if (rangeMap.size === 0 &&
|
|
31814
|
+
if (rangeMap.size === 0 && state6.temperatureMin !== null && state6.temperatureMax !== null) {
|
|
31808
31815
|
rangeMap.set("global", {
|
|
31809
|
-
min:
|
|
31810
|
-
max:
|
|
31816
|
+
min: state6.temperatureMin,
|
|
31817
|
+
max: state6.temperatureMax,
|
|
31811
31818
|
customerName: "Global",
|
|
31812
31819
|
color: colors.success,
|
|
31813
31820
|
deviceLabels: []
|
|
@@ -31928,10 +31935,10 @@
|
|
|
31928
31935
|
const point = xAxisPoints[i];
|
|
31929
31936
|
const date = new Date(point.x);
|
|
31930
31937
|
let label;
|
|
31931
|
-
if (
|
|
31932
|
-
label = date.toLocaleTimeString(
|
|
31938
|
+
if (state6.granularity === "hour") {
|
|
31939
|
+
label = date.toLocaleTimeString(state6.locale, { hour: "2-digit", minute: "2-digit" });
|
|
31933
31940
|
} else {
|
|
31934
|
-
label = date.toLocaleDateString(
|
|
31941
|
+
label = date.toLocaleDateString(state6.locale, { day: "2-digit", month: "2-digit" });
|
|
31935
31942
|
}
|
|
31936
31943
|
ctx.strokeStyle = colors.chartGrid;
|
|
31937
31944
|
ctx.lineWidth = 1;
|
|
@@ -31950,16 +31957,16 @@
|
|
|
31950
31957
|
ctx.lineTo(width - paddingRight, height - paddingBottom);
|
|
31951
31958
|
ctx.stroke();
|
|
31952
31959
|
const allChartPoints = processedData.flatMap((pd) => pd.points);
|
|
31953
|
-
setupComparisonChartTooltip(canvas, chartContainer, allChartPoints,
|
|
31960
|
+
setupComparisonChartTooltip(canvas, chartContainer, allChartPoints, state6, colors);
|
|
31954
31961
|
}
|
|
31955
|
-
function setupComparisonChartTooltip(canvas, container, chartData,
|
|
31962
|
+
function setupComparisonChartTooltip(canvas, container, chartData, state6, colors) {
|
|
31956
31963
|
const existingTooltip = container.querySelector(".myio-chart-tooltip");
|
|
31957
31964
|
if (existingTooltip) existingTooltip.remove();
|
|
31958
31965
|
const tooltip = document.createElement("div");
|
|
31959
31966
|
tooltip.className = "myio-chart-tooltip";
|
|
31960
31967
|
tooltip.style.cssText = `
|
|
31961
31968
|
position: absolute;
|
|
31962
|
-
background: ${
|
|
31969
|
+
background: ${state6.theme === "dark" ? "rgba(30, 30, 40, 0.95)" : "rgba(255, 255, 255, 0.98)"};
|
|
31963
31970
|
border: 1px solid ${colors.border};
|
|
31964
31971
|
border-radius: 8px;
|
|
31965
31972
|
padding: 10px 14px;
|
|
@@ -31996,17 +32003,17 @@
|
|
|
31996
32003
|
if (point) {
|
|
31997
32004
|
const date = new Date(point.x);
|
|
31998
32005
|
let dateStr;
|
|
31999
|
-
if (
|
|
32000
|
-
dateStr = date.toLocaleDateString(
|
|
32006
|
+
if (state6.granularity === "hour") {
|
|
32007
|
+
dateStr = date.toLocaleDateString(state6.locale, {
|
|
32001
32008
|
day: "2-digit",
|
|
32002
32009
|
month: "2-digit",
|
|
32003
32010
|
year: "numeric"
|
|
32004
|
-
}) + " " + date.toLocaleTimeString(
|
|
32011
|
+
}) + " " + date.toLocaleTimeString(state6.locale, {
|
|
32005
32012
|
hour: "2-digit",
|
|
32006
32013
|
minute: "2-digit"
|
|
32007
32014
|
});
|
|
32008
32015
|
} else {
|
|
32009
|
-
dateStr = date.toLocaleDateString(
|
|
32016
|
+
dateStr = date.toLocaleDateString(state6.locale, {
|
|
32010
32017
|
day: "2-digit",
|
|
32011
32018
|
month: "2-digit",
|
|
32012
32019
|
year: "numeric"
|
|
@@ -32048,7 +32055,7 @@
|
|
|
32048
32055
|
canvas.style.cursor = "default";
|
|
32049
32056
|
});
|
|
32050
32057
|
}
|
|
32051
|
-
async function setupEventListeners2(container,
|
|
32058
|
+
async function setupEventListeners2(container, state6, modalId, onClose) {
|
|
32052
32059
|
const closeModal = () => {
|
|
32053
32060
|
container.remove();
|
|
32054
32061
|
onClose?.();
|
|
@@ -32059,19 +32066,19 @@
|
|
|
32059
32066
|
document.getElementById(`${modalId}-close`)?.addEventListener("click", closeModal);
|
|
32060
32067
|
document.getElementById(`${modalId}-close-btn`)?.addEventListener("click", closeModal);
|
|
32061
32068
|
const dateRangeInput = document.getElementById(`${modalId}-date-range`);
|
|
32062
|
-
if (dateRangeInput && !
|
|
32069
|
+
if (dateRangeInput && !state6.dateRangePicker) {
|
|
32063
32070
|
try {
|
|
32064
|
-
|
|
32065
|
-
presetStart: new Date(
|
|
32066
|
-
presetEnd: new Date(
|
|
32071
|
+
state6.dateRangePicker = await createDateRangePicker2(dateRangeInput, {
|
|
32072
|
+
presetStart: new Date(state6.startTs).toISOString(),
|
|
32073
|
+
presetEnd: new Date(state6.endTs).toISOString(),
|
|
32067
32074
|
includeTime: true,
|
|
32068
32075
|
timePrecision: "minute",
|
|
32069
32076
|
maxRangeDays: 90,
|
|
32070
|
-
locale:
|
|
32077
|
+
locale: state6.locale,
|
|
32071
32078
|
parentEl: container.querySelector(".myio-temp-comparison-content"),
|
|
32072
32079
|
onApply: (result) => {
|
|
32073
|
-
|
|
32074
|
-
|
|
32080
|
+
state6.startTs = new Date(result.startISO).getTime();
|
|
32081
|
+
state6.endTs = new Date(result.endISO).getTime();
|
|
32075
32082
|
console.log("[TemperatureComparisonModal] Date range applied:", result);
|
|
32076
32083
|
}
|
|
32077
32084
|
});
|
|
@@ -32080,23 +32087,23 @@
|
|
|
32080
32087
|
}
|
|
32081
32088
|
}
|
|
32082
32089
|
document.getElementById(`${modalId}-theme-toggle`)?.addEventListener("click", async () => {
|
|
32083
|
-
|
|
32084
|
-
localStorage.setItem("myio-temp-comparison-theme",
|
|
32085
|
-
|
|
32086
|
-
renderModal2(container,
|
|
32087
|
-
if (
|
|
32088
|
-
drawComparisonChart(modalId,
|
|
32089
|
-
}
|
|
32090
|
-
await setupEventListeners2(container,
|
|
32090
|
+
state6.theme = state6.theme === "dark" ? "light" : "dark";
|
|
32091
|
+
localStorage.setItem("myio-temp-comparison-theme", state6.theme);
|
|
32092
|
+
state6.dateRangePicker = null;
|
|
32093
|
+
renderModal2(container, state6, modalId);
|
|
32094
|
+
if (state6.deviceData.some((dd) => dd.data.length > 0)) {
|
|
32095
|
+
drawComparisonChart(modalId, state6);
|
|
32096
|
+
}
|
|
32097
|
+
await setupEventListeners2(container, state6, modalId, onClose);
|
|
32091
32098
|
});
|
|
32092
32099
|
document.getElementById(`${modalId}-maximize`)?.addEventListener("click", async () => {
|
|
32093
32100
|
container.__isMaximized = !container.__isMaximized;
|
|
32094
|
-
|
|
32095
|
-
renderModal2(container,
|
|
32096
|
-
if (
|
|
32097
|
-
drawComparisonChart(modalId,
|
|
32101
|
+
state6.dateRangePicker = null;
|
|
32102
|
+
renderModal2(container, state6, modalId);
|
|
32103
|
+
if (state6.deviceData.some((dd) => dd.data.length > 0)) {
|
|
32104
|
+
drawComparisonChart(modalId, state6);
|
|
32098
32105
|
}
|
|
32099
|
-
await setupEventListeners2(container,
|
|
32106
|
+
await setupEventListeners2(container, state6, modalId, onClose);
|
|
32100
32107
|
});
|
|
32101
32108
|
const periodBtn = document.getElementById(`${modalId}-period-btn`);
|
|
32102
32109
|
const periodDropdown = document.getElementById(`${modalId}-period-dropdown`);
|
|
@@ -32115,13 +32122,13 @@
|
|
|
32115
32122
|
periodCheckboxes.forEach((checkbox) => {
|
|
32116
32123
|
checkbox.addEventListener("change", () => {
|
|
32117
32124
|
const checked = Array.from(periodCheckboxes).filter((cb) => cb.checked).map((cb) => cb.value);
|
|
32118
|
-
|
|
32125
|
+
state6.selectedPeriods = checked;
|
|
32119
32126
|
const btnLabel = periodBtn?.querySelector("span:first-child");
|
|
32120
32127
|
if (btnLabel) {
|
|
32121
|
-
btnLabel.textContent = getSelectedPeriodsLabel(
|
|
32128
|
+
btnLabel.textContent = getSelectedPeriodsLabel(state6.selectedPeriods);
|
|
32122
32129
|
}
|
|
32123
|
-
if (
|
|
32124
|
-
drawComparisonChart(modalId,
|
|
32130
|
+
if (state6.deviceData.some((dd) => dd.data.length > 0)) {
|
|
32131
|
+
drawComparisonChart(modalId, state6);
|
|
32125
32132
|
}
|
|
32126
32133
|
});
|
|
32127
32134
|
});
|
|
@@ -32129,77 +32136,77 @@
|
|
|
32129
32136
|
periodCheckboxes.forEach((cb) => {
|
|
32130
32137
|
cb.checked = true;
|
|
32131
32138
|
});
|
|
32132
|
-
|
|
32139
|
+
state6.selectedPeriods = ["madrugada", "manha", "tarde", "noite"];
|
|
32133
32140
|
const btnLabel = periodBtn?.querySelector("span:first-child");
|
|
32134
32141
|
if (btnLabel) {
|
|
32135
|
-
btnLabel.textContent = getSelectedPeriodsLabel(
|
|
32142
|
+
btnLabel.textContent = getSelectedPeriodsLabel(state6.selectedPeriods);
|
|
32136
32143
|
}
|
|
32137
|
-
if (
|
|
32138
|
-
drawComparisonChart(modalId,
|
|
32144
|
+
if (state6.deviceData.some((dd) => dd.data.length > 0)) {
|
|
32145
|
+
drawComparisonChart(modalId, state6);
|
|
32139
32146
|
}
|
|
32140
32147
|
});
|
|
32141
32148
|
document.getElementById(`${modalId}-period-clear`)?.addEventListener("click", () => {
|
|
32142
32149
|
periodCheckboxes.forEach((cb) => {
|
|
32143
32150
|
cb.checked = false;
|
|
32144
32151
|
});
|
|
32145
|
-
|
|
32152
|
+
state6.selectedPeriods = [];
|
|
32146
32153
|
const btnLabel = periodBtn?.querySelector("span:first-child");
|
|
32147
32154
|
if (btnLabel) {
|
|
32148
|
-
btnLabel.textContent = getSelectedPeriodsLabel(
|
|
32155
|
+
btnLabel.textContent = getSelectedPeriodsLabel(state6.selectedPeriods);
|
|
32149
32156
|
}
|
|
32150
|
-
if (
|
|
32151
|
-
drawComparisonChart(modalId,
|
|
32157
|
+
if (state6.deviceData.some((dd) => dd.data.length > 0)) {
|
|
32158
|
+
drawComparisonChart(modalId, state6);
|
|
32152
32159
|
}
|
|
32153
32160
|
});
|
|
32154
32161
|
document.getElementById(`${modalId}-granularity`)?.addEventListener("change", (e) => {
|
|
32155
|
-
|
|
32156
|
-
localStorage.setItem("myio-temp-comparison-granularity",
|
|
32157
|
-
if (
|
|
32158
|
-
drawComparisonChart(modalId,
|
|
32162
|
+
state6.granularity = e.target.value;
|
|
32163
|
+
localStorage.setItem("myio-temp-comparison-granularity", state6.granularity);
|
|
32164
|
+
if (state6.deviceData.some((dd) => dd.data.length > 0)) {
|
|
32165
|
+
drawComparisonChart(modalId, state6);
|
|
32159
32166
|
}
|
|
32160
32167
|
});
|
|
32161
32168
|
document.getElementById(`${modalId}-query`)?.addEventListener("click", async () => {
|
|
32162
|
-
if (
|
|
32169
|
+
if (state6.startTs >= state6.endTs) {
|
|
32163
32170
|
alert("Por favor, selecione um per\xEDodo v\xE1lido");
|
|
32164
32171
|
return;
|
|
32165
32172
|
}
|
|
32166
|
-
|
|
32167
|
-
|
|
32168
|
-
renderModal2(container,
|
|
32169
|
-
await fetchAllDevicesData(
|
|
32170
|
-
renderModal2(container,
|
|
32171
|
-
drawComparisonChart(modalId,
|
|
32172
|
-
await setupEventListeners2(container,
|
|
32173
|
+
state6.isLoading = true;
|
|
32174
|
+
state6.dateRangePicker = null;
|
|
32175
|
+
renderModal2(container, state6, modalId);
|
|
32176
|
+
await fetchAllDevicesData(state6);
|
|
32177
|
+
renderModal2(container, state6, modalId);
|
|
32178
|
+
drawComparisonChart(modalId, state6);
|
|
32179
|
+
await setupEventListeners2(container, state6, modalId, onClose);
|
|
32173
32180
|
});
|
|
32174
32181
|
document.getElementById(`${modalId}-export`)?.addEventListener("click", () => {
|
|
32175
|
-
if (
|
|
32176
|
-
exportComparisonCSV(
|
|
32182
|
+
if (state6.deviceData.every((dd) => dd.data.length === 0)) return;
|
|
32183
|
+
exportComparisonCSV(state6);
|
|
32177
32184
|
});
|
|
32178
32185
|
}
|
|
32179
|
-
function exportComparisonCSV(
|
|
32180
|
-
const startDateStr = new Date(
|
|
32181
|
-
const endDateStr = new Date(
|
|
32186
|
+
function exportComparisonCSV(state6) {
|
|
32187
|
+
const startDateStr = new Date(state6.startTs).toLocaleDateString(state6.locale).replace(/\//g, "-");
|
|
32188
|
+
const endDateStr = new Date(state6.endTs).toLocaleDateString(state6.locale).replace(/\//g, "-");
|
|
32182
32189
|
const BOM = "\uFEFF";
|
|
32183
32190
|
let csvContent = BOM;
|
|
32184
32191
|
csvContent += `Compara\xE7\xE3o de Temperatura
|
|
32185
32192
|
`;
|
|
32186
32193
|
csvContent += `Per\xEDodo: ${startDateStr} at\xE9 ${endDateStr}
|
|
32187
32194
|
`;
|
|
32188
|
-
csvContent += `Sensores: ${
|
|
32195
|
+
csvContent += `Sensores: ${state6.devices.map((d) => d.label).join(", ")}
|
|
32189
32196
|
`;
|
|
32190
32197
|
csvContent += "\n";
|
|
32191
32198
|
csvContent += "Estat\xEDsticas por Sensor:\n";
|
|
32192
32199
|
csvContent += "Sensor,M\xE9dia (\xB0C),Min (\xB0C),Max (\xB0C),Leituras\n";
|
|
32193
|
-
|
|
32200
|
+
state6.deviceData.forEach((dd) => {
|
|
32194
32201
|
csvContent += `"${dd.device.label}",${dd.stats.avg.toFixed(2)},${dd.stats.min.toFixed(2)},${dd.stats.max.toFixed(2)},${dd.stats.count}
|
|
32195
32202
|
`;
|
|
32196
32203
|
});
|
|
32197
32204
|
csvContent += "\n";
|
|
32198
32205
|
csvContent += "Dados Detalhados:\n";
|
|
32199
32206
|
csvContent += "Data/Hora,Sensor,Temperatura (\xB0C)\n";
|
|
32200
|
-
|
|
32207
|
+
state6.deviceData.forEach((dd) => {
|
|
32201
32208
|
dd.data.forEach((item) => {
|
|
32202
|
-
const date = new Date(item.ts).toLocaleString(
|
|
32209
|
+
const date = new Date(item.ts).toLocaleString(state6.locale);
|
|
32203
32210
|
const temp = Number(item.value).toFixed(2);
|
|
32204
32211
|
csvContent += `"${date}","${dd.device.label}",${temp}
|
|
32205
32212
|
`;
|
|
@@ -32307,10 +32314,10 @@
|
|
|
32307
32314
|
throw new Error(`Failed to save attributes: ${response.status}`);
|
|
32308
32315
|
}
|
|
32309
32316
|
}
|
|
32310
|
-
function renderModal3(container,
|
|
32311
|
-
const colors = getColors(
|
|
32312
|
-
const minValue =
|
|
32313
|
-
const maxValue =
|
|
32317
|
+
function renderModal3(container, state6, modalId, onClose, onSave) {
|
|
32318
|
+
const colors = getColors(state6.theme);
|
|
32319
|
+
const minValue = state6.minTemperature !== null ? state6.minTemperature : "";
|
|
32320
|
+
const maxValue = state6.maxTemperature !== null ? state6.maxTemperature : "";
|
|
32314
32321
|
container.innerHTML = `
|
|
32315
32322
|
<style>
|
|
32316
32323
|
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
|
|
@@ -32575,23 +32582,23 @@
|
|
|
32575
32582
|
</div>
|
|
32576
32583
|
|
|
32577
32584
|
<div class="modal-body">
|
|
32578
|
-
${
|
|
32585
|
+
${state6.isLoading ? `
|
|
32579
32586
|
<div class="loading-overlay">
|
|
32580
32587
|
<div class="loading-spinner"></div>
|
|
32581
32588
|
<div>Carregando configura\xE7\xF5es...</div>
|
|
32582
32589
|
</div>
|
|
32583
32590
|
` : `
|
|
32584
|
-
${
|
|
32585
|
-
<div class="message message-error">${
|
|
32591
|
+
${state6.error ? `
|
|
32592
|
+
<div class="message message-error">${state6.error}</div>
|
|
32586
32593
|
` : ""}
|
|
32587
32594
|
|
|
32588
|
-
${
|
|
32589
|
-
<div class="message message-success">${
|
|
32595
|
+
${state6.successMessage ? `
|
|
32596
|
+
<div class="message message-success">${state6.successMessage}</div>
|
|
32590
32597
|
` : ""}
|
|
32591
32598
|
|
|
32592
32599
|
<div class="customer-info">
|
|
32593
32600
|
<div class="customer-label">Shopping / Cliente</div>
|
|
32594
|
-
<div class="customer-name">${
|
|
32601
|
+
<div class="customer-name">${state6.customerName || "N\xE3o identificado"}</div>
|
|
32595
32602
|
</div>
|
|
32596
32603
|
|
|
32597
32604
|
<div class="form-group">
|
|
@@ -32635,11 +32642,11 @@
|
|
|
32635
32642
|
`}
|
|
32636
32643
|
</div>
|
|
32637
32644
|
|
|
32638
|
-
${!
|
|
32645
|
+
${!state6.isLoading ? `
|
|
32639
32646
|
<div class="modal-footer">
|
|
32640
32647
|
<button class="btn btn-secondary" id="${modalId}-cancel">Cancelar</button>
|
|
32641
|
-
<button class="btn btn-primary" id="${modalId}-save" ${
|
|
32642
|
-
${
|
|
32648
|
+
<button class="btn btn-primary" id="${modalId}-save" ${state6.isSaving ? "disabled" : ""}>
|
|
32649
|
+
${state6.isSaving ? '<div class="spinner"></div> Salvando...' : "Salvar"}
|
|
32643
32650
|
</button>
|
|
32644
32651
|
</div>
|
|
32645
32652
|
` : ""}
|
|
@@ -32676,18 +32683,18 @@
|
|
|
32676
32683
|
const min = parseFloat(minInput.value);
|
|
32677
32684
|
const max = parseFloat(maxInput.value);
|
|
32678
32685
|
if (isNaN(min) || isNaN(max)) {
|
|
32679
|
-
|
|
32680
|
-
renderModal3(container,
|
|
32686
|
+
state6.error = "Por favor, preencha ambos os valores.";
|
|
32687
|
+
renderModal3(container, state6, modalId, onClose, onSave);
|
|
32681
32688
|
return;
|
|
32682
32689
|
}
|
|
32683
32690
|
if (min >= max) {
|
|
32684
|
-
|
|
32685
|
-
renderModal3(container,
|
|
32691
|
+
state6.error = "A temperatura m\xEDnima deve ser menor que a m\xE1xima.";
|
|
32692
|
+
renderModal3(container, state6, modalId, onClose, onSave);
|
|
32686
32693
|
return;
|
|
32687
32694
|
}
|
|
32688
32695
|
if (min < 0 || max > 50) {
|
|
32689
|
-
|
|
32690
|
-
renderModal3(container,
|
|
32696
|
+
state6.error = "Os valores devem estar entre 0\xB0C e 50\xB0C.";
|
|
32697
|
+
renderModal3(container, state6, modalId, onClose, onSave);
|
|
32691
32698
|
return;
|
|
32692
32699
|
}
|
|
32693
32700
|
await onSave(min, max);
|
|
@@ -32695,7 +32702,7 @@
|
|
|
32695
32702
|
}
|
|
32696
32703
|
function openTemperatureSettingsModal(params) {
|
|
32697
32704
|
const modalId = `myio-temp-settings-${Date.now()}`;
|
|
32698
|
-
const
|
|
32705
|
+
const state6 = {
|
|
32699
32706
|
customerId: params.customerId,
|
|
32700
32707
|
customerName: params.customerName || "",
|
|
32701
32708
|
token: params.token,
|
|
@@ -32715,37 +32722,37 @@
|
|
|
32715
32722
|
params.onClose?.();
|
|
32716
32723
|
};
|
|
32717
32724
|
const handleSave = async (min, max) => {
|
|
32718
|
-
|
|
32719
|
-
|
|
32720
|
-
|
|
32721
|
-
renderModal3(container,
|
|
32725
|
+
state6.isSaving = true;
|
|
32726
|
+
state6.error = null;
|
|
32727
|
+
state6.successMessage = null;
|
|
32728
|
+
renderModal3(container, state6, modalId, destroy, handleSave);
|
|
32722
32729
|
try {
|
|
32723
|
-
await saveCustomerAttributes(
|
|
32724
|
-
|
|
32725
|
-
|
|
32726
|
-
|
|
32727
|
-
|
|
32728
|
-
renderModal3(container,
|
|
32730
|
+
await saveCustomerAttributes(state6.customerId, state6.token, min, max, params.onError);
|
|
32731
|
+
state6.minTemperature = min;
|
|
32732
|
+
state6.maxTemperature = max;
|
|
32733
|
+
state6.isSaving = false;
|
|
32734
|
+
state6.successMessage = "Configura\xE7\xF5es salvas com sucesso!";
|
|
32735
|
+
renderModal3(container, state6, modalId, destroy, handleSave);
|
|
32729
32736
|
params.onSave?.({ minTemperature: min, maxTemperature: max });
|
|
32730
32737
|
setTimeout(() => {
|
|
32731
32738
|
destroy();
|
|
32732
32739
|
}, 1500);
|
|
32733
32740
|
} catch (error) {
|
|
32734
|
-
|
|
32735
|
-
|
|
32736
|
-
renderModal3(container,
|
|
32741
|
+
state6.isSaving = false;
|
|
32742
|
+
state6.error = `Erro ao salvar: ${error.message}`;
|
|
32743
|
+
renderModal3(container, state6, modalId, destroy, handleSave);
|
|
32737
32744
|
}
|
|
32738
32745
|
};
|
|
32739
|
-
renderModal3(container,
|
|
32740
|
-
fetchCustomerAttributes(
|
|
32741
|
-
|
|
32742
|
-
|
|
32743
|
-
|
|
32744
|
-
renderModal3(container,
|
|
32746
|
+
renderModal3(container, state6, modalId, destroy, handleSave);
|
|
32747
|
+
fetchCustomerAttributes(state6.customerId, state6.token, params.onError).then(({ minTemperature, maxTemperature }) => {
|
|
32748
|
+
state6.minTemperature = minTemperature;
|
|
32749
|
+
state6.maxTemperature = maxTemperature;
|
|
32750
|
+
state6.isLoading = false;
|
|
32751
|
+
renderModal3(container, state6, modalId, destroy, handleSave);
|
|
32745
32752
|
}).catch((error) => {
|
|
32746
|
-
|
|
32747
|
-
|
|
32748
|
-
renderModal3(container,
|
|
32753
|
+
state6.isLoading = false;
|
|
32754
|
+
state6.error = `Erro ao carregar: ${error.message}`;
|
|
32755
|
+
renderModal3(container, state6, modalId, destroy, handleSave);
|
|
32749
32756
|
});
|
|
32750
32757
|
return { destroy };
|
|
32751
32758
|
}
|
|
@@ -34015,7 +34022,7 @@
|
|
|
34015
34022
|
* RFC-0105 Enhancement: Now fetches device lists from MyIOOrchestratorData
|
|
34016
34023
|
* to populate device lists for status popup display
|
|
34017
34024
|
*/
|
|
34018
|
-
buildSummaryFromState(
|
|
34025
|
+
buildSummaryFromState(state6, receivedData, domain = "energy") {
|
|
34019
34026
|
const summary = {
|
|
34020
34027
|
totalDevices: 0,
|
|
34021
34028
|
totalConsumption: 0,
|
|
@@ -34038,22 +34045,22 @@
|
|
|
34038
34045
|
},
|
|
34039
34046
|
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
34040
34047
|
};
|
|
34041
|
-
if (!
|
|
34048
|
+
if (!state6) return summary;
|
|
34042
34049
|
const entrada = {
|
|
34043
34050
|
id: "entrada",
|
|
34044
34051
|
name: "Entrada",
|
|
34045
34052
|
icon: CATEGORY_ICONS.entrada,
|
|
34046
|
-
deviceCount:
|
|
34047
|
-
consumption:
|
|
34053
|
+
deviceCount: state6.entrada?.devices?.length || (receivedData?.entrada_total?.device_count || 0),
|
|
34054
|
+
consumption: state6.entrada?.total || 0,
|
|
34048
34055
|
percentage: 100
|
|
34049
34056
|
};
|
|
34050
34057
|
const lojas = {
|
|
34051
34058
|
id: "lojas",
|
|
34052
34059
|
name: "Lojas",
|
|
34053
34060
|
icon: CATEGORY_ICONS.lojas,
|
|
34054
|
-
deviceCount:
|
|
34055
|
-
consumption:
|
|
34056
|
-
percentage:
|
|
34061
|
+
deviceCount: state6.consumidores?.lojas?.devices?.length || (receivedData?.lojas_total?.device_count || 0),
|
|
34062
|
+
consumption: state6.consumidores?.lojas?.total || 0,
|
|
34063
|
+
percentage: state6.consumidores?.lojas?.perc || 0
|
|
34057
34064
|
};
|
|
34058
34065
|
const climatizacaoData = receivedData?.climatizacao || {};
|
|
34059
34066
|
const elevadoresData = receivedData?.elevadores || {};
|
|
@@ -34064,9 +34071,9 @@
|
|
|
34064
34071
|
id: "climatizacao",
|
|
34065
34072
|
name: "Climatizacao",
|
|
34066
34073
|
icon: CATEGORY_ICONS.climatizacao,
|
|
34067
|
-
deviceCount: climatizacaoData.count ||
|
|
34068
|
-
consumption:
|
|
34069
|
-
percentage:
|
|
34074
|
+
deviceCount: climatizacaoData.count || state6.consumidores?.climatizacao?.devices?.length || 0,
|
|
34075
|
+
consumption: state6.consumidores?.climatizacao?.total || 0,
|
|
34076
|
+
percentage: state6.consumidores?.climatizacao?.perc || 0
|
|
34070
34077
|
};
|
|
34071
34078
|
if (climatizacaoData.subcategories) {
|
|
34072
34079
|
climatizacao.children = [];
|
|
@@ -34117,40 +34124,40 @@
|
|
|
34117
34124
|
id: "elevadores",
|
|
34118
34125
|
name: "Elevadores",
|
|
34119
34126
|
icon: CATEGORY_ICONS.elevadores,
|
|
34120
|
-
deviceCount: elevadoresData.count ||
|
|
34121
|
-
consumption:
|
|
34122
|
-
percentage:
|
|
34127
|
+
deviceCount: elevadoresData.count || state6.consumidores?.elevadores?.devices?.length || 0,
|
|
34128
|
+
consumption: state6.consumidores?.elevadores?.total || 0,
|
|
34129
|
+
percentage: state6.consumidores?.elevadores?.perc || 0
|
|
34123
34130
|
});
|
|
34124
34131
|
areaComumChildren.push({
|
|
34125
34132
|
id: "escadasRolantes",
|
|
34126
34133
|
name: "Esc. Rolantes",
|
|
34127
34134
|
icon: CATEGORY_ICONS.escadas,
|
|
34128
|
-
deviceCount: escadasData.count ||
|
|
34129
|
-
consumption:
|
|
34130
|
-
percentage:
|
|
34135
|
+
deviceCount: escadasData.count || state6.consumidores?.escadasRolantes?.devices?.length || 0,
|
|
34136
|
+
consumption: state6.consumidores?.escadasRolantes?.total || 0,
|
|
34137
|
+
percentage: state6.consumidores?.escadasRolantes?.perc || 0
|
|
34131
34138
|
});
|
|
34132
34139
|
areaComumChildren.push({
|
|
34133
34140
|
id: "outros",
|
|
34134
34141
|
name: "Outros",
|
|
34135
34142
|
icon: CATEGORY_ICONS.outros,
|
|
34136
|
-
deviceCount: outrosData.count ||
|
|
34137
|
-
consumption:
|
|
34138
|
-
percentage:
|
|
34143
|
+
deviceCount: outrosData.count || state6.consumidores?.outros?.devices?.length || 0,
|
|
34144
|
+
consumption: state6.consumidores?.outros?.total || 0,
|
|
34145
|
+
percentage: state6.consumidores?.outros?.perc || 0
|
|
34139
34146
|
});
|
|
34140
34147
|
const areaComumDeviceCount = areaComumChildren.reduce((sum, c) => sum + c.deviceCount, 0);
|
|
34141
|
-
const areaComumConsumption =
|
|
34148
|
+
const areaComumConsumption = state6.consumidores?.areaComum?.total || areaComumChildren.reduce((sum, c) => sum + c.consumption, 0);
|
|
34142
34149
|
const areaComum = {
|
|
34143
34150
|
id: "areaComum",
|
|
34144
34151
|
name: "Area Comum",
|
|
34145
34152
|
icon: CATEGORY_ICONS.areaComum,
|
|
34146
34153
|
deviceCount: areaComumDeviceCount,
|
|
34147
34154
|
consumption: areaComumConsumption,
|
|
34148
|
-
percentage:
|
|
34155
|
+
percentage: state6.consumidores?.areaComum?.perc || 0,
|
|
34149
34156
|
children: areaComumChildren
|
|
34150
34157
|
};
|
|
34151
34158
|
summary.byCategory = [entrada, lojas, areaComum];
|
|
34152
34159
|
summary.totalDevices = entrada.deviceCount + lojas.deviceCount + areaComumDeviceCount;
|
|
34153
|
-
summary.totalConsumption =
|
|
34160
|
+
summary.totalConsumption = state6.grandTotal || entrada.consumption;
|
|
34154
34161
|
const widgetAggregation = receivedData?.deviceStatusAggregation;
|
|
34155
34162
|
if (widgetAggregation && widgetAggregation.hasData) {
|
|
34156
34163
|
summary.byStatus = {
|
|
@@ -35438,7 +35445,7 @@
|
|
|
35438
35445
|
* RFC-0105 Enhancement: Now fetches device lists from MyIOOrchestratorData
|
|
35439
35446
|
* to populate device lists for status popup display
|
|
35440
35447
|
*/
|
|
35441
|
-
buildSummaryFromState(
|
|
35448
|
+
buildSummaryFromState(state6, receivedData, includeBathrooms = false, domain = "water") {
|
|
35442
35449
|
const summary = {
|
|
35443
35450
|
totalDevices: 0,
|
|
35444
35451
|
totalConsumption: 0,
|
|
@@ -35462,22 +35469,22 @@
|
|
|
35462
35469
|
lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
|
|
35463
35470
|
includeBathrooms
|
|
35464
35471
|
};
|
|
35465
|
-
if (!
|
|
35472
|
+
if (!state6) return summary;
|
|
35466
35473
|
const entrada = {
|
|
35467
35474
|
id: "entrada",
|
|
35468
35475
|
name: "Entrada",
|
|
35469
35476
|
icon: WATER_CATEGORY_ICONS.entrada,
|
|
35470
|
-
deviceCount:
|
|
35471
|
-
consumption:
|
|
35477
|
+
deviceCount: state6.entrada?.devices?.length || (receivedData?.entrada_total?.device_count || 0),
|
|
35478
|
+
consumption: state6.entrada?.total || 0,
|
|
35472
35479
|
percentage: 100
|
|
35473
35480
|
};
|
|
35474
35481
|
const lojas = {
|
|
35475
35482
|
id: "lojas",
|
|
35476
35483
|
name: "Lojas",
|
|
35477
35484
|
icon: WATER_CATEGORY_ICONS.lojas,
|
|
35478
|
-
deviceCount:
|
|
35479
|
-
consumption:
|
|
35480
|
-
percentage:
|
|
35485
|
+
deviceCount: state6.lojas?.devices?.length || (receivedData?.lojas_total?.device_count || 0),
|
|
35486
|
+
consumption: state6.lojas?.total || 0,
|
|
35487
|
+
percentage: state6.lojas?.perc || 0
|
|
35481
35488
|
};
|
|
35482
35489
|
summary.byCategory = [entrada, lojas];
|
|
35483
35490
|
if (includeBathrooms) {
|
|
@@ -35485,9 +35492,9 @@
|
|
|
35485
35492
|
id: "banheiros",
|
|
35486
35493
|
name: "Banheiros",
|
|
35487
35494
|
icon: WATER_CATEGORY_ICONS.banheiros,
|
|
35488
|
-
deviceCount:
|
|
35489
|
-
consumption:
|
|
35490
|
-
percentage:
|
|
35495
|
+
deviceCount: state6.banheiros?.devices?.length || (receivedData?.banheiros_total?.device_count || 0),
|
|
35496
|
+
consumption: state6.banheiros?.total || 0,
|
|
35497
|
+
percentage: state6.banheiros?.perc || 0
|
|
35491
35498
|
};
|
|
35492
35499
|
summary.byCategory.push(banheiros);
|
|
35493
35500
|
}
|
|
@@ -35495,24 +35502,24 @@
|
|
|
35495
35502
|
id: "areaComum",
|
|
35496
35503
|
name: "\xC1rea Comum",
|
|
35497
35504
|
icon: WATER_CATEGORY_ICONS.areaComum,
|
|
35498
|
-
deviceCount:
|
|
35499
|
-
consumption:
|
|
35500
|
-
percentage:
|
|
35505
|
+
deviceCount: state6.areaComum?.devices?.length || (receivedData?.area_comum_total?.device_count || 0),
|
|
35506
|
+
consumption: state6.areaComum?.total || 0,
|
|
35507
|
+
percentage: state6.areaComum?.perc || 0
|
|
35501
35508
|
};
|
|
35502
35509
|
summary.byCategory.push(areaComum);
|
|
35503
|
-
if (
|
|
35510
|
+
if (state6.pontosNaoMapeados && state6.pontosNaoMapeados.total > 0) {
|
|
35504
35511
|
const pontosNaoMapeados = {
|
|
35505
35512
|
id: "pontosNaoMapeados",
|
|
35506
35513
|
name: "Pontos N\xE3o Mapeados",
|
|
35507
35514
|
icon: WATER_CATEGORY_ICONS.pontosNaoMapeados,
|
|
35508
|
-
deviceCount:
|
|
35509
|
-
consumption:
|
|
35510
|
-
percentage:
|
|
35515
|
+
deviceCount: state6.pontosNaoMapeados?.devices?.length || 0,
|
|
35516
|
+
consumption: state6.pontosNaoMapeados?.total || 0,
|
|
35517
|
+
percentage: state6.pontosNaoMapeados?.perc || 0
|
|
35511
35518
|
};
|
|
35512
35519
|
summary.byCategory.push(pontosNaoMapeados);
|
|
35513
35520
|
}
|
|
35514
35521
|
summary.totalDevices = summary.byCategory.reduce((sum, cat) => sum + cat.deviceCount, 0);
|
|
35515
|
-
summary.totalConsumption =
|
|
35522
|
+
summary.totalConsumption = state6.entrada?.total || 0;
|
|
35516
35523
|
const widgetAggregation = receivedData?.deviceStatusAggregation;
|
|
35517
35524
|
if (widgetAggregation && widgetAggregation.hasData) {
|
|
35518
35525
|
summary.byStatus = {
|
|
@@ -37049,6 +37056,996 @@
|
|
|
37049
37056
|
}
|
|
37050
37057
|
};
|
|
37051
37058
|
|
|
37059
|
+
// src/utils/ContractSummaryTooltip.ts
|
|
37060
|
+
var CONTRACT_SUMMARY_TOOLTIP_CSS = `
|
|
37061
|
+
/* ============================================
|
|
37062
|
+
Contract Summary Tooltip (RFC-0107)
|
|
37063
|
+
Premium draggable tooltip with dark theme
|
|
37064
|
+
============================================ */
|
|
37065
|
+
|
|
37066
|
+
.myio-contract-summary-tooltip {
|
|
37067
|
+
position: fixed;
|
|
37068
|
+
z-index: 99999;
|
|
37069
|
+
pointer-events: none;
|
|
37070
|
+
opacity: 0;
|
|
37071
|
+
transition: opacity 0.25s ease, transform 0.25s ease;
|
|
37072
|
+
transform: translateY(5px);
|
|
37073
|
+
}
|
|
37074
|
+
|
|
37075
|
+
.myio-contract-summary-tooltip.visible {
|
|
37076
|
+
opacity: 1;
|
|
37077
|
+
pointer-events: auto;
|
|
37078
|
+
transform: translateY(0);
|
|
37079
|
+
}
|
|
37080
|
+
|
|
37081
|
+
.myio-contract-summary-tooltip.closing {
|
|
37082
|
+
opacity: 0;
|
|
37083
|
+
transform: translateY(8px);
|
|
37084
|
+
transition: opacity 0.4s ease, transform 0.4s ease;
|
|
37085
|
+
}
|
|
37086
|
+
|
|
37087
|
+
.myio-contract-summary-tooltip.pinned {
|
|
37088
|
+
box-shadow: 0 0 0 2px #9684B5, 0 10px 40px rgba(0, 0, 0, 0.3);
|
|
37089
|
+
border-radius: 16px;
|
|
37090
|
+
}
|
|
37091
|
+
|
|
37092
|
+
.myio-contract-summary-tooltip.dragging {
|
|
37093
|
+
transition: none !important;
|
|
37094
|
+
cursor: move;
|
|
37095
|
+
}
|
|
37096
|
+
|
|
37097
|
+
.myio-contract-summary-tooltip.maximized {
|
|
37098
|
+
top: 20px !important;
|
|
37099
|
+
left: 20px !important;
|
|
37100
|
+
right: 20px !important;
|
|
37101
|
+
bottom: 20px !important;
|
|
37102
|
+
width: auto !important;
|
|
37103
|
+
max-width: none !important;
|
|
37104
|
+
}
|
|
37105
|
+
|
|
37106
|
+
.myio-contract-summary-tooltip.maximized .myio-contract-summary-tooltip__panel {
|
|
37107
|
+
width: 100%;
|
|
37108
|
+
height: 100%;
|
|
37109
|
+
max-width: none;
|
|
37110
|
+
display: flex;
|
|
37111
|
+
flex-direction: column;
|
|
37112
|
+
}
|
|
37113
|
+
|
|
37114
|
+
.myio-contract-summary-tooltip.maximized .myio-contract-summary-tooltip__body {
|
|
37115
|
+
flex: 1;
|
|
37116
|
+
overflow-y: auto;
|
|
37117
|
+
}
|
|
37118
|
+
|
|
37119
|
+
.myio-contract-summary-tooltip__panel {
|
|
37120
|
+
background: #2d1458;
|
|
37121
|
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
37122
|
+
border-radius: 16px;
|
|
37123
|
+
box-shadow:
|
|
37124
|
+
0 20px 60px rgba(0, 0, 0, 0.4),
|
|
37125
|
+
0 8px 20px rgba(0, 0, 0, 0.25),
|
|
37126
|
+
0 0 0 1px rgba(255, 255, 255, 0.05);
|
|
37127
|
+
min-width: 320px;
|
|
37128
|
+
max-width: 380px;
|
|
37129
|
+
font-family: Inter, system-ui, -apple-system, sans-serif;
|
|
37130
|
+
font-size: 12px;
|
|
37131
|
+
color: #ffffff;
|
|
37132
|
+
overflow: hidden;
|
|
37133
|
+
}
|
|
37134
|
+
|
|
37135
|
+
/* Header */
|
|
37136
|
+
.myio-contract-summary-tooltip__header {
|
|
37137
|
+
display: flex;
|
|
37138
|
+
align-items: center;
|
|
37139
|
+
gap: 10px;
|
|
37140
|
+
padding: 14px 16px;
|
|
37141
|
+
background: linear-gradient(135deg, #9684B5 0%, #2d1458 100%);
|
|
37142
|
+
border-radius: 16px 16px 0 0;
|
|
37143
|
+
position: relative;
|
|
37144
|
+
overflow: hidden;
|
|
37145
|
+
cursor: move;
|
|
37146
|
+
user-select: none;
|
|
37147
|
+
}
|
|
37148
|
+
|
|
37149
|
+
.myio-contract-summary-tooltip__header::before {
|
|
37150
|
+
content: '';
|
|
37151
|
+
position: absolute;
|
|
37152
|
+
top: 0;
|
|
37153
|
+
left: 0;
|
|
37154
|
+
right: 0;
|
|
37155
|
+
bottom: 0;
|
|
37156
|
+
background: url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%23ffffff' fill-opacity='0.05'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
|
|
37157
|
+
opacity: 0.3;
|
|
37158
|
+
}
|
|
37159
|
+
|
|
37160
|
+
.myio-contract-summary-tooltip__icon {
|
|
37161
|
+
width: 40px;
|
|
37162
|
+
height: 40px;
|
|
37163
|
+
background: rgba(255, 255, 255, 0.15);
|
|
37164
|
+
border-radius: 12px;
|
|
37165
|
+
display: flex;
|
|
37166
|
+
align-items: center;
|
|
37167
|
+
justify-content: center;
|
|
37168
|
+
font-size: 20px;
|
|
37169
|
+
backdrop-filter: blur(10px);
|
|
37170
|
+
position: relative;
|
|
37171
|
+
z-index: 1;
|
|
37172
|
+
}
|
|
37173
|
+
|
|
37174
|
+
.myio-contract-summary-tooltip__icon.valid {
|
|
37175
|
+
background: rgba(76, 175, 80, 0.3);
|
|
37176
|
+
}
|
|
37177
|
+
|
|
37178
|
+
.myio-contract-summary-tooltip__icon.invalid {
|
|
37179
|
+
background: rgba(244, 67, 54, 0.3);
|
|
37180
|
+
}
|
|
37181
|
+
|
|
37182
|
+
.myio-contract-summary-tooltip__header-info {
|
|
37183
|
+
flex: 1;
|
|
37184
|
+
position: relative;
|
|
37185
|
+
z-index: 1;
|
|
37186
|
+
}
|
|
37187
|
+
|
|
37188
|
+
.myio-contract-summary-tooltip__title {
|
|
37189
|
+
font-weight: 700;
|
|
37190
|
+
font-size: 15px;
|
|
37191
|
+
color: #ffffff;
|
|
37192
|
+
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
|
|
37193
|
+
margin-bottom: 2px;
|
|
37194
|
+
}
|
|
37195
|
+
|
|
37196
|
+
.myio-contract-summary-tooltip__subtitle {
|
|
37197
|
+
font-size: 11px;
|
|
37198
|
+
color: rgba(255, 255, 255, 0.7);
|
|
37199
|
+
}
|
|
37200
|
+
|
|
37201
|
+
.myio-contract-summary-tooltip__header-actions {
|
|
37202
|
+
display: flex;
|
|
37203
|
+
align-items: center;
|
|
37204
|
+
gap: 4px;
|
|
37205
|
+
position: relative;
|
|
37206
|
+
z-index: 1;
|
|
37207
|
+
}
|
|
37208
|
+
|
|
37209
|
+
.myio-contract-summary-tooltip__header-btn {
|
|
37210
|
+
width: 28px;
|
|
37211
|
+
height: 28px;
|
|
37212
|
+
border: none;
|
|
37213
|
+
background: rgba(255, 255, 255, 0.15);
|
|
37214
|
+
border-radius: 8px;
|
|
37215
|
+
cursor: pointer;
|
|
37216
|
+
display: flex;
|
|
37217
|
+
align-items: center;
|
|
37218
|
+
justify-content: center;
|
|
37219
|
+
transition: all 0.2s ease;
|
|
37220
|
+
color: rgba(255, 255, 255, 0.8);
|
|
37221
|
+
}
|
|
37222
|
+
|
|
37223
|
+
.myio-contract-summary-tooltip__header-btn:hover {
|
|
37224
|
+
background: rgba(255, 255, 255, 0.25);
|
|
37225
|
+
color: #ffffff;
|
|
37226
|
+
transform: scale(1.05);
|
|
37227
|
+
}
|
|
37228
|
+
|
|
37229
|
+
.myio-contract-summary-tooltip__header-btn.pinned {
|
|
37230
|
+
background: rgba(255, 255, 255, 0.9);
|
|
37231
|
+
color: #9684B5;
|
|
37232
|
+
}
|
|
37233
|
+
|
|
37234
|
+
.myio-contract-summary-tooltip__header-btn.pinned:hover {
|
|
37235
|
+
background: #ffffff;
|
|
37236
|
+
color: #2d1458;
|
|
37237
|
+
}
|
|
37238
|
+
|
|
37239
|
+
.myio-contract-summary-tooltip__header-btn svg {
|
|
37240
|
+
width: 14px;
|
|
37241
|
+
height: 14px;
|
|
37242
|
+
}
|
|
37243
|
+
|
|
37244
|
+
/* Body */
|
|
37245
|
+
.myio-contract-summary-tooltip__body {
|
|
37246
|
+
padding: 16px;
|
|
37247
|
+
}
|
|
37248
|
+
|
|
37249
|
+
/* Domain Section */
|
|
37250
|
+
.myio-contract-summary-tooltip__domain {
|
|
37251
|
+
margin-bottom: 14px;
|
|
37252
|
+
background: rgba(255, 255, 255, 0.05);
|
|
37253
|
+
border-radius: 12px;
|
|
37254
|
+
overflow: hidden;
|
|
37255
|
+
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
37256
|
+
}
|
|
37257
|
+
|
|
37258
|
+
.myio-contract-summary-tooltip__domain:last-child {
|
|
37259
|
+
margin-bottom: 0;
|
|
37260
|
+
}
|
|
37261
|
+
|
|
37262
|
+
.myio-contract-summary-tooltip__domain-header {
|
|
37263
|
+
display: flex;
|
|
37264
|
+
align-items: center;
|
|
37265
|
+
justify-content: space-between;
|
|
37266
|
+
padding: 10px 14px;
|
|
37267
|
+
cursor: pointer;
|
|
37268
|
+
transition: background 0.2s ease;
|
|
37269
|
+
}
|
|
37270
|
+
|
|
37271
|
+
.myio-contract-summary-tooltip__domain-header:hover {
|
|
37272
|
+
background: rgba(255, 255, 255, 0.05);
|
|
37273
|
+
}
|
|
37274
|
+
|
|
37275
|
+
.myio-contract-summary-tooltip__domain-info {
|
|
37276
|
+
display: flex;
|
|
37277
|
+
align-items: center;
|
|
37278
|
+
gap: 8px;
|
|
37279
|
+
}
|
|
37280
|
+
|
|
37281
|
+
.myio-contract-summary-tooltip__domain-icon {
|
|
37282
|
+
font-size: 18px;
|
|
37283
|
+
}
|
|
37284
|
+
|
|
37285
|
+
.myio-contract-summary-tooltip__domain-name {
|
|
37286
|
+
font-weight: 600;
|
|
37287
|
+
font-size: 13px;
|
|
37288
|
+
}
|
|
37289
|
+
|
|
37290
|
+
.myio-contract-summary-tooltip__domain-count {
|
|
37291
|
+
font-size: 12px;
|
|
37292
|
+
color: #81c784;
|
|
37293
|
+
font-weight: 600;
|
|
37294
|
+
}
|
|
37295
|
+
|
|
37296
|
+
.myio-contract-summary-tooltip__expand-icon {
|
|
37297
|
+
font-size: 10px;
|
|
37298
|
+
opacity: 0.6;
|
|
37299
|
+
transition: transform 0.3s ease;
|
|
37300
|
+
}
|
|
37301
|
+
|
|
37302
|
+
.myio-contract-summary-tooltip__domain.expanded .myio-contract-summary-tooltip__expand-icon {
|
|
37303
|
+
transform: rotate(180deg);
|
|
37304
|
+
}
|
|
37305
|
+
|
|
37306
|
+
/* Domain Details */
|
|
37307
|
+
.myio-contract-summary-tooltip__domain-details {
|
|
37308
|
+
max-height: 0;
|
|
37309
|
+
overflow: hidden;
|
|
37310
|
+
transition: max-height 0.3s ease;
|
|
37311
|
+
background: rgba(0, 0, 0, 0.15);
|
|
37312
|
+
}
|
|
37313
|
+
|
|
37314
|
+
.myio-contract-summary-tooltip__domain.expanded .myio-contract-summary-tooltip__domain-details {
|
|
37315
|
+
max-height: 150px;
|
|
37316
|
+
}
|
|
37317
|
+
|
|
37318
|
+
.myio-contract-summary-tooltip__detail-row {
|
|
37319
|
+
display: flex;
|
|
37320
|
+
align-items: center;
|
|
37321
|
+
justify-content: space-between;
|
|
37322
|
+
padding: 6px 14px 6px 40px;
|
|
37323
|
+
font-size: 12px;
|
|
37324
|
+
border-top: 1px solid rgba(255, 255, 255, 0.05);
|
|
37325
|
+
}
|
|
37326
|
+
|
|
37327
|
+
.myio-contract-summary-tooltip__detail-row:first-child {
|
|
37328
|
+
border-top: none;
|
|
37329
|
+
}
|
|
37330
|
+
|
|
37331
|
+
.myio-contract-summary-tooltip__detail-label {
|
|
37332
|
+
opacity: 0.7;
|
|
37333
|
+
display: flex;
|
|
37334
|
+
align-items: center;
|
|
37335
|
+
gap: 6px;
|
|
37336
|
+
}
|
|
37337
|
+
|
|
37338
|
+
.myio-contract-summary-tooltip__detail-label::before {
|
|
37339
|
+
content: '';
|
|
37340
|
+
width: 4px;
|
|
37341
|
+
height: 4px;
|
|
37342
|
+
border-radius: 50%;
|
|
37343
|
+
background: currentColor;
|
|
37344
|
+
opacity: 0.5;
|
|
37345
|
+
}
|
|
37346
|
+
|
|
37347
|
+
.myio-contract-summary-tooltip__detail-count {
|
|
37348
|
+
font-weight: 500;
|
|
37349
|
+
color: #81c784;
|
|
37350
|
+
}
|
|
37351
|
+
|
|
37352
|
+
/* Status Banner */
|
|
37353
|
+
.myio-contract-summary-tooltip__status {
|
|
37354
|
+
display: flex;
|
|
37355
|
+
align-items: center;
|
|
37356
|
+
justify-content: center;
|
|
37357
|
+
gap: 8px;
|
|
37358
|
+
padding: 10px 14px;
|
|
37359
|
+
border-radius: 10px;
|
|
37360
|
+
margin-bottom: 14px;
|
|
37361
|
+
font-size: 12px;
|
|
37362
|
+
font-weight: 600;
|
|
37363
|
+
}
|
|
37364
|
+
|
|
37365
|
+
.myio-contract-summary-tooltip__status.valid {
|
|
37366
|
+
background: rgba(76, 175, 80, 0.2);
|
|
37367
|
+
color: #81c784;
|
|
37368
|
+
border: 1px solid rgba(76, 175, 80, 0.3);
|
|
37369
|
+
}
|
|
37370
|
+
|
|
37371
|
+
.myio-contract-summary-tooltip__status.invalid {
|
|
37372
|
+
background: rgba(244, 67, 54, 0.2);
|
|
37373
|
+
color: #ef5350;
|
|
37374
|
+
border: 1px solid rgba(244, 67, 54, 0.3);
|
|
37375
|
+
}
|
|
37376
|
+
|
|
37377
|
+
.myio-contract-summary-tooltip__status-icon {
|
|
37378
|
+
font-size: 14px;
|
|
37379
|
+
}
|
|
37380
|
+
|
|
37381
|
+
/* Discrepancies */
|
|
37382
|
+
.myio-contract-summary-tooltip__discrepancies {
|
|
37383
|
+
background: rgba(244, 67, 54, 0.15);
|
|
37384
|
+
border: 1px solid rgba(244, 67, 54, 0.3);
|
|
37385
|
+
border-radius: 10px;
|
|
37386
|
+
padding: 10px 14px;
|
|
37387
|
+
margin-bottom: 14px;
|
|
37388
|
+
}
|
|
37389
|
+
|
|
37390
|
+
.myio-contract-summary-tooltip__discrepancies-title {
|
|
37391
|
+
font-size: 11px;
|
|
37392
|
+
font-weight: 600;
|
|
37393
|
+
color: #ef5350;
|
|
37394
|
+
margin-bottom: 6px;
|
|
37395
|
+
text-transform: uppercase;
|
|
37396
|
+
letter-spacing: 0.5px;
|
|
37397
|
+
}
|
|
37398
|
+
|
|
37399
|
+
.myio-contract-summary-tooltip__discrepancy-item {
|
|
37400
|
+
font-size: 11px;
|
|
37401
|
+
color: rgba(255, 255, 255, 0.8);
|
|
37402
|
+
padding: 3px 0;
|
|
37403
|
+
}
|
|
37404
|
+
|
|
37405
|
+
/* Footer */
|
|
37406
|
+
.myio-contract-summary-tooltip__footer {
|
|
37407
|
+
display: flex;
|
|
37408
|
+
justify-content: space-between;
|
|
37409
|
+
align-items: center;
|
|
37410
|
+
padding: 12px 16px;
|
|
37411
|
+
background: rgba(0, 0, 0, 0.2);
|
|
37412
|
+
border-top: 1px solid rgba(255, 255, 255, 0.05);
|
|
37413
|
+
border-radius: 0 0 16px 16px;
|
|
37414
|
+
}
|
|
37415
|
+
|
|
37416
|
+
.myio-contract-summary-tooltip__footer-label {
|
|
37417
|
+
font-size: 10px;
|
|
37418
|
+
color: rgba(255, 255, 255, 0.5);
|
|
37419
|
+
}
|
|
37420
|
+
|
|
37421
|
+
.myio-contract-summary-tooltip__footer-value {
|
|
37422
|
+
font-size: 11px;
|
|
37423
|
+
font-weight: 600;
|
|
37424
|
+
color: rgba(255, 255, 255, 0.8);
|
|
37425
|
+
}
|
|
37426
|
+
|
|
37427
|
+
/* Total Devices Badge */
|
|
37428
|
+
.myio-contract-summary-tooltip__total {
|
|
37429
|
+
display: flex;
|
|
37430
|
+
align-items: center;
|
|
37431
|
+
justify-content: center;
|
|
37432
|
+
gap: 8px;
|
|
37433
|
+
padding: 12px;
|
|
37434
|
+
background: rgba(255, 255, 255, 0.08);
|
|
37435
|
+
border-radius: 10px;
|
|
37436
|
+
margin-bottom: 14px;
|
|
37437
|
+
}
|
|
37438
|
+
|
|
37439
|
+
.myio-contract-summary-tooltip__total-label {
|
|
37440
|
+
font-size: 12px;
|
|
37441
|
+
opacity: 0.8;
|
|
37442
|
+
}
|
|
37443
|
+
|
|
37444
|
+
.myio-contract-summary-tooltip__total-value {
|
|
37445
|
+
font-size: 18px;
|
|
37446
|
+
font-weight: 700;
|
|
37447
|
+
color: #81c784;
|
|
37448
|
+
}
|
|
37449
|
+
|
|
37450
|
+
/* Responsive */
|
|
37451
|
+
@media (max-width: 400px) {
|
|
37452
|
+
.myio-contract-summary-tooltip__panel {
|
|
37453
|
+
min-width: 280px;
|
|
37454
|
+
max-width: 95vw;
|
|
37455
|
+
}
|
|
37456
|
+
}
|
|
37457
|
+
`;
|
|
37458
|
+
var cssInjected9 = false;
|
|
37459
|
+
function injectCSS9() {
|
|
37460
|
+
if (cssInjected9) return;
|
|
37461
|
+
if (typeof document === "undefined") return;
|
|
37462
|
+
const styleId = "myio-contract-summary-tooltip-styles";
|
|
37463
|
+
if (document.getElementById(styleId)) {
|
|
37464
|
+
cssInjected9 = true;
|
|
37465
|
+
return;
|
|
37466
|
+
}
|
|
37467
|
+
const style = document.createElement("style");
|
|
37468
|
+
style.id = styleId;
|
|
37469
|
+
style.textContent = CONTRACT_SUMMARY_TOOLTIP_CSS;
|
|
37470
|
+
document.head.appendChild(style);
|
|
37471
|
+
cssInjected9 = true;
|
|
37472
|
+
}
|
|
37473
|
+
var state5 = {
|
|
37474
|
+
hideTimer: null,
|
|
37475
|
+
isMouseOverTooltip: false,
|
|
37476
|
+
isMaximized: false,
|
|
37477
|
+
isDragging: false,
|
|
37478
|
+
dragOffset: { x: 0, y: 0 },
|
|
37479
|
+
savedPosition: null,
|
|
37480
|
+
pinnedCounter: 0,
|
|
37481
|
+
expandedDomains: /* @__PURE__ */ new Set(["energy", "water", "temperature"])
|
|
37482
|
+
};
|
|
37483
|
+
function formatTimestamp5(isoString) {
|
|
37484
|
+
if (!isoString) return "Agora";
|
|
37485
|
+
try {
|
|
37486
|
+
const date = new Date(isoString);
|
|
37487
|
+
return date.toLocaleString("pt-BR", {
|
|
37488
|
+
hour: "2-digit",
|
|
37489
|
+
minute: "2-digit",
|
|
37490
|
+
day: "2-digit",
|
|
37491
|
+
month: "2-digit"
|
|
37492
|
+
});
|
|
37493
|
+
} catch {
|
|
37494
|
+
return "Agora";
|
|
37495
|
+
}
|
|
37496
|
+
}
|
|
37497
|
+
function calculateTotalDevices(data) {
|
|
37498
|
+
return data.energy.total + data.water.total + data.temperature.total;
|
|
37499
|
+
}
|
|
37500
|
+
function generateHeaderHTML5(data) {
|
|
37501
|
+
const iconClass = data.isValid ? "valid" : "invalid";
|
|
37502
|
+
const iconSymbol = data.isValid ? "\u2713" : "!";
|
|
37503
|
+
const totalDevices = calculateTotalDevices(data);
|
|
37504
|
+
return `
|
|
37505
|
+
<div class="myio-contract-summary-tooltip__header" data-drag-handle>
|
|
37506
|
+
<div class="myio-contract-summary-tooltip__icon ${iconClass}">${iconSymbol}</div>
|
|
37507
|
+
<div class="myio-contract-summary-tooltip__header-info">
|
|
37508
|
+
<div class="myio-contract-summary-tooltip__title">Contract Summary</div>
|
|
37509
|
+
<div class="myio-contract-summary-tooltip__subtitle">${totalDevices} devices loaded</div>
|
|
37510
|
+
</div>
|
|
37511
|
+
<div class="myio-contract-summary-tooltip__header-actions">
|
|
37512
|
+
<button class="myio-contract-summary-tooltip__header-btn" data-action="pin" title="Pin to screen">
|
|
37513
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
37514
|
+
<path d="M9 4v6l-2 4v2h10v-2l-2-4V4"/>
|
|
37515
|
+
<line x1="12" y1="16" x2="12" y2="21"/>
|
|
37516
|
+
<line x1="8" y1="4" x2="16" y2="4"/>
|
|
37517
|
+
</svg>
|
|
37518
|
+
</button>
|
|
37519
|
+
<button class="myio-contract-summary-tooltip__header-btn" data-action="maximize" title="Maximize">
|
|
37520
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
37521
|
+
<rect x="3" y="3" width="18" height="18" rx="2"/>
|
|
37522
|
+
</svg>
|
|
37523
|
+
</button>
|
|
37524
|
+
<button class="myio-contract-summary-tooltip__header-btn" data-action="close" title="Close">
|
|
37525
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
37526
|
+
<path d="M18 6L6 18M6 6l12 12"/>
|
|
37527
|
+
</svg>
|
|
37528
|
+
</button>
|
|
37529
|
+
</div>
|
|
37530
|
+
</div>
|
|
37531
|
+
`;
|
|
37532
|
+
}
|
|
37533
|
+
function generateDomainHTML(domain, icon, name, counts, isTemperature = false) {
|
|
37534
|
+
const isExpanded = state5.expandedDomains.has(domain);
|
|
37535
|
+
const expandedClass = isExpanded ? "expanded" : "";
|
|
37536
|
+
let detailsHTML = "";
|
|
37537
|
+
if (isTemperature) {
|
|
37538
|
+
const tempCounts = counts;
|
|
37539
|
+
detailsHTML = `
|
|
37540
|
+
<div class="myio-contract-summary-tooltip__detail-row">
|
|
37541
|
+
<span class="myio-contract-summary-tooltip__detail-label">Internal (Climate)</span>
|
|
37542
|
+
<span class="myio-contract-summary-tooltip__detail-count">${tempCounts.internal}</span>
|
|
37543
|
+
</div>
|
|
37544
|
+
<div class="myio-contract-summary-tooltip__detail-row">
|
|
37545
|
+
<span class="myio-contract-summary-tooltip__detail-label">Stores (Non-Climate)</span>
|
|
37546
|
+
<span class="myio-contract-summary-tooltip__detail-count">${tempCounts.stores}</span>
|
|
37547
|
+
</div>
|
|
37548
|
+
`;
|
|
37549
|
+
} else {
|
|
37550
|
+
const domainCounts = counts;
|
|
37551
|
+
detailsHTML = `
|
|
37552
|
+
<div class="myio-contract-summary-tooltip__detail-row">
|
|
37553
|
+
<span class="myio-contract-summary-tooltip__detail-label">Entries</span>
|
|
37554
|
+
<span class="myio-contract-summary-tooltip__detail-count">${domainCounts.entries}</span>
|
|
37555
|
+
</div>
|
|
37556
|
+
<div class="myio-contract-summary-tooltip__detail-row">
|
|
37557
|
+
<span class="myio-contract-summary-tooltip__detail-label">Common Area</span>
|
|
37558
|
+
<span class="myio-contract-summary-tooltip__detail-count">${domainCounts.commonArea}</span>
|
|
37559
|
+
</div>
|
|
37560
|
+
<div class="myio-contract-summary-tooltip__detail-row">
|
|
37561
|
+
<span class="myio-contract-summary-tooltip__detail-label">Stores</span>
|
|
37562
|
+
<span class="myio-contract-summary-tooltip__detail-count">${domainCounts.stores}</span>
|
|
37563
|
+
</div>
|
|
37564
|
+
`;
|
|
37565
|
+
}
|
|
37566
|
+
return `
|
|
37567
|
+
<div class="myio-contract-summary-tooltip__domain ${expandedClass}" data-domain="${domain}">
|
|
37568
|
+
<div class="myio-contract-summary-tooltip__domain-header" data-toggle-domain="${domain}">
|
|
37569
|
+
<div class="myio-contract-summary-tooltip__domain-info">
|
|
37570
|
+
<span class="myio-contract-summary-tooltip__domain-icon">${icon}</span>
|
|
37571
|
+
<span class="myio-contract-summary-tooltip__domain-name">${name}</span>
|
|
37572
|
+
</div>
|
|
37573
|
+
<div style="display: flex; align-items: center; gap: 8px;">
|
|
37574
|
+
<span class="myio-contract-summary-tooltip__domain-count">${counts.total} devices</span>
|
|
37575
|
+
<span class="myio-contract-summary-tooltip__expand-icon">\u25BC</span>
|
|
37576
|
+
</div>
|
|
37577
|
+
</div>
|
|
37578
|
+
<div class="myio-contract-summary-tooltip__domain-details">
|
|
37579
|
+
${detailsHTML}
|
|
37580
|
+
</div>
|
|
37581
|
+
</div>
|
|
37582
|
+
`;
|
|
37583
|
+
}
|
|
37584
|
+
function generateBodyHTML3(data) {
|
|
37585
|
+
const totalDevices = calculateTotalDevices(data);
|
|
37586
|
+
const timestamp = formatTimestamp5(data.timestamp);
|
|
37587
|
+
const statusClass = data.isValid ? "valid" : "invalid";
|
|
37588
|
+
const statusIcon = data.isValid ? "\u2713" : "\u26A0";
|
|
37589
|
+
const statusText = data.isValid ? "Contract validated successfully" : "Validation issues detected";
|
|
37590
|
+
let discrepanciesHTML = "";
|
|
37591
|
+
if (data.discrepancies && data.discrepancies.length > 0) {
|
|
37592
|
+
const items = data.discrepancies.map((d) => `<div class="myio-contract-summary-tooltip__discrepancy-item">${d.domain}: expected ${d.expected}, found ${d.actual}</div>`).join("");
|
|
37593
|
+
discrepanciesHTML = `
|
|
37594
|
+
<div class="myio-contract-summary-tooltip__discrepancies">
|
|
37595
|
+
<div class="myio-contract-summary-tooltip__discrepancies-title">Discrepancies</div>
|
|
37596
|
+
${items}
|
|
37597
|
+
</div>
|
|
37598
|
+
`;
|
|
37599
|
+
}
|
|
37600
|
+
return `
|
|
37601
|
+
<div class="myio-contract-summary-tooltip__body">
|
|
37602
|
+
<!-- Status Banner -->
|
|
37603
|
+
<div class="myio-contract-summary-tooltip__status ${statusClass}">
|
|
37604
|
+
<span class="myio-contract-summary-tooltip__status-icon">${statusIcon}</span>
|
|
37605
|
+
<span>${statusText}</span>
|
|
37606
|
+
</div>
|
|
37607
|
+
|
|
37608
|
+
${discrepanciesHTML}
|
|
37609
|
+
|
|
37610
|
+
<!-- Total Devices -->
|
|
37611
|
+
<div class="myio-contract-summary-tooltip__total">
|
|
37612
|
+
<span class="myio-contract-summary-tooltip__total-label">Total Devices:</span>
|
|
37613
|
+
<span class="myio-contract-summary-tooltip__total-value">${totalDevices}</span>
|
|
37614
|
+
</div>
|
|
37615
|
+
|
|
37616
|
+
<!-- Energy -->
|
|
37617
|
+
${generateDomainHTML("energy", "\u26A1", "Energy", data.energy)}
|
|
37618
|
+
|
|
37619
|
+
<!-- Water -->
|
|
37620
|
+
${generateDomainHTML("water", "\u{1F4A7}", "Water", data.water)}
|
|
37621
|
+
|
|
37622
|
+
<!-- Temperature -->
|
|
37623
|
+
${generateDomainHTML("temperature", "\u{1F321}\uFE0F", "Temperature", data.temperature, true)}
|
|
37624
|
+
</div>
|
|
37625
|
+
|
|
37626
|
+
<!-- Footer -->
|
|
37627
|
+
<div class="myio-contract-summary-tooltip__footer">
|
|
37628
|
+
<span class="myio-contract-summary-tooltip__footer-label">Loaded at</span>
|
|
37629
|
+
<span class="myio-contract-summary-tooltip__footer-value">${timestamp}</span>
|
|
37630
|
+
</div>
|
|
37631
|
+
`;
|
|
37632
|
+
}
|
|
37633
|
+
function setupHoverListeners5(container) {
|
|
37634
|
+
container.onmouseenter = () => {
|
|
37635
|
+
state5.isMouseOverTooltip = true;
|
|
37636
|
+
if (state5.hideTimer) {
|
|
37637
|
+
clearTimeout(state5.hideTimer);
|
|
37638
|
+
state5.hideTimer = null;
|
|
37639
|
+
}
|
|
37640
|
+
};
|
|
37641
|
+
container.onmouseleave = () => {
|
|
37642
|
+
state5.isMouseOverTooltip = false;
|
|
37643
|
+
startDelayedHide5();
|
|
37644
|
+
};
|
|
37645
|
+
}
|
|
37646
|
+
function setupDomainToggleListeners(container) {
|
|
37647
|
+
const toggles = container.querySelectorAll("[data-toggle-domain]");
|
|
37648
|
+
toggles.forEach((toggle) => {
|
|
37649
|
+
toggle.onclick = (e) => {
|
|
37650
|
+
e.stopPropagation();
|
|
37651
|
+
const domain = toggle.dataset.toggleDomain;
|
|
37652
|
+
if (!domain) return;
|
|
37653
|
+
const domainEl = container.querySelector(`[data-domain="${domain}"]`);
|
|
37654
|
+
if (!domainEl) return;
|
|
37655
|
+
if (state5.expandedDomains.has(domain)) {
|
|
37656
|
+
state5.expandedDomains.delete(domain);
|
|
37657
|
+
domainEl.classList.remove("expanded");
|
|
37658
|
+
} else {
|
|
37659
|
+
state5.expandedDomains.add(domain);
|
|
37660
|
+
domainEl.classList.add("expanded");
|
|
37661
|
+
}
|
|
37662
|
+
};
|
|
37663
|
+
});
|
|
37664
|
+
}
|
|
37665
|
+
function setupButtonListeners5(container) {
|
|
37666
|
+
const buttons = container.querySelectorAll("[data-action]");
|
|
37667
|
+
buttons.forEach((btn) => {
|
|
37668
|
+
btn.onclick = (e) => {
|
|
37669
|
+
e.stopPropagation();
|
|
37670
|
+
const action = btn.dataset.action;
|
|
37671
|
+
switch (action) {
|
|
37672
|
+
case "pin":
|
|
37673
|
+
createPinnedClone5(container);
|
|
37674
|
+
break;
|
|
37675
|
+
case "maximize":
|
|
37676
|
+
toggleMaximize5(container);
|
|
37677
|
+
break;
|
|
37678
|
+
case "close":
|
|
37679
|
+
ContractSummaryTooltip.close();
|
|
37680
|
+
break;
|
|
37681
|
+
}
|
|
37682
|
+
};
|
|
37683
|
+
});
|
|
37684
|
+
}
|
|
37685
|
+
function setupDragListeners5(container) {
|
|
37686
|
+
const header = container.querySelector("[data-drag-handle]");
|
|
37687
|
+
if (!header) return;
|
|
37688
|
+
header.onmousedown = (e) => {
|
|
37689
|
+
if (e.target.closest("[data-action]")) return;
|
|
37690
|
+
if (e.target.closest("[data-toggle-domain]")) return;
|
|
37691
|
+
if (state5.isMaximized) return;
|
|
37692
|
+
state5.isDragging = true;
|
|
37693
|
+
container.classList.add("dragging");
|
|
37694
|
+
const rect = container.getBoundingClientRect();
|
|
37695
|
+
state5.dragOffset = {
|
|
37696
|
+
x: e.clientX - rect.left,
|
|
37697
|
+
y: e.clientY - rect.top
|
|
37698
|
+
};
|
|
37699
|
+
const onMouseMove = (e2) => {
|
|
37700
|
+
if (!state5.isDragging) return;
|
|
37701
|
+
const newLeft = e2.clientX - state5.dragOffset.x;
|
|
37702
|
+
const newTop = e2.clientY - state5.dragOffset.y;
|
|
37703
|
+
const maxLeft = window.innerWidth - container.offsetWidth;
|
|
37704
|
+
const maxTop = window.innerHeight - container.offsetHeight;
|
|
37705
|
+
container.style.left = Math.max(0, Math.min(newLeft, maxLeft)) + "px";
|
|
37706
|
+
container.style.top = Math.max(0, Math.min(newTop, maxTop)) + "px";
|
|
37707
|
+
};
|
|
37708
|
+
const onMouseUp = () => {
|
|
37709
|
+
state5.isDragging = false;
|
|
37710
|
+
container.classList.remove("dragging");
|
|
37711
|
+
document.removeEventListener("mousemove", onMouseMove);
|
|
37712
|
+
document.removeEventListener("mouseup", onMouseUp);
|
|
37713
|
+
};
|
|
37714
|
+
document.addEventListener("mousemove", onMouseMove);
|
|
37715
|
+
document.addEventListener("mouseup", onMouseUp);
|
|
37716
|
+
};
|
|
37717
|
+
}
|
|
37718
|
+
function createPinnedClone5(container) {
|
|
37719
|
+
state5.pinnedCounter++;
|
|
37720
|
+
const pinnedId = `myio-contract-summary-tooltip-pinned-${state5.pinnedCounter}`;
|
|
37721
|
+
const clone = container.cloneNode(true);
|
|
37722
|
+
clone.id = pinnedId;
|
|
37723
|
+
clone.classList.add("pinned");
|
|
37724
|
+
clone.classList.remove("closing");
|
|
37725
|
+
const pinBtn = clone.querySelector('[data-action="pin"]');
|
|
37726
|
+
if (pinBtn) {
|
|
37727
|
+
pinBtn.classList.add("pinned");
|
|
37728
|
+
pinBtn.setAttribute("title", "Unpin");
|
|
37729
|
+
pinBtn.innerHTML = `
|
|
37730
|
+
<svg viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="1">
|
|
37731
|
+
<path d="M9 4v6l-2 4v2h10v-2l-2-4V4"/>
|
|
37732
|
+
<line x1="12" y1="16" x2="12" y2="21"/>
|
|
37733
|
+
<line x1="8" y1="4" x2="16" y2="4"/>
|
|
37734
|
+
</svg>
|
|
37735
|
+
`;
|
|
37736
|
+
}
|
|
37737
|
+
document.body.appendChild(clone);
|
|
37738
|
+
setupPinnedCloneListeners5(clone, pinnedId);
|
|
37739
|
+
ContractSummaryTooltip.hide();
|
|
37740
|
+
}
|
|
37741
|
+
function setupPinnedCloneListeners5(clone, cloneId) {
|
|
37742
|
+
let isMaximized = false;
|
|
37743
|
+
let savedPosition = null;
|
|
37744
|
+
const cloneExpandedDomains = new Set(state5.expandedDomains);
|
|
37745
|
+
const toggles = clone.querySelectorAll("[data-toggle-domain]");
|
|
37746
|
+
toggles.forEach((toggle) => {
|
|
37747
|
+
toggle.onclick = (e) => {
|
|
37748
|
+
e.stopPropagation();
|
|
37749
|
+
const domain = toggle.dataset.toggleDomain;
|
|
37750
|
+
if (!domain) return;
|
|
37751
|
+
const domainEl = clone.querySelector(`[data-domain="${domain}"]`);
|
|
37752
|
+
if (!domainEl) return;
|
|
37753
|
+
if (cloneExpandedDomains.has(domain)) {
|
|
37754
|
+
cloneExpandedDomains.delete(domain);
|
|
37755
|
+
domainEl.classList.remove("expanded");
|
|
37756
|
+
} else {
|
|
37757
|
+
cloneExpandedDomains.add(domain);
|
|
37758
|
+
domainEl.classList.add("expanded");
|
|
37759
|
+
}
|
|
37760
|
+
};
|
|
37761
|
+
});
|
|
37762
|
+
const pinBtn = clone.querySelector('[data-action="pin"]');
|
|
37763
|
+
if (pinBtn) {
|
|
37764
|
+
pinBtn.onclick = (e) => {
|
|
37765
|
+
e.stopPropagation();
|
|
37766
|
+
closePinnedClone5(cloneId);
|
|
37767
|
+
};
|
|
37768
|
+
}
|
|
37769
|
+
const closeBtn = clone.querySelector('[data-action="close"]');
|
|
37770
|
+
if (closeBtn) {
|
|
37771
|
+
closeBtn.onclick = (e) => {
|
|
37772
|
+
e.stopPropagation();
|
|
37773
|
+
closePinnedClone5(cloneId);
|
|
37774
|
+
};
|
|
37775
|
+
}
|
|
37776
|
+
const maxBtn = clone.querySelector('[data-action="maximize"]');
|
|
37777
|
+
if (maxBtn) {
|
|
37778
|
+
maxBtn.onclick = (e) => {
|
|
37779
|
+
e.stopPropagation();
|
|
37780
|
+
isMaximized = !isMaximized;
|
|
37781
|
+
if (isMaximized) {
|
|
37782
|
+
savedPosition = { left: clone.style.left, top: clone.style.top };
|
|
37783
|
+
maxBtn.innerHTML = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="5" y="5" width="14" height="14" rx="2"/><path d="M9 5V3h12v12h-2"/></svg>`;
|
|
37784
|
+
maxBtn.setAttribute("title", "Restore");
|
|
37785
|
+
} else {
|
|
37786
|
+
maxBtn.innerHTML = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/></svg>`;
|
|
37787
|
+
maxBtn.setAttribute("title", "Maximize");
|
|
37788
|
+
if (savedPosition) {
|
|
37789
|
+
clone.style.left = savedPosition.left;
|
|
37790
|
+
clone.style.top = savedPosition.top;
|
|
37791
|
+
}
|
|
37792
|
+
}
|
|
37793
|
+
clone.classList.toggle("maximized", isMaximized);
|
|
37794
|
+
};
|
|
37795
|
+
}
|
|
37796
|
+
const header = clone.querySelector("[data-drag-handle]");
|
|
37797
|
+
if (header) {
|
|
37798
|
+
let isDragging = false;
|
|
37799
|
+
let dragOffset = { x: 0, y: 0 };
|
|
37800
|
+
header.onmousedown = (e) => {
|
|
37801
|
+
if (e.target.closest("[data-action]")) return;
|
|
37802
|
+
if (e.target.closest("[data-toggle-domain]")) return;
|
|
37803
|
+
if (isMaximized) return;
|
|
37804
|
+
isDragging = true;
|
|
37805
|
+
clone.classList.add("dragging");
|
|
37806
|
+
const rect = clone.getBoundingClientRect();
|
|
37807
|
+
dragOffset = { x: e.clientX - rect.left, y: e.clientY - rect.top };
|
|
37808
|
+
const onMouseMove = (e2) => {
|
|
37809
|
+
if (!isDragging) return;
|
|
37810
|
+
const newLeft = e2.clientX - dragOffset.x;
|
|
37811
|
+
const newTop = e2.clientY - dragOffset.y;
|
|
37812
|
+
const maxLeft = window.innerWidth - clone.offsetWidth;
|
|
37813
|
+
const maxTop = window.innerHeight - clone.offsetHeight;
|
|
37814
|
+
clone.style.left = Math.max(0, Math.min(newLeft, maxLeft)) + "px";
|
|
37815
|
+
clone.style.top = Math.max(0, Math.min(newTop, maxTop)) + "px";
|
|
37816
|
+
};
|
|
37817
|
+
const onMouseUp = () => {
|
|
37818
|
+
isDragging = false;
|
|
37819
|
+
clone.classList.remove("dragging");
|
|
37820
|
+
document.removeEventListener("mousemove", onMouseMove);
|
|
37821
|
+
document.removeEventListener("mouseup", onMouseUp);
|
|
37822
|
+
};
|
|
37823
|
+
document.addEventListener("mousemove", onMouseMove);
|
|
37824
|
+
document.addEventListener("mouseup", onMouseUp);
|
|
37825
|
+
};
|
|
37826
|
+
}
|
|
37827
|
+
}
|
|
37828
|
+
function closePinnedClone5(cloneId) {
|
|
37829
|
+
const clone = document.getElementById(cloneId);
|
|
37830
|
+
if (clone) {
|
|
37831
|
+
clone.classList.add("closing");
|
|
37832
|
+
setTimeout(() => clone.remove(), 400);
|
|
37833
|
+
}
|
|
37834
|
+
}
|
|
37835
|
+
function toggleMaximize5(container) {
|
|
37836
|
+
state5.isMaximized = !state5.isMaximized;
|
|
37837
|
+
if (state5.isMaximized) {
|
|
37838
|
+
state5.savedPosition = {
|
|
37839
|
+
left: container.style.left,
|
|
37840
|
+
top: container.style.top
|
|
37841
|
+
};
|
|
37842
|
+
}
|
|
37843
|
+
container.classList.toggle("maximized", state5.isMaximized);
|
|
37844
|
+
const maxBtn = container.querySelector('[data-action="maximize"]');
|
|
37845
|
+
if (maxBtn) {
|
|
37846
|
+
if (state5.isMaximized) {
|
|
37847
|
+
maxBtn.innerHTML = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="5" y="5" width="14" height="14" rx="2"/><path d="M9 5V3h12v12h-2"/></svg>`;
|
|
37848
|
+
maxBtn.setAttribute("title", "Restore");
|
|
37849
|
+
} else {
|
|
37850
|
+
maxBtn.innerHTML = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/></svg>`;
|
|
37851
|
+
maxBtn.setAttribute("title", "Maximize");
|
|
37852
|
+
if (state5.savedPosition) {
|
|
37853
|
+
container.style.left = state5.savedPosition.left;
|
|
37854
|
+
container.style.top = state5.savedPosition.top;
|
|
37855
|
+
}
|
|
37856
|
+
}
|
|
37857
|
+
}
|
|
37858
|
+
}
|
|
37859
|
+
function startDelayedHide5() {
|
|
37860
|
+
if (state5.isMouseOverTooltip) return;
|
|
37861
|
+
if (state5.hideTimer) {
|
|
37862
|
+
clearTimeout(state5.hideTimer);
|
|
37863
|
+
}
|
|
37864
|
+
state5.hideTimer = setTimeout(() => {
|
|
37865
|
+
hideWithAnimation5();
|
|
37866
|
+
}, 1500);
|
|
37867
|
+
}
|
|
37868
|
+
function hideWithAnimation5() {
|
|
37869
|
+
const container = document.getElementById("myio-contract-summary-tooltip");
|
|
37870
|
+
if (container && container.classList.contains("visible")) {
|
|
37871
|
+
container.classList.add("closing");
|
|
37872
|
+
setTimeout(() => {
|
|
37873
|
+
container.classList.remove("visible", "closing");
|
|
37874
|
+
}, 400);
|
|
37875
|
+
}
|
|
37876
|
+
}
|
|
37877
|
+
function positionTooltip5(container, triggerElement) {
|
|
37878
|
+
const rect = triggerElement.getBoundingClientRect();
|
|
37879
|
+
let left = rect.left;
|
|
37880
|
+
let top = rect.bottom + 8;
|
|
37881
|
+
const tooltipWidth = 360;
|
|
37882
|
+
if (left + tooltipWidth > window.innerWidth - 20) {
|
|
37883
|
+
left = window.innerWidth - tooltipWidth - 20;
|
|
37884
|
+
}
|
|
37885
|
+
if (left < 10) left = 10;
|
|
37886
|
+
if (top + 500 > window.innerHeight) {
|
|
37887
|
+
top = rect.top - 8 - 500;
|
|
37888
|
+
if (top < 10) top = 10;
|
|
37889
|
+
}
|
|
37890
|
+
container.style.left = left + "px";
|
|
37891
|
+
container.style.top = top + "px";
|
|
37892
|
+
}
|
|
37893
|
+
var ContractSummaryTooltip = {
|
|
37894
|
+
containerId: "myio-contract-summary-tooltip",
|
|
37895
|
+
/**
|
|
37896
|
+
* Get or create container
|
|
37897
|
+
*/
|
|
37898
|
+
getContainer() {
|
|
37899
|
+
injectCSS9();
|
|
37900
|
+
let container = document.getElementById(this.containerId);
|
|
37901
|
+
if (!container) {
|
|
37902
|
+
container = document.createElement("div");
|
|
37903
|
+
container.id = this.containerId;
|
|
37904
|
+
container.className = "myio-contract-summary-tooltip";
|
|
37905
|
+
document.body.appendChild(container);
|
|
37906
|
+
}
|
|
37907
|
+
return container;
|
|
37908
|
+
},
|
|
37909
|
+
/**
|
|
37910
|
+
* Show tooltip
|
|
37911
|
+
*/
|
|
37912
|
+
show(triggerElement, data) {
|
|
37913
|
+
if (state5.hideTimer) {
|
|
37914
|
+
clearTimeout(state5.hideTimer);
|
|
37915
|
+
state5.hideTimer = null;
|
|
37916
|
+
}
|
|
37917
|
+
const container = this.getContainer();
|
|
37918
|
+
container.classList.remove("closing");
|
|
37919
|
+
container.innerHTML = `
|
|
37920
|
+
<div class="myio-contract-summary-tooltip__panel">
|
|
37921
|
+
${generateHeaderHTML5(data)}
|
|
37922
|
+
${generateBodyHTML3(data)}
|
|
37923
|
+
</div>
|
|
37924
|
+
`;
|
|
37925
|
+
positionTooltip5(container, triggerElement);
|
|
37926
|
+
container.classList.add("visible");
|
|
37927
|
+
setupHoverListeners5(container);
|
|
37928
|
+
setupButtonListeners5(container);
|
|
37929
|
+
setupDragListeners5(container);
|
|
37930
|
+
setupDomainToggleListeners(container);
|
|
37931
|
+
},
|
|
37932
|
+
/**
|
|
37933
|
+
* Start delayed hide
|
|
37934
|
+
*/
|
|
37935
|
+
startDelayedHide() {
|
|
37936
|
+
startDelayedHide5();
|
|
37937
|
+
},
|
|
37938
|
+
/**
|
|
37939
|
+
* Hide immediately
|
|
37940
|
+
*/
|
|
37941
|
+
hide() {
|
|
37942
|
+
if (state5.hideTimer) {
|
|
37943
|
+
clearTimeout(state5.hideTimer);
|
|
37944
|
+
state5.hideTimer = null;
|
|
37945
|
+
}
|
|
37946
|
+
state5.isMouseOverTooltip = false;
|
|
37947
|
+
const container = document.getElementById(this.containerId);
|
|
37948
|
+
if (container) {
|
|
37949
|
+
container.classList.remove("visible", "closing");
|
|
37950
|
+
}
|
|
37951
|
+
},
|
|
37952
|
+
/**
|
|
37953
|
+
* Close and reset all states
|
|
37954
|
+
*/
|
|
37955
|
+
close() {
|
|
37956
|
+
state5.isMaximized = false;
|
|
37957
|
+
state5.isDragging = false;
|
|
37958
|
+
state5.savedPosition = null;
|
|
37959
|
+
if (state5.hideTimer) {
|
|
37960
|
+
clearTimeout(state5.hideTimer);
|
|
37961
|
+
state5.hideTimer = null;
|
|
37962
|
+
}
|
|
37963
|
+
state5.isMouseOverTooltip = false;
|
|
37964
|
+
const container = document.getElementById(this.containerId);
|
|
37965
|
+
if (container) {
|
|
37966
|
+
container.classList.remove("visible", "pinned", "maximized", "dragging", "closing");
|
|
37967
|
+
}
|
|
37968
|
+
},
|
|
37969
|
+
/**
|
|
37970
|
+
* Attach tooltip to trigger element with click behavior
|
|
37971
|
+
*/
|
|
37972
|
+
attach(triggerElement, getDataFn) {
|
|
37973
|
+
const self = this;
|
|
37974
|
+
const handleClick = () => {
|
|
37975
|
+
if (state5.hideTimer) {
|
|
37976
|
+
clearTimeout(state5.hideTimer);
|
|
37977
|
+
state5.hideTimer = null;
|
|
37978
|
+
}
|
|
37979
|
+
const data = getDataFn();
|
|
37980
|
+
if (data) {
|
|
37981
|
+
self.show(triggerElement, data);
|
|
37982
|
+
}
|
|
37983
|
+
};
|
|
37984
|
+
triggerElement.addEventListener("click", handleClick);
|
|
37985
|
+
return () => {
|
|
37986
|
+
triggerElement.removeEventListener("click", handleClick);
|
|
37987
|
+
self.hide();
|
|
37988
|
+
};
|
|
37989
|
+
},
|
|
37990
|
+
/**
|
|
37991
|
+
* Attach tooltip with hover behavior
|
|
37992
|
+
*/
|
|
37993
|
+
attachHover(triggerElement, getDataFn) {
|
|
37994
|
+
const self = this;
|
|
37995
|
+
const handleMouseEnter = () => {
|
|
37996
|
+
if (state5.hideTimer) {
|
|
37997
|
+
clearTimeout(state5.hideTimer);
|
|
37998
|
+
state5.hideTimer = null;
|
|
37999
|
+
}
|
|
38000
|
+
const data = getDataFn();
|
|
38001
|
+
if (data) {
|
|
38002
|
+
self.show(triggerElement, data);
|
|
38003
|
+
}
|
|
38004
|
+
};
|
|
38005
|
+
const handleMouseLeave = () => {
|
|
38006
|
+
startDelayedHide5();
|
|
38007
|
+
};
|
|
38008
|
+
triggerElement.addEventListener("mouseenter", handleMouseEnter);
|
|
38009
|
+
triggerElement.addEventListener("mouseleave", handleMouseLeave);
|
|
38010
|
+
return () => {
|
|
38011
|
+
triggerElement.removeEventListener("mouseenter", handleMouseEnter);
|
|
38012
|
+
triggerElement.removeEventListener("mouseleave", handleMouseLeave);
|
|
38013
|
+
self.hide();
|
|
38014
|
+
};
|
|
38015
|
+
},
|
|
38016
|
+
/**
|
|
38017
|
+
* Build contract data from window.CONTRACT_STATE
|
|
38018
|
+
* Helper method to build the data structure from global state
|
|
38019
|
+
*/
|
|
38020
|
+
buildFromGlobalState() {
|
|
38021
|
+
const globalState = window.CONTRACT_STATE;
|
|
38022
|
+
if (!globalState) return null;
|
|
38023
|
+
return {
|
|
38024
|
+
isLoaded: globalState.isLoaded ?? false,
|
|
38025
|
+
isValid: globalState.isValid ?? false,
|
|
38026
|
+
timestamp: globalState.timestamp ?? null,
|
|
38027
|
+
energy: {
|
|
38028
|
+
total: globalState.energy?.total ?? 0,
|
|
38029
|
+
entries: globalState.energy?.entries ?? 0,
|
|
38030
|
+
commonArea: globalState.energy?.commonArea ?? 0,
|
|
38031
|
+
stores: globalState.energy?.stores ?? 0
|
|
38032
|
+
},
|
|
38033
|
+
water: {
|
|
38034
|
+
total: globalState.water?.total ?? 0,
|
|
38035
|
+
entries: globalState.water?.entries ?? 0,
|
|
38036
|
+
commonArea: globalState.water?.commonArea ?? 0,
|
|
38037
|
+
stores: globalState.water?.stores ?? 0
|
|
38038
|
+
},
|
|
38039
|
+
temperature: {
|
|
38040
|
+
total: globalState.temperature?.total ?? 0,
|
|
38041
|
+
internal: globalState.temperature?.internal ?? 0,
|
|
38042
|
+
stores: globalState.temperature?.stores ?? 0
|
|
38043
|
+
},
|
|
38044
|
+
discrepancies: globalState.discrepancies
|
|
38045
|
+
};
|
|
38046
|
+
}
|
|
38047
|
+
};
|
|
38048
|
+
|
|
37052
38049
|
// src/components/ModalHeader/index.ts
|
|
37053
38050
|
var DEFAULT_BG_COLOR = "#3e1a7d";
|
|
37054
38051
|
var DEFAULT_TEXT_COLOR = "white";
|
|
@@ -40807,6 +41804,7 @@
|
|
|
40807
41804
|
exports.CONSUMPTION_CHART_DEFAULTS = DEFAULT_CONFIG;
|
|
40808
41805
|
exports.CONSUMPTION_THEME_COLORS = THEME_COLORS;
|
|
40809
41806
|
exports.ConnectionStatusType = ConnectionStatusType;
|
|
41807
|
+
exports.ContractSummaryTooltip = ContractSummaryTooltip;
|
|
40810
41808
|
exports.DEFAULT_CLAMP_RANGE = DEFAULT_CLAMP_RANGE;
|
|
40811
41809
|
exports.DEFAULT_ENERGY_GROUP_COLORS = DEFAULT_ENERGY_GROUP_COLORS;
|
|
40812
41810
|
exports.DEFAULT_GAS_GROUP_COLORS = DEFAULT_GAS_GROUP_COLORS;
|