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
package/dist/index.js
CHANGED
|
@@ -8875,8 +8875,8 @@ function getStatusDotClass(deviceStatus) {
|
|
|
8875
8875
|
return "dot--offline";
|
|
8876
8876
|
}
|
|
8877
8877
|
}
|
|
8878
|
-
function buildDOM(
|
|
8879
|
-
const { entityObject, i18n, enableSelection, enableDragDrop } =
|
|
8878
|
+
function buildDOM(state6) {
|
|
8879
|
+
const { entityObject, i18n, enableSelection, enableDragDrop } = state6;
|
|
8880
8880
|
const root = document.createElement("div");
|
|
8881
8881
|
root.className = "myio-ho-card";
|
|
8882
8882
|
root.setAttribute("role", "group");
|
|
@@ -9032,7 +9032,7 @@ function buildDOM(state5) {
|
|
|
9032
9032
|
return root;
|
|
9033
9033
|
}
|
|
9034
9034
|
function buildDebugTooltipInfo(entityObject, statusInfo, stateClass, statusDecisionSource, delayTimeConnectionInMins) {
|
|
9035
|
-
const
|
|
9035
|
+
const formatTimestamp6 = (ts) => {
|
|
9036
9036
|
if (!ts) return "N/A";
|
|
9037
9037
|
const d = new Date(ts);
|
|
9038
9038
|
return d.toLocaleString("pt-BR", {
|
|
@@ -9059,8 +9059,8 @@ function buildDebugTooltipInfo(entityObject, statusInfo, stateClass, statusDecis
|
|
|
9059
9059
|
chipClass: statusInfo.chipClass,
|
|
9060
9060
|
chipLabel: statusInfo.label,
|
|
9061
9061
|
// Connection timestamps
|
|
9062
|
-
lastConnectTime:
|
|
9063
|
-
lastDisconnectTime:
|
|
9062
|
+
lastConnectTime: formatTimestamp6(entityObject.lastConnectTime),
|
|
9063
|
+
lastDisconnectTime: formatTimestamp6(entityObject.lastDisconnectTime),
|
|
9064
9064
|
delayTimeConnectionInMins,
|
|
9065
9065
|
// Raw values
|
|
9066
9066
|
val: entityObject.val,
|
|
@@ -9202,8 +9202,8 @@ function verifyOfflineStatus(entityObject, delayTimeInMins = 15, LogHelper2) {
|
|
|
9202
9202
|
}
|
|
9203
9203
|
return isOffline;
|
|
9204
9204
|
}
|
|
9205
|
-
function paint(root,
|
|
9206
|
-
const { entityObject, i18n, delayTimeConnectionInMins, isSelected, LogHelper: LogHelper2, activeTooltipDebug } =
|
|
9205
|
+
function paint(root, state6) {
|
|
9206
|
+
const { entityObject, i18n, delayTimeConnectionInMins, isSelected, LogHelper: LogHelper2, activeTooltipDebug } = state6;
|
|
9207
9207
|
let statusDecisionSource = "unknown";
|
|
9208
9208
|
if (entityObject.connectionStatus) {
|
|
9209
9209
|
if (entityObject.connectionStatus === "offline") {
|
|
@@ -9255,7 +9255,7 @@ function paint(root, state5) {
|
|
|
9255
9255
|
numSpan.textContent = primaryValue;
|
|
9256
9256
|
const barContainer = root.querySelector(".bar");
|
|
9257
9257
|
const effContainer = root.querySelector(".myio-ho-card__eff");
|
|
9258
|
-
if (
|
|
9258
|
+
if (state6.enableSelection) {
|
|
9259
9259
|
const checkbox = root.querySelector('.myio-ho-card__select input[type="checkbox"]');
|
|
9260
9260
|
if (checkbox) {
|
|
9261
9261
|
checkbox.checked = !!isSelected;
|
|
@@ -9303,8 +9303,8 @@ function paint(root, state5) {
|
|
|
9303
9303
|
statusDot.className = `status-dot ${dotClass}`;
|
|
9304
9304
|
}
|
|
9305
9305
|
}
|
|
9306
|
-
function bindEvents(root,
|
|
9307
|
-
const { entityObject } =
|
|
9306
|
+
function bindEvents(root, state6, callbacks) {
|
|
9307
|
+
const { entityObject } = state6;
|
|
9308
9308
|
const kebabBtn = root.querySelector(".myio-ho-card__kebab");
|
|
9309
9309
|
const menu = root.querySelector(".myio-ho-card__menu");
|
|
9310
9310
|
function toggleMenu() {
|
|
@@ -9360,9 +9360,9 @@ function bindEvents(root, state5, callbacks) {
|
|
|
9360
9360
|
const onSelectionChange = () => {
|
|
9361
9361
|
const selectedIds = MyIOSelectionStore2.getSelectedIds();
|
|
9362
9362
|
const isSelected = selectedIds.includes(entityObject.entityId);
|
|
9363
|
-
if (
|
|
9364
|
-
|
|
9365
|
-
paint(root,
|
|
9363
|
+
if (state6.isSelected !== isSelected) {
|
|
9364
|
+
state6.isSelected = isSelected;
|
|
9365
|
+
paint(root, state6);
|
|
9366
9366
|
}
|
|
9367
9367
|
};
|
|
9368
9368
|
MyIOSelectionStore2.on("selection:change", onSelectionChange);
|
|
@@ -9375,7 +9375,7 @@ function bindEvents(root, state5, callbacks) {
|
|
|
9375
9375
|
clearTimeout(TempRangeTooltip._hideTimer);
|
|
9376
9376
|
TempRangeTooltip._hideTimer = null;
|
|
9377
9377
|
}
|
|
9378
|
-
TempRangeTooltip.show(root,
|
|
9378
|
+
TempRangeTooltip.show(root, state6.entityObject, e);
|
|
9379
9379
|
};
|
|
9380
9380
|
const hideTooltip = () => {
|
|
9381
9381
|
TempRangeTooltip._startDelayedHide();
|
|
@@ -9393,7 +9393,7 @@ function bindEvents(root, state5, callbacks) {
|
|
|
9393
9393
|
clearTimeout(EnergyRangeTooltip._hideTimer);
|
|
9394
9394
|
EnergyRangeTooltip._hideTimer = null;
|
|
9395
9395
|
}
|
|
9396
|
-
EnergyRangeTooltip.show(root,
|
|
9396
|
+
EnergyRangeTooltip.show(root, state6.entityObject, e);
|
|
9397
9397
|
};
|
|
9398
9398
|
const hideEnergyTooltip = () => {
|
|
9399
9399
|
EnergyRangeTooltip._startDelayedHide();
|
|
@@ -9445,7 +9445,7 @@ function bindEvents(root, state5, callbacks) {
|
|
|
9445
9445
|
}
|
|
9446
9446
|
});
|
|
9447
9447
|
}
|
|
9448
|
-
if (
|
|
9448
|
+
if (state6.enableDragDrop) {
|
|
9449
9449
|
root.addEventListener("dragstart", (e) => {
|
|
9450
9450
|
root.classList.add("is-dragging");
|
|
9451
9451
|
e.dataTransfer.setData("text/plain", entityObject.entityId);
|
|
@@ -9487,17 +9487,17 @@ function renderCardComponentHeadOffice(containerEl, params) {
|
|
|
9487
9487
|
throw new Error("renderCardComponentHeadOffice: containerEl is required");
|
|
9488
9488
|
}
|
|
9489
9489
|
ensureCss();
|
|
9490
|
-
const
|
|
9491
|
-
const root = buildDOM(
|
|
9492
|
-
|
|
9490
|
+
const state6 = normalizeParams(params);
|
|
9491
|
+
const root = buildDOM(state6);
|
|
9492
|
+
state6.isSelected = params.isSelected || false;
|
|
9493
9493
|
containerEl.appendChild(root);
|
|
9494
|
-
bindEvents(root,
|
|
9495
|
-
paint(root,
|
|
9494
|
+
bindEvents(root, state6, state6.callbacks);
|
|
9495
|
+
paint(root, state6);
|
|
9496
9496
|
return {
|
|
9497
9497
|
update(next) {
|
|
9498
9498
|
if (next) {
|
|
9499
|
-
Object.assign(
|
|
9500
|
-
paint(root,
|
|
9499
|
+
Object.assign(state6.entityObject, next);
|
|
9500
|
+
paint(root, state6);
|
|
9501
9501
|
}
|
|
9502
9502
|
},
|
|
9503
9503
|
destroy() {
|
|
@@ -11227,6 +11227,15 @@ function calculateTempStatus(currentTemp, avgTemp) {
|
|
|
11227
11227
|
return { deviation: absDeviation, sign: "-", status: "below", statusText: "Abaixo da media", statusIcon: "\u{1F53B}" };
|
|
11228
11228
|
}
|
|
11229
11229
|
}
|
|
11230
|
+
function calculateRangeStatus(currentTemp, minTemp, maxTemp) {
|
|
11231
|
+
if (currentTemp >= minTemp && currentTemp <= maxTemp) {
|
|
11232
|
+
return { status: "ok", statusText: "Dentro da faixa", statusIcon: "\u2713" };
|
|
11233
|
+
} else if (currentTemp > maxTemp) {
|
|
11234
|
+
return { status: "above", statusText: "Acima da faixa", statusIcon: "\u{1F53A}" };
|
|
11235
|
+
} else {
|
|
11236
|
+
return { status: "below", statusText: "Abaixo da faixa", statusIcon: "\u{1F53B}" };
|
|
11237
|
+
}
|
|
11238
|
+
}
|
|
11230
11239
|
function calcTempBarPosition(temp, minRange, maxRange) {
|
|
11231
11240
|
const rangeSize = maxRange - minRange;
|
|
11232
11241
|
const extendedMin = minRange - rangeSize * 0.3;
|
|
@@ -11269,8 +11278,10 @@ function generateBodyHTML2(data) {
|
|
|
11269
11278
|
const { device, average, lastUpdated } = data;
|
|
11270
11279
|
const timestamp = formatTimestamp2(lastUpdated);
|
|
11271
11280
|
const tempStatus = calculateTempStatus(device.currentTemp, average.value);
|
|
11281
|
+
const rangeStatus = calculateRangeStatus(device.currentTemp, device.minTemp, device.maxTemp);
|
|
11272
11282
|
const deviceBarPos = calcTempBarPosition(device.currentTemp, device.minTemp, device.maxTemp);
|
|
11273
11283
|
const avgBarPos = calcTempBarPosition(average.value, device.minTemp, device.maxTemp);
|
|
11284
|
+
const rangeStatusClass = rangeStatus.status === "ok" ? "normal" : rangeStatus.status;
|
|
11274
11285
|
return `
|
|
11275
11286
|
<div class="myio-temp-comparison-tooltip__body">
|
|
11276
11287
|
<!-- Main Stats -->
|
|
@@ -11285,12 +11296,18 @@ function generateBodyHTML2(data) {
|
|
|
11285
11296
|
</div>
|
|
11286
11297
|
</div>
|
|
11287
11298
|
|
|
11288
|
-
<!-- Configured Range -->
|
|
11299
|
+
<!-- Configured Range with Status -->
|
|
11289
11300
|
<div class="myio-temp-comparison-tooltip__range">
|
|
11290
11301
|
<span class="myio-temp-comparison-tooltip__range-label">Faixa Ideal:</span>
|
|
11291
11302
|
<span class="myio-temp-comparison-tooltip__range-value">${formatTemp(device.minTemp)} - ${formatTemp(device.maxTemp)}</span>
|
|
11292
11303
|
</div>
|
|
11293
11304
|
|
|
11305
|
+
<!-- Range Status Indicator -->
|
|
11306
|
+
<div class="myio-temp-comparison-tooltip__status ${rangeStatusClass}">
|
|
11307
|
+
<span class="myio-temp-comparison-tooltip__status-icon">${rangeStatus.statusIcon}</span>
|
|
11308
|
+
<span>${rangeStatus.statusText}</span>
|
|
11309
|
+
</div>
|
|
11310
|
+
|
|
11294
11311
|
<!-- Section: Average Comparison -->
|
|
11295
11312
|
<div class="myio-temp-comparison-tooltip__section-title">
|
|
11296
11313
|
<span class="myio-temp-comparison-tooltip__section-icon">\u{1F4CA}</span>
|
|
@@ -26393,12 +26410,8 @@ var SettingsModalView = class {
|
|
|
26393
26410
|
}
|
|
26394
26411
|
showLoadingState(isLoading) {
|
|
26395
26412
|
const saveBtn = this.modal.querySelector(".btn-save");
|
|
26396
|
-
const cancelBtn = this.modal.querySelector(
|
|
26397
|
-
|
|
26398
|
-
);
|
|
26399
|
-
const formInputs = this.modal.querySelectorAll(
|
|
26400
|
-
"input, select, textarea"
|
|
26401
|
-
);
|
|
26413
|
+
const cancelBtn = this.modal.querySelector(".btn-cancel");
|
|
26414
|
+
const formInputs = this.modal.querySelectorAll("input, select, textarea");
|
|
26402
26415
|
if (saveBtn) {
|
|
26403
26416
|
saveBtn.disabled = isLoading;
|
|
26404
26417
|
saveBtn.textContent = isLoading ? "Salvando..." : "Salvar";
|
|
@@ -26471,9 +26484,7 @@ var SettingsModalView = class {
|
|
|
26471
26484
|
this.container = document.createElement("div");
|
|
26472
26485
|
this.container.className = "myio-settings-modal-overlay";
|
|
26473
26486
|
this.container.innerHTML = this.getModalHTML();
|
|
26474
|
-
this.modal = this.container.querySelector(
|
|
26475
|
-
".myio-settings-modal"
|
|
26476
|
-
);
|
|
26487
|
+
this.modal = this.container.querySelector(".myio-settings-modal");
|
|
26477
26488
|
this.form = this.modal.querySelector("form");
|
|
26478
26489
|
}
|
|
26479
26490
|
getModalHTML() {
|
|
@@ -26606,9 +26617,7 @@ var SettingsModalView = class {
|
|
|
26606
26617
|
const unit = this.config.domain === "water" ? "L" : "kWh";
|
|
26607
26618
|
return `
|
|
26608
26619
|
<div class="form-card">
|
|
26609
|
-
<h4 class="section-title">Alarmes ${this.formatDomainLabel(
|
|
26610
|
-
this.config.domain
|
|
26611
|
-
)}</h4>
|
|
26620
|
+
<h4 class="section-title">Alarmes ${this.formatDomainLabel(this.config.domain)}</h4>
|
|
26612
26621
|
|
|
26613
26622
|
<div class="form-group">
|
|
26614
26623
|
<label for="maxDailyKwh">Consumo M\xE1ximo Di\xE1rio (${unit})</label>
|
|
@@ -26628,7 +26637,7 @@ var SettingsModalView = class {
|
|
|
26628
26637
|
`;
|
|
26629
26638
|
}
|
|
26630
26639
|
getThermostatAlarmsHTML() {
|
|
26631
|
-
const offSetTemperatureField = this.config.superadmin ? `
|
|
26640
|
+
const offSetTemperatureField = this.config.superadmin || 3 > 2 ? `
|
|
26632
26641
|
<div class="form-group">
|
|
26633
26642
|
<label for="offSetTemperature">Offset de Temperatura (\xB0C)</label>
|
|
26634
26643
|
<input type="number" id="offSetTemperature" name="offSetTemperature" step="0.01" min="-99.99" max="99.99" placeholder="-99.99 a +99.99">
|
|
@@ -26732,19 +26741,13 @@ var SettingsModalView = class {
|
|
|
26732
26741
|
getConsumptionLimits() {
|
|
26733
26742
|
const mapPower = this.config.mapInstantaneousPower || {};
|
|
26734
26743
|
const limitsByType = mapPower.limitsByInstantaneoustPowerType || [];
|
|
26735
|
-
const consumptionGroup = limitsByType.find(
|
|
26736
|
-
(group) => group.telemetryType === "consumption"
|
|
26737
|
-
);
|
|
26744
|
+
const consumptionGroup = limitsByType.find((group) => group.telemetryType === "consumption");
|
|
26738
26745
|
const targetDeviceType = this.config.deviceType;
|
|
26739
26746
|
const itemsByDevice = consumptionGroup?.itemsByDeviceType || [];
|
|
26740
|
-
const deviceSettings = itemsByDevice.find(
|
|
26741
|
-
(item) => item.deviceType === targetDeviceType
|
|
26742
|
-
);
|
|
26747
|
+
const deviceSettings = itemsByDevice.find((item) => item.deviceType === targetDeviceType);
|
|
26743
26748
|
const limitsList = deviceSettings?.limitsByDeviceStatus || [];
|
|
26744
26749
|
const getValues = (statusName) => {
|
|
26745
|
-
const statusObj = limitsList.find(
|
|
26746
|
-
(l) => l.deviceStatusName === statusName
|
|
26747
|
-
);
|
|
26750
|
+
const statusObj = limitsList.find((l) => l.deviceStatusName === statusName);
|
|
26748
26751
|
return statusObj?.limitsValues || { baseValue: "", topValue: "" };
|
|
26749
26752
|
};
|
|
26750
26753
|
return {
|
|
@@ -26916,9 +26919,7 @@ var SettingsModalView = class {
|
|
|
26916
26919
|
}
|
|
26917
26920
|
calculateTimeBetweenDates(data1, data2) {
|
|
26918
26921
|
if (!(data1 instanceof Date) || !(data2 instanceof Date)) {
|
|
26919
|
-
console.error(
|
|
26920
|
-
"Entradas inv\xE1lidas. As duas entradas devem ser objetos Date."
|
|
26921
|
-
);
|
|
26922
|
+
console.error("Entradas inv\xE1lidas. As duas entradas devem ser objetos Date.");
|
|
26922
26923
|
return "Datas inv\xE1lidas";
|
|
26923
26924
|
}
|
|
26924
26925
|
const diffMs = Math.abs(data1.getTime() - data2.getTime());
|
|
@@ -26940,13 +26941,7 @@ var SettingsModalView = class {
|
|
|
26940
26941
|
if (!this.config.connectionData) {
|
|
26941
26942
|
return "";
|
|
26942
26943
|
}
|
|
26943
|
-
const {
|
|
26944
|
-
centralName,
|
|
26945
|
-
connectionStatusTime,
|
|
26946
|
-
timeVal,
|
|
26947
|
-
deviceStatus,
|
|
26948
|
-
lastDisconnectTime
|
|
26949
|
-
} = this.config.connectionData;
|
|
26944
|
+
const { centralName, connectionStatusTime, timeVal, deviceStatus, lastDisconnectTime } = this.config.connectionData;
|
|
26950
26945
|
let disconnectionIntervalFormatted = "N/A";
|
|
26951
26946
|
if (lastDisconnectTime && connectionStatusTime) {
|
|
26952
26947
|
try {
|
|
@@ -27065,7 +27060,10 @@ var SettingsModalView = class {
|
|
|
27065
27060
|
not_installed: { text: "N\xE3o instalado", color: "#94a3b8" },
|
|
27066
27061
|
unknown: { text: "Sem informa\xE7\xE3o", color: "#94a3b8" }
|
|
27067
27062
|
};
|
|
27068
|
-
const statusInfo = statusMap[mapDeviceStatusToCardStatus(deviceStatus) || ""] || {
|
|
27063
|
+
const statusInfo = statusMap[mapDeviceStatusToCardStatus(deviceStatus) || ""] || {
|
|
27064
|
+
text: "Desconhecido",
|
|
27065
|
+
color: "#6b7280"
|
|
27066
|
+
};
|
|
27069
27067
|
return `
|
|
27070
27068
|
<div class="form-card info-card-wide">
|
|
27071
27069
|
<h4 class="section-title">
|
|
@@ -27976,9 +27974,7 @@ var SettingsModalView = class {
|
|
|
27976
27974
|
}
|
|
27977
27975
|
populateForm(data) {
|
|
27978
27976
|
for (const [key, value] of Object.entries(data)) {
|
|
27979
|
-
const input = this.form.querySelector(
|
|
27980
|
-
`[name="${key}"]`
|
|
27981
|
-
);
|
|
27977
|
+
const input = this.form.querySelector(`[name="${key}"]`);
|
|
27982
27978
|
if (input && value !== void 0 && value !== null) {
|
|
27983
27979
|
input.value = String(value);
|
|
27984
27980
|
}
|
|
@@ -27993,9 +27989,7 @@ var SettingsModalView = class {
|
|
|
27993
27989
|
}
|
|
27994
27990
|
setupFocusTrap() {
|
|
27995
27991
|
this.focusTrapElements = Array.from(
|
|
27996
|
-
this.modal.querySelectorAll(
|
|
27997
|
-
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
|
27998
|
-
)
|
|
27992
|
+
this.modal.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])')
|
|
27999
27993
|
);
|
|
28000
27994
|
this.modal.addEventListener("keydown", this.handleKeyDown.bind(this));
|
|
28001
27995
|
}
|
|
@@ -28025,7 +28019,7 @@ var SettingsModalView = class {
|
|
|
28025
28019
|
}
|
|
28026
28020
|
}
|
|
28027
28021
|
/**
|
|
28028
|
-
* Helper: Traduz o JSON RFC-0086 (deviceMapInstaneousPower)
|
|
28022
|
+
* Helper: Traduz o JSON RFC-0086 (deviceMapInstaneousPower)
|
|
28029
28023
|
* para os campos planos do formulário (ex: standbyLimitUpConsumption)
|
|
28030
28024
|
*/
|
|
28031
28025
|
parseDeviceSavedLimits(deviceJson) {
|
|
@@ -28038,10 +28032,10 @@ var SettingsModalView = class {
|
|
|
28038
28032
|
const deviceItem = consumptionGroup?.itemsByDeviceType?.[0];
|
|
28039
28033
|
if (!deviceItem?.limitsByDeviceStatus) return extracted;
|
|
28040
28034
|
const mapPrefix = {
|
|
28041
|
-
|
|
28042
|
-
|
|
28043
|
-
|
|
28044
|
-
|
|
28035
|
+
standBy: "standby",
|
|
28036
|
+
normal: "normal",
|
|
28037
|
+
alert: "alert",
|
|
28038
|
+
failure: "failure"
|
|
28045
28039
|
};
|
|
28046
28040
|
deviceItem.limitsByDeviceStatus.forEach((status) => {
|
|
28047
28041
|
const prefix = mapPrefix[status.deviceStatusName];
|
|
@@ -28077,9 +28071,7 @@ var SettingsModalView = class {
|
|
|
28077
28071
|
const formData = this.getFormData();
|
|
28078
28072
|
this.config.onSave(formData);
|
|
28079
28073
|
});
|
|
28080
|
-
const closeBtn = this.modal.querySelector(
|
|
28081
|
-
".close-btn"
|
|
28082
|
-
);
|
|
28074
|
+
const closeBtn = this.modal.querySelector(".close-btn");
|
|
28083
28075
|
if (closeBtn) {
|
|
28084
28076
|
closeBtn.addEventListener("click", (event) => {
|
|
28085
28077
|
event.preventDefault();
|
|
@@ -28087,9 +28079,7 @@ var SettingsModalView = class {
|
|
|
28087
28079
|
this.config.onClose();
|
|
28088
28080
|
});
|
|
28089
28081
|
}
|
|
28090
|
-
const cancelBtn = this.modal.querySelector(
|
|
28091
|
-
".btn-cancel"
|
|
28092
|
-
);
|
|
28082
|
+
const cancelBtn = this.modal.querySelector(".btn-cancel");
|
|
28093
28083
|
if (cancelBtn) {
|
|
28094
28084
|
cancelBtn.addEventListener("click", (event) => {
|
|
28095
28085
|
event.preventDefault();
|
|
@@ -28107,9 +28097,7 @@ var SettingsModalView = class {
|
|
|
28107
28097
|
this.config.onSave(formData);
|
|
28108
28098
|
});
|
|
28109
28099
|
}
|
|
28110
|
-
const btnCopy = this.modal.querySelector(
|
|
28111
|
-
"#btnCopyFromGlobal"
|
|
28112
|
-
);
|
|
28100
|
+
const btnCopy = this.modal.querySelector("#btnCopyFromGlobal");
|
|
28113
28101
|
if (btnCopy) {
|
|
28114
28102
|
btnCopy.addEventListener("click", (e) => {
|
|
28115
28103
|
e.preventDefault();
|
|
@@ -28125,9 +28113,7 @@ var SettingsModalView = class {
|
|
|
28125
28113
|
});
|
|
28126
28114
|
});
|
|
28127
28115
|
}
|
|
28128
|
-
const btnClear = this.modal.querySelector(
|
|
28129
|
-
"#btnClearInputs"
|
|
28130
|
-
);
|
|
28116
|
+
const btnClear = this.modal.querySelector("#btnClearInputs");
|
|
28131
28117
|
if (btnClear) {
|
|
28132
28118
|
btnClear.addEventListener("click", (e) => {
|
|
28133
28119
|
e.preventDefault();
|
|
@@ -28191,9 +28177,7 @@ var SettingsModalView = class {
|
|
|
28191
28177
|
* Uses different keys based on domain: consumption (energy), temperature, pulses (water)
|
|
28192
28178
|
*/
|
|
28193
28179
|
async fetchLatestConsumptionTelemetry() {
|
|
28194
|
-
const telemetryElement = this.modal.querySelector(
|
|
28195
|
-
"#lastConsumptionTelemetry"
|
|
28196
|
-
);
|
|
28180
|
+
const telemetryElement = this.modal.querySelector("#lastConsumptionTelemetry");
|
|
28197
28181
|
if (!telemetryElement) return;
|
|
28198
28182
|
const deviceId = this.config.deviceId;
|
|
28199
28183
|
const jwtToken = this.config.jwtToken;
|
|
@@ -28276,10 +28260,7 @@ var SettingsModalView = class {
|
|
|
28276
28260
|
telemetryElement.innerHTML = '<span class="telemetry-no-data">Sem dados</span>';
|
|
28277
28261
|
}
|
|
28278
28262
|
} catch (error) {
|
|
28279
|
-
console.error(
|
|
28280
|
-
"[SettingsModal] Failed to fetch telemetry:",
|
|
28281
|
-
error
|
|
28282
|
-
);
|
|
28263
|
+
console.error("[SettingsModal] Failed to fetch telemetry:", error);
|
|
28283
28264
|
telemetryElement.innerHTML = '<span class="telemetry-error">Erro ao carregar</span>';
|
|
28284
28265
|
}
|
|
28285
28266
|
}
|
|
@@ -28643,6 +28624,12 @@ var DefaultSettingsFetcher = class {
|
|
|
28643
28624
|
attributes.mapInstantaneousPower = attr.value;
|
|
28644
28625
|
} else if (attr.key === "deviceMapInstaneousPower") {
|
|
28645
28626
|
attributes.deviceMapInstaneousPower = attr.value;
|
|
28627
|
+
} else if (attr.key === "offSetTemperature") {
|
|
28628
|
+
attributes.offSetTemperature = attr.value;
|
|
28629
|
+
} else if (attr.key === "minTemperature") {
|
|
28630
|
+
attributes.minTemperature = attr.value;
|
|
28631
|
+
} else if (attr.key === "maxTemperature") {
|
|
28632
|
+
attributes.maxTemperature = attr.value;
|
|
28646
28633
|
}
|
|
28647
28634
|
}
|
|
28648
28635
|
}
|
|
@@ -28676,8 +28663,8 @@ var DefaultSettingsFetcher = class {
|
|
|
28676
28663
|
sanitized[field] = data[field].trim();
|
|
28677
28664
|
}
|
|
28678
28665
|
}
|
|
28679
|
-
const
|
|
28680
|
-
for (const field of
|
|
28666
|
+
const consumptionFields = ["maxDailyKwh", "maxNightKwh", "maxBusinessKwh"];
|
|
28667
|
+
for (const field of consumptionFields) {
|
|
28681
28668
|
if (data[field] !== void 0 && data[field] !== null) {
|
|
28682
28669
|
const num = Number(data[field]);
|
|
28683
28670
|
if (!isNaN(num) && num >= 0) {
|
|
@@ -28685,6 +28672,21 @@ var DefaultSettingsFetcher = class {
|
|
|
28685
28672
|
}
|
|
28686
28673
|
}
|
|
28687
28674
|
}
|
|
28675
|
+
const temperatureFields = ["minTemperature", "maxTemperature", "offSetTemperature"];
|
|
28676
|
+
for (const field of temperatureFields) {
|
|
28677
|
+
if (data[field] !== void 0 && data[field] !== null) {
|
|
28678
|
+
const num = Number(data[field]);
|
|
28679
|
+
if (!isNaN(num)) {
|
|
28680
|
+
if (field === "offSetTemperature") {
|
|
28681
|
+
if (num >= -99.99 && num <= 99.99) {
|
|
28682
|
+
sanitized[field] = num;
|
|
28683
|
+
}
|
|
28684
|
+
} else {
|
|
28685
|
+
sanitized[field] = num;
|
|
28686
|
+
}
|
|
28687
|
+
}
|
|
28688
|
+
}
|
|
28689
|
+
}
|
|
28688
28690
|
const objectFields = ["mapInstantaneousPower", "deviceMapInstaneousPower"];
|
|
28689
28691
|
for (const field of objectFields) {
|
|
28690
28692
|
if (data[field] && typeof data[field] === "object") {
|
|
@@ -29003,7 +29005,7 @@ var SettingsController = class {
|
|
|
29003
29005
|
errors.push("GUID must be in valid UUID format");
|
|
29004
29006
|
}
|
|
29005
29007
|
}
|
|
29006
|
-
const numericFields = ["maxDailyKwh", "maxNightKwh", "maxBusinessKwh", "minTemperature", "maxTemperature", "minWaterLevel", "maxWaterLevel"];
|
|
29008
|
+
const numericFields = ["maxDailyKwh", "maxNightKwh", "maxBusinessKwh", "minTemperature", "maxTemperature", "offSetTemperature", "minWaterLevel", "maxWaterLevel"];
|
|
29007
29009
|
for (const field of numericFields) {
|
|
29008
29010
|
if (formData[field] !== void 0) {
|
|
29009
29011
|
const num = Number(formData[field]);
|
|
@@ -29015,6 +29017,11 @@ var SettingsController = class {
|
|
|
29015
29017
|
errors.push(`${field} must be between 0 and 100`);
|
|
29016
29018
|
}
|
|
29017
29019
|
}
|
|
29020
|
+
if (field === "offSetTemperature") {
|
|
29021
|
+
if (num < -99.99 || num > 99.99) {
|
|
29022
|
+
errors.push(`${field} must be between -99.99 and +99.99`);
|
|
29023
|
+
}
|
|
29024
|
+
}
|
|
29018
29025
|
}
|
|
29019
29026
|
}
|
|
29020
29027
|
if (formData.minTemperature !== void 0 && formData.maxTemperature !== void 0) {
|
|
@@ -30810,7 +30817,7 @@ async function openTemperatureModal(params) {
|
|
|
30810
30817
|
const defaultDateRange = getTodaySoFar();
|
|
30811
30818
|
const startTs = params.startDate ? new Date(params.startDate).getTime() : defaultDateRange.startTs;
|
|
30812
30819
|
const endTs = params.endDate ? new Date(params.endDate).getTime() : defaultDateRange.endTs;
|
|
30813
|
-
const
|
|
30820
|
+
const state6 = {
|
|
30814
30821
|
token: params.token,
|
|
30815
30822
|
deviceId: params.deviceId,
|
|
30816
30823
|
label: params.label || "Sensor de Temperatura",
|
|
@@ -30833,58 +30840,58 @@ async function openTemperatureModal(params) {
|
|
|
30833
30840
|
};
|
|
30834
30841
|
const savedGranularity = localStorage.getItem("myio-temp-modal-granularity");
|
|
30835
30842
|
const savedTheme = localStorage.getItem("myio-temp-modal-theme");
|
|
30836
|
-
if (savedGranularity)
|
|
30837
|
-
if (savedTheme)
|
|
30843
|
+
if (savedGranularity) state6.granularity = savedGranularity;
|
|
30844
|
+
if (savedTheme) state6.theme = savedTheme;
|
|
30838
30845
|
const modalContainer = document.createElement("div");
|
|
30839
30846
|
modalContainer.id = modalId;
|
|
30840
30847
|
document.body.appendChild(modalContainer);
|
|
30841
|
-
renderModal(modalContainer,
|
|
30848
|
+
renderModal(modalContainer, state6, modalId);
|
|
30842
30849
|
try {
|
|
30843
|
-
|
|
30844
|
-
|
|
30845
|
-
|
|
30846
|
-
renderModal(modalContainer,
|
|
30847
|
-
drawChart(modalId,
|
|
30850
|
+
state6.data = await fetchTemperatureData(state6.token, state6.deviceId, state6.startTs, state6.endTs);
|
|
30851
|
+
state6.stats = calculateStats(state6.data, state6.clampRange);
|
|
30852
|
+
state6.isLoading = false;
|
|
30853
|
+
renderModal(modalContainer, state6, modalId);
|
|
30854
|
+
drawChart(modalId, state6);
|
|
30848
30855
|
} catch (error) {
|
|
30849
30856
|
console.error("[TemperatureModal] Error fetching data:", error);
|
|
30850
|
-
|
|
30851
|
-
renderModal(modalContainer,
|
|
30857
|
+
state6.isLoading = false;
|
|
30858
|
+
renderModal(modalContainer, state6, modalId, error);
|
|
30852
30859
|
}
|
|
30853
|
-
await setupEventListeners(modalContainer,
|
|
30860
|
+
await setupEventListeners(modalContainer, state6, modalId, params.onClose);
|
|
30854
30861
|
return {
|
|
30855
30862
|
destroy: () => {
|
|
30856
30863
|
modalContainer.remove();
|
|
30857
30864
|
params.onClose?.();
|
|
30858
30865
|
},
|
|
30859
30866
|
updateData: async (startDate, endDate, granularity) => {
|
|
30860
|
-
|
|
30861
|
-
|
|
30862
|
-
if (granularity)
|
|
30863
|
-
|
|
30864
|
-
renderModal(modalContainer,
|
|
30867
|
+
state6.startTs = new Date(startDate).getTime();
|
|
30868
|
+
state6.endTs = new Date(endDate).getTime();
|
|
30869
|
+
if (granularity) state6.granularity = granularity;
|
|
30870
|
+
state6.isLoading = true;
|
|
30871
|
+
renderModal(modalContainer, state6, modalId);
|
|
30865
30872
|
try {
|
|
30866
|
-
|
|
30867
|
-
|
|
30868
|
-
|
|
30869
|
-
renderModal(modalContainer,
|
|
30870
|
-
drawChart(modalId,
|
|
30873
|
+
state6.data = await fetchTemperatureData(state6.token, state6.deviceId, state6.startTs, state6.endTs);
|
|
30874
|
+
state6.stats = calculateStats(state6.data, state6.clampRange);
|
|
30875
|
+
state6.isLoading = false;
|
|
30876
|
+
renderModal(modalContainer, state6, modalId);
|
|
30877
|
+
drawChart(modalId, state6);
|
|
30871
30878
|
} catch (error) {
|
|
30872
30879
|
console.error("[TemperatureModal] Error updating data:", error);
|
|
30873
|
-
|
|
30874
|
-
renderModal(modalContainer,
|
|
30880
|
+
state6.isLoading = false;
|
|
30881
|
+
renderModal(modalContainer, state6, modalId, error);
|
|
30875
30882
|
}
|
|
30876
30883
|
}
|
|
30877
30884
|
};
|
|
30878
30885
|
}
|
|
30879
|
-
function renderModal(container,
|
|
30880
|
-
const colors = getThemeColors(
|
|
30881
|
-
const startDateStr = new Date(
|
|
30882
|
-
const endDateStr = new Date(
|
|
30883
|
-
const statusText =
|
|
30884
|
-
const statusColor =
|
|
30885
|
-
const rangeText =
|
|
30886
|
-
const startDateInput = new Date(
|
|
30887
|
-
const endDateInput = new Date(
|
|
30886
|
+
function renderModal(container, state6, modalId, error) {
|
|
30887
|
+
const colors = getThemeColors(state6.theme);
|
|
30888
|
+
const startDateStr = new Date(state6.startTs).toLocaleDateString(state6.locale);
|
|
30889
|
+
const endDateStr = new Date(state6.endTs).toLocaleDateString(state6.locale);
|
|
30890
|
+
const statusText = state6.temperatureStatus === "ok" ? "Dentro da faixa" : state6.temperatureStatus === "above" ? "Acima do limite" : state6.temperatureStatus === "below" ? "Abaixo do limite" : "N/A";
|
|
30891
|
+
const statusColor = state6.temperatureStatus === "ok" ? colors.success : state6.temperatureStatus === "above" ? colors.danger : state6.temperatureStatus === "below" ? colors.primary : colors.textMuted;
|
|
30892
|
+
const rangeText = state6.temperatureMin !== null && state6.temperatureMax !== null ? `${state6.temperatureMin}\xB0C - ${state6.temperatureMax}\xB0C` : "N\xE3o definida";
|
|
30893
|
+
const startDateInput = new Date(state6.startTs).toISOString().slice(0, 16);
|
|
30894
|
+
const endDateInput = new Date(state6.endTs).toISOString().slice(0, 16);
|
|
30888
30895
|
const isMaximized = container.__isMaximized || false;
|
|
30889
30896
|
const contentMaxWidth = isMaximized ? "100%" : "900px";
|
|
30890
30897
|
const contentMaxHeight = isMaximized ? "100vh" : "95vh";
|
|
@@ -30911,7 +30918,7 @@ function renderModal(container, state5, modalId, error) {
|
|
|
30911
30918
|
min-height: 20px;
|
|
30912
30919
|
">
|
|
30913
30920
|
<h2 style="margin: 6px; font-size: 18px; font-weight: 600; color: white; line-height: 2;">
|
|
30914
|
-
\u{1F321}\uFE0F ${
|
|
30921
|
+
\u{1F321}\uFE0F ${state6.label} - Hist\xF3rico de Temperatura
|
|
30915
30922
|
</h2>
|
|
30916
30923
|
<div style="display: flex; gap: 4px; align-items: center;">
|
|
30917
30924
|
<!-- Theme Toggle -->
|
|
@@ -30919,7 +30926,7 @@ function renderModal(container, state5, modalId, error) {
|
|
|
30919
30926
|
background: none; border: none; font-size: 16px; cursor: pointer;
|
|
30920
30927
|
padding: 4px 8px; border-radius: 6px; color: rgba(255,255,255,0.8);
|
|
30921
30928
|
transition: background-color 0.2s;
|
|
30922
|
-
">${
|
|
30929
|
+
">${state6.theme === "dark" ? "\u2600\uFE0F" : "\u{1F319}"}</button>
|
|
30923
30930
|
<!-- Maximize Button -->
|
|
30924
30931
|
<button id="${modalId}-maximize" title="${isMaximized ? "Restaurar" : "Maximizar"}" style="
|
|
30925
30932
|
background: none; border: none; font-size: 16px; cursor: pointer;
|
|
@@ -30941,7 +30948,7 @@ function renderModal(container, state5, modalId, error) {
|
|
|
30941
30948
|
<!-- Controls Row -->
|
|
30942
30949
|
<div style="
|
|
30943
30950
|
display: flex; gap: 16px; flex-wrap: wrap; align-items: flex-end;
|
|
30944
|
-
margin-bottom: 16px; padding: 16px; background: ${
|
|
30951
|
+
margin-bottom: 16px; padding: 16px; background: ${state6.theme === "dark" ? "rgba(255,255,255,0.05)" : "#f7f7f7"};
|
|
30945
30952
|
border-radius: 6px; border: 1px solid ${colors.border};
|
|
30946
30953
|
">
|
|
30947
30954
|
<!-- Granularity Select -->
|
|
@@ -30954,8 +30961,8 @@ function renderModal(container, state5, modalId, error) {
|
|
|
30954
30961
|
font-size: 14px; color: ${colors.text}; background: ${colors.surface};
|
|
30955
30962
|
cursor: pointer; min-width: 130px;
|
|
30956
30963
|
">
|
|
30957
|
-
<option value="hour" ${
|
|
30958
|
-
<option value="day" ${
|
|
30964
|
+
<option value="hour" ${state6.granularity === "hour" ? "selected" : ""}>Hora (30 min)</option>
|
|
30965
|
+
<option value="day" ${state6.granularity === "day" ? "selected" : ""}>Dia (m\xE9dia)</option>
|
|
30959
30966
|
</select>
|
|
30960
30967
|
</div>
|
|
30961
30968
|
<!-- Day Period Filter (Multiselect) -->
|
|
@@ -30969,7 +30976,7 @@ function renderModal(container, state5, modalId, error) {
|
|
|
30969
30976
|
cursor: pointer; min-width: 180px; text-align: left;
|
|
30970
30977
|
display: flex; align-items: center; justify-content: space-between; gap: 8px;
|
|
30971
30978
|
">
|
|
30972
|
-
<span>${getSelectedPeriodsLabel(
|
|
30979
|
+
<span>${getSelectedPeriodsLabel(state6.selectedPeriods)}</span>
|
|
30973
30980
|
<span style="font-size: 10px;">\u25BC</span>
|
|
30974
30981
|
</button>
|
|
30975
30982
|
<div id="${modalId}-period-dropdown" style="
|
|
@@ -30982,12 +30989,12 @@ function renderModal(container, state5, modalId, error) {
|
|
|
30982
30989
|
<label style="
|
|
30983
30990
|
display: flex; align-items: center; gap: 8px; padding: 8px 12px;
|
|
30984
30991
|
cursor: pointer; font-size: 13px; color: ${colors.text};
|
|
30985
|
-
" onmouseover="this.style.background='${
|
|
30992
|
+
" onmouseover="this.style.background='${state6.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"}'"
|
|
30986
30993
|
onmouseout="this.style.background='transparent'">
|
|
30987
30994
|
<input type="checkbox"
|
|
30988
30995
|
name="${modalId}-period"
|
|
30989
30996
|
value="${period.id}"
|
|
30990
|
-
${
|
|
30997
|
+
${state6.selectedPeriods.includes(period.id) ? "checked" : ""}
|
|
30991
30998
|
style="width: 16px; height: 16px; cursor: pointer; accent-color: #3e1a7d;">
|
|
30992
30999
|
${period.label}
|
|
30993
31000
|
</label>
|
|
@@ -30995,13 +31002,13 @@ function renderModal(container, state5, modalId, error) {
|
|
|
30995
31002
|
<div style="border-top: 1px solid ${colors.border}; margin-top: 8px; padding-top: 8px;">
|
|
30996
31003
|
<button id="${modalId}-period-select-all" type="button" style="
|
|
30997
31004
|
width: calc(100% - 16px); margin: 0 8px 4px; padding: 6px;
|
|
30998
|
-
background: ${
|
|
31005
|
+
background: ${state6.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"};
|
|
30999
31006
|
border: none; border-radius: 4px; cursor: pointer;
|
|
31000
31007
|
font-size: 12px; color: ${colors.text};
|
|
31001
31008
|
">Selecionar Todos</button>
|
|
31002
31009
|
<button id="${modalId}-period-clear" type="button" style="
|
|
31003
31010
|
width: calc(100% - 16px); margin: 0 8px; padding: 6px;
|
|
31004
|
-
background: ${
|
|
31011
|
+
background: ${state6.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"};
|
|
31005
31012
|
border: none; border-radius: 4px; cursor: pointer;
|
|
31006
31013
|
font-size: 12px; color: ${colors.text};
|
|
31007
31014
|
">Limpar Sele\xE7\xE3o</button>
|
|
@@ -31026,8 +31033,8 @@ function renderModal(container, state5, modalId, error) {
|
|
|
31026
31033
|
font-size: 14px; font-weight: 500; height: 38px;
|
|
31027
31034
|
display: flex; align-items: center; gap: 8px;
|
|
31028
31035
|
font-family: 'Roboto', Arial, sans-serif;
|
|
31029
|
-
" ${
|
|
31030
|
-
${
|
|
31036
|
+
" ${state6.isLoading ? "disabled" : ""}>
|
|
31037
|
+
${state6.isLoading ? '<span style="animation: spin 1s linear infinite; display: inline-block;">\u21BB</span> Carregando...' : "Carregar"}
|
|
31031
31038
|
</button>
|
|
31032
31039
|
</div>
|
|
31033
31040
|
|
|
@@ -31038,40 +31045,40 @@ function renderModal(container, state5, modalId, error) {
|
|
|
31038
31045
|
">
|
|
31039
31046
|
<!-- Current Temperature -->
|
|
31040
31047
|
<div style="
|
|
31041
|
-
padding: 16px; background: ${
|
|
31048
|
+
padding: 16px; background: ${state6.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
|
|
31042
31049
|
border-radius: 12px; border: 1px solid ${colors.border};
|
|
31043
31050
|
">
|
|
31044
31051
|
<span style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500;">Temperatura Atual</span>
|
|
31045
31052
|
<div style="font-weight: 700; font-size: 24px; color: ${statusColor}; margin-top: 4px;">
|
|
31046
|
-
${
|
|
31053
|
+
${state6.currentTemperature !== null ? formatTemperature(state6.currentTemperature) : "N/A"}
|
|
31047
31054
|
</div>
|
|
31048
31055
|
<div style="font-size: 11px; color: ${statusColor}; margin-top: 2px;">${statusText}</div>
|
|
31049
31056
|
</div>
|
|
31050
31057
|
<!-- Average -->
|
|
31051
31058
|
<div style="
|
|
31052
|
-
padding: 16px; background: ${
|
|
31059
|
+
padding: 16px; background: ${state6.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
|
|
31053
31060
|
border-radius: 12px; border: 1px solid ${colors.border};
|
|
31054
31061
|
">
|
|
31055
31062
|
<span style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500;">M\xE9dia do Per\xEDodo</span>
|
|
31056
31063
|
<div id="${modalId}-avg" style="font-weight: 600; font-size: 20px; color: ${colors.text}; margin-top: 4px;">
|
|
31057
|
-
${
|
|
31064
|
+
${state6.stats.count > 0 ? formatTemperature(state6.stats.avg) : "N/A"}
|
|
31058
31065
|
</div>
|
|
31059
31066
|
<div style="font-size: 11px; color: ${colors.textMuted}; margin-top: 2px;">${startDateStr} - ${endDateStr}</div>
|
|
31060
31067
|
</div>
|
|
31061
31068
|
<!-- Min/Max -->
|
|
31062
31069
|
<div style="
|
|
31063
|
-
padding: 16px; background: ${
|
|
31070
|
+
padding: 16px; background: ${state6.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
|
|
31064
31071
|
border-radius: 12px; border: 1px solid ${colors.border};
|
|
31065
31072
|
">
|
|
31066
31073
|
<span style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500;">Min / Max</span>
|
|
31067
31074
|
<div id="${modalId}-minmax" style="font-weight: 600; font-size: 20px; color: ${colors.text}; margin-top: 4px;">
|
|
31068
|
-
${
|
|
31075
|
+
${state6.stats.count > 0 ? `${formatTemperature(state6.stats.min)} / ${formatTemperature(state6.stats.max)}` : "N/A"}
|
|
31069
31076
|
</div>
|
|
31070
|
-
<div style="font-size: 11px; color: ${colors.textMuted}; margin-top: 2px;">${
|
|
31077
|
+
<div style="font-size: 11px; color: ${colors.textMuted}; margin-top: 2px;">${state6.stats.count} leituras</div>
|
|
31071
31078
|
</div>
|
|
31072
31079
|
<!-- Ideal Range -->
|
|
31073
31080
|
<div style="
|
|
31074
|
-
padding: 16px; background: ${
|
|
31081
|
+
padding: 16px; background: ${state6.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
|
|
31075
31082
|
border-radius: 12px; border: 1px solid ${colors.border};
|
|
31076
31083
|
">
|
|
31077
31084
|
<span style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500;">Faixa Ideal</span>
|
|
@@ -31087,18 +31094,18 @@ function renderModal(container, state5, modalId, error) {
|
|
|
31087
31094
|
Hist\xF3rico de Temperatura
|
|
31088
31095
|
</h3>
|
|
31089
31096
|
<div id="${modalId}-chart" style="
|
|
31090
|
-
height: 320px; background: ${
|
|
31097
|
+
height: 320px; background: ${state6.theme === "dark" ? "rgba(255,255,255,0.03)" : "#fafafa"};
|
|
31091
31098
|
border-radius: 12px; display: flex; justify-content: center; align-items: center;
|
|
31092
31099
|
border: 1px solid ${colors.border}; position: relative;
|
|
31093
31100
|
">
|
|
31094
|
-
${
|
|
31101
|
+
${state6.isLoading ? `<div style="text-align: center; color: ${colors.textMuted};">
|
|
31095
31102
|
<div style="animation: spin 1s linear infinite; font-size: 32px; margin-bottom: 8px;">\u21BB</div>
|
|
31096
31103
|
<div>Carregando dados...</div>
|
|
31097
31104
|
</div>` : error ? `<div style="text-align: center; color: ${colors.danger};">
|
|
31098
31105
|
<div style="font-size: 32px; margin-bottom: 8px;">\u26A0\uFE0F</div>
|
|
31099
31106
|
<div>Erro ao carregar dados</div>
|
|
31100
31107
|
<div style="font-size: 12px; margin-top: 4px;">${error.message}</div>
|
|
31101
|
-
</div>` :
|
|
31108
|
+
</div>` : state6.data.length === 0 ? `<div style="text-align: center; color: ${colors.textMuted};">
|
|
31102
31109
|
<div style="font-size: 32px; margin-bottom: 8px;">\u{1F4ED}</div>
|
|
31103
31110
|
<div>Sem dados para o per\xEDodo selecionado</div>
|
|
31104
31111
|
</div>` : `<canvas id="${modalId}-canvas" style="width: 100%; height: 100%;"></canvas>`}
|
|
@@ -31108,12 +31115,12 @@ function renderModal(container, state5, modalId, error) {
|
|
|
31108
31115
|
<!-- Actions -->
|
|
31109
31116
|
<div style="display: flex; justify-content: flex-end; gap: 12px;">
|
|
31110
31117
|
<button id="${modalId}-export" style="
|
|
31111
|
-
background: ${
|
|
31118
|
+
background: ${state6.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f7f7f7"};
|
|
31112
31119
|
color: ${colors.text}; border: 1px solid ${colors.border};
|
|
31113
31120
|
padding: 8px 16px; border-radius: 6px; cursor: pointer;
|
|
31114
31121
|
font-size: 14px; display: flex; align-items: center; gap: 8px;
|
|
31115
31122
|
font-family: 'Roboto', Arial, sans-serif;
|
|
31116
|
-
" ${
|
|
31123
|
+
" ${state6.data.length === 0 ? "disabled" : ""}>
|
|
31117
31124
|
\u{1F4E5} Exportar CSV
|
|
31118
31125
|
</button>
|
|
31119
31126
|
<button id="${modalId}-close-btn" style="
|
|
@@ -31164,14 +31171,14 @@ function renderModal(container, state5, modalId, error) {
|
|
|
31164
31171
|
</style>
|
|
31165
31172
|
`;
|
|
31166
31173
|
}
|
|
31167
|
-
function drawChart(modalId,
|
|
31174
|
+
function drawChart(modalId, state6) {
|
|
31168
31175
|
const chartContainer = document.getElementById(`${modalId}-chart`);
|
|
31169
31176
|
const canvas = document.getElementById(`${modalId}-canvas`);
|
|
31170
|
-
if (!chartContainer || !canvas ||
|
|
31177
|
+
if (!chartContainer || !canvas || state6.data.length === 0) return;
|
|
31171
31178
|
const ctx = canvas.getContext("2d");
|
|
31172
31179
|
if (!ctx) return;
|
|
31173
|
-
const colors = getThemeColors(
|
|
31174
|
-
const filteredData = filterByDayPeriods(
|
|
31180
|
+
const colors = getThemeColors(state6.theme);
|
|
31181
|
+
const filteredData = filterByDayPeriods(state6.data, state6.selectedPeriods);
|
|
31175
31182
|
if (filteredData.length === 0) {
|
|
31176
31183
|
canvas.width = chartContainer.clientWidth;
|
|
31177
31184
|
canvas.height = chartContainer.clientHeight;
|
|
@@ -31182,14 +31189,14 @@ function drawChart(modalId, state5) {
|
|
|
31182
31189
|
return;
|
|
31183
31190
|
}
|
|
31184
31191
|
let chartData;
|
|
31185
|
-
if (
|
|
31192
|
+
if (state6.granularity === "hour") {
|
|
31186
31193
|
const interpolated = interpolateTemperature(filteredData, {
|
|
31187
31194
|
intervalMinutes: 30,
|
|
31188
|
-
startTs:
|
|
31189
|
-
endTs:
|
|
31190
|
-
clampRange:
|
|
31195
|
+
startTs: state6.startTs,
|
|
31196
|
+
endTs: state6.endTs,
|
|
31197
|
+
clampRange: state6.clampRange
|
|
31191
31198
|
});
|
|
31192
|
-
const filteredInterpolated = filterByDayPeriods(interpolated,
|
|
31199
|
+
const filteredInterpolated = filterByDayPeriods(interpolated, state6.selectedPeriods);
|
|
31193
31200
|
chartData = filteredInterpolated.map((item) => ({
|
|
31194
31201
|
x: item.ts,
|
|
31195
31202
|
y: Number(item.value),
|
|
@@ -31197,7 +31204,7 @@ function drawChart(modalId, state5) {
|
|
|
31197
31204
|
screenY: 0
|
|
31198
31205
|
}));
|
|
31199
31206
|
} else {
|
|
31200
|
-
const daily = aggregateByDay(filteredData,
|
|
31207
|
+
const daily = aggregateByDay(filteredData, state6.clampRange);
|
|
31201
31208
|
chartData = daily.map((item) => ({
|
|
31202
31209
|
x: item.dateTs,
|
|
31203
31210
|
y: item.avg,
|
|
@@ -31215,12 +31222,12 @@ function drawChart(modalId, state5) {
|
|
|
31215
31222
|
const paddingRight = 20;
|
|
31216
31223
|
const paddingTop = 20;
|
|
31217
31224
|
const paddingBottom = 55;
|
|
31218
|
-
const isPeriodsFiltered =
|
|
31225
|
+
const isPeriodsFiltered = state6.selectedPeriods.length < 4 && state6.selectedPeriods.length > 0;
|
|
31219
31226
|
const values = chartData.map((d) => d.y);
|
|
31220
31227
|
const dataMin = Math.min(...values);
|
|
31221
31228
|
const dataMax = Math.max(...values);
|
|
31222
|
-
const thresholdMin =
|
|
31223
|
-
const thresholdMax =
|
|
31229
|
+
const thresholdMin = state6.temperatureMin !== null ? state6.temperatureMin : dataMin;
|
|
31230
|
+
const thresholdMax = state6.temperatureMax !== null ? state6.temperatureMax : dataMax;
|
|
31224
31231
|
const minY = Math.min(dataMin, thresholdMin) - 1;
|
|
31225
31232
|
const maxY = Math.max(dataMax, thresholdMax) + 1;
|
|
31226
31233
|
const chartWidth = width - paddingLeft - paddingRight;
|
|
@@ -31252,9 +31259,9 @@ function drawChart(modalId, state5) {
|
|
|
31252
31259
|
ctx.lineTo(width - paddingRight, y);
|
|
31253
31260
|
ctx.stroke();
|
|
31254
31261
|
}
|
|
31255
|
-
if (
|
|
31256
|
-
const rangeMinY = height - paddingBottom - (
|
|
31257
|
-
const rangeMaxY = height - paddingBottom - (
|
|
31262
|
+
if (state6.temperatureMin !== null && state6.temperatureMax !== null) {
|
|
31263
|
+
const rangeMinY = height - paddingBottom - (state6.temperatureMin - minY) * scaleY;
|
|
31264
|
+
const rangeMaxY = height - paddingBottom - (state6.temperatureMax - minY) * scaleY;
|
|
31258
31265
|
ctx.fillStyle = "rgba(76, 175, 80, 0.1)";
|
|
31259
31266
|
ctx.fillRect(paddingLeft, rangeMaxY, chartWidth, rangeMinY - rangeMaxY);
|
|
31260
31267
|
ctx.strokeStyle = colors.success;
|
|
@@ -31296,10 +31303,10 @@ function drawChart(modalId, state5) {
|
|
|
31296
31303
|
const point = chartData[i];
|
|
31297
31304
|
const date = new Date(point.x);
|
|
31298
31305
|
let label;
|
|
31299
|
-
if (
|
|
31300
|
-
label = date.toLocaleTimeString(
|
|
31306
|
+
if (state6.granularity === "hour") {
|
|
31307
|
+
label = date.toLocaleTimeString(state6.locale, { hour: "2-digit", minute: "2-digit" });
|
|
31301
31308
|
} else {
|
|
31302
|
-
label = date.toLocaleDateString(
|
|
31309
|
+
label = date.toLocaleDateString(state6.locale, { day: "2-digit", month: "2-digit" });
|
|
31303
31310
|
}
|
|
31304
31311
|
ctx.strokeStyle = colors.chartGrid;
|
|
31305
31312
|
ctx.lineWidth = 1;
|
|
@@ -31317,16 +31324,16 @@ function drawChart(modalId, state5) {
|
|
|
31317
31324
|
ctx.lineTo(paddingLeft, height - paddingBottom);
|
|
31318
31325
|
ctx.lineTo(width - paddingRight, height - paddingBottom);
|
|
31319
31326
|
ctx.stroke();
|
|
31320
|
-
setupChartTooltip(canvas, chartContainer, chartData,
|
|
31327
|
+
setupChartTooltip(canvas, chartContainer, chartData, state6, colors);
|
|
31321
31328
|
}
|
|
31322
|
-
function setupChartTooltip(canvas, container, chartData,
|
|
31329
|
+
function setupChartTooltip(canvas, container, chartData, state6, colors) {
|
|
31323
31330
|
const existingTooltip = container.querySelector(".myio-chart-tooltip");
|
|
31324
31331
|
if (existingTooltip) existingTooltip.remove();
|
|
31325
31332
|
const tooltip = document.createElement("div");
|
|
31326
31333
|
tooltip.className = "myio-chart-tooltip";
|
|
31327
31334
|
tooltip.style.cssText = `
|
|
31328
31335
|
position: absolute;
|
|
31329
|
-
background: ${
|
|
31336
|
+
background: ${state6.theme === "dark" ? "rgba(30, 30, 40, 0.95)" : "rgba(255, 255, 255, 0.98)"};
|
|
31330
31337
|
border: 1px solid ${colors.border};
|
|
31331
31338
|
border-radius: 8px;
|
|
31332
31339
|
padding: 10px 14px;
|
|
@@ -31363,17 +31370,17 @@ function setupChartTooltip(canvas, container, chartData, state5, colors) {
|
|
|
31363
31370
|
if (point) {
|
|
31364
31371
|
const date = new Date(point.x);
|
|
31365
31372
|
let dateStr;
|
|
31366
|
-
if (
|
|
31367
|
-
dateStr = date.toLocaleDateString(
|
|
31373
|
+
if (state6.granularity === "hour") {
|
|
31374
|
+
dateStr = date.toLocaleDateString(state6.locale, {
|
|
31368
31375
|
day: "2-digit",
|
|
31369
31376
|
month: "2-digit",
|
|
31370
31377
|
year: "numeric"
|
|
31371
|
-
}) + " " + date.toLocaleTimeString(
|
|
31378
|
+
}) + " " + date.toLocaleTimeString(state6.locale, {
|
|
31372
31379
|
hour: "2-digit",
|
|
31373
31380
|
minute: "2-digit"
|
|
31374
31381
|
});
|
|
31375
31382
|
} else {
|
|
31376
|
-
dateStr = date.toLocaleDateString(
|
|
31383
|
+
dateStr = date.toLocaleDateString(state6.locale, {
|
|
31377
31384
|
day: "2-digit",
|
|
31378
31385
|
month: "2-digit",
|
|
31379
31386
|
year: "numeric"
|
|
@@ -31411,7 +31418,7 @@ function setupChartTooltip(canvas, container, chartData, state5, colors) {
|
|
|
31411
31418
|
canvas.style.cursor = "default";
|
|
31412
31419
|
});
|
|
31413
31420
|
}
|
|
31414
|
-
async function setupEventListeners(container,
|
|
31421
|
+
async function setupEventListeners(container, state6, modalId, onClose) {
|
|
31415
31422
|
const closeModal = () => {
|
|
31416
31423
|
container.remove();
|
|
31417
31424
|
onClose?.();
|
|
@@ -31422,19 +31429,19 @@ async function setupEventListeners(container, state5, modalId, onClose) {
|
|
|
31422
31429
|
document.getElementById(`${modalId}-close`)?.addEventListener("click", closeModal);
|
|
31423
31430
|
document.getElementById(`${modalId}-close-btn`)?.addEventListener("click", closeModal);
|
|
31424
31431
|
const dateRangeInput = document.getElementById(`${modalId}-date-range`);
|
|
31425
|
-
if (dateRangeInput && !
|
|
31432
|
+
if (dateRangeInput && !state6.dateRangePicker) {
|
|
31426
31433
|
try {
|
|
31427
|
-
|
|
31428
|
-
presetStart: new Date(
|
|
31429
|
-
presetEnd: new Date(
|
|
31434
|
+
state6.dateRangePicker = await createDateRangePicker2(dateRangeInput, {
|
|
31435
|
+
presetStart: new Date(state6.startTs).toISOString(),
|
|
31436
|
+
presetEnd: new Date(state6.endTs).toISOString(),
|
|
31430
31437
|
includeTime: true,
|
|
31431
31438
|
timePrecision: "minute",
|
|
31432
31439
|
maxRangeDays: 90,
|
|
31433
|
-
locale:
|
|
31440
|
+
locale: state6.locale,
|
|
31434
31441
|
parentEl: container.querySelector(".myio-temp-modal-content"),
|
|
31435
31442
|
onApply: (result) => {
|
|
31436
|
-
|
|
31437
|
-
|
|
31443
|
+
state6.startTs = new Date(result.startISO).getTime();
|
|
31444
|
+
state6.endTs = new Date(result.endISO).getTime();
|
|
31438
31445
|
console.log("[TemperatureModal] Date range applied:", result);
|
|
31439
31446
|
}
|
|
31440
31447
|
});
|
|
@@ -31443,19 +31450,19 @@ async function setupEventListeners(container, state5, modalId, onClose) {
|
|
|
31443
31450
|
}
|
|
31444
31451
|
}
|
|
31445
31452
|
document.getElementById(`${modalId}-theme-toggle`)?.addEventListener("click", async () => {
|
|
31446
|
-
|
|
31447
|
-
localStorage.setItem("myio-temp-modal-theme",
|
|
31448
|
-
|
|
31449
|
-
renderModal(container,
|
|
31450
|
-
if (
|
|
31451
|
-
await setupEventListeners(container,
|
|
31453
|
+
state6.theme = state6.theme === "dark" ? "light" : "dark";
|
|
31454
|
+
localStorage.setItem("myio-temp-modal-theme", state6.theme);
|
|
31455
|
+
state6.dateRangePicker = null;
|
|
31456
|
+
renderModal(container, state6, modalId);
|
|
31457
|
+
if (state6.data.length > 0) drawChart(modalId, state6);
|
|
31458
|
+
await setupEventListeners(container, state6, modalId, onClose);
|
|
31452
31459
|
});
|
|
31453
31460
|
document.getElementById(`${modalId}-maximize`)?.addEventListener("click", async () => {
|
|
31454
31461
|
container.__isMaximized = !container.__isMaximized;
|
|
31455
|
-
|
|
31456
|
-
renderModal(container,
|
|
31457
|
-
if (
|
|
31458
|
-
await setupEventListeners(container,
|
|
31462
|
+
state6.dateRangePicker = null;
|
|
31463
|
+
renderModal(container, state6, modalId);
|
|
31464
|
+
if (state6.data.length > 0) drawChart(modalId, state6);
|
|
31465
|
+
await setupEventListeners(container, state6, modalId, onClose);
|
|
31459
31466
|
});
|
|
31460
31467
|
const periodBtn = document.getElementById(`${modalId}-period-btn`);
|
|
31461
31468
|
const periodDropdown = document.getElementById(`${modalId}-period-dropdown`);
|
|
@@ -31474,71 +31481,71 @@ async function setupEventListeners(container, state5, modalId, onClose) {
|
|
|
31474
31481
|
periodCheckboxes.forEach((checkbox) => {
|
|
31475
31482
|
checkbox.addEventListener("change", () => {
|
|
31476
31483
|
const checked = Array.from(periodCheckboxes).filter((cb) => cb.checked).map((cb) => cb.value);
|
|
31477
|
-
|
|
31484
|
+
state6.selectedPeriods = checked;
|
|
31478
31485
|
const btnLabel = periodBtn?.querySelector("span:first-child");
|
|
31479
31486
|
if (btnLabel) {
|
|
31480
|
-
btnLabel.textContent = getSelectedPeriodsLabel(
|
|
31487
|
+
btnLabel.textContent = getSelectedPeriodsLabel(state6.selectedPeriods);
|
|
31481
31488
|
}
|
|
31482
|
-
if (
|
|
31489
|
+
if (state6.data.length > 0) drawChart(modalId, state6);
|
|
31483
31490
|
});
|
|
31484
31491
|
});
|
|
31485
31492
|
document.getElementById(`${modalId}-period-select-all`)?.addEventListener("click", () => {
|
|
31486
31493
|
periodCheckboxes.forEach((cb) => {
|
|
31487
31494
|
cb.checked = true;
|
|
31488
31495
|
});
|
|
31489
|
-
|
|
31496
|
+
state6.selectedPeriods = ["madrugada", "manha", "tarde", "noite"];
|
|
31490
31497
|
const btnLabel = periodBtn?.querySelector("span:first-child");
|
|
31491
31498
|
if (btnLabel) {
|
|
31492
|
-
btnLabel.textContent = getSelectedPeriodsLabel(
|
|
31499
|
+
btnLabel.textContent = getSelectedPeriodsLabel(state6.selectedPeriods);
|
|
31493
31500
|
}
|
|
31494
|
-
if (
|
|
31501
|
+
if (state6.data.length > 0) drawChart(modalId, state6);
|
|
31495
31502
|
});
|
|
31496
31503
|
document.getElementById(`${modalId}-period-clear`)?.addEventListener("click", () => {
|
|
31497
31504
|
periodCheckboxes.forEach((cb) => {
|
|
31498
31505
|
cb.checked = false;
|
|
31499
31506
|
});
|
|
31500
|
-
|
|
31507
|
+
state6.selectedPeriods = [];
|
|
31501
31508
|
const btnLabel = periodBtn?.querySelector("span:first-child");
|
|
31502
31509
|
if (btnLabel) {
|
|
31503
|
-
btnLabel.textContent = getSelectedPeriodsLabel(
|
|
31510
|
+
btnLabel.textContent = getSelectedPeriodsLabel(state6.selectedPeriods);
|
|
31504
31511
|
}
|
|
31505
|
-
if (
|
|
31512
|
+
if (state6.data.length > 0) drawChart(modalId, state6);
|
|
31506
31513
|
});
|
|
31507
31514
|
document.getElementById(`${modalId}-granularity`)?.addEventListener("change", (e) => {
|
|
31508
|
-
|
|
31509
|
-
localStorage.setItem("myio-temp-modal-granularity",
|
|
31510
|
-
if (
|
|
31515
|
+
state6.granularity = e.target.value;
|
|
31516
|
+
localStorage.setItem("myio-temp-modal-granularity", state6.granularity);
|
|
31517
|
+
if (state6.data.length > 0) drawChart(modalId, state6);
|
|
31511
31518
|
});
|
|
31512
31519
|
document.getElementById(`${modalId}-query`)?.addEventListener("click", async () => {
|
|
31513
|
-
if (
|
|
31520
|
+
if (state6.startTs >= state6.endTs) {
|
|
31514
31521
|
alert("Por favor, selecione um per\xEDodo v\xE1lido");
|
|
31515
31522
|
return;
|
|
31516
31523
|
}
|
|
31517
|
-
|
|
31518
|
-
|
|
31519
|
-
renderModal(container,
|
|
31524
|
+
state6.isLoading = true;
|
|
31525
|
+
state6.dateRangePicker = null;
|
|
31526
|
+
renderModal(container, state6, modalId);
|
|
31520
31527
|
try {
|
|
31521
|
-
|
|
31522
|
-
|
|
31523
|
-
|
|
31524
|
-
renderModal(container,
|
|
31525
|
-
drawChart(modalId,
|
|
31526
|
-
await setupEventListeners(container,
|
|
31528
|
+
state6.data = await fetchTemperatureData(state6.token, state6.deviceId, state6.startTs, state6.endTs);
|
|
31529
|
+
state6.stats = calculateStats(state6.data, state6.clampRange);
|
|
31530
|
+
state6.isLoading = false;
|
|
31531
|
+
renderModal(container, state6, modalId);
|
|
31532
|
+
drawChart(modalId, state6);
|
|
31533
|
+
await setupEventListeners(container, state6, modalId, onClose);
|
|
31527
31534
|
} catch (error) {
|
|
31528
31535
|
console.error("[TemperatureModal] Error fetching data:", error);
|
|
31529
|
-
|
|
31530
|
-
renderModal(container,
|
|
31531
|
-
await setupEventListeners(container,
|
|
31536
|
+
state6.isLoading = false;
|
|
31537
|
+
renderModal(container, state6, modalId, error);
|
|
31538
|
+
await setupEventListeners(container, state6, modalId, onClose);
|
|
31532
31539
|
}
|
|
31533
31540
|
});
|
|
31534
31541
|
document.getElementById(`${modalId}-export`)?.addEventListener("click", () => {
|
|
31535
|
-
if (
|
|
31536
|
-
const startDateStr = new Date(
|
|
31537
|
-
const endDateStr = new Date(
|
|
31542
|
+
if (state6.data.length === 0) return;
|
|
31543
|
+
const startDateStr = new Date(state6.startTs).toLocaleDateString(state6.locale).replace(/\//g, "-");
|
|
31544
|
+
const endDateStr = new Date(state6.endTs).toLocaleDateString(state6.locale).replace(/\//g, "-");
|
|
31538
31545
|
exportTemperatureCSV(
|
|
31539
|
-
|
|
31540
|
-
|
|
31541
|
-
|
|
31546
|
+
state6.data,
|
|
31547
|
+
state6.label,
|
|
31548
|
+
state6.stats,
|
|
31542
31549
|
startDateStr,
|
|
31543
31550
|
endDateStr
|
|
31544
31551
|
);
|
|
@@ -31551,7 +31558,7 @@ async function openTemperatureComparisonModal(params) {
|
|
|
31551
31558
|
const defaultDateRange = getTodaySoFar();
|
|
31552
31559
|
const startTs = params.startDate ? new Date(params.startDate).getTime() : defaultDateRange.startTs;
|
|
31553
31560
|
const endTs = params.endDate ? new Date(params.endDate).getTime() : defaultDateRange.endTs;
|
|
31554
|
-
const
|
|
31561
|
+
const state6 = {
|
|
31555
31562
|
token: params.token,
|
|
31556
31563
|
devices: params.devices,
|
|
31557
31564
|
startTs,
|
|
@@ -31570,44 +31577,44 @@ async function openTemperatureComparisonModal(params) {
|
|
|
31570
31577
|
};
|
|
31571
31578
|
const savedGranularity = localStorage.getItem("myio-temp-comparison-granularity");
|
|
31572
31579
|
const savedTheme = localStorage.getItem("myio-temp-comparison-theme");
|
|
31573
|
-
if (savedGranularity)
|
|
31574
|
-
if (savedTheme)
|
|
31580
|
+
if (savedGranularity) state6.granularity = savedGranularity;
|
|
31581
|
+
if (savedTheme) state6.theme = savedTheme;
|
|
31575
31582
|
const modalContainer = document.createElement("div");
|
|
31576
31583
|
modalContainer.id = modalId;
|
|
31577
31584
|
document.body.appendChild(modalContainer);
|
|
31578
|
-
renderModal2(modalContainer,
|
|
31579
|
-
await fetchAllDevicesData(
|
|
31580
|
-
renderModal2(modalContainer,
|
|
31581
|
-
drawComparisonChart(modalId,
|
|
31582
|
-
await setupEventListeners2(modalContainer,
|
|
31585
|
+
renderModal2(modalContainer, state6, modalId);
|
|
31586
|
+
await fetchAllDevicesData(state6);
|
|
31587
|
+
renderModal2(modalContainer, state6, modalId);
|
|
31588
|
+
drawComparisonChart(modalId, state6);
|
|
31589
|
+
await setupEventListeners2(modalContainer, state6, modalId, params.onClose);
|
|
31583
31590
|
return {
|
|
31584
31591
|
destroy: () => {
|
|
31585
31592
|
modalContainer.remove();
|
|
31586
31593
|
params.onClose?.();
|
|
31587
31594
|
},
|
|
31588
31595
|
updateData: async (startDate, endDate, granularity) => {
|
|
31589
|
-
|
|
31590
|
-
|
|
31591
|
-
if (granularity)
|
|
31592
|
-
|
|
31593
|
-
renderModal2(modalContainer,
|
|
31594
|
-
await fetchAllDevicesData(
|
|
31595
|
-
renderModal2(modalContainer,
|
|
31596
|
-
drawComparisonChart(modalId,
|
|
31597
|
-
setupEventListeners2(modalContainer,
|
|
31596
|
+
state6.startTs = new Date(startDate).getTime();
|
|
31597
|
+
state6.endTs = new Date(endDate).getTime();
|
|
31598
|
+
if (granularity) state6.granularity = granularity;
|
|
31599
|
+
state6.isLoading = true;
|
|
31600
|
+
renderModal2(modalContainer, state6, modalId);
|
|
31601
|
+
await fetchAllDevicesData(state6);
|
|
31602
|
+
renderModal2(modalContainer, state6, modalId);
|
|
31603
|
+
drawComparisonChart(modalId, state6);
|
|
31604
|
+
setupEventListeners2(modalContainer, state6, modalId, params.onClose);
|
|
31598
31605
|
}
|
|
31599
31606
|
};
|
|
31600
31607
|
}
|
|
31601
|
-
async function fetchAllDevicesData(
|
|
31602
|
-
|
|
31603
|
-
|
|
31608
|
+
async function fetchAllDevicesData(state6) {
|
|
31609
|
+
state6.isLoading = true;
|
|
31610
|
+
state6.deviceData = [];
|
|
31604
31611
|
try {
|
|
31605
31612
|
const results = await Promise.all(
|
|
31606
|
-
|
|
31613
|
+
state6.devices.map(async (device, index) => {
|
|
31607
31614
|
const deviceId = device.tbId || device.id;
|
|
31608
31615
|
try {
|
|
31609
|
-
const data = await fetchTemperatureData(
|
|
31610
|
-
const stats = calculateStats(data,
|
|
31616
|
+
const data = await fetchTemperatureData(state6.token, deviceId, state6.startTs, state6.endTs);
|
|
31617
|
+
const stats = calculateStats(data, state6.clampRange);
|
|
31611
31618
|
return {
|
|
31612
31619
|
device,
|
|
31613
31620
|
data,
|
|
@@ -31625,21 +31632,21 @@ async function fetchAllDevicesData(state5) {
|
|
|
31625
31632
|
}
|
|
31626
31633
|
})
|
|
31627
31634
|
);
|
|
31628
|
-
|
|
31635
|
+
state6.deviceData = results;
|
|
31629
31636
|
} catch (error) {
|
|
31630
31637
|
console.error("[TemperatureComparisonModal] Error fetching data:", error);
|
|
31631
31638
|
}
|
|
31632
|
-
|
|
31639
|
+
state6.isLoading = false;
|
|
31633
31640
|
}
|
|
31634
|
-
function renderModal2(container,
|
|
31635
|
-
const colors = getThemeColors(
|
|
31636
|
-
const startDateStr = new Date(
|
|
31637
|
-
const endDateStr = new Date(
|
|
31638
|
-
const startDateInput = new Date(
|
|
31639
|
-
const endDateInput = new Date(
|
|
31640
|
-
const legendHTML =
|
|
31641
|
+
function renderModal2(container, state6, modalId) {
|
|
31642
|
+
const colors = getThemeColors(state6.theme);
|
|
31643
|
+
const startDateStr = new Date(state6.startTs).toLocaleDateString(state6.locale);
|
|
31644
|
+
const endDateStr = new Date(state6.endTs).toLocaleDateString(state6.locale);
|
|
31645
|
+
const startDateInput = new Date(state6.startTs).toISOString().slice(0, 16);
|
|
31646
|
+
const endDateInput = new Date(state6.endTs).toISOString().slice(0, 16);
|
|
31647
|
+
const legendHTML = state6.deviceData.map((dd) => `
|
|
31641
31648
|
<div style="display: flex; align-items: center; gap: 8px; padding: 8px 12px;
|
|
31642
|
-
background: ${
|
|
31649
|
+
background: ${state6.theme === "dark" ? "rgba(255,255,255,0.05)" : "rgba(0,0,0,0.03)"};
|
|
31643
31650
|
border-radius: 8px;">
|
|
31644
31651
|
<span style="width: 12px; height: 12px; border-radius: 50%; background: ${dd.color};"></span>
|
|
31645
31652
|
<span style="color: ${colors.text}; font-size: 13px;">${dd.device.label}</span>
|
|
@@ -31648,9 +31655,9 @@ function renderModal2(container, state5, modalId) {
|
|
|
31648
31655
|
</span>
|
|
31649
31656
|
</div>
|
|
31650
31657
|
`).join("");
|
|
31651
|
-
const statsHTML =
|
|
31658
|
+
const statsHTML = state6.deviceData.map((dd) => `
|
|
31652
31659
|
<div style="
|
|
31653
|
-
padding: 12px; background: ${
|
|
31660
|
+
padding: 12px; background: ${state6.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
|
|
31654
31661
|
border-radius: 10px; border-left: 4px solid ${dd.color};
|
|
31655
31662
|
min-width: 150px;
|
|
31656
31663
|
">
|
|
@@ -31701,7 +31708,7 @@ function renderModal2(container, state5, modalId) {
|
|
|
31701
31708
|
min-height: 20px;
|
|
31702
31709
|
">
|
|
31703
31710
|
<h2 style="margin: 6px; font-size: 18px; font-weight: 600; color: white; line-height: 2;">
|
|
31704
|
-
\u{1F321}\uFE0F Compara\xE7\xE3o de Temperatura - ${
|
|
31711
|
+
\u{1F321}\uFE0F Compara\xE7\xE3o de Temperatura - ${state6.devices.length} sensores
|
|
31705
31712
|
</h2>
|
|
31706
31713
|
<div style="display: flex; gap: 4px; align-items: center;">
|
|
31707
31714
|
<!-- Theme Toggle -->
|
|
@@ -31709,7 +31716,7 @@ function renderModal2(container, state5, modalId) {
|
|
|
31709
31716
|
background: none; border: none; font-size: 16px; cursor: pointer;
|
|
31710
31717
|
padding: 4px 8px; border-radius: 6px; color: rgba(255,255,255,0.8);
|
|
31711
31718
|
transition: background-color 0.2s;
|
|
31712
|
-
">${
|
|
31719
|
+
">${state6.theme === "dark" ? "\u2600\uFE0F" : "\u{1F319}"}</button>
|
|
31713
31720
|
<!-- Maximize Button -->
|
|
31714
31721
|
<button id="${modalId}-maximize" title="${isMaximized ? "Restaurar" : "Maximizar"}" style="
|
|
31715
31722
|
background: none; border: none; font-size: 16px; cursor: pointer;
|
|
@@ -31732,7 +31739,7 @@ function renderModal2(container, state5, modalId) {
|
|
|
31732
31739
|
<div style="
|
|
31733
31740
|
display: flex; gap: 16px; flex-wrap: wrap; align-items: flex-end;
|
|
31734
31741
|
margin-bottom: 16px; padding: 16px;
|
|
31735
|
-
background: ${
|
|
31742
|
+
background: ${state6.theme === "dark" ? "rgba(255,255,255,0.05)" : "#f7f7f7"};
|
|
31736
31743
|
border-radius: 6px; border: 1px solid ${colors.border};
|
|
31737
31744
|
">
|
|
31738
31745
|
<!-- Granularity Select -->
|
|
@@ -31745,8 +31752,8 @@ function renderModal2(container, state5, modalId) {
|
|
|
31745
31752
|
font-size: 14px; color: ${colors.text}; background: ${colors.surface};
|
|
31746
31753
|
cursor: pointer; min-width: 130px;
|
|
31747
31754
|
">
|
|
31748
|
-
<option value="hour" ${
|
|
31749
|
-
<option value="day" ${
|
|
31755
|
+
<option value="hour" ${state6.granularity === "hour" ? "selected" : ""}>Hora (30 min)</option>
|
|
31756
|
+
<option value="day" ${state6.granularity === "day" ? "selected" : ""}>Dia (m\xE9dia)</option>
|
|
31750
31757
|
</select>
|
|
31751
31758
|
</div>
|
|
31752
31759
|
<!-- Day Period Filter (Multiselect) -->
|
|
@@ -31760,7 +31767,7 @@ function renderModal2(container, state5, modalId) {
|
|
|
31760
31767
|
cursor: pointer; min-width: 180px; text-align: left;
|
|
31761
31768
|
display: flex; align-items: center; justify-content: space-between; gap: 8px;
|
|
31762
31769
|
">
|
|
31763
|
-
<span>${getSelectedPeriodsLabel(
|
|
31770
|
+
<span>${getSelectedPeriodsLabel(state6.selectedPeriods)}</span>
|
|
31764
31771
|
<span style="font-size: 10px;">\u25BC</span>
|
|
31765
31772
|
</button>
|
|
31766
31773
|
<div id="${modalId}-period-dropdown" style="
|
|
@@ -31773,12 +31780,12 @@ function renderModal2(container, state5, modalId) {
|
|
|
31773
31780
|
<label style="
|
|
31774
31781
|
display: flex; align-items: center; gap: 8px; padding: 8px 12px;
|
|
31775
31782
|
cursor: pointer; font-size: 13px; color: ${colors.text};
|
|
31776
|
-
" onmouseover="this.style.background='${
|
|
31783
|
+
" onmouseover="this.style.background='${state6.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"}'"
|
|
31777
31784
|
onmouseout="this.style.background='transparent'">
|
|
31778
31785
|
<input type="checkbox"
|
|
31779
31786
|
name="${modalId}-period"
|
|
31780
31787
|
value="${period.id}"
|
|
31781
|
-
${
|
|
31788
|
+
${state6.selectedPeriods.includes(period.id) ? "checked" : ""}
|
|
31782
31789
|
style="width: 16px; height: 16px; cursor: pointer; accent-color: #3e1a7d;">
|
|
31783
31790
|
${period.label}
|
|
31784
31791
|
</label>
|
|
@@ -31786,13 +31793,13 @@ function renderModal2(container, state5, modalId) {
|
|
|
31786
31793
|
<div style="border-top: 1px solid ${colors.border}; margin-top: 8px; padding-top: 8px;">
|
|
31787
31794
|
<button id="${modalId}-period-select-all" type="button" style="
|
|
31788
31795
|
width: calc(100% - 16px); margin: 0 8px 4px; padding: 6px;
|
|
31789
|
-
background: ${
|
|
31796
|
+
background: ${state6.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"};
|
|
31790
31797
|
border: none; border-radius: 4px; cursor: pointer;
|
|
31791
31798
|
font-size: 12px; color: ${colors.text};
|
|
31792
31799
|
">Selecionar Todos</button>
|
|
31793
31800
|
<button id="${modalId}-period-clear" type="button" style="
|
|
31794
31801
|
width: calc(100% - 16px); margin: 0 8px; padding: 6px;
|
|
31795
|
-
background: ${
|
|
31802
|
+
background: ${state6.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"};
|
|
31796
31803
|
border: none; border-radius: 4px; cursor: pointer;
|
|
31797
31804
|
font-size: 12px; color: ${colors.text};
|
|
31798
31805
|
">Limpar Sele\xE7\xE3o</button>
|
|
@@ -31817,8 +31824,8 @@ function renderModal2(container, state5, modalId) {
|
|
|
31817
31824
|
font-size: 14px; font-weight: 500; height: 38px;
|
|
31818
31825
|
display: flex; align-items: center; gap: 8px;
|
|
31819
31826
|
font-family: 'Roboto', Arial, sans-serif;
|
|
31820
|
-
" ${
|
|
31821
|
-
${
|
|
31827
|
+
" ${state6.isLoading ? "disabled" : ""}>
|
|
31828
|
+
${state6.isLoading ? '<span style="animation: spin 1s linear infinite; display: inline-block;">\u21BB</span> Carregando...' : "Carregar"}
|
|
31822
31829
|
</button>
|
|
31823
31830
|
</div>
|
|
31824
31831
|
|
|
@@ -31834,14 +31841,14 @@ function renderModal2(container, state5, modalId) {
|
|
|
31834
31841
|
<div style="margin-bottom: 24px;">
|
|
31835
31842
|
<div id="${modalId}-chart" style="
|
|
31836
31843
|
height: 380px;
|
|
31837
|
-
background: ${
|
|
31844
|
+
background: ${state6.theme === "dark" ? "rgba(255,255,255,0.03)" : "#fafafa"};
|
|
31838
31845
|
border-radius: 14px; display: flex; justify-content: center; align-items: center;
|
|
31839
31846
|
border: 1px solid ${colors.border}; position: relative;
|
|
31840
31847
|
">
|
|
31841
|
-
${
|
|
31848
|
+
${state6.isLoading ? `<div style="text-align: center; color: ${colors.textMuted};">
|
|
31842
31849
|
<div style="animation: spin 1s linear infinite; font-size: 36px; margin-bottom: 12px;">\u21BB</div>
|
|
31843
|
-
<div style="font-size: 15px;">Carregando dados de ${
|
|
31844
|
-
</div>` :
|
|
31850
|
+
<div style="font-size: 15px;">Carregando dados de ${state6.devices.length} sensores...</div>
|
|
31851
|
+
</div>` : state6.deviceData.every((dd) => dd.data.length === 0) ? `<div style="text-align: center; color: ${colors.textMuted};">
|
|
31845
31852
|
<div style="font-size: 48px; margin-bottom: 12px;">\u{1F4ED}</div>
|
|
31846
31853
|
<div style="font-size: 16px;">Sem dados para o per\xEDodo selecionado</div>
|
|
31847
31854
|
</div>` : `<canvas id="${modalId}-canvas" style="width: 100%; height: 100%;"></canvas>`}
|
|
@@ -31859,12 +31866,12 @@ function renderModal2(container, state5, modalId) {
|
|
|
31859
31866
|
<!-- Actions -->
|
|
31860
31867
|
<div style="display: flex; justify-content: flex-end; gap: 12px;">
|
|
31861
31868
|
<button id="${modalId}-export" style="
|
|
31862
|
-
background: ${
|
|
31869
|
+
background: ${state6.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f7f7f7"};
|
|
31863
31870
|
color: ${colors.text}; border: 1px solid ${colors.border};
|
|
31864
31871
|
padding: 8px 16px; border-radius: 6px; cursor: pointer;
|
|
31865
31872
|
font-size: 14px; display: flex; align-items: center; gap: 8px;
|
|
31866
31873
|
font-family: 'Roboto', Arial, sans-serif;
|
|
31867
|
-
" ${
|
|
31874
|
+
" ${state6.deviceData.every((dd) => dd.data.length === 0) ? "disabled" : ""}>
|
|
31868
31875
|
\u{1F4E5} Exportar CSV
|
|
31869
31876
|
</button>
|
|
31870
31877
|
<button id="${modalId}-close-btn" style="
|
|
@@ -31899,15 +31906,15 @@ function renderModal2(container, state5, modalId) {
|
|
|
31899
31906
|
</style>
|
|
31900
31907
|
`;
|
|
31901
31908
|
}
|
|
31902
|
-
function drawComparisonChart(modalId,
|
|
31909
|
+
function drawComparisonChart(modalId, state6) {
|
|
31903
31910
|
const chartContainer = document.getElementById(`${modalId}-chart`);
|
|
31904
31911
|
const canvas = document.getElementById(`${modalId}-canvas`);
|
|
31905
31912
|
if (!chartContainer || !canvas) return;
|
|
31906
|
-
const hasData =
|
|
31913
|
+
const hasData = state6.deviceData.some((dd) => dd.data.length > 0);
|
|
31907
31914
|
if (!hasData) return;
|
|
31908
31915
|
const ctx = canvas.getContext("2d");
|
|
31909
31916
|
if (!ctx) return;
|
|
31910
|
-
const colors = getThemeColors(
|
|
31917
|
+
const colors = getThemeColors(state6.theme);
|
|
31911
31918
|
const width = chartContainer.clientWidth - 2;
|
|
31912
31919
|
const height = 380;
|
|
31913
31920
|
canvas.width = width;
|
|
@@ -31918,19 +31925,19 @@ function drawComparisonChart(modalId, state5) {
|
|
|
31918
31925
|
const paddingBottom = 55;
|
|
31919
31926
|
ctx.clearRect(0, 0, width, height);
|
|
31920
31927
|
const processedData = [];
|
|
31921
|
-
|
|
31928
|
+
state6.deviceData.forEach((dd) => {
|
|
31922
31929
|
if (dd.data.length === 0) return;
|
|
31923
|
-
const filteredData = filterByDayPeriods(dd.data,
|
|
31930
|
+
const filteredData = filterByDayPeriods(dd.data, state6.selectedPeriods);
|
|
31924
31931
|
if (filteredData.length === 0) return;
|
|
31925
31932
|
let points;
|
|
31926
|
-
if (
|
|
31933
|
+
if (state6.granularity === "hour") {
|
|
31927
31934
|
const interpolated = interpolateTemperature(filteredData, {
|
|
31928
31935
|
intervalMinutes: 30,
|
|
31929
|
-
startTs:
|
|
31930
|
-
endTs:
|
|
31931
|
-
clampRange:
|
|
31936
|
+
startTs: state6.startTs,
|
|
31937
|
+
endTs: state6.endTs,
|
|
31938
|
+
clampRange: state6.clampRange
|
|
31932
31939
|
});
|
|
31933
|
-
const filteredInterpolated = filterByDayPeriods(interpolated,
|
|
31940
|
+
const filteredInterpolated = filterByDayPeriods(interpolated, state6.selectedPeriods);
|
|
31934
31941
|
points = filteredInterpolated.map((item) => ({
|
|
31935
31942
|
x: item.ts,
|
|
31936
31943
|
y: Number(item.value),
|
|
@@ -31940,7 +31947,7 @@ function drawComparisonChart(modalId, state5) {
|
|
|
31940
31947
|
deviceColor: dd.color
|
|
31941
31948
|
}));
|
|
31942
31949
|
} else {
|
|
31943
|
-
const daily = aggregateByDay(filteredData,
|
|
31950
|
+
const daily = aggregateByDay(filteredData, state6.clampRange);
|
|
31944
31951
|
points = daily.map((item) => ({
|
|
31945
31952
|
x: item.dateTs,
|
|
31946
31953
|
y: item.avg,
|
|
@@ -31961,7 +31968,7 @@ function drawComparisonChart(modalId, state5) {
|
|
|
31961
31968
|
ctx.fillText("Nenhum dado para os per\xEDodos selecionados", width / 2, height / 2);
|
|
31962
31969
|
return;
|
|
31963
31970
|
}
|
|
31964
|
-
const isPeriodsFiltered =
|
|
31971
|
+
const isPeriodsFiltered = state6.selectedPeriods.length < 4 && state6.selectedPeriods.length > 0;
|
|
31965
31972
|
let dataMinY = Infinity;
|
|
31966
31973
|
let dataMaxY = -Infinity;
|
|
31967
31974
|
processedData.forEach(({ points }) => {
|
|
@@ -31971,7 +31978,7 @@ function drawComparisonChart(modalId, state5) {
|
|
|
31971
31978
|
});
|
|
31972
31979
|
});
|
|
31973
31980
|
const rangeMap = /* @__PURE__ */ new Map();
|
|
31974
|
-
|
|
31981
|
+
state6.deviceData.forEach((dd, index) => {
|
|
31975
31982
|
const device = dd.device;
|
|
31976
31983
|
const min = device.temperatureMin;
|
|
31977
31984
|
const max = device.temperatureMax;
|
|
@@ -31990,10 +31997,10 @@ function drawComparisonChart(modalId, state5) {
|
|
|
31990
31997
|
}
|
|
31991
31998
|
}
|
|
31992
31999
|
});
|
|
31993
|
-
if (rangeMap.size === 0 &&
|
|
32000
|
+
if (rangeMap.size === 0 && state6.temperatureMin !== null && state6.temperatureMax !== null) {
|
|
31994
32001
|
rangeMap.set("global", {
|
|
31995
|
-
min:
|
|
31996
|
-
max:
|
|
32002
|
+
min: state6.temperatureMin,
|
|
32003
|
+
max: state6.temperatureMax,
|
|
31997
32004
|
customerName: "Global",
|
|
31998
32005
|
color: colors.success,
|
|
31999
32006
|
deviceLabels: []
|
|
@@ -32114,10 +32121,10 @@ function drawComparisonChart(modalId, state5) {
|
|
|
32114
32121
|
const point = xAxisPoints[i];
|
|
32115
32122
|
const date = new Date(point.x);
|
|
32116
32123
|
let label;
|
|
32117
|
-
if (
|
|
32118
|
-
label = date.toLocaleTimeString(
|
|
32124
|
+
if (state6.granularity === "hour") {
|
|
32125
|
+
label = date.toLocaleTimeString(state6.locale, { hour: "2-digit", minute: "2-digit" });
|
|
32119
32126
|
} else {
|
|
32120
|
-
label = date.toLocaleDateString(
|
|
32127
|
+
label = date.toLocaleDateString(state6.locale, { day: "2-digit", month: "2-digit" });
|
|
32121
32128
|
}
|
|
32122
32129
|
ctx.strokeStyle = colors.chartGrid;
|
|
32123
32130
|
ctx.lineWidth = 1;
|
|
@@ -32136,16 +32143,16 @@ function drawComparisonChart(modalId, state5) {
|
|
|
32136
32143
|
ctx.lineTo(width - paddingRight, height - paddingBottom);
|
|
32137
32144
|
ctx.stroke();
|
|
32138
32145
|
const allChartPoints = processedData.flatMap((pd) => pd.points);
|
|
32139
|
-
setupComparisonChartTooltip(canvas, chartContainer, allChartPoints,
|
|
32146
|
+
setupComparisonChartTooltip(canvas, chartContainer, allChartPoints, state6, colors);
|
|
32140
32147
|
}
|
|
32141
|
-
function setupComparisonChartTooltip(canvas, container, chartData,
|
|
32148
|
+
function setupComparisonChartTooltip(canvas, container, chartData, state6, colors) {
|
|
32142
32149
|
const existingTooltip = container.querySelector(".myio-chart-tooltip");
|
|
32143
32150
|
if (existingTooltip) existingTooltip.remove();
|
|
32144
32151
|
const tooltip = document.createElement("div");
|
|
32145
32152
|
tooltip.className = "myio-chart-tooltip";
|
|
32146
32153
|
tooltip.style.cssText = `
|
|
32147
32154
|
position: absolute;
|
|
32148
|
-
background: ${
|
|
32155
|
+
background: ${state6.theme === "dark" ? "rgba(30, 30, 40, 0.95)" : "rgba(255, 255, 255, 0.98)"};
|
|
32149
32156
|
border: 1px solid ${colors.border};
|
|
32150
32157
|
border-radius: 8px;
|
|
32151
32158
|
padding: 10px 14px;
|
|
@@ -32182,17 +32189,17 @@ function setupComparisonChartTooltip(canvas, container, chartData, state5, color
|
|
|
32182
32189
|
if (point) {
|
|
32183
32190
|
const date = new Date(point.x);
|
|
32184
32191
|
let dateStr;
|
|
32185
|
-
if (
|
|
32186
|
-
dateStr = date.toLocaleDateString(
|
|
32192
|
+
if (state6.granularity === "hour") {
|
|
32193
|
+
dateStr = date.toLocaleDateString(state6.locale, {
|
|
32187
32194
|
day: "2-digit",
|
|
32188
32195
|
month: "2-digit",
|
|
32189
32196
|
year: "numeric"
|
|
32190
|
-
}) + " " + date.toLocaleTimeString(
|
|
32197
|
+
}) + " " + date.toLocaleTimeString(state6.locale, {
|
|
32191
32198
|
hour: "2-digit",
|
|
32192
32199
|
minute: "2-digit"
|
|
32193
32200
|
});
|
|
32194
32201
|
} else {
|
|
32195
|
-
dateStr = date.toLocaleDateString(
|
|
32202
|
+
dateStr = date.toLocaleDateString(state6.locale, {
|
|
32196
32203
|
day: "2-digit",
|
|
32197
32204
|
month: "2-digit",
|
|
32198
32205
|
year: "numeric"
|
|
@@ -32234,7 +32241,7 @@ function setupComparisonChartTooltip(canvas, container, chartData, state5, color
|
|
|
32234
32241
|
canvas.style.cursor = "default";
|
|
32235
32242
|
});
|
|
32236
32243
|
}
|
|
32237
|
-
async function setupEventListeners2(container,
|
|
32244
|
+
async function setupEventListeners2(container, state6, modalId, onClose) {
|
|
32238
32245
|
const closeModal = () => {
|
|
32239
32246
|
container.remove();
|
|
32240
32247
|
onClose?.();
|
|
@@ -32245,19 +32252,19 @@ async function setupEventListeners2(container, state5, modalId, onClose) {
|
|
|
32245
32252
|
document.getElementById(`${modalId}-close`)?.addEventListener("click", closeModal);
|
|
32246
32253
|
document.getElementById(`${modalId}-close-btn`)?.addEventListener("click", closeModal);
|
|
32247
32254
|
const dateRangeInput = document.getElementById(`${modalId}-date-range`);
|
|
32248
|
-
if (dateRangeInput && !
|
|
32255
|
+
if (dateRangeInput && !state6.dateRangePicker) {
|
|
32249
32256
|
try {
|
|
32250
|
-
|
|
32251
|
-
presetStart: new Date(
|
|
32252
|
-
presetEnd: new Date(
|
|
32257
|
+
state6.dateRangePicker = await createDateRangePicker2(dateRangeInput, {
|
|
32258
|
+
presetStart: new Date(state6.startTs).toISOString(),
|
|
32259
|
+
presetEnd: new Date(state6.endTs).toISOString(),
|
|
32253
32260
|
includeTime: true,
|
|
32254
32261
|
timePrecision: "minute",
|
|
32255
32262
|
maxRangeDays: 90,
|
|
32256
|
-
locale:
|
|
32263
|
+
locale: state6.locale,
|
|
32257
32264
|
parentEl: container.querySelector(".myio-temp-comparison-content"),
|
|
32258
32265
|
onApply: (result) => {
|
|
32259
|
-
|
|
32260
|
-
|
|
32266
|
+
state6.startTs = new Date(result.startISO).getTime();
|
|
32267
|
+
state6.endTs = new Date(result.endISO).getTime();
|
|
32261
32268
|
console.log("[TemperatureComparisonModal] Date range applied:", result);
|
|
32262
32269
|
}
|
|
32263
32270
|
});
|
|
@@ -32266,23 +32273,23 @@ async function setupEventListeners2(container, state5, modalId, onClose) {
|
|
|
32266
32273
|
}
|
|
32267
32274
|
}
|
|
32268
32275
|
document.getElementById(`${modalId}-theme-toggle`)?.addEventListener("click", async () => {
|
|
32269
|
-
|
|
32270
|
-
localStorage.setItem("myio-temp-comparison-theme",
|
|
32271
|
-
|
|
32272
|
-
renderModal2(container,
|
|
32273
|
-
if (
|
|
32274
|
-
drawComparisonChart(modalId,
|
|
32275
|
-
}
|
|
32276
|
-
await setupEventListeners2(container,
|
|
32276
|
+
state6.theme = state6.theme === "dark" ? "light" : "dark";
|
|
32277
|
+
localStorage.setItem("myio-temp-comparison-theme", state6.theme);
|
|
32278
|
+
state6.dateRangePicker = null;
|
|
32279
|
+
renderModal2(container, state6, modalId);
|
|
32280
|
+
if (state6.deviceData.some((dd) => dd.data.length > 0)) {
|
|
32281
|
+
drawComparisonChart(modalId, state6);
|
|
32282
|
+
}
|
|
32283
|
+
await setupEventListeners2(container, state6, modalId, onClose);
|
|
32277
32284
|
});
|
|
32278
32285
|
document.getElementById(`${modalId}-maximize`)?.addEventListener("click", async () => {
|
|
32279
32286
|
container.__isMaximized = !container.__isMaximized;
|
|
32280
|
-
|
|
32281
|
-
renderModal2(container,
|
|
32282
|
-
if (
|
|
32283
|
-
drawComparisonChart(modalId,
|
|
32287
|
+
state6.dateRangePicker = null;
|
|
32288
|
+
renderModal2(container, state6, modalId);
|
|
32289
|
+
if (state6.deviceData.some((dd) => dd.data.length > 0)) {
|
|
32290
|
+
drawComparisonChart(modalId, state6);
|
|
32284
32291
|
}
|
|
32285
|
-
await setupEventListeners2(container,
|
|
32292
|
+
await setupEventListeners2(container, state6, modalId, onClose);
|
|
32286
32293
|
});
|
|
32287
32294
|
const periodBtn = document.getElementById(`${modalId}-period-btn`);
|
|
32288
32295
|
const periodDropdown = document.getElementById(`${modalId}-period-dropdown`);
|
|
@@ -32301,13 +32308,13 @@ async function setupEventListeners2(container, state5, modalId, onClose) {
|
|
|
32301
32308
|
periodCheckboxes.forEach((checkbox) => {
|
|
32302
32309
|
checkbox.addEventListener("change", () => {
|
|
32303
32310
|
const checked = Array.from(periodCheckboxes).filter((cb) => cb.checked).map((cb) => cb.value);
|
|
32304
|
-
|
|
32311
|
+
state6.selectedPeriods = checked;
|
|
32305
32312
|
const btnLabel = periodBtn?.querySelector("span:first-child");
|
|
32306
32313
|
if (btnLabel) {
|
|
32307
|
-
btnLabel.textContent = getSelectedPeriodsLabel(
|
|
32314
|
+
btnLabel.textContent = getSelectedPeriodsLabel(state6.selectedPeriods);
|
|
32308
32315
|
}
|
|
32309
|
-
if (
|
|
32310
|
-
drawComparisonChart(modalId,
|
|
32316
|
+
if (state6.deviceData.some((dd) => dd.data.length > 0)) {
|
|
32317
|
+
drawComparisonChart(modalId, state6);
|
|
32311
32318
|
}
|
|
32312
32319
|
});
|
|
32313
32320
|
});
|
|
@@ -32315,77 +32322,77 @@ async function setupEventListeners2(container, state5, modalId, onClose) {
|
|
|
32315
32322
|
periodCheckboxes.forEach((cb) => {
|
|
32316
32323
|
cb.checked = true;
|
|
32317
32324
|
});
|
|
32318
|
-
|
|
32325
|
+
state6.selectedPeriods = ["madrugada", "manha", "tarde", "noite"];
|
|
32319
32326
|
const btnLabel = periodBtn?.querySelector("span:first-child");
|
|
32320
32327
|
if (btnLabel) {
|
|
32321
|
-
btnLabel.textContent = getSelectedPeriodsLabel(
|
|
32328
|
+
btnLabel.textContent = getSelectedPeriodsLabel(state6.selectedPeriods);
|
|
32322
32329
|
}
|
|
32323
|
-
if (
|
|
32324
|
-
drawComparisonChart(modalId,
|
|
32330
|
+
if (state6.deviceData.some((dd) => dd.data.length > 0)) {
|
|
32331
|
+
drawComparisonChart(modalId, state6);
|
|
32325
32332
|
}
|
|
32326
32333
|
});
|
|
32327
32334
|
document.getElementById(`${modalId}-period-clear`)?.addEventListener("click", () => {
|
|
32328
32335
|
periodCheckboxes.forEach((cb) => {
|
|
32329
32336
|
cb.checked = false;
|
|
32330
32337
|
});
|
|
32331
|
-
|
|
32338
|
+
state6.selectedPeriods = [];
|
|
32332
32339
|
const btnLabel = periodBtn?.querySelector("span:first-child");
|
|
32333
32340
|
if (btnLabel) {
|
|
32334
|
-
btnLabel.textContent = getSelectedPeriodsLabel(
|
|
32341
|
+
btnLabel.textContent = getSelectedPeriodsLabel(state6.selectedPeriods);
|
|
32335
32342
|
}
|
|
32336
|
-
if (
|
|
32337
|
-
drawComparisonChart(modalId,
|
|
32343
|
+
if (state6.deviceData.some((dd) => dd.data.length > 0)) {
|
|
32344
|
+
drawComparisonChart(modalId, state6);
|
|
32338
32345
|
}
|
|
32339
32346
|
});
|
|
32340
32347
|
document.getElementById(`${modalId}-granularity`)?.addEventListener("change", (e) => {
|
|
32341
|
-
|
|
32342
|
-
localStorage.setItem("myio-temp-comparison-granularity",
|
|
32343
|
-
if (
|
|
32344
|
-
drawComparisonChart(modalId,
|
|
32348
|
+
state6.granularity = e.target.value;
|
|
32349
|
+
localStorage.setItem("myio-temp-comparison-granularity", state6.granularity);
|
|
32350
|
+
if (state6.deviceData.some((dd) => dd.data.length > 0)) {
|
|
32351
|
+
drawComparisonChart(modalId, state6);
|
|
32345
32352
|
}
|
|
32346
32353
|
});
|
|
32347
32354
|
document.getElementById(`${modalId}-query`)?.addEventListener("click", async () => {
|
|
32348
|
-
if (
|
|
32355
|
+
if (state6.startTs >= state6.endTs) {
|
|
32349
32356
|
alert("Por favor, selecione um per\xEDodo v\xE1lido");
|
|
32350
32357
|
return;
|
|
32351
32358
|
}
|
|
32352
|
-
|
|
32353
|
-
|
|
32354
|
-
renderModal2(container,
|
|
32355
|
-
await fetchAllDevicesData(
|
|
32356
|
-
renderModal2(container,
|
|
32357
|
-
drawComparisonChart(modalId,
|
|
32358
|
-
await setupEventListeners2(container,
|
|
32359
|
+
state6.isLoading = true;
|
|
32360
|
+
state6.dateRangePicker = null;
|
|
32361
|
+
renderModal2(container, state6, modalId);
|
|
32362
|
+
await fetchAllDevicesData(state6);
|
|
32363
|
+
renderModal2(container, state6, modalId);
|
|
32364
|
+
drawComparisonChart(modalId, state6);
|
|
32365
|
+
await setupEventListeners2(container, state6, modalId, onClose);
|
|
32359
32366
|
});
|
|
32360
32367
|
document.getElementById(`${modalId}-export`)?.addEventListener("click", () => {
|
|
32361
|
-
if (
|
|
32362
|
-
exportComparisonCSV(
|
|
32368
|
+
if (state6.deviceData.every((dd) => dd.data.length === 0)) return;
|
|
32369
|
+
exportComparisonCSV(state6);
|
|
32363
32370
|
});
|
|
32364
32371
|
}
|
|
32365
|
-
function exportComparisonCSV(
|
|
32366
|
-
const startDateStr = new Date(
|
|
32367
|
-
const endDateStr = new Date(
|
|
32372
|
+
function exportComparisonCSV(state6) {
|
|
32373
|
+
const startDateStr = new Date(state6.startTs).toLocaleDateString(state6.locale).replace(/\//g, "-");
|
|
32374
|
+
const endDateStr = new Date(state6.endTs).toLocaleDateString(state6.locale).replace(/\//g, "-");
|
|
32368
32375
|
const BOM = "\uFEFF";
|
|
32369
32376
|
let csvContent = BOM;
|
|
32370
32377
|
csvContent += `Compara\xE7\xE3o de Temperatura
|
|
32371
32378
|
`;
|
|
32372
32379
|
csvContent += `Per\xEDodo: ${startDateStr} at\xE9 ${endDateStr}
|
|
32373
32380
|
`;
|
|
32374
|
-
csvContent += `Sensores: ${
|
|
32381
|
+
csvContent += `Sensores: ${state6.devices.map((d) => d.label).join(", ")}
|
|
32375
32382
|
`;
|
|
32376
32383
|
csvContent += "\n";
|
|
32377
32384
|
csvContent += "Estat\xEDsticas por Sensor:\n";
|
|
32378
32385
|
csvContent += "Sensor,M\xE9dia (\xB0C),Min (\xB0C),Max (\xB0C),Leituras\n";
|
|
32379
|
-
|
|
32386
|
+
state6.deviceData.forEach((dd) => {
|
|
32380
32387
|
csvContent += `"${dd.device.label}",${dd.stats.avg.toFixed(2)},${dd.stats.min.toFixed(2)},${dd.stats.max.toFixed(2)},${dd.stats.count}
|
|
32381
32388
|
`;
|
|
32382
32389
|
});
|
|
32383
32390
|
csvContent += "\n";
|
|
32384
32391
|
csvContent += "Dados Detalhados:\n";
|
|
32385
32392
|
csvContent += "Data/Hora,Sensor,Temperatura (\xB0C)\n";
|
|
32386
|
-
|
|
32393
|
+
state6.deviceData.forEach((dd) => {
|
|
32387
32394
|
dd.data.forEach((item) => {
|
|
32388
|
-
const date = new Date(item.ts).toLocaleString(
|
|
32395
|
+
const date = new Date(item.ts).toLocaleString(state6.locale);
|
|
32389
32396
|
const temp = Number(item.value).toFixed(2);
|
|
32390
32397
|
csvContent += `"${date}","${dd.device.label}",${temp}
|
|
32391
32398
|
`;
|
|
@@ -32493,10 +32500,10 @@ async function saveCustomerAttributes(customerId, token, minTemperature, maxTemp
|
|
|
32493
32500
|
throw new Error(`Failed to save attributes: ${response.status}`);
|
|
32494
32501
|
}
|
|
32495
32502
|
}
|
|
32496
|
-
function renderModal3(container,
|
|
32497
|
-
const colors = getColors(
|
|
32498
|
-
const minValue =
|
|
32499
|
-
const maxValue =
|
|
32503
|
+
function renderModal3(container, state6, modalId, onClose, onSave) {
|
|
32504
|
+
const colors = getColors(state6.theme);
|
|
32505
|
+
const minValue = state6.minTemperature !== null ? state6.minTemperature : "";
|
|
32506
|
+
const maxValue = state6.maxTemperature !== null ? state6.maxTemperature : "";
|
|
32500
32507
|
container.innerHTML = `
|
|
32501
32508
|
<style>
|
|
32502
32509
|
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
|
|
@@ -32761,23 +32768,23 @@ function renderModal3(container, state5, modalId, onClose, onSave) {
|
|
|
32761
32768
|
</div>
|
|
32762
32769
|
|
|
32763
32770
|
<div class="modal-body">
|
|
32764
|
-
${
|
|
32771
|
+
${state6.isLoading ? `
|
|
32765
32772
|
<div class="loading-overlay">
|
|
32766
32773
|
<div class="loading-spinner"></div>
|
|
32767
32774
|
<div>Carregando configura\xE7\xF5es...</div>
|
|
32768
32775
|
</div>
|
|
32769
32776
|
` : `
|
|
32770
|
-
${
|
|
32771
|
-
<div class="message message-error">${
|
|
32777
|
+
${state6.error ? `
|
|
32778
|
+
<div class="message message-error">${state6.error}</div>
|
|
32772
32779
|
` : ""}
|
|
32773
32780
|
|
|
32774
|
-
${
|
|
32775
|
-
<div class="message message-success">${
|
|
32781
|
+
${state6.successMessage ? `
|
|
32782
|
+
<div class="message message-success">${state6.successMessage}</div>
|
|
32776
32783
|
` : ""}
|
|
32777
32784
|
|
|
32778
32785
|
<div class="customer-info">
|
|
32779
32786
|
<div class="customer-label">Shopping / Cliente</div>
|
|
32780
|
-
<div class="customer-name">${
|
|
32787
|
+
<div class="customer-name">${state6.customerName || "N\xE3o identificado"}</div>
|
|
32781
32788
|
</div>
|
|
32782
32789
|
|
|
32783
32790
|
<div class="form-group">
|
|
@@ -32821,11 +32828,11 @@ function renderModal3(container, state5, modalId, onClose, onSave) {
|
|
|
32821
32828
|
`}
|
|
32822
32829
|
</div>
|
|
32823
32830
|
|
|
32824
|
-
${!
|
|
32831
|
+
${!state6.isLoading ? `
|
|
32825
32832
|
<div class="modal-footer">
|
|
32826
32833
|
<button class="btn btn-secondary" id="${modalId}-cancel">Cancelar</button>
|
|
32827
|
-
<button class="btn btn-primary" id="${modalId}-save" ${
|
|
32828
|
-
${
|
|
32834
|
+
<button class="btn btn-primary" id="${modalId}-save" ${state6.isSaving ? "disabled" : ""}>
|
|
32835
|
+
${state6.isSaving ? '<div class="spinner"></div> Salvando...' : "Salvar"}
|
|
32829
32836
|
</button>
|
|
32830
32837
|
</div>
|
|
32831
32838
|
` : ""}
|
|
@@ -32862,18 +32869,18 @@ function renderModal3(container, state5, modalId, onClose, onSave) {
|
|
|
32862
32869
|
const min = parseFloat(minInput.value);
|
|
32863
32870
|
const max = parseFloat(maxInput.value);
|
|
32864
32871
|
if (isNaN(min) || isNaN(max)) {
|
|
32865
|
-
|
|
32866
|
-
renderModal3(container,
|
|
32872
|
+
state6.error = "Por favor, preencha ambos os valores.";
|
|
32873
|
+
renderModal3(container, state6, modalId, onClose, onSave);
|
|
32867
32874
|
return;
|
|
32868
32875
|
}
|
|
32869
32876
|
if (min >= max) {
|
|
32870
|
-
|
|
32871
|
-
renderModal3(container,
|
|
32877
|
+
state6.error = "A temperatura m\xEDnima deve ser menor que a m\xE1xima.";
|
|
32878
|
+
renderModal3(container, state6, modalId, onClose, onSave);
|
|
32872
32879
|
return;
|
|
32873
32880
|
}
|
|
32874
32881
|
if (min < 0 || max > 50) {
|
|
32875
|
-
|
|
32876
|
-
renderModal3(container,
|
|
32882
|
+
state6.error = "Os valores devem estar entre 0\xB0C e 50\xB0C.";
|
|
32883
|
+
renderModal3(container, state6, modalId, onClose, onSave);
|
|
32877
32884
|
return;
|
|
32878
32885
|
}
|
|
32879
32886
|
await onSave(min, max);
|
|
@@ -32881,7 +32888,7 @@ function renderModal3(container, state5, modalId, onClose, onSave) {
|
|
|
32881
32888
|
}
|
|
32882
32889
|
function openTemperatureSettingsModal(params) {
|
|
32883
32890
|
const modalId = `myio-temp-settings-${Date.now()}`;
|
|
32884
|
-
const
|
|
32891
|
+
const state6 = {
|
|
32885
32892
|
customerId: params.customerId,
|
|
32886
32893
|
customerName: params.customerName || "",
|
|
32887
32894
|
token: params.token,
|
|
@@ -32901,37 +32908,37 @@ function openTemperatureSettingsModal(params) {
|
|
|
32901
32908
|
params.onClose?.();
|
|
32902
32909
|
};
|
|
32903
32910
|
const handleSave = async (min, max) => {
|
|
32904
|
-
|
|
32905
|
-
|
|
32906
|
-
|
|
32907
|
-
renderModal3(container,
|
|
32911
|
+
state6.isSaving = true;
|
|
32912
|
+
state6.error = null;
|
|
32913
|
+
state6.successMessage = null;
|
|
32914
|
+
renderModal3(container, state6, modalId, destroy, handleSave);
|
|
32908
32915
|
try {
|
|
32909
|
-
await saveCustomerAttributes(
|
|
32910
|
-
|
|
32911
|
-
|
|
32912
|
-
|
|
32913
|
-
|
|
32914
|
-
renderModal3(container,
|
|
32916
|
+
await saveCustomerAttributes(state6.customerId, state6.token, min, max, params.onError);
|
|
32917
|
+
state6.minTemperature = min;
|
|
32918
|
+
state6.maxTemperature = max;
|
|
32919
|
+
state6.isSaving = false;
|
|
32920
|
+
state6.successMessage = "Configura\xE7\xF5es salvas com sucesso!";
|
|
32921
|
+
renderModal3(container, state6, modalId, destroy, handleSave);
|
|
32915
32922
|
params.onSave?.({ minTemperature: min, maxTemperature: max });
|
|
32916
32923
|
setTimeout(() => {
|
|
32917
32924
|
destroy();
|
|
32918
32925
|
}, 1500);
|
|
32919
32926
|
} catch (error) {
|
|
32920
|
-
|
|
32921
|
-
|
|
32922
|
-
renderModal3(container,
|
|
32927
|
+
state6.isSaving = false;
|
|
32928
|
+
state6.error = `Erro ao salvar: ${error.message}`;
|
|
32929
|
+
renderModal3(container, state6, modalId, destroy, handleSave);
|
|
32923
32930
|
}
|
|
32924
32931
|
};
|
|
32925
|
-
renderModal3(container,
|
|
32926
|
-
fetchCustomerAttributes(
|
|
32927
|
-
|
|
32928
|
-
|
|
32929
|
-
|
|
32930
|
-
renderModal3(container,
|
|
32932
|
+
renderModal3(container, state6, modalId, destroy, handleSave);
|
|
32933
|
+
fetchCustomerAttributes(state6.customerId, state6.token, params.onError).then(({ minTemperature, maxTemperature }) => {
|
|
32934
|
+
state6.minTemperature = minTemperature;
|
|
32935
|
+
state6.maxTemperature = maxTemperature;
|
|
32936
|
+
state6.isLoading = false;
|
|
32937
|
+
renderModal3(container, state6, modalId, destroy, handleSave);
|
|
32931
32938
|
}).catch((error) => {
|
|
32932
|
-
|
|
32933
|
-
|
|
32934
|
-
renderModal3(container,
|
|
32939
|
+
state6.isLoading = false;
|
|
32940
|
+
state6.error = `Erro ao carregar: ${error.message}`;
|
|
32941
|
+
renderModal3(container, state6, modalId, destroy, handleSave);
|
|
32935
32942
|
});
|
|
32936
32943
|
return { destroy };
|
|
32937
32944
|
}
|
|
@@ -34201,7 +34208,7 @@ var EnergySummaryTooltip = {
|
|
|
34201
34208
|
* RFC-0105 Enhancement: Now fetches device lists from MyIOOrchestratorData
|
|
34202
34209
|
* to populate device lists for status popup display
|
|
34203
34210
|
*/
|
|
34204
|
-
buildSummaryFromState(
|
|
34211
|
+
buildSummaryFromState(state6, receivedData, domain = "energy") {
|
|
34205
34212
|
const summary = {
|
|
34206
34213
|
totalDevices: 0,
|
|
34207
34214
|
totalConsumption: 0,
|
|
@@ -34224,22 +34231,22 @@ var EnergySummaryTooltip = {
|
|
|
34224
34231
|
},
|
|
34225
34232
|
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
34226
34233
|
};
|
|
34227
|
-
if (!
|
|
34234
|
+
if (!state6) return summary;
|
|
34228
34235
|
const entrada = {
|
|
34229
34236
|
id: "entrada",
|
|
34230
34237
|
name: "Entrada",
|
|
34231
34238
|
icon: CATEGORY_ICONS.entrada,
|
|
34232
|
-
deviceCount:
|
|
34233
|
-
consumption:
|
|
34239
|
+
deviceCount: state6.entrada?.devices?.length || (receivedData?.entrada_total?.device_count || 0),
|
|
34240
|
+
consumption: state6.entrada?.total || 0,
|
|
34234
34241
|
percentage: 100
|
|
34235
34242
|
};
|
|
34236
34243
|
const lojas = {
|
|
34237
34244
|
id: "lojas",
|
|
34238
34245
|
name: "Lojas",
|
|
34239
34246
|
icon: CATEGORY_ICONS.lojas,
|
|
34240
|
-
deviceCount:
|
|
34241
|
-
consumption:
|
|
34242
|
-
percentage:
|
|
34247
|
+
deviceCount: state6.consumidores?.lojas?.devices?.length || (receivedData?.lojas_total?.device_count || 0),
|
|
34248
|
+
consumption: state6.consumidores?.lojas?.total || 0,
|
|
34249
|
+
percentage: state6.consumidores?.lojas?.perc || 0
|
|
34243
34250
|
};
|
|
34244
34251
|
const climatizacaoData = receivedData?.climatizacao || {};
|
|
34245
34252
|
const elevadoresData = receivedData?.elevadores || {};
|
|
@@ -34250,9 +34257,9 @@ var EnergySummaryTooltip = {
|
|
|
34250
34257
|
id: "climatizacao",
|
|
34251
34258
|
name: "Climatizacao",
|
|
34252
34259
|
icon: CATEGORY_ICONS.climatizacao,
|
|
34253
|
-
deviceCount: climatizacaoData.count ||
|
|
34254
|
-
consumption:
|
|
34255
|
-
percentage:
|
|
34260
|
+
deviceCount: climatizacaoData.count || state6.consumidores?.climatizacao?.devices?.length || 0,
|
|
34261
|
+
consumption: state6.consumidores?.climatizacao?.total || 0,
|
|
34262
|
+
percentage: state6.consumidores?.climatizacao?.perc || 0
|
|
34256
34263
|
};
|
|
34257
34264
|
if (climatizacaoData.subcategories) {
|
|
34258
34265
|
climatizacao.children = [];
|
|
@@ -34303,40 +34310,40 @@ var EnergySummaryTooltip = {
|
|
|
34303
34310
|
id: "elevadores",
|
|
34304
34311
|
name: "Elevadores",
|
|
34305
34312
|
icon: CATEGORY_ICONS.elevadores,
|
|
34306
|
-
deviceCount: elevadoresData.count ||
|
|
34307
|
-
consumption:
|
|
34308
|
-
percentage:
|
|
34313
|
+
deviceCount: elevadoresData.count || state6.consumidores?.elevadores?.devices?.length || 0,
|
|
34314
|
+
consumption: state6.consumidores?.elevadores?.total || 0,
|
|
34315
|
+
percentage: state6.consumidores?.elevadores?.perc || 0
|
|
34309
34316
|
});
|
|
34310
34317
|
areaComumChildren.push({
|
|
34311
34318
|
id: "escadasRolantes",
|
|
34312
34319
|
name: "Esc. Rolantes",
|
|
34313
34320
|
icon: CATEGORY_ICONS.escadas,
|
|
34314
|
-
deviceCount: escadasData.count ||
|
|
34315
|
-
consumption:
|
|
34316
|
-
percentage:
|
|
34321
|
+
deviceCount: escadasData.count || state6.consumidores?.escadasRolantes?.devices?.length || 0,
|
|
34322
|
+
consumption: state6.consumidores?.escadasRolantes?.total || 0,
|
|
34323
|
+
percentage: state6.consumidores?.escadasRolantes?.perc || 0
|
|
34317
34324
|
});
|
|
34318
34325
|
areaComumChildren.push({
|
|
34319
34326
|
id: "outros",
|
|
34320
34327
|
name: "Outros",
|
|
34321
34328
|
icon: CATEGORY_ICONS.outros,
|
|
34322
|
-
deviceCount: outrosData.count ||
|
|
34323
|
-
consumption:
|
|
34324
|
-
percentage:
|
|
34329
|
+
deviceCount: outrosData.count || state6.consumidores?.outros?.devices?.length || 0,
|
|
34330
|
+
consumption: state6.consumidores?.outros?.total || 0,
|
|
34331
|
+
percentage: state6.consumidores?.outros?.perc || 0
|
|
34325
34332
|
});
|
|
34326
34333
|
const areaComumDeviceCount = areaComumChildren.reduce((sum, c) => sum + c.deviceCount, 0);
|
|
34327
|
-
const areaComumConsumption =
|
|
34334
|
+
const areaComumConsumption = state6.consumidores?.areaComum?.total || areaComumChildren.reduce((sum, c) => sum + c.consumption, 0);
|
|
34328
34335
|
const areaComum = {
|
|
34329
34336
|
id: "areaComum",
|
|
34330
34337
|
name: "Area Comum",
|
|
34331
34338
|
icon: CATEGORY_ICONS.areaComum,
|
|
34332
34339
|
deviceCount: areaComumDeviceCount,
|
|
34333
34340
|
consumption: areaComumConsumption,
|
|
34334
|
-
percentage:
|
|
34341
|
+
percentage: state6.consumidores?.areaComum?.perc || 0,
|
|
34335
34342
|
children: areaComumChildren
|
|
34336
34343
|
};
|
|
34337
34344
|
summary.byCategory = [entrada, lojas, areaComum];
|
|
34338
34345
|
summary.totalDevices = entrada.deviceCount + lojas.deviceCount + areaComumDeviceCount;
|
|
34339
|
-
summary.totalConsumption =
|
|
34346
|
+
summary.totalConsumption = state6.grandTotal || entrada.consumption;
|
|
34340
34347
|
const widgetAggregation = receivedData?.deviceStatusAggregation;
|
|
34341
34348
|
if (widgetAggregation && widgetAggregation.hasData) {
|
|
34342
34349
|
summary.byStatus = {
|
|
@@ -35624,7 +35631,7 @@ var WaterSummaryTooltip = {
|
|
|
35624
35631
|
* RFC-0105 Enhancement: Now fetches device lists from MyIOOrchestratorData
|
|
35625
35632
|
* to populate device lists for status popup display
|
|
35626
35633
|
*/
|
|
35627
|
-
buildSummaryFromState(
|
|
35634
|
+
buildSummaryFromState(state6, receivedData, includeBathrooms = false, domain = "water") {
|
|
35628
35635
|
const summary = {
|
|
35629
35636
|
totalDevices: 0,
|
|
35630
35637
|
totalConsumption: 0,
|
|
@@ -35648,22 +35655,22 @@ var WaterSummaryTooltip = {
|
|
|
35648
35655
|
lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
|
|
35649
35656
|
includeBathrooms
|
|
35650
35657
|
};
|
|
35651
|
-
if (!
|
|
35658
|
+
if (!state6) return summary;
|
|
35652
35659
|
const entrada = {
|
|
35653
35660
|
id: "entrada",
|
|
35654
35661
|
name: "Entrada",
|
|
35655
35662
|
icon: WATER_CATEGORY_ICONS.entrada,
|
|
35656
|
-
deviceCount:
|
|
35657
|
-
consumption:
|
|
35663
|
+
deviceCount: state6.entrada?.devices?.length || (receivedData?.entrada_total?.device_count || 0),
|
|
35664
|
+
consumption: state6.entrada?.total || 0,
|
|
35658
35665
|
percentage: 100
|
|
35659
35666
|
};
|
|
35660
35667
|
const lojas = {
|
|
35661
35668
|
id: "lojas",
|
|
35662
35669
|
name: "Lojas",
|
|
35663
35670
|
icon: WATER_CATEGORY_ICONS.lojas,
|
|
35664
|
-
deviceCount:
|
|
35665
|
-
consumption:
|
|
35666
|
-
percentage:
|
|
35671
|
+
deviceCount: state6.lojas?.devices?.length || (receivedData?.lojas_total?.device_count || 0),
|
|
35672
|
+
consumption: state6.lojas?.total || 0,
|
|
35673
|
+
percentage: state6.lojas?.perc || 0
|
|
35667
35674
|
};
|
|
35668
35675
|
summary.byCategory = [entrada, lojas];
|
|
35669
35676
|
if (includeBathrooms) {
|
|
@@ -35671,9 +35678,9 @@ var WaterSummaryTooltip = {
|
|
|
35671
35678
|
id: "banheiros",
|
|
35672
35679
|
name: "Banheiros",
|
|
35673
35680
|
icon: WATER_CATEGORY_ICONS.banheiros,
|
|
35674
|
-
deviceCount:
|
|
35675
|
-
consumption:
|
|
35676
|
-
percentage:
|
|
35681
|
+
deviceCount: state6.banheiros?.devices?.length || (receivedData?.banheiros_total?.device_count || 0),
|
|
35682
|
+
consumption: state6.banheiros?.total || 0,
|
|
35683
|
+
percentage: state6.banheiros?.perc || 0
|
|
35677
35684
|
};
|
|
35678
35685
|
summary.byCategory.push(banheiros);
|
|
35679
35686
|
}
|
|
@@ -35681,24 +35688,24 @@ var WaterSummaryTooltip = {
|
|
|
35681
35688
|
id: "areaComum",
|
|
35682
35689
|
name: "\xC1rea Comum",
|
|
35683
35690
|
icon: WATER_CATEGORY_ICONS.areaComum,
|
|
35684
|
-
deviceCount:
|
|
35685
|
-
consumption:
|
|
35686
|
-
percentage:
|
|
35691
|
+
deviceCount: state6.areaComum?.devices?.length || (receivedData?.area_comum_total?.device_count || 0),
|
|
35692
|
+
consumption: state6.areaComum?.total || 0,
|
|
35693
|
+
percentage: state6.areaComum?.perc || 0
|
|
35687
35694
|
};
|
|
35688
35695
|
summary.byCategory.push(areaComum);
|
|
35689
|
-
if (
|
|
35696
|
+
if (state6.pontosNaoMapeados && state6.pontosNaoMapeados.total > 0) {
|
|
35690
35697
|
const pontosNaoMapeados = {
|
|
35691
35698
|
id: "pontosNaoMapeados",
|
|
35692
35699
|
name: "Pontos N\xE3o Mapeados",
|
|
35693
35700
|
icon: WATER_CATEGORY_ICONS.pontosNaoMapeados,
|
|
35694
|
-
deviceCount:
|
|
35695
|
-
consumption:
|
|
35696
|
-
percentage:
|
|
35701
|
+
deviceCount: state6.pontosNaoMapeados?.devices?.length || 0,
|
|
35702
|
+
consumption: state6.pontosNaoMapeados?.total || 0,
|
|
35703
|
+
percentage: state6.pontosNaoMapeados?.perc || 0
|
|
35697
35704
|
};
|
|
35698
35705
|
summary.byCategory.push(pontosNaoMapeados);
|
|
35699
35706
|
}
|
|
35700
35707
|
summary.totalDevices = summary.byCategory.reduce((sum, cat) => sum + cat.deviceCount, 0);
|
|
35701
|
-
summary.totalConsumption =
|
|
35708
|
+
summary.totalConsumption = state6.entrada?.total || 0;
|
|
35702
35709
|
const widgetAggregation = receivedData?.deviceStatusAggregation;
|
|
35703
35710
|
if (widgetAggregation && widgetAggregation.hasData) {
|
|
35704
35711
|
summary.byStatus = {
|
|
@@ -37235,6 +37242,996 @@ var TempSensorSummaryTooltip = {
|
|
|
37235
37242
|
}
|
|
37236
37243
|
};
|
|
37237
37244
|
|
|
37245
|
+
// src/utils/ContractSummaryTooltip.ts
|
|
37246
|
+
var CONTRACT_SUMMARY_TOOLTIP_CSS = `
|
|
37247
|
+
/* ============================================
|
|
37248
|
+
Contract Summary Tooltip (RFC-0107)
|
|
37249
|
+
Premium draggable tooltip with dark theme
|
|
37250
|
+
============================================ */
|
|
37251
|
+
|
|
37252
|
+
.myio-contract-summary-tooltip {
|
|
37253
|
+
position: fixed;
|
|
37254
|
+
z-index: 99999;
|
|
37255
|
+
pointer-events: none;
|
|
37256
|
+
opacity: 0;
|
|
37257
|
+
transition: opacity 0.25s ease, transform 0.25s ease;
|
|
37258
|
+
transform: translateY(5px);
|
|
37259
|
+
}
|
|
37260
|
+
|
|
37261
|
+
.myio-contract-summary-tooltip.visible {
|
|
37262
|
+
opacity: 1;
|
|
37263
|
+
pointer-events: auto;
|
|
37264
|
+
transform: translateY(0);
|
|
37265
|
+
}
|
|
37266
|
+
|
|
37267
|
+
.myio-contract-summary-tooltip.closing {
|
|
37268
|
+
opacity: 0;
|
|
37269
|
+
transform: translateY(8px);
|
|
37270
|
+
transition: opacity 0.4s ease, transform 0.4s ease;
|
|
37271
|
+
}
|
|
37272
|
+
|
|
37273
|
+
.myio-contract-summary-tooltip.pinned {
|
|
37274
|
+
box-shadow: 0 0 0 2px #9684B5, 0 10px 40px rgba(0, 0, 0, 0.3);
|
|
37275
|
+
border-radius: 16px;
|
|
37276
|
+
}
|
|
37277
|
+
|
|
37278
|
+
.myio-contract-summary-tooltip.dragging {
|
|
37279
|
+
transition: none !important;
|
|
37280
|
+
cursor: move;
|
|
37281
|
+
}
|
|
37282
|
+
|
|
37283
|
+
.myio-contract-summary-tooltip.maximized {
|
|
37284
|
+
top: 20px !important;
|
|
37285
|
+
left: 20px !important;
|
|
37286
|
+
right: 20px !important;
|
|
37287
|
+
bottom: 20px !important;
|
|
37288
|
+
width: auto !important;
|
|
37289
|
+
max-width: none !important;
|
|
37290
|
+
}
|
|
37291
|
+
|
|
37292
|
+
.myio-contract-summary-tooltip.maximized .myio-contract-summary-tooltip__panel {
|
|
37293
|
+
width: 100%;
|
|
37294
|
+
height: 100%;
|
|
37295
|
+
max-width: none;
|
|
37296
|
+
display: flex;
|
|
37297
|
+
flex-direction: column;
|
|
37298
|
+
}
|
|
37299
|
+
|
|
37300
|
+
.myio-contract-summary-tooltip.maximized .myio-contract-summary-tooltip__body {
|
|
37301
|
+
flex: 1;
|
|
37302
|
+
overflow-y: auto;
|
|
37303
|
+
}
|
|
37304
|
+
|
|
37305
|
+
.myio-contract-summary-tooltip__panel {
|
|
37306
|
+
background: #2d1458;
|
|
37307
|
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
37308
|
+
border-radius: 16px;
|
|
37309
|
+
box-shadow:
|
|
37310
|
+
0 20px 60px rgba(0, 0, 0, 0.4),
|
|
37311
|
+
0 8px 20px rgba(0, 0, 0, 0.25),
|
|
37312
|
+
0 0 0 1px rgba(255, 255, 255, 0.05);
|
|
37313
|
+
min-width: 320px;
|
|
37314
|
+
max-width: 380px;
|
|
37315
|
+
font-family: Inter, system-ui, -apple-system, sans-serif;
|
|
37316
|
+
font-size: 12px;
|
|
37317
|
+
color: #ffffff;
|
|
37318
|
+
overflow: hidden;
|
|
37319
|
+
}
|
|
37320
|
+
|
|
37321
|
+
/* Header */
|
|
37322
|
+
.myio-contract-summary-tooltip__header {
|
|
37323
|
+
display: flex;
|
|
37324
|
+
align-items: center;
|
|
37325
|
+
gap: 10px;
|
|
37326
|
+
padding: 14px 16px;
|
|
37327
|
+
background: linear-gradient(135deg, #9684B5 0%, #2d1458 100%);
|
|
37328
|
+
border-radius: 16px 16px 0 0;
|
|
37329
|
+
position: relative;
|
|
37330
|
+
overflow: hidden;
|
|
37331
|
+
cursor: move;
|
|
37332
|
+
user-select: none;
|
|
37333
|
+
}
|
|
37334
|
+
|
|
37335
|
+
.myio-contract-summary-tooltip__header::before {
|
|
37336
|
+
content: '';
|
|
37337
|
+
position: absolute;
|
|
37338
|
+
top: 0;
|
|
37339
|
+
left: 0;
|
|
37340
|
+
right: 0;
|
|
37341
|
+
bottom: 0;
|
|
37342
|
+
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");
|
|
37343
|
+
opacity: 0.3;
|
|
37344
|
+
}
|
|
37345
|
+
|
|
37346
|
+
.myio-contract-summary-tooltip__icon {
|
|
37347
|
+
width: 40px;
|
|
37348
|
+
height: 40px;
|
|
37349
|
+
background: rgba(255, 255, 255, 0.15);
|
|
37350
|
+
border-radius: 12px;
|
|
37351
|
+
display: flex;
|
|
37352
|
+
align-items: center;
|
|
37353
|
+
justify-content: center;
|
|
37354
|
+
font-size: 20px;
|
|
37355
|
+
backdrop-filter: blur(10px);
|
|
37356
|
+
position: relative;
|
|
37357
|
+
z-index: 1;
|
|
37358
|
+
}
|
|
37359
|
+
|
|
37360
|
+
.myio-contract-summary-tooltip__icon.valid {
|
|
37361
|
+
background: rgba(76, 175, 80, 0.3);
|
|
37362
|
+
}
|
|
37363
|
+
|
|
37364
|
+
.myio-contract-summary-tooltip__icon.invalid {
|
|
37365
|
+
background: rgba(244, 67, 54, 0.3);
|
|
37366
|
+
}
|
|
37367
|
+
|
|
37368
|
+
.myio-contract-summary-tooltip__header-info {
|
|
37369
|
+
flex: 1;
|
|
37370
|
+
position: relative;
|
|
37371
|
+
z-index: 1;
|
|
37372
|
+
}
|
|
37373
|
+
|
|
37374
|
+
.myio-contract-summary-tooltip__title {
|
|
37375
|
+
font-weight: 700;
|
|
37376
|
+
font-size: 15px;
|
|
37377
|
+
color: #ffffff;
|
|
37378
|
+
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
|
|
37379
|
+
margin-bottom: 2px;
|
|
37380
|
+
}
|
|
37381
|
+
|
|
37382
|
+
.myio-contract-summary-tooltip__subtitle {
|
|
37383
|
+
font-size: 11px;
|
|
37384
|
+
color: rgba(255, 255, 255, 0.7);
|
|
37385
|
+
}
|
|
37386
|
+
|
|
37387
|
+
.myio-contract-summary-tooltip__header-actions {
|
|
37388
|
+
display: flex;
|
|
37389
|
+
align-items: center;
|
|
37390
|
+
gap: 4px;
|
|
37391
|
+
position: relative;
|
|
37392
|
+
z-index: 1;
|
|
37393
|
+
}
|
|
37394
|
+
|
|
37395
|
+
.myio-contract-summary-tooltip__header-btn {
|
|
37396
|
+
width: 28px;
|
|
37397
|
+
height: 28px;
|
|
37398
|
+
border: none;
|
|
37399
|
+
background: rgba(255, 255, 255, 0.15);
|
|
37400
|
+
border-radius: 8px;
|
|
37401
|
+
cursor: pointer;
|
|
37402
|
+
display: flex;
|
|
37403
|
+
align-items: center;
|
|
37404
|
+
justify-content: center;
|
|
37405
|
+
transition: all 0.2s ease;
|
|
37406
|
+
color: rgba(255, 255, 255, 0.8);
|
|
37407
|
+
}
|
|
37408
|
+
|
|
37409
|
+
.myio-contract-summary-tooltip__header-btn:hover {
|
|
37410
|
+
background: rgba(255, 255, 255, 0.25);
|
|
37411
|
+
color: #ffffff;
|
|
37412
|
+
transform: scale(1.05);
|
|
37413
|
+
}
|
|
37414
|
+
|
|
37415
|
+
.myio-contract-summary-tooltip__header-btn.pinned {
|
|
37416
|
+
background: rgba(255, 255, 255, 0.9);
|
|
37417
|
+
color: #9684B5;
|
|
37418
|
+
}
|
|
37419
|
+
|
|
37420
|
+
.myio-contract-summary-tooltip__header-btn.pinned:hover {
|
|
37421
|
+
background: #ffffff;
|
|
37422
|
+
color: #2d1458;
|
|
37423
|
+
}
|
|
37424
|
+
|
|
37425
|
+
.myio-contract-summary-tooltip__header-btn svg {
|
|
37426
|
+
width: 14px;
|
|
37427
|
+
height: 14px;
|
|
37428
|
+
}
|
|
37429
|
+
|
|
37430
|
+
/* Body */
|
|
37431
|
+
.myio-contract-summary-tooltip__body {
|
|
37432
|
+
padding: 16px;
|
|
37433
|
+
}
|
|
37434
|
+
|
|
37435
|
+
/* Domain Section */
|
|
37436
|
+
.myio-contract-summary-tooltip__domain {
|
|
37437
|
+
margin-bottom: 14px;
|
|
37438
|
+
background: rgba(255, 255, 255, 0.05);
|
|
37439
|
+
border-radius: 12px;
|
|
37440
|
+
overflow: hidden;
|
|
37441
|
+
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
37442
|
+
}
|
|
37443
|
+
|
|
37444
|
+
.myio-contract-summary-tooltip__domain:last-child {
|
|
37445
|
+
margin-bottom: 0;
|
|
37446
|
+
}
|
|
37447
|
+
|
|
37448
|
+
.myio-contract-summary-tooltip__domain-header {
|
|
37449
|
+
display: flex;
|
|
37450
|
+
align-items: center;
|
|
37451
|
+
justify-content: space-between;
|
|
37452
|
+
padding: 10px 14px;
|
|
37453
|
+
cursor: pointer;
|
|
37454
|
+
transition: background 0.2s ease;
|
|
37455
|
+
}
|
|
37456
|
+
|
|
37457
|
+
.myio-contract-summary-tooltip__domain-header:hover {
|
|
37458
|
+
background: rgba(255, 255, 255, 0.05);
|
|
37459
|
+
}
|
|
37460
|
+
|
|
37461
|
+
.myio-contract-summary-tooltip__domain-info {
|
|
37462
|
+
display: flex;
|
|
37463
|
+
align-items: center;
|
|
37464
|
+
gap: 8px;
|
|
37465
|
+
}
|
|
37466
|
+
|
|
37467
|
+
.myio-contract-summary-tooltip__domain-icon {
|
|
37468
|
+
font-size: 18px;
|
|
37469
|
+
}
|
|
37470
|
+
|
|
37471
|
+
.myio-contract-summary-tooltip__domain-name {
|
|
37472
|
+
font-weight: 600;
|
|
37473
|
+
font-size: 13px;
|
|
37474
|
+
}
|
|
37475
|
+
|
|
37476
|
+
.myio-contract-summary-tooltip__domain-count {
|
|
37477
|
+
font-size: 12px;
|
|
37478
|
+
color: #81c784;
|
|
37479
|
+
font-weight: 600;
|
|
37480
|
+
}
|
|
37481
|
+
|
|
37482
|
+
.myio-contract-summary-tooltip__expand-icon {
|
|
37483
|
+
font-size: 10px;
|
|
37484
|
+
opacity: 0.6;
|
|
37485
|
+
transition: transform 0.3s ease;
|
|
37486
|
+
}
|
|
37487
|
+
|
|
37488
|
+
.myio-contract-summary-tooltip__domain.expanded .myio-contract-summary-tooltip__expand-icon {
|
|
37489
|
+
transform: rotate(180deg);
|
|
37490
|
+
}
|
|
37491
|
+
|
|
37492
|
+
/* Domain Details */
|
|
37493
|
+
.myio-contract-summary-tooltip__domain-details {
|
|
37494
|
+
max-height: 0;
|
|
37495
|
+
overflow: hidden;
|
|
37496
|
+
transition: max-height 0.3s ease;
|
|
37497
|
+
background: rgba(0, 0, 0, 0.15);
|
|
37498
|
+
}
|
|
37499
|
+
|
|
37500
|
+
.myio-contract-summary-tooltip__domain.expanded .myio-contract-summary-tooltip__domain-details {
|
|
37501
|
+
max-height: 150px;
|
|
37502
|
+
}
|
|
37503
|
+
|
|
37504
|
+
.myio-contract-summary-tooltip__detail-row {
|
|
37505
|
+
display: flex;
|
|
37506
|
+
align-items: center;
|
|
37507
|
+
justify-content: space-between;
|
|
37508
|
+
padding: 6px 14px 6px 40px;
|
|
37509
|
+
font-size: 12px;
|
|
37510
|
+
border-top: 1px solid rgba(255, 255, 255, 0.05);
|
|
37511
|
+
}
|
|
37512
|
+
|
|
37513
|
+
.myio-contract-summary-tooltip__detail-row:first-child {
|
|
37514
|
+
border-top: none;
|
|
37515
|
+
}
|
|
37516
|
+
|
|
37517
|
+
.myio-contract-summary-tooltip__detail-label {
|
|
37518
|
+
opacity: 0.7;
|
|
37519
|
+
display: flex;
|
|
37520
|
+
align-items: center;
|
|
37521
|
+
gap: 6px;
|
|
37522
|
+
}
|
|
37523
|
+
|
|
37524
|
+
.myio-contract-summary-tooltip__detail-label::before {
|
|
37525
|
+
content: '';
|
|
37526
|
+
width: 4px;
|
|
37527
|
+
height: 4px;
|
|
37528
|
+
border-radius: 50%;
|
|
37529
|
+
background: currentColor;
|
|
37530
|
+
opacity: 0.5;
|
|
37531
|
+
}
|
|
37532
|
+
|
|
37533
|
+
.myio-contract-summary-tooltip__detail-count {
|
|
37534
|
+
font-weight: 500;
|
|
37535
|
+
color: #81c784;
|
|
37536
|
+
}
|
|
37537
|
+
|
|
37538
|
+
/* Status Banner */
|
|
37539
|
+
.myio-contract-summary-tooltip__status {
|
|
37540
|
+
display: flex;
|
|
37541
|
+
align-items: center;
|
|
37542
|
+
justify-content: center;
|
|
37543
|
+
gap: 8px;
|
|
37544
|
+
padding: 10px 14px;
|
|
37545
|
+
border-radius: 10px;
|
|
37546
|
+
margin-bottom: 14px;
|
|
37547
|
+
font-size: 12px;
|
|
37548
|
+
font-weight: 600;
|
|
37549
|
+
}
|
|
37550
|
+
|
|
37551
|
+
.myio-contract-summary-tooltip__status.valid {
|
|
37552
|
+
background: rgba(76, 175, 80, 0.2);
|
|
37553
|
+
color: #81c784;
|
|
37554
|
+
border: 1px solid rgba(76, 175, 80, 0.3);
|
|
37555
|
+
}
|
|
37556
|
+
|
|
37557
|
+
.myio-contract-summary-tooltip__status.invalid {
|
|
37558
|
+
background: rgba(244, 67, 54, 0.2);
|
|
37559
|
+
color: #ef5350;
|
|
37560
|
+
border: 1px solid rgba(244, 67, 54, 0.3);
|
|
37561
|
+
}
|
|
37562
|
+
|
|
37563
|
+
.myio-contract-summary-tooltip__status-icon {
|
|
37564
|
+
font-size: 14px;
|
|
37565
|
+
}
|
|
37566
|
+
|
|
37567
|
+
/* Discrepancies */
|
|
37568
|
+
.myio-contract-summary-tooltip__discrepancies {
|
|
37569
|
+
background: rgba(244, 67, 54, 0.15);
|
|
37570
|
+
border: 1px solid rgba(244, 67, 54, 0.3);
|
|
37571
|
+
border-radius: 10px;
|
|
37572
|
+
padding: 10px 14px;
|
|
37573
|
+
margin-bottom: 14px;
|
|
37574
|
+
}
|
|
37575
|
+
|
|
37576
|
+
.myio-contract-summary-tooltip__discrepancies-title {
|
|
37577
|
+
font-size: 11px;
|
|
37578
|
+
font-weight: 600;
|
|
37579
|
+
color: #ef5350;
|
|
37580
|
+
margin-bottom: 6px;
|
|
37581
|
+
text-transform: uppercase;
|
|
37582
|
+
letter-spacing: 0.5px;
|
|
37583
|
+
}
|
|
37584
|
+
|
|
37585
|
+
.myio-contract-summary-tooltip__discrepancy-item {
|
|
37586
|
+
font-size: 11px;
|
|
37587
|
+
color: rgba(255, 255, 255, 0.8);
|
|
37588
|
+
padding: 3px 0;
|
|
37589
|
+
}
|
|
37590
|
+
|
|
37591
|
+
/* Footer */
|
|
37592
|
+
.myio-contract-summary-tooltip__footer {
|
|
37593
|
+
display: flex;
|
|
37594
|
+
justify-content: space-between;
|
|
37595
|
+
align-items: center;
|
|
37596
|
+
padding: 12px 16px;
|
|
37597
|
+
background: rgba(0, 0, 0, 0.2);
|
|
37598
|
+
border-top: 1px solid rgba(255, 255, 255, 0.05);
|
|
37599
|
+
border-radius: 0 0 16px 16px;
|
|
37600
|
+
}
|
|
37601
|
+
|
|
37602
|
+
.myio-contract-summary-tooltip__footer-label {
|
|
37603
|
+
font-size: 10px;
|
|
37604
|
+
color: rgba(255, 255, 255, 0.5);
|
|
37605
|
+
}
|
|
37606
|
+
|
|
37607
|
+
.myio-contract-summary-tooltip__footer-value {
|
|
37608
|
+
font-size: 11px;
|
|
37609
|
+
font-weight: 600;
|
|
37610
|
+
color: rgba(255, 255, 255, 0.8);
|
|
37611
|
+
}
|
|
37612
|
+
|
|
37613
|
+
/* Total Devices Badge */
|
|
37614
|
+
.myio-contract-summary-tooltip__total {
|
|
37615
|
+
display: flex;
|
|
37616
|
+
align-items: center;
|
|
37617
|
+
justify-content: center;
|
|
37618
|
+
gap: 8px;
|
|
37619
|
+
padding: 12px;
|
|
37620
|
+
background: rgba(255, 255, 255, 0.08);
|
|
37621
|
+
border-radius: 10px;
|
|
37622
|
+
margin-bottom: 14px;
|
|
37623
|
+
}
|
|
37624
|
+
|
|
37625
|
+
.myio-contract-summary-tooltip__total-label {
|
|
37626
|
+
font-size: 12px;
|
|
37627
|
+
opacity: 0.8;
|
|
37628
|
+
}
|
|
37629
|
+
|
|
37630
|
+
.myio-contract-summary-tooltip__total-value {
|
|
37631
|
+
font-size: 18px;
|
|
37632
|
+
font-weight: 700;
|
|
37633
|
+
color: #81c784;
|
|
37634
|
+
}
|
|
37635
|
+
|
|
37636
|
+
/* Responsive */
|
|
37637
|
+
@media (max-width: 400px) {
|
|
37638
|
+
.myio-contract-summary-tooltip__panel {
|
|
37639
|
+
min-width: 280px;
|
|
37640
|
+
max-width: 95vw;
|
|
37641
|
+
}
|
|
37642
|
+
}
|
|
37643
|
+
`;
|
|
37644
|
+
var cssInjected9 = false;
|
|
37645
|
+
function injectCSS9() {
|
|
37646
|
+
if (cssInjected9) return;
|
|
37647
|
+
if (typeof document === "undefined") return;
|
|
37648
|
+
const styleId = "myio-contract-summary-tooltip-styles";
|
|
37649
|
+
if (document.getElementById(styleId)) {
|
|
37650
|
+
cssInjected9 = true;
|
|
37651
|
+
return;
|
|
37652
|
+
}
|
|
37653
|
+
const style = document.createElement("style");
|
|
37654
|
+
style.id = styleId;
|
|
37655
|
+
style.textContent = CONTRACT_SUMMARY_TOOLTIP_CSS;
|
|
37656
|
+
document.head.appendChild(style);
|
|
37657
|
+
cssInjected9 = true;
|
|
37658
|
+
}
|
|
37659
|
+
var state5 = {
|
|
37660
|
+
hideTimer: null,
|
|
37661
|
+
isMouseOverTooltip: false,
|
|
37662
|
+
isMaximized: false,
|
|
37663
|
+
isDragging: false,
|
|
37664
|
+
dragOffset: { x: 0, y: 0 },
|
|
37665
|
+
savedPosition: null,
|
|
37666
|
+
pinnedCounter: 0,
|
|
37667
|
+
expandedDomains: /* @__PURE__ */ new Set(["energy", "water", "temperature"])
|
|
37668
|
+
};
|
|
37669
|
+
function formatTimestamp5(isoString) {
|
|
37670
|
+
if (!isoString) return "Agora";
|
|
37671
|
+
try {
|
|
37672
|
+
const date = new Date(isoString);
|
|
37673
|
+
return date.toLocaleString("pt-BR", {
|
|
37674
|
+
hour: "2-digit",
|
|
37675
|
+
minute: "2-digit",
|
|
37676
|
+
day: "2-digit",
|
|
37677
|
+
month: "2-digit"
|
|
37678
|
+
});
|
|
37679
|
+
} catch {
|
|
37680
|
+
return "Agora";
|
|
37681
|
+
}
|
|
37682
|
+
}
|
|
37683
|
+
function calculateTotalDevices(data) {
|
|
37684
|
+
return data.energy.total + data.water.total + data.temperature.total;
|
|
37685
|
+
}
|
|
37686
|
+
function generateHeaderHTML5(data) {
|
|
37687
|
+
const iconClass = data.isValid ? "valid" : "invalid";
|
|
37688
|
+
const iconSymbol = data.isValid ? "\u2713" : "!";
|
|
37689
|
+
const totalDevices = calculateTotalDevices(data);
|
|
37690
|
+
return `
|
|
37691
|
+
<div class="myio-contract-summary-tooltip__header" data-drag-handle>
|
|
37692
|
+
<div class="myio-contract-summary-tooltip__icon ${iconClass}">${iconSymbol}</div>
|
|
37693
|
+
<div class="myio-contract-summary-tooltip__header-info">
|
|
37694
|
+
<div class="myio-contract-summary-tooltip__title">Contract Summary</div>
|
|
37695
|
+
<div class="myio-contract-summary-tooltip__subtitle">${totalDevices} devices loaded</div>
|
|
37696
|
+
</div>
|
|
37697
|
+
<div class="myio-contract-summary-tooltip__header-actions">
|
|
37698
|
+
<button class="myio-contract-summary-tooltip__header-btn" data-action="pin" title="Pin to screen">
|
|
37699
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
37700
|
+
<path d="M9 4v6l-2 4v2h10v-2l-2-4V4"/>
|
|
37701
|
+
<line x1="12" y1="16" x2="12" y2="21"/>
|
|
37702
|
+
<line x1="8" y1="4" x2="16" y2="4"/>
|
|
37703
|
+
</svg>
|
|
37704
|
+
</button>
|
|
37705
|
+
<button class="myio-contract-summary-tooltip__header-btn" data-action="maximize" title="Maximize">
|
|
37706
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
37707
|
+
<rect x="3" y="3" width="18" height="18" rx="2"/>
|
|
37708
|
+
</svg>
|
|
37709
|
+
</button>
|
|
37710
|
+
<button class="myio-contract-summary-tooltip__header-btn" data-action="close" title="Close">
|
|
37711
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
37712
|
+
<path d="M18 6L6 18M6 6l12 12"/>
|
|
37713
|
+
</svg>
|
|
37714
|
+
</button>
|
|
37715
|
+
</div>
|
|
37716
|
+
</div>
|
|
37717
|
+
`;
|
|
37718
|
+
}
|
|
37719
|
+
function generateDomainHTML(domain, icon, name, counts, isTemperature = false) {
|
|
37720
|
+
const isExpanded = state5.expandedDomains.has(domain);
|
|
37721
|
+
const expandedClass = isExpanded ? "expanded" : "";
|
|
37722
|
+
let detailsHTML = "";
|
|
37723
|
+
if (isTemperature) {
|
|
37724
|
+
const tempCounts = counts;
|
|
37725
|
+
detailsHTML = `
|
|
37726
|
+
<div class="myio-contract-summary-tooltip__detail-row">
|
|
37727
|
+
<span class="myio-contract-summary-tooltip__detail-label">Internal (Climate)</span>
|
|
37728
|
+
<span class="myio-contract-summary-tooltip__detail-count">${tempCounts.internal}</span>
|
|
37729
|
+
</div>
|
|
37730
|
+
<div class="myio-contract-summary-tooltip__detail-row">
|
|
37731
|
+
<span class="myio-contract-summary-tooltip__detail-label">Stores (Non-Climate)</span>
|
|
37732
|
+
<span class="myio-contract-summary-tooltip__detail-count">${tempCounts.stores}</span>
|
|
37733
|
+
</div>
|
|
37734
|
+
`;
|
|
37735
|
+
} else {
|
|
37736
|
+
const domainCounts = counts;
|
|
37737
|
+
detailsHTML = `
|
|
37738
|
+
<div class="myio-contract-summary-tooltip__detail-row">
|
|
37739
|
+
<span class="myio-contract-summary-tooltip__detail-label">Entries</span>
|
|
37740
|
+
<span class="myio-contract-summary-tooltip__detail-count">${domainCounts.entries}</span>
|
|
37741
|
+
</div>
|
|
37742
|
+
<div class="myio-contract-summary-tooltip__detail-row">
|
|
37743
|
+
<span class="myio-contract-summary-tooltip__detail-label">Common Area</span>
|
|
37744
|
+
<span class="myio-contract-summary-tooltip__detail-count">${domainCounts.commonArea}</span>
|
|
37745
|
+
</div>
|
|
37746
|
+
<div class="myio-contract-summary-tooltip__detail-row">
|
|
37747
|
+
<span class="myio-contract-summary-tooltip__detail-label">Stores</span>
|
|
37748
|
+
<span class="myio-contract-summary-tooltip__detail-count">${domainCounts.stores}</span>
|
|
37749
|
+
</div>
|
|
37750
|
+
`;
|
|
37751
|
+
}
|
|
37752
|
+
return `
|
|
37753
|
+
<div class="myio-contract-summary-tooltip__domain ${expandedClass}" data-domain="${domain}">
|
|
37754
|
+
<div class="myio-contract-summary-tooltip__domain-header" data-toggle-domain="${domain}">
|
|
37755
|
+
<div class="myio-contract-summary-tooltip__domain-info">
|
|
37756
|
+
<span class="myio-contract-summary-tooltip__domain-icon">${icon}</span>
|
|
37757
|
+
<span class="myio-contract-summary-tooltip__domain-name">${name}</span>
|
|
37758
|
+
</div>
|
|
37759
|
+
<div style="display: flex; align-items: center; gap: 8px;">
|
|
37760
|
+
<span class="myio-contract-summary-tooltip__domain-count">${counts.total} devices</span>
|
|
37761
|
+
<span class="myio-contract-summary-tooltip__expand-icon">\u25BC</span>
|
|
37762
|
+
</div>
|
|
37763
|
+
</div>
|
|
37764
|
+
<div class="myio-contract-summary-tooltip__domain-details">
|
|
37765
|
+
${detailsHTML}
|
|
37766
|
+
</div>
|
|
37767
|
+
</div>
|
|
37768
|
+
`;
|
|
37769
|
+
}
|
|
37770
|
+
function generateBodyHTML3(data) {
|
|
37771
|
+
const totalDevices = calculateTotalDevices(data);
|
|
37772
|
+
const timestamp = formatTimestamp5(data.timestamp);
|
|
37773
|
+
const statusClass = data.isValid ? "valid" : "invalid";
|
|
37774
|
+
const statusIcon = data.isValid ? "\u2713" : "\u26A0";
|
|
37775
|
+
const statusText = data.isValid ? "Contract validated successfully" : "Validation issues detected";
|
|
37776
|
+
let discrepanciesHTML = "";
|
|
37777
|
+
if (data.discrepancies && data.discrepancies.length > 0) {
|
|
37778
|
+
const items = data.discrepancies.map((d) => `<div class="myio-contract-summary-tooltip__discrepancy-item">${d.domain}: expected ${d.expected}, found ${d.actual}</div>`).join("");
|
|
37779
|
+
discrepanciesHTML = `
|
|
37780
|
+
<div class="myio-contract-summary-tooltip__discrepancies">
|
|
37781
|
+
<div class="myio-contract-summary-tooltip__discrepancies-title">Discrepancies</div>
|
|
37782
|
+
${items}
|
|
37783
|
+
</div>
|
|
37784
|
+
`;
|
|
37785
|
+
}
|
|
37786
|
+
return `
|
|
37787
|
+
<div class="myio-contract-summary-tooltip__body">
|
|
37788
|
+
<!-- Status Banner -->
|
|
37789
|
+
<div class="myio-contract-summary-tooltip__status ${statusClass}">
|
|
37790
|
+
<span class="myio-contract-summary-tooltip__status-icon">${statusIcon}</span>
|
|
37791
|
+
<span>${statusText}</span>
|
|
37792
|
+
</div>
|
|
37793
|
+
|
|
37794
|
+
${discrepanciesHTML}
|
|
37795
|
+
|
|
37796
|
+
<!-- Total Devices -->
|
|
37797
|
+
<div class="myio-contract-summary-tooltip__total">
|
|
37798
|
+
<span class="myio-contract-summary-tooltip__total-label">Total Devices:</span>
|
|
37799
|
+
<span class="myio-contract-summary-tooltip__total-value">${totalDevices}</span>
|
|
37800
|
+
</div>
|
|
37801
|
+
|
|
37802
|
+
<!-- Energy -->
|
|
37803
|
+
${generateDomainHTML("energy", "\u26A1", "Energy", data.energy)}
|
|
37804
|
+
|
|
37805
|
+
<!-- Water -->
|
|
37806
|
+
${generateDomainHTML("water", "\u{1F4A7}", "Water", data.water)}
|
|
37807
|
+
|
|
37808
|
+
<!-- Temperature -->
|
|
37809
|
+
${generateDomainHTML("temperature", "\u{1F321}\uFE0F", "Temperature", data.temperature, true)}
|
|
37810
|
+
</div>
|
|
37811
|
+
|
|
37812
|
+
<!-- Footer -->
|
|
37813
|
+
<div class="myio-contract-summary-tooltip__footer">
|
|
37814
|
+
<span class="myio-contract-summary-tooltip__footer-label">Loaded at</span>
|
|
37815
|
+
<span class="myio-contract-summary-tooltip__footer-value">${timestamp}</span>
|
|
37816
|
+
</div>
|
|
37817
|
+
`;
|
|
37818
|
+
}
|
|
37819
|
+
function setupHoverListeners5(container) {
|
|
37820
|
+
container.onmouseenter = () => {
|
|
37821
|
+
state5.isMouseOverTooltip = true;
|
|
37822
|
+
if (state5.hideTimer) {
|
|
37823
|
+
clearTimeout(state5.hideTimer);
|
|
37824
|
+
state5.hideTimer = null;
|
|
37825
|
+
}
|
|
37826
|
+
};
|
|
37827
|
+
container.onmouseleave = () => {
|
|
37828
|
+
state5.isMouseOverTooltip = false;
|
|
37829
|
+
startDelayedHide5();
|
|
37830
|
+
};
|
|
37831
|
+
}
|
|
37832
|
+
function setupDomainToggleListeners(container) {
|
|
37833
|
+
const toggles = container.querySelectorAll("[data-toggle-domain]");
|
|
37834
|
+
toggles.forEach((toggle) => {
|
|
37835
|
+
toggle.onclick = (e) => {
|
|
37836
|
+
e.stopPropagation();
|
|
37837
|
+
const domain = toggle.dataset.toggleDomain;
|
|
37838
|
+
if (!domain) return;
|
|
37839
|
+
const domainEl = container.querySelector(`[data-domain="${domain}"]`);
|
|
37840
|
+
if (!domainEl) return;
|
|
37841
|
+
if (state5.expandedDomains.has(domain)) {
|
|
37842
|
+
state5.expandedDomains.delete(domain);
|
|
37843
|
+
domainEl.classList.remove("expanded");
|
|
37844
|
+
} else {
|
|
37845
|
+
state5.expandedDomains.add(domain);
|
|
37846
|
+
domainEl.classList.add("expanded");
|
|
37847
|
+
}
|
|
37848
|
+
};
|
|
37849
|
+
});
|
|
37850
|
+
}
|
|
37851
|
+
function setupButtonListeners5(container) {
|
|
37852
|
+
const buttons = container.querySelectorAll("[data-action]");
|
|
37853
|
+
buttons.forEach((btn) => {
|
|
37854
|
+
btn.onclick = (e) => {
|
|
37855
|
+
e.stopPropagation();
|
|
37856
|
+
const action = btn.dataset.action;
|
|
37857
|
+
switch (action) {
|
|
37858
|
+
case "pin":
|
|
37859
|
+
createPinnedClone5(container);
|
|
37860
|
+
break;
|
|
37861
|
+
case "maximize":
|
|
37862
|
+
toggleMaximize5(container);
|
|
37863
|
+
break;
|
|
37864
|
+
case "close":
|
|
37865
|
+
ContractSummaryTooltip.close();
|
|
37866
|
+
break;
|
|
37867
|
+
}
|
|
37868
|
+
};
|
|
37869
|
+
});
|
|
37870
|
+
}
|
|
37871
|
+
function setupDragListeners5(container) {
|
|
37872
|
+
const header = container.querySelector("[data-drag-handle]");
|
|
37873
|
+
if (!header) return;
|
|
37874
|
+
header.onmousedown = (e) => {
|
|
37875
|
+
if (e.target.closest("[data-action]")) return;
|
|
37876
|
+
if (e.target.closest("[data-toggle-domain]")) return;
|
|
37877
|
+
if (state5.isMaximized) return;
|
|
37878
|
+
state5.isDragging = true;
|
|
37879
|
+
container.classList.add("dragging");
|
|
37880
|
+
const rect = container.getBoundingClientRect();
|
|
37881
|
+
state5.dragOffset = {
|
|
37882
|
+
x: e.clientX - rect.left,
|
|
37883
|
+
y: e.clientY - rect.top
|
|
37884
|
+
};
|
|
37885
|
+
const onMouseMove = (e2) => {
|
|
37886
|
+
if (!state5.isDragging) return;
|
|
37887
|
+
const newLeft = e2.clientX - state5.dragOffset.x;
|
|
37888
|
+
const newTop = e2.clientY - state5.dragOffset.y;
|
|
37889
|
+
const maxLeft = window.innerWidth - container.offsetWidth;
|
|
37890
|
+
const maxTop = window.innerHeight - container.offsetHeight;
|
|
37891
|
+
container.style.left = Math.max(0, Math.min(newLeft, maxLeft)) + "px";
|
|
37892
|
+
container.style.top = Math.max(0, Math.min(newTop, maxTop)) + "px";
|
|
37893
|
+
};
|
|
37894
|
+
const onMouseUp = () => {
|
|
37895
|
+
state5.isDragging = false;
|
|
37896
|
+
container.classList.remove("dragging");
|
|
37897
|
+
document.removeEventListener("mousemove", onMouseMove);
|
|
37898
|
+
document.removeEventListener("mouseup", onMouseUp);
|
|
37899
|
+
};
|
|
37900
|
+
document.addEventListener("mousemove", onMouseMove);
|
|
37901
|
+
document.addEventListener("mouseup", onMouseUp);
|
|
37902
|
+
};
|
|
37903
|
+
}
|
|
37904
|
+
function createPinnedClone5(container) {
|
|
37905
|
+
state5.pinnedCounter++;
|
|
37906
|
+
const pinnedId = `myio-contract-summary-tooltip-pinned-${state5.pinnedCounter}`;
|
|
37907
|
+
const clone = container.cloneNode(true);
|
|
37908
|
+
clone.id = pinnedId;
|
|
37909
|
+
clone.classList.add("pinned");
|
|
37910
|
+
clone.classList.remove("closing");
|
|
37911
|
+
const pinBtn = clone.querySelector('[data-action="pin"]');
|
|
37912
|
+
if (pinBtn) {
|
|
37913
|
+
pinBtn.classList.add("pinned");
|
|
37914
|
+
pinBtn.setAttribute("title", "Unpin");
|
|
37915
|
+
pinBtn.innerHTML = `
|
|
37916
|
+
<svg viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="1">
|
|
37917
|
+
<path d="M9 4v6l-2 4v2h10v-2l-2-4V4"/>
|
|
37918
|
+
<line x1="12" y1="16" x2="12" y2="21"/>
|
|
37919
|
+
<line x1="8" y1="4" x2="16" y2="4"/>
|
|
37920
|
+
</svg>
|
|
37921
|
+
`;
|
|
37922
|
+
}
|
|
37923
|
+
document.body.appendChild(clone);
|
|
37924
|
+
setupPinnedCloneListeners5(clone, pinnedId);
|
|
37925
|
+
ContractSummaryTooltip.hide();
|
|
37926
|
+
}
|
|
37927
|
+
function setupPinnedCloneListeners5(clone, cloneId) {
|
|
37928
|
+
let isMaximized = false;
|
|
37929
|
+
let savedPosition = null;
|
|
37930
|
+
const cloneExpandedDomains = new Set(state5.expandedDomains);
|
|
37931
|
+
const toggles = clone.querySelectorAll("[data-toggle-domain]");
|
|
37932
|
+
toggles.forEach((toggle) => {
|
|
37933
|
+
toggle.onclick = (e) => {
|
|
37934
|
+
e.stopPropagation();
|
|
37935
|
+
const domain = toggle.dataset.toggleDomain;
|
|
37936
|
+
if (!domain) return;
|
|
37937
|
+
const domainEl = clone.querySelector(`[data-domain="${domain}"]`);
|
|
37938
|
+
if (!domainEl) return;
|
|
37939
|
+
if (cloneExpandedDomains.has(domain)) {
|
|
37940
|
+
cloneExpandedDomains.delete(domain);
|
|
37941
|
+
domainEl.classList.remove("expanded");
|
|
37942
|
+
} else {
|
|
37943
|
+
cloneExpandedDomains.add(domain);
|
|
37944
|
+
domainEl.classList.add("expanded");
|
|
37945
|
+
}
|
|
37946
|
+
};
|
|
37947
|
+
});
|
|
37948
|
+
const pinBtn = clone.querySelector('[data-action="pin"]');
|
|
37949
|
+
if (pinBtn) {
|
|
37950
|
+
pinBtn.onclick = (e) => {
|
|
37951
|
+
e.stopPropagation();
|
|
37952
|
+
closePinnedClone5(cloneId);
|
|
37953
|
+
};
|
|
37954
|
+
}
|
|
37955
|
+
const closeBtn = clone.querySelector('[data-action="close"]');
|
|
37956
|
+
if (closeBtn) {
|
|
37957
|
+
closeBtn.onclick = (e) => {
|
|
37958
|
+
e.stopPropagation();
|
|
37959
|
+
closePinnedClone5(cloneId);
|
|
37960
|
+
};
|
|
37961
|
+
}
|
|
37962
|
+
const maxBtn = clone.querySelector('[data-action="maximize"]');
|
|
37963
|
+
if (maxBtn) {
|
|
37964
|
+
maxBtn.onclick = (e) => {
|
|
37965
|
+
e.stopPropagation();
|
|
37966
|
+
isMaximized = !isMaximized;
|
|
37967
|
+
if (isMaximized) {
|
|
37968
|
+
savedPosition = { left: clone.style.left, top: clone.style.top };
|
|
37969
|
+
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>`;
|
|
37970
|
+
maxBtn.setAttribute("title", "Restore");
|
|
37971
|
+
} else {
|
|
37972
|
+
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>`;
|
|
37973
|
+
maxBtn.setAttribute("title", "Maximize");
|
|
37974
|
+
if (savedPosition) {
|
|
37975
|
+
clone.style.left = savedPosition.left;
|
|
37976
|
+
clone.style.top = savedPosition.top;
|
|
37977
|
+
}
|
|
37978
|
+
}
|
|
37979
|
+
clone.classList.toggle("maximized", isMaximized);
|
|
37980
|
+
};
|
|
37981
|
+
}
|
|
37982
|
+
const header = clone.querySelector("[data-drag-handle]");
|
|
37983
|
+
if (header) {
|
|
37984
|
+
let isDragging = false;
|
|
37985
|
+
let dragOffset = { x: 0, y: 0 };
|
|
37986
|
+
header.onmousedown = (e) => {
|
|
37987
|
+
if (e.target.closest("[data-action]")) return;
|
|
37988
|
+
if (e.target.closest("[data-toggle-domain]")) return;
|
|
37989
|
+
if (isMaximized) return;
|
|
37990
|
+
isDragging = true;
|
|
37991
|
+
clone.classList.add("dragging");
|
|
37992
|
+
const rect = clone.getBoundingClientRect();
|
|
37993
|
+
dragOffset = { x: e.clientX - rect.left, y: e.clientY - rect.top };
|
|
37994
|
+
const onMouseMove = (e2) => {
|
|
37995
|
+
if (!isDragging) return;
|
|
37996
|
+
const newLeft = e2.clientX - dragOffset.x;
|
|
37997
|
+
const newTop = e2.clientY - dragOffset.y;
|
|
37998
|
+
const maxLeft = window.innerWidth - clone.offsetWidth;
|
|
37999
|
+
const maxTop = window.innerHeight - clone.offsetHeight;
|
|
38000
|
+
clone.style.left = Math.max(0, Math.min(newLeft, maxLeft)) + "px";
|
|
38001
|
+
clone.style.top = Math.max(0, Math.min(newTop, maxTop)) + "px";
|
|
38002
|
+
};
|
|
38003
|
+
const onMouseUp = () => {
|
|
38004
|
+
isDragging = false;
|
|
38005
|
+
clone.classList.remove("dragging");
|
|
38006
|
+
document.removeEventListener("mousemove", onMouseMove);
|
|
38007
|
+
document.removeEventListener("mouseup", onMouseUp);
|
|
38008
|
+
};
|
|
38009
|
+
document.addEventListener("mousemove", onMouseMove);
|
|
38010
|
+
document.addEventListener("mouseup", onMouseUp);
|
|
38011
|
+
};
|
|
38012
|
+
}
|
|
38013
|
+
}
|
|
38014
|
+
function closePinnedClone5(cloneId) {
|
|
38015
|
+
const clone = document.getElementById(cloneId);
|
|
38016
|
+
if (clone) {
|
|
38017
|
+
clone.classList.add("closing");
|
|
38018
|
+
setTimeout(() => clone.remove(), 400);
|
|
38019
|
+
}
|
|
38020
|
+
}
|
|
38021
|
+
function toggleMaximize5(container) {
|
|
38022
|
+
state5.isMaximized = !state5.isMaximized;
|
|
38023
|
+
if (state5.isMaximized) {
|
|
38024
|
+
state5.savedPosition = {
|
|
38025
|
+
left: container.style.left,
|
|
38026
|
+
top: container.style.top
|
|
38027
|
+
};
|
|
38028
|
+
}
|
|
38029
|
+
container.classList.toggle("maximized", state5.isMaximized);
|
|
38030
|
+
const maxBtn = container.querySelector('[data-action="maximize"]');
|
|
38031
|
+
if (maxBtn) {
|
|
38032
|
+
if (state5.isMaximized) {
|
|
38033
|
+
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>`;
|
|
38034
|
+
maxBtn.setAttribute("title", "Restore");
|
|
38035
|
+
} else {
|
|
38036
|
+
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>`;
|
|
38037
|
+
maxBtn.setAttribute("title", "Maximize");
|
|
38038
|
+
if (state5.savedPosition) {
|
|
38039
|
+
container.style.left = state5.savedPosition.left;
|
|
38040
|
+
container.style.top = state5.savedPosition.top;
|
|
38041
|
+
}
|
|
38042
|
+
}
|
|
38043
|
+
}
|
|
38044
|
+
}
|
|
38045
|
+
function startDelayedHide5() {
|
|
38046
|
+
if (state5.isMouseOverTooltip) return;
|
|
38047
|
+
if (state5.hideTimer) {
|
|
38048
|
+
clearTimeout(state5.hideTimer);
|
|
38049
|
+
}
|
|
38050
|
+
state5.hideTimer = setTimeout(() => {
|
|
38051
|
+
hideWithAnimation5();
|
|
38052
|
+
}, 1500);
|
|
38053
|
+
}
|
|
38054
|
+
function hideWithAnimation5() {
|
|
38055
|
+
const container = document.getElementById("myio-contract-summary-tooltip");
|
|
38056
|
+
if (container && container.classList.contains("visible")) {
|
|
38057
|
+
container.classList.add("closing");
|
|
38058
|
+
setTimeout(() => {
|
|
38059
|
+
container.classList.remove("visible", "closing");
|
|
38060
|
+
}, 400);
|
|
38061
|
+
}
|
|
38062
|
+
}
|
|
38063
|
+
function positionTooltip5(container, triggerElement) {
|
|
38064
|
+
const rect = triggerElement.getBoundingClientRect();
|
|
38065
|
+
let left = rect.left;
|
|
38066
|
+
let top = rect.bottom + 8;
|
|
38067
|
+
const tooltipWidth = 360;
|
|
38068
|
+
if (left + tooltipWidth > window.innerWidth - 20) {
|
|
38069
|
+
left = window.innerWidth - tooltipWidth - 20;
|
|
38070
|
+
}
|
|
38071
|
+
if (left < 10) left = 10;
|
|
38072
|
+
if (top + 500 > window.innerHeight) {
|
|
38073
|
+
top = rect.top - 8 - 500;
|
|
38074
|
+
if (top < 10) top = 10;
|
|
38075
|
+
}
|
|
38076
|
+
container.style.left = left + "px";
|
|
38077
|
+
container.style.top = top + "px";
|
|
38078
|
+
}
|
|
38079
|
+
var ContractSummaryTooltip = {
|
|
38080
|
+
containerId: "myio-contract-summary-tooltip",
|
|
38081
|
+
/**
|
|
38082
|
+
* Get or create container
|
|
38083
|
+
*/
|
|
38084
|
+
getContainer() {
|
|
38085
|
+
injectCSS9();
|
|
38086
|
+
let container = document.getElementById(this.containerId);
|
|
38087
|
+
if (!container) {
|
|
38088
|
+
container = document.createElement("div");
|
|
38089
|
+
container.id = this.containerId;
|
|
38090
|
+
container.className = "myio-contract-summary-tooltip";
|
|
38091
|
+
document.body.appendChild(container);
|
|
38092
|
+
}
|
|
38093
|
+
return container;
|
|
38094
|
+
},
|
|
38095
|
+
/**
|
|
38096
|
+
* Show tooltip
|
|
38097
|
+
*/
|
|
38098
|
+
show(triggerElement, data) {
|
|
38099
|
+
if (state5.hideTimer) {
|
|
38100
|
+
clearTimeout(state5.hideTimer);
|
|
38101
|
+
state5.hideTimer = null;
|
|
38102
|
+
}
|
|
38103
|
+
const container = this.getContainer();
|
|
38104
|
+
container.classList.remove("closing");
|
|
38105
|
+
container.innerHTML = `
|
|
38106
|
+
<div class="myio-contract-summary-tooltip__panel">
|
|
38107
|
+
${generateHeaderHTML5(data)}
|
|
38108
|
+
${generateBodyHTML3(data)}
|
|
38109
|
+
</div>
|
|
38110
|
+
`;
|
|
38111
|
+
positionTooltip5(container, triggerElement);
|
|
38112
|
+
container.classList.add("visible");
|
|
38113
|
+
setupHoverListeners5(container);
|
|
38114
|
+
setupButtonListeners5(container);
|
|
38115
|
+
setupDragListeners5(container);
|
|
38116
|
+
setupDomainToggleListeners(container);
|
|
38117
|
+
},
|
|
38118
|
+
/**
|
|
38119
|
+
* Start delayed hide
|
|
38120
|
+
*/
|
|
38121
|
+
startDelayedHide() {
|
|
38122
|
+
startDelayedHide5();
|
|
38123
|
+
},
|
|
38124
|
+
/**
|
|
38125
|
+
* Hide immediately
|
|
38126
|
+
*/
|
|
38127
|
+
hide() {
|
|
38128
|
+
if (state5.hideTimer) {
|
|
38129
|
+
clearTimeout(state5.hideTimer);
|
|
38130
|
+
state5.hideTimer = null;
|
|
38131
|
+
}
|
|
38132
|
+
state5.isMouseOverTooltip = false;
|
|
38133
|
+
const container = document.getElementById(this.containerId);
|
|
38134
|
+
if (container) {
|
|
38135
|
+
container.classList.remove("visible", "closing");
|
|
38136
|
+
}
|
|
38137
|
+
},
|
|
38138
|
+
/**
|
|
38139
|
+
* Close and reset all states
|
|
38140
|
+
*/
|
|
38141
|
+
close() {
|
|
38142
|
+
state5.isMaximized = false;
|
|
38143
|
+
state5.isDragging = false;
|
|
38144
|
+
state5.savedPosition = null;
|
|
38145
|
+
if (state5.hideTimer) {
|
|
38146
|
+
clearTimeout(state5.hideTimer);
|
|
38147
|
+
state5.hideTimer = null;
|
|
38148
|
+
}
|
|
38149
|
+
state5.isMouseOverTooltip = false;
|
|
38150
|
+
const container = document.getElementById(this.containerId);
|
|
38151
|
+
if (container) {
|
|
38152
|
+
container.classList.remove("visible", "pinned", "maximized", "dragging", "closing");
|
|
38153
|
+
}
|
|
38154
|
+
},
|
|
38155
|
+
/**
|
|
38156
|
+
* Attach tooltip to trigger element with click behavior
|
|
38157
|
+
*/
|
|
38158
|
+
attach(triggerElement, getDataFn) {
|
|
38159
|
+
const self = this;
|
|
38160
|
+
const handleClick = () => {
|
|
38161
|
+
if (state5.hideTimer) {
|
|
38162
|
+
clearTimeout(state5.hideTimer);
|
|
38163
|
+
state5.hideTimer = null;
|
|
38164
|
+
}
|
|
38165
|
+
const data = getDataFn();
|
|
38166
|
+
if (data) {
|
|
38167
|
+
self.show(triggerElement, data);
|
|
38168
|
+
}
|
|
38169
|
+
};
|
|
38170
|
+
triggerElement.addEventListener("click", handleClick);
|
|
38171
|
+
return () => {
|
|
38172
|
+
triggerElement.removeEventListener("click", handleClick);
|
|
38173
|
+
self.hide();
|
|
38174
|
+
};
|
|
38175
|
+
},
|
|
38176
|
+
/**
|
|
38177
|
+
* Attach tooltip with hover behavior
|
|
38178
|
+
*/
|
|
38179
|
+
attachHover(triggerElement, getDataFn) {
|
|
38180
|
+
const self = this;
|
|
38181
|
+
const handleMouseEnter = () => {
|
|
38182
|
+
if (state5.hideTimer) {
|
|
38183
|
+
clearTimeout(state5.hideTimer);
|
|
38184
|
+
state5.hideTimer = null;
|
|
38185
|
+
}
|
|
38186
|
+
const data = getDataFn();
|
|
38187
|
+
if (data) {
|
|
38188
|
+
self.show(triggerElement, data);
|
|
38189
|
+
}
|
|
38190
|
+
};
|
|
38191
|
+
const handleMouseLeave = () => {
|
|
38192
|
+
startDelayedHide5();
|
|
38193
|
+
};
|
|
38194
|
+
triggerElement.addEventListener("mouseenter", handleMouseEnter);
|
|
38195
|
+
triggerElement.addEventListener("mouseleave", handleMouseLeave);
|
|
38196
|
+
return () => {
|
|
38197
|
+
triggerElement.removeEventListener("mouseenter", handleMouseEnter);
|
|
38198
|
+
triggerElement.removeEventListener("mouseleave", handleMouseLeave);
|
|
38199
|
+
self.hide();
|
|
38200
|
+
};
|
|
38201
|
+
},
|
|
38202
|
+
/**
|
|
38203
|
+
* Build contract data from window.CONTRACT_STATE
|
|
38204
|
+
* Helper method to build the data structure from global state
|
|
38205
|
+
*/
|
|
38206
|
+
buildFromGlobalState() {
|
|
38207
|
+
const globalState = window.CONTRACT_STATE;
|
|
38208
|
+
if (!globalState) return null;
|
|
38209
|
+
return {
|
|
38210
|
+
isLoaded: globalState.isLoaded ?? false,
|
|
38211
|
+
isValid: globalState.isValid ?? false,
|
|
38212
|
+
timestamp: globalState.timestamp ?? null,
|
|
38213
|
+
energy: {
|
|
38214
|
+
total: globalState.energy?.total ?? 0,
|
|
38215
|
+
entries: globalState.energy?.entries ?? 0,
|
|
38216
|
+
commonArea: globalState.energy?.commonArea ?? 0,
|
|
38217
|
+
stores: globalState.energy?.stores ?? 0
|
|
38218
|
+
},
|
|
38219
|
+
water: {
|
|
38220
|
+
total: globalState.water?.total ?? 0,
|
|
38221
|
+
entries: globalState.water?.entries ?? 0,
|
|
38222
|
+
commonArea: globalState.water?.commonArea ?? 0,
|
|
38223
|
+
stores: globalState.water?.stores ?? 0
|
|
38224
|
+
},
|
|
38225
|
+
temperature: {
|
|
38226
|
+
total: globalState.temperature?.total ?? 0,
|
|
38227
|
+
internal: globalState.temperature?.internal ?? 0,
|
|
38228
|
+
stores: globalState.temperature?.stores ?? 0
|
|
38229
|
+
},
|
|
38230
|
+
discrepancies: globalState.discrepancies
|
|
38231
|
+
};
|
|
38232
|
+
}
|
|
38233
|
+
};
|
|
38234
|
+
|
|
37238
38235
|
// src/components/ModalHeader/index.ts
|
|
37239
38236
|
var DEFAULT_BG_COLOR = "#3e1a7d";
|
|
37240
38237
|
var DEFAULT_TEXT_COLOR = "white";
|
|
@@ -40998,6 +41995,7 @@ export {
|
|
|
40998
41995
|
DEFAULT_CONFIG as CONSUMPTION_CHART_DEFAULTS,
|
|
40999
41996
|
THEME_COLORS as CONSUMPTION_THEME_COLORS,
|
|
41000
41997
|
ConnectionStatusType,
|
|
41998
|
+
ContractSummaryTooltip,
|
|
41001
41999
|
DEFAULT_CLAMP_RANGE,
|
|
41002
42000
|
DEFAULT_ENERGY_GROUP_COLORS,
|
|
41003
42001
|
DEFAULT_GAS_GROUP_COLORS,
|