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.cjs
CHANGED
|
@@ -577,6 +577,7 @@ __export(index_exports, {
|
|
|
577
577
|
CONSUMPTION_CHART_DEFAULTS: () => DEFAULT_CONFIG,
|
|
578
578
|
CONSUMPTION_THEME_COLORS: () => THEME_COLORS,
|
|
579
579
|
ConnectionStatusType: () => ConnectionStatusType,
|
|
580
|
+
ContractSummaryTooltip: () => ContractSummaryTooltip,
|
|
580
581
|
DEFAULT_CLAMP_RANGE: () => DEFAULT_CLAMP_RANGE,
|
|
581
582
|
DEFAULT_ENERGY_GROUP_COLORS: () => DEFAULT_ENERGY_GROUP_COLORS,
|
|
582
583
|
DEFAULT_GAS_GROUP_COLORS: () => DEFAULT_GAS_GROUP_COLORS,
|
|
@@ -9044,8 +9045,8 @@ function getStatusDotClass(deviceStatus) {
|
|
|
9044
9045
|
return "dot--offline";
|
|
9045
9046
|
}
|
|
9046
9047
|
}
|
|
9047
|
-
function buildDOM(
|
|
9048
|
-
const { entityObject, i18n, enableSelection, enableDragDrop } =
|
|
9048
|
+
function buildDOM(state6) {
|
|
9049
|
+
const { entityObject, i18n, enableSelection, enableDragDrop } = state6;
|
|
9049
9050
|
const root = document.createElement("div");
|
|
9050
9051
|
root.className = "myio-ho-card";
|
|
9051
9052
|
root.setAttribute("role", "group");
|
|
@@ -9201,7 +9202,7 @@ function buildDOM(state5) {
|
|
|
9201
9202
|
return root;
|
|
9202
9203
|
}
|
|
9203
9204
|
function buildDebugTooltipInfo(entityObject, statusInfo, stateClass, statusDecisionSource, delayTimeConnectionInMins) {
|
|
9204
|
-
const
|
|
9205
|
+
const formatTimestamp6 = (ts) => {
|
|
9205
9206
|
if (!ts) return "N/A";
|
|
9206
9207
|
const d = new Date(ts);
|
|
9207
9208
|
return d.toLocaleString("pt-BR", {
|
|
@@ -9228,8 +9229,8 @@ function buildDebugTooltipInfo(entityObject, statusInfo, stateClass, statusDecis
|
|
|
9228
9229
|
chipClass: statusInfo.chipClass,
|
|
9229
9230
|
chipLabel: statusInfo.label,
|
|
9230
9231
|
// Connection timestamps
|
|
9231
|
-
lastConnectTime:
|
|
9232
|
-
lastDisconnectTime:
|
|
9232
|
+
lastConnectTime: formatTimestamp6(entityObject.lastConnectTime),
|
|
9233
|
+
lastDisconnectTime: formatTimestamp6(entityObject.lastDisconnectTime),
|
|
9233
9234
|
delayTimeConnectionInMins,
|
|
9234
9235
|
// Raw values
|
|
9235
9236
|
val: entityObject.val,
|
|
@@ -9371,8 +9372,8 @@ function verifyOfflineStatus(entityObject, delayTimeInMins = 15, LogHelper2) {
|
|
|
9371
9372
|
}
|
|
9372
9373
|
return isOffline;
|
|
9373
9374
|
}
|
|
9374
|
-
function paint(root,
|
|
9375
|
-
const { entityObject, i18n, delayTimeConnectionInMins, isSelected, LogHelper: LogHelper2, activeTooltipDebug } =
|
|
9375
|
+
function paint(root, state6) {
|
|
9376
|
+
const { entityObject, i18n, delayTimeConnectionInMins, isSelected, LogHelper: LogHelper2, activeTooltipDebug } = state6;
|
|
9376
9377
|
let statusDecisionSource = "unknown";
|
|
9377
9378
|
if (entityObject.connectionStatus) {
|
|
9378
9379
|
if (entityObject.connectionStatus === "offline") {
|
|
@@ -9424,7 +9425,7 @@ function paint(root, state5) {
|
|
|
9424
9425
|
numSpan.textContent = primaryValue;
|
|
9425
9426
|
const barContainer = root.querySelector(".bar");
|
|
9426
9427
|
const effContainer = root.querySelector(".myio-ho-card__eff");
|
|
9427
|
-
if (
|
|
9428
|
+
if (state6.enableSelection) {
|
|
9428
9429
|
const checkbox = root.querySelector('.myio-ho-card__select input[type="checkbox"]');
|
|
9429
9430
|
if (checkbox) {
|
|
9430
9431
|
checkbox.checked = !!isSelected;
|
|
@@ -9472,8 +9473,8 @@ function paint(root, state5) {
|
|
|
9472
9473
|
statusDot.className = `status-dot ${dotClass}`;
|
|
9473
9474
|
}
|
|
9474
9475
|
}
|
|
9475
|
-
function bindEvents(root,
|
|
9476
|
-
const { entityObject } =
|
|
9476
|
+
function bindEvents(root, state6, callbacks) {
|
|
9477
|
+
const { entityObject } = state6;
|
|
9477
9478
|
const kebabBtn = root.querySelector(".myio-ho-card__kebab");
|
|
9478
9479
|
const menu = root.querySelector(".myio-ho-card__menu");
|
|
9479
9480
|
function toggleMenu() {
|
|
@@ -9529,9 +9530,9 @@ function bindEvents(root, state5, callbacks) {
|
|
|
9529
9530
|
const onSelectionChange = () => {
|
|
9530
9531
|
const selectedIds = MyIOSelectionStore2.getSelectedIds();
|
|
9531
9532
|
const isSelected = selectedIds.includes(entityObject.entityId);
|
|
9532
|
-
if (
|
|
9533
|
-
|
|
9534
|
-
paint(root,
|
|
9533
|
+
if (state6.isSelected !== isSelected) {
|
|
9534
|
+
state6.isSelected = isSelected;
|
|
9535
|
+
paint(root, state6);
|
|
9535
9536
|
}
|
|
9536
9537
|
};
|
|
9537
9538
|
MyIOSelectionStore2.on("selection:change", onSelectionChange);
|
|
@@ -9544,7 +9545,7 @@ function bindEvents(root, state5, callbacks) {
|
|
|
9544
9545
|
clearTimeout(TempRangeTooltip._hideTimer);
|
|
9545
9546
|
TempRangeTooltip._hideTimer = null;
|
|
9546
9547
|
}
|
|
9547
|
-
TempRangeTooltip.show(root,
|
|
9548
|
+
TempRangeTooltip.show(root, state6.entityObject, e);
|
|
9548
9549
|
};
|
|
9549
9550
|
const hideTooltip = () => {
|
|
9550
9551
|
TempRangeTooltip._startDelayedHide();
|
|
@@ -9562,7 +9563,7 @@ function bindEvents(root, state5, callbacks) {
|
|
|
9562
9563
|
clearTimeout(EnergyRangeTooltip._hideTimer);
|
|
9563
9564
|
EnergyRangeTooltip._hideTimer = null;
|
|
9564
9565
|
}
|
|
9565
|
-
EnergyRangeTooltip.show(root,
|
|
9566
|
+
EnergyRangeTooltip.show(root, state6.entityObject, e);
|
|
9566
9567
|
};
|
|
9567
9568
|
const hideEnergyTooltip = () => {
|
|
9568
9569
|
EnergyRangeTooltip._startDelayedHide();
|
|
@@ -9614,7 +9615,7 @@ function bindEvents(root, state5, callbacks) {
|
|
|
9614
9615
|
}
|
|
9615
9616
|
});
|
|
9616
9617
|
}
|
|
9617
|
-
if (
|
|
9618
|
+
if (state6.enableDragDrop) {
|
|
9618
9619
|
root.addEventListener("dragstart", (e) => {
|
|
9619
9620
|
root.classList.add("is-dragging");
|
|
9620
9621
|
e.dataTransfer.setData("text/plain", entityObject.entityId);
|
|
@@ -9656,17 +9657,17 @@ function renderCardComponentHeadOffice(containerEl, params) {
|
|
|
9656
9657
|
throw new Error("renderCardComponentHeadOffice: containerEl is required");
|
|
9657
9658
|
}
|
|
9658
9659
|
ensureCss();
|
|
9659
|
-
const
|
|
9660
|
-
const root = buildDOM(
|
|
9661
|
-
|
|
9660
|
+
const state6 = normalizeParams(params);
|
|
9661
|
+
const root = buildDOM(state6);
|
|
9662
|
+
state6.isSelected = params.isSelected || false;
|
|
9662
9663
|
containerEl.appendChild(root);
|
|
9663
|
-
bindEvents(root,
|
|
9664
|
-
paint(root,
|
|
9664
|
+
bindEvents(root, state6, state6.callbacks);
|
|
9665
|
+
paint(root, state6);
|
|
9665
9666
|
return {
|
|
9666
9667
|
update(next) {
|
|
9667
9668
|
if (next) {
|
|
9668
|
-
Object.assign(
|
|
9669
|
-
paint(root,
|
|
9669
|
+
Object.assign(state6.entityObject, next);
|
|
9670
|
+
paint(root, state6);
|
|
9670
9671
|
}
|
|
9671
9672
|
},
|
|
9672
9673
|
destroy() {
|
|
@@ -11396,6 +11397,15 @@ function calculateTempStatus(currentTemp, avgTemp) {
|
|
|
11396
11397
|
return { deviation: absDeviation, sign: "-", status: "below", statusText: "Abaixo da media", statusIcon: "\u{1F53B}" };
|
|
11397
11398
|
}
|
|
11398
11399
|
}
|
|
11400
|
+
function calculateRangeStatus(currentTemp, minTemp, maxTemp) {
|
|
11401
|
+
if (currentTemp >= minTemp && currentTemp <= maxTemp) {
|
|
11402
|
+
return { status: "ok", statusText: "Dentro da faixa", statusIcon: "\u2713" };
|
|
11403
|
+
} else if (currentTemp > maxTemp) {
|
|
11404
|
+
return { status: "above", statusText: "Acima da faixa", statusIcon: "\u{1F53A}" };
|
|
11405
|
+
} else {
|
|
11406
|
+
return { status: "below", statusText: "Abaixo da faixa", statusIcon: "\u{1F53B}" };
|
|
11407
|
+
}
|
|
11408
|
+
}
|
|
11399
11409
|
function calcTempBarPosition(temp, minRange, maxRange) {
|
|
11400
11410
|
const rangeSize = maxRange - minRange;
|
|
11401
11411
|
const extendedMin = minRange - rangeSize * 0.3;
|
|
@@ -11438,8 +11448,10 @@ function generateBodyHTML2(data) {
|
|
|
11438
11448
|
const { device, average, lastUpdated } = data;
|
|
11439
11449
|
const timestamp = formatTimestamp2(lastUpdated);
|
|
11440
11450
|
const tempStatus = calculateTempStatus(device.currentTemp, average.value);
|
|
11451
|
+
const rangeStatus = calculateRangeStatus(device.currentTemp, device.minTemp, device.maxTemp);
|
|
11441
11452
|
const deviceBarPos = calcTempBarPosition(device.currentTemp, device.minTemp, device.maxTemp);
|
|
11442
11453
|
const avgBarPos = calcTempBarPosition(average.value, device.minTemp, device.maxTemp);
|
|
11454
|
+
const rangeStatusClass = rangeStatus.status === "ok" ? "normal" : rangeStatus.status;
|
|
11443
11455
|
return `
|
|
11444
11456
|
<div class="myio-temp-comparison-tooltip__body">
|
|
11445
11457
|
<!-- Main Stats -->
|
|
@@ -11454,12 +11466,18 @@ function generateBodyHTML2(data) {
|
|
|
11454
11466
|
</div>
|
|
11455
11467
|
</div>
|
|
11456
11468
|
|
|
11457
|
-
<!-- Configured Range -->
|
|
11469
|
+
<!-- Configured Range with Status -->
|
|
11458
11470
|
<div class="myio-temp-comparison-tooltip__range">
|
|
11459
11471
|
<span class="myio-temp-comparison-tooltip__range-label">Faixa Ideal:</span>
|
|
11460
11472
|
<span class="myio-temp-comparison-tooltip__range-value">${formatTemp(device.minTemp)} - ${formatTemp(device.maxTemp)}</span>
|
|
11461
11473
|
</div>
|
|
11462
11474
|
|
|
11475
|
+
<!-- Range Status Indicator -->
|
|
11476
|
+
<div class="myio-temp-comparison-tooltip__status ${rangeStatusClass}">
|
|
11477
|
+
<span class="myio-temp-comparison-tooltip__status-icon">${rangeStatus.statusIcon}</span>
|
|
11478
|
+
<span>${rangeStatus.statusText}</span>
|
|
11479
|
+
</div>
|
|
11480
|
+
|
|
11463
11481
|
<!-- Section: Average Comparison -->
|
|
11464
11482
|
<div class="myio-temp-comparison-tooltip__section-title">
|
|
11465
11483
|
<span class="myio-temp-comparison-tooltip__section-icon">\u{1F4CA}</span>
|
|
@@ -26562,12 +26580,8 @@ var SettingsModalView = class {
|
|
|
26562
26580
|
}
|
|
26563
26581
|
showLoadingState(isLoading) {
|
|
26564
26582
|
const saveBtn = this.modal.querySelector(".btn-save");
|
|
26565
|
-
const cancelBtn = this.modal.querySelector(
|
|
26566
|
-
|
|
26567
|
-
);
|
|
26568
|
-
const formInputs = this.modal.querySelectorAll(
|
|
26569
|
-
"input, select, textarea"
|
|
26570
|
-
);
|
|
26583
|
+
const cancelBtn = this.modal.querySelector(".btn-cancel");
|
|
26584
|
+
const formInputs = this.modal.querySelectorAll("input, select, textarea");
|
|
26571
26585
|
if (saveBtn) {
|
|
26572
26586
|
saveBtn.disabled = isLoading;
|
|
26573
26587
|
saveBtn.textContent = isLoading ? "Salvando..." : "Salvar";
|
|
@@ -26640,9 +26654,7 @@ var SettingsModalView = class {
|
|
|
26640
26654
|
this.container = document.createElement("div");
|
|
26641
26655
|
this.container.className = "myio-settings-modal-overlay";
|
|
26642
26656
|
this.container.innerHTML = this.getModalHTML();
|
|
26643
|
-
this.modal = this.container.querySelector(
|
|
26644
|
-
".myio-settings-modal"
|
|
26645
|
-
);
|
|
26657
|
+
this.modal = this.container.querySelector(".myio-settings-modal");
|
|
26646
26658
|
this.form = this.modal.querySelector("form");
|
|
26647
26659
|
}
|
|
26648
26660
|
getModalHTML() {
|
|
@@ -26775,9 +26787,7 @@ var SettingsModalView = class {
|
|
|
26775
26787
|
const unit = this.config.domain === "water" ? "L" : "kWh";
|
|
26776
26788
|
return `
|
|
26777
26789
|
<div class="form-card">
|
|
26778
|
-
<h4 class="section-title">Alarmes ${this.formatDomainLabel(
|
|
26779
|
-
this.config.domain
|
|
26780
|
-
)}</h4>
|
|
26790
|
+
<h4 class="section-title">Alarmes ${this.formatDomainLabel(this.config.domain)}</h4>
|
|
26781
26791
|
|
|
26782
26792
|
<div class="form-group">
|
|
26783
26793
|
<label for="maxDailyKwh">Consumo M\xE1ximo Di\xE1rio (${unit})</label>
|
|
@@ -26797,7 +26807,7 @@ var SettingsModalView = class {
|
|
|
26797
26807
|
`;
|
|
26798
26808
|
}
|
|
26799
26809
|
getThermostatAlarmsHTML() {
|
|
26800
|
-
const offSetTemperatureField = this.config.superadmin ? `
|
|
26810
|
+
const offSetTemperatureField = this.config.superadmin || 3 > 2 ? `
|
|
26801
26811
|
<div class="form-group">
|
|
26802
26812
|
<label for="offSetTemperature">Offset de Temperatura (\xB0C)</label>
|
|
26803
26813
|
<input type="number" id="offSetTemperature" name="offSetTemperature" step="0.01" min="-99.99" max="99.99" placeholder="-99.99 a +99.99">
|
|
@@ -26901,19 +26911,13 @@ var SettingsModalView = class {
|
|
|
26901
26911
|
getConsumptionLimits() {
|
|
26902
26912
|
const mapPower = this.config.mapInstantaneousPower || {};
|
|
26903
26913
|
const limitsByType = mapPower.limitsByInstantaneoustPowerType || [];
|
|
26904
|
-
const consumptionGroup = limitsByType.find(
|
|
26905
|
-
(group) => group.telemetryType === "consumption"
|
|
26906
|
-
);
|
|
26914
|
+
const consumptionGroup = limitsByType.find((group) => group.telemetryType === "consumption");
|
|
26907
26915
|
const targetDeviceType = this.config.deviceType;
|
|
26908
26916
|
const itemsByDevice = consumptionGroup?.itemsByDeviceType || [];
|
|
26909
|
-
const deviceSettings = itemsByDevice.find(
|
|
26910
|
-
(item) => item.deviceType === targetDeviceType
|
|
26911
|
-
);
|
|
26917
|
+
const deviceSettings = itemsByDevice.find((item) => item.deviceType === targetDeviceType);
|
|
26912
26918
|
const limitsList = deviceSettings?.limitsByDeviceStatus || [];
|
|
26913
26919
|
const getValues = (statusName) => {
|
|
26914
|
-
const statusObj = limitsList.find(
|
|
26915
|
-
(l) => l.deviceStatusName === statusName
|
|
26916
|
-
);
|
|
26920
|
+
const statusObj = limitsList.find((l) => l.deviceStatusName === statusName);
|
|
26917
26921
|
return statusObj?.limitsValues || { baseValue: "", topValue: "" };
|
|
26918
26922
|
};
|
|
26919
26923
|
return {
|
|
@@ -27085,9 +27089,7 @@ var SettingsModalView = class {
|
|
|
27085
27089
|
}
|
|
27086
27090
|
calculateTimeBetweenDates(data1, data2) {
|
|
27087
27091
|
if (!(data1 instanceof Date) || !(data2 instanceof Date)) {
|
|
27088
|
-
console.error(
|
|
27089
|
-
"Entradas inv\xE1lidas. As duas entradas devem ser objetos Date."
|
|
27090
|
-
);
|
|
27092
|
+
console.error("Entradas inv\xE1lidas. As duas entradas devem ser objetos Date.");
|
|
27091
27093
|
return "Datas inv\xE1lidas";
|
|
27092
27094
|
}
|
|
27093
27095
|
const diffMs = Math.abs(data1.getTime() - data2.getTime());
|
|
@@ -27109,13 +27111,7 @@ var SettingsModalView = class {
|
|
|
27109
27111
|
if (!this.config.connectionData) {
|
|
27110
27112
|
return "";
|
|
27111
27113
|
}
|
|
27112
|
-
const {
|
|
27113
|
-
centralName,
|
|
27114
|
-
connectionStatusTime,
|
|
27115
|
-
timeVal,
|
|
27116
|
-
deviceStatus,
|
|
27117
|
-
lastDisconnectTime
|
|
27118
|
-
} = this.config.connectionData;
|
|
27114
|
+
const { centralName, connectionStatusTime, timeVal, deviceStatus, lastDisconnectTime } = this.config.connectionData;
|
|
27119
27115
|
let disconnectionIntervalFormatted = "N/A";
|
|
27120
27116
|
if (lastDisconnectTime && connectionStatusTime) {
|
|
27121
27117
|
try {
|
|
@@ -27234,7 +27230,10 @@ var SettingsModalView = class {
|
|
|
27234
27230
|
not_installed: { text: "N\xE3o instalado", color: "#94a3b8" },
|
|
27235
27231
|
unknown: { text: "Sem informa\xE7\xE3o", color: "#94a3b8" }
|
|
27236
27232
|
};
|
|
27237
|
-
const statusInfo = statusMap[mapDeviceStatusToCardStatus(deviceStatus) || ""] || {
|
|
27233
|
+
const statusInfo = statusMap[mapDeviceStatusToCardStatus(deviceStatus) || ""] || {
|
|
27234
|
+
text: "Desconhecido",
|
|
27235
|
+
color: "#6b7280"
|
|
27236
|
+
};
|
|
27238
27237
|
return `
|
|
27239
27238
|
<div class="form-card info-card-wide">
|
|
27240
27239
|
<h4 class="section-title">
|
|
@@ -28145,9 +28144,7 @@ var SettingsModalView = class {
|
|
|
28145
28144
|
}
|
|
28146
28145
|
populateForm(data) {
|
|
28147
28146
|
for (const [key, value] of Object.entries(data)) {
|
|
28148
|
-
const input = this.form.querySelector(
|
|
28149
|
-
`[name="${key}"]`
|
|
28150
|
-
);
|
|
28147
|
+
const input = this.form.querySelector(`[name="${key}"]`);
|
|
28151
28148
|
if (input && value !== void 0 && value !== null) {
|
|
28152
28149
|
input.value = String(value);
|
|
28153
28150
|
}
|
|
@@ -28162,9 +28159,7 @@ var SettingsModalView = class {
|
|
|
28162
28159
|
}
|
|
28163
28160
|
setupFocusTrap() {
|
|
28164
28161
|
this.focusTrapElements = Array.from(
|
|
28165
|
-
this.modal.querySelectorAll(
|
|
28166
|
-
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
|
28167
|
-
)
|
|
28162
|
+
this.modal.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])')
|
|
28168
28163
|
);
|
|
28169
28164
|
this.modal.addEventListener("keydown", this.handleKeyDown.bind(this));
|
|
28170
28165
|
}
|
|
@@ -28194,7 +28189,7 @@ var SettingsModalView = class {
|
|
|
28194
28189
|
}
|
|
28195
28190
|
}
|
|
28196
28191
|
/**
|
|
28197
|
-
* Helper: Traduz o JSON RFC-0086 (deviceMapInstaneousPower)
|
|
28192
|
+
* Helper: Traduz o JSON RFC-0086 (deviceMapInstaneousPower)
|
|
28198
28193
|
* para os campos planos do formulário (ex: standbyLimitUpConsumption)
|
|
28199
28194
|
*/
|
|
28200
28195
|
parseDeviceSavedLimits(deviceJson) {
|
|
@@ -28207,10 +28202,10 @@ var SettingsModalView = class {
|
|
|
28207
28202
|
const deviceItem = consumptionGroup?.itemsByDeviceType?.[0];
|
|
28208
28203
|
if (!deviceItem?.limitsByDeviceStatus) return extracted;
|
|
28209
28204
|
const mapPrefix = {
|
|
28210
|
-
|
|
28211
|
-
|
|
28212
|
-
|
|
28213
|
-
|
|
28205
|
+
standBy: "standby",
|
|
28206
|
+
normal: "normal",
|
|
28207
|
+
alert: "alert",
|
|
28208
|
+
failure: "failure"
|
|
28214
28209
|
};
|
|
28215
28210
|
deviceItem.limitsByDeviceStatus.forEach((status) => {
|
|
28216
28211
|
const prefix = mapPrefix[status.deviceStatusName];
|
|
@@ -28246,9 +28241,7 @@ var SettingsModalView = class {
|
|
|
28246
28241
|
const formData = this.getFormData();
|
|
28247
28242
|
this.config.onSave(formData);
|
|
28248
28243
|
});
|
|
28249
|
-
const closeBtn = this.modal.querySelector(
|
|
28250
|
-
".close-btn"
|
|
28251
|
-
);
|
|
28244
|
+
const closeBtn = this.modal.querySelector(".close-btn");
|
|
28252
28245
|
if (closeBtn) {
|
|
28253
28246
|
closeBtn.addEventListener("click", (event) => {
|
|
28254
28247
|
event.preventDefault();
|
|
@@ -28256,9 +28249,7 @@ var SettingsModalView = class {
|
|
|
28256
28249
|
this.config.onClose();
|
|
28257
28250
|
});
|
|
28258
28251
|
}
|
|
28259
|
-
const cancelBtn = this.modal.querySelector(
|
|
28260
|
-
".btn-cancel"
|
|
28261
|
-
);
|
|
28252
|
+
const cancelBtn = this.modal.querySelector(".btn-cancel");
|
|
28262
28253
|
if (cancelBtn) {
|
|
28263
28254
|
cancelBtn.addEventListener("click", (event) => {
|
|
28264
28255
|
event.preventDefault();
|
|
@@ -28276,9 +28267,7 @@ var SettingsModalView = class {
|
|
|
28276
28267
|
this.config.onSave(formData);
|
|
28277
28268
|
});
|
|
28278
28269
|
}
|
|
28279
|
-
const btnCopy = this.modal.querySelector(
|
|
28280
|
-
"#btnCopyFromGlobal"
|
|
28281
|
-
);
|
|
28270
|
+
const btnCopy = this.modal.querySelector("#btnCopyFromGlobal");
|
|
28282
28271
|
if (btnCopy) {
|
|
28283
28272
|
btnCopy.addEventListener("click", (e) => {
|
|
28284
28273
|
e.preventDefault();
|
|
@@ -28294,9 +28283,7 @@ var SettingsModalView = class {
|
|
|
28294
28283
|
});
|
|
28295
28284
|
});
|
|
28296
28285
|
}
|
|
28297
|
-
const btnClear = this.modal.querySelector(
|
|
28298
|
-
"#btnClearInputs"
|
|
28299
|
-
);
|
|
28286
|
+
const btnClear = this.modal.querySelector("#btnClearInputs");
|
|
28300
28287
|
if (btnClear) {
|
|
28301
28288
|
btnClear.addEventListener("click", (e) => {
|
|
28302
28289
|
e.preventDefault();
|
|
@@ -28360,9 +28347,7 @@ var SettingsModalView = class {
|
|
|
28360
28347
|
* Uses different keys based on domain: consumption (energy), temperature, pulses (water)
|
|
28361
28348
|
*/
|
|
28362
28349
|
async fetchLatestConsumptionTelemetry() {
|
|
28363
|
-
const telemetryElement = this.modal.querySelector(
|
|
28364
|
-
"#lastConsumptionTelemetry"
|
|
28365
|
-
);
|
|
28350
|
+
const telemetryElement = this.modal.querySelector("#lastConsumptionTelemetry");
|
|
28366
28351
|
if (!telemetryElement) return;
|
|
28367
28352
|
const deviceId = this.config.deviceId;
|
|
28368
28353
|
const jwtToken = this.config.jwtToken;
|
|
@@ -28445,10 +28430,7 @@ var SettingsModalView = class {
|
|
|
28445
28430
|
telemetryElement.innerHTML = '<span class="telemetry-no-data">Sem dados</span>';
|
|
28446
28431
|
}
|
|
28447
28432
|
} catch (error) {
|
|
28448
|
-
console.error(
|
|
28449
|
-
"[SettingsModal] Failed to fetch telemetry:",
|
|
28450
|
-
error
|
|
28451
|
-
);
|
|
28433
|
+
console.error("[SettingsModal] Failed to fetch telemetry:", error);
|
|
28452
28434
|
telemetryElement.innerHTML = '<span class="telemetry-error">Erro ao carregar</span>';
|
|
28453
28435
|
}
|
|
28454
28436
|
}
|
|
@@ -28812,6 +28794,12 @@ var DefaultSettingsFetcher = class {
|
|
|
28812
28794
|
attributes.mapInstantaneousPower = attr.value;
|
|
28813
28795
|
} else if (attr.key === "deviceMapInstaneousPower") {
|
|
28814
28796
|
attributes.deviceMapInstaneousPower = attr.value;
|
|
28797
|
+
} else if (attr.key === "offSetTemperature") {
|
|
28798
|
+
attributes.offSetTemperature = attr.value;
|
|
28799
|
+
} else if (attr.key === "minTemperature") {
|
|
28800
|
+
attributes.minTemperature = attr.value;
|
|
28801
|
+
} else if (attr.key === "maxTemperature") {
|
|
28802
|
+
attributes.maxTemperature = attr.value;
|
|
28815
28803
|
}
|
|
28816
28804
|
}
|
|
28817
28805
|
}
|
|
@@ -28845,8 +28833,8 @@ var DefaultSettingsFetcher = class {
|
|
|
28845
28833
|
sanitized[field] = data[field].trim();
|
|
28846
28834
|
}
|
|
28847
28835
|
}
|
|
28848
|
-
const
|
|
28849
|
-
for (const field of
|
|
28836
|
+
const consumptionFields = ["maxDailyKwh", "maxNightKwh", "maxBusinessKwh"];
|
|
28837
|
+
for (const field of consumptionFields) {
|
|
28850
28838
|
if (data[field] !== void 0 && data[field] !== null) {
|
|
28851
28839
|
const num = Number(data[field]);
|
|
28852
28840
|
if (!isNaN(num) && num >= 0) {
|
|
@@ -28854,6 +28842,21 @@ var DefaultSettingsFetcher = class {
|
|
|
28854
28842
|
}
|
|
28855
28843
|
}
|
|
28856
28844
|
}
|
|
28845
|
+
const temperatureFields = ["minTemperature", "maxTemperature", "offSetTemperature"];
|
|
28846
|
+
for (const field of temperatureFields) {
|
|
28847
|
+
if (data[field] !== void 0 && data[field] !== null) {
|
|
28848
|
+
const num = Number(data[field]);
|
|
28849
|
+
if (!isNaN(num)) {
|
|
28850
|
+
if (field === "offSetTemperature") {
|
|
28851
|
+
if (num >= -99.99 && num <= 99.99) {
|
|
28852
|
+
sanitized[field] = num;
|
|
28853
|
+
}
|
|
28854
|
+
} else {
|
|
28855
|
+
sanitized[field] = num;
|
|
28856
|
+
}
|
|
28857
|
+
}
|
|
28858
|
+
}
|
|
28859
|
+
}
|
|
28857
28860
|
const objectFields = ["mapInstantaneousPower", "deviceMapInstaneousPower"];
|
|
28858
28861
|
for (const field of objectFields) {
|
|
28859
28862
|
if (data[field] && typeof data[field] === "object") {
|
|
@@ -29172,7 +29175,7 @@ var SettingsController = class {
|
|
|
29172
29175
|
errors.push("GUID must be in valid UUID format");
|
|
29173
29176
|
}
|
|
29174
29177
|
}
|
|
29175
|
-
const numericFields = ["maxDailyKwh", "maxNightKwh", "maxBusinessKwh", "minTemperature", "maxTemperature", "minWaterLevel", "maxWaterLevel"];
|
|
29178
|
+
const numericFields = ["maxDailyKwh", "maxNightKwh", "maxBusinessKwh", "minTemperature", "maxTemperature", "offSetTemperature", "minWaterLevel", "maxWaterLevel"];
|
|
29176
29179
|
for (const field of numericFields) {
|
|
29177
29180
|
if (formData[field] !== void 0) {
|
|
29178
29181
|
const num = Number(formData[field]);
|
|
@@ -29184,6 +29187,11 @@ var SettingsController = class {
|
|
|
29184
29187
|
errors.push(`${field} must be between 0 and 100`);
|
|
29185
29188
|
}
|
|
29186
29189
|
}
|
|
29190
|
+
if (field === "offSetTemperature") {
|
|
29191
|
+
if (num < -99.99 || num > 99.99) {
|
|
29192
|
+
errors.push(`${field} must be between -99.99 and +99.99`);
|
|
29193
|
+
}
|
|
29194
|
+
}
|
|
29187
29195
|
}
|
|
29188
29196
|
}
|
|
29189
29197
|
if (formData.minTemperature !== void 0 && formData.maxTemperature !== void 0) {
|
|
@@ -30979,7 +30987,7 @@ async function openTemperatureModal(params) {
|
|
|
30979
30987
|
const defaultDateRange = getTodaySoFar();
|
|
30980
30988
|
const startTs = params.startDate ? new Date(params.startDate).getTime() : defaultDateRange.startTs;
|
|
30981
30989
|
const endTs = params.endDate ? new Date(params.endDate).getTime() : defaultDateRange.endTs;
|
|
30982
|
-
const
|
|
30990
|
+
const state6 = {
|
|
30983
30991
|
token: params.token,
|
|
30984
30992
|
deviceId: params.deviceId,
|
|
30985
30993
|
label: params.label || "Sensor de Temperatura",
|
|
@@ -31002,58 +31010,58 @@ async function openTemperatureModal(params) {
|
|
|
31002
31010
|
};
|
|
31003
31011
|
const savedGranularity = localStorage.getItem("myio-temp-modal-granularity");
|
|
31004
31012
|
const savedTheme = localStorage.getItem("myio-temp-modal-theme");
|
|
31005
|
-
if (savedGranularity)
|
|
31006
|
-
if (savedTheme)
|
|
31013
|
+
if (savedGranularity) state6.granularity = savedGranularity;
|
|
31014
|
+
if (savedTheme) state6.theme = savedTheme;
|
|
31007
31015
|
const modalContainer = document.createElement("div");
|
|
31008
31016
|
modalContainer.id = modalId;
|
|
31009
31017
|
document.body.appendChild(modalContainer);
|
|
31010
|
-
renderModal(modalContainer,
|
|
31018
|
+
renderModal(modalContainer, state6, modalId);
|
|
31011
31019
|
try {
|
|
31012
|
-
|
|
31013
|
-
|
|
31014
|
-
|
|
31015
|
-
renderModal(modalContainer,
|
|
31016
|
-
drawChart(modalId,
|
|
31020
|
+
state6.data = await fetchTemperatureData(state6.token, state6.deviceId, state6.startTs, state6.endTs);
|
|
31021
|
+
state6.stats = calculateStats(state6.data, state6.clampRange);
|
|
31022
|
+
state6.isLoading = false;
|
|
31023
|
+
renderModal(modalContainer, state6, modalId);
|
|
31024
|
+
drawChart(modalId, state6);
|
|
31017
31025
|
} catch (error) {
|
|
31018
31026
|
console.error("[TemperatureModal] Error fetching data:", error);
|
|
31019
|
-
|
|
31020
|
-
renderModal(modalContainer,
|
|
31027
|
+
state6.isLoading = false;
|
|
31028
|
+
renderModal(modalContainer, state6, modalId, error);
|
|
31021
31029
|
}
|
|
31022
|
-
await setupEventListeners(modalContainer,
|
|
31030
|
+
await setupEventListeners(modalContainer, state6, modalId, params.onClose);
|
|
31023
31031
|
return {
|
|
31024
31032
|
destroy: () => {
|
|
31025
31033
|
modalContainer.remove();
|
|
31026
31034
|
params.onClose?.();
|
|
31027
31035
|
},
|
|
31028
31036
|
updateData: async (startDate, endDate, granularity) => {
|
|
31029
|
-
|
|
31030
|
-
|
|
31031
|
-
if (granularity)
|
|
31032
|
-
|
|
31033
|
-
renderModal(modalContainer,
|
|
31037
|
+
state6.startTs = new Date(startDate).getTime();
|
|
31038
|
+
state6.endTs = new Date(endDate).getTime();
|
|
31039
|
+
if (granularity) state6.granularity = granularity;
|
|
31040
|
+
state6.isLoading = true;
|
|
31041
|
+
renderModal(modalContainer, state6, modalId);
|
|
31034
31042
|
try {
|
|
31035
|
-
|
|
31036
|
-
|
|
31037
|
-
|
|
31038
|
-
renderModal(modalContainer,
|
|
31039
|
-
drawChart(modalId,
|
|
31043
|
+
state6.data = await fetchTemperatureData(state6.token, state6.deviceId, state6.startTs, state6.endTs);
|
|
31044
|
+
state6.stats = calculateStats(state6.data, state6.clampRange);
|
|
31045
|
+
state6.isLoading = false;
|
|
31046
|
+
renderModal(modalContainer, state6, modalId);
|
|
31047
|
+
drawChart(modalId, state6);
|
|
31040
31048
|
} catch (error) {
|
|
31041
31049
|
console.error("[TemperatureModal] Error updating data:", error);
|
|
31042
|
-
|
|
31043
|
-
renderModal(modalContainer,
|
|
31050
|
+
state6.isLoading = false;
|
|
31051
|
+
renderModal(modalContainer, state6, modalId, error);
|
|
31044
31052
|
}
|
|
31045
31053
|
}
|
|
31046
31054
|
};
|
|
31047
31055
|
}
|
|
31048
|
-
function renderModal(container,
|
|
31049
|
-
const colors = getThemeColors(
|
|
31050
|
-
const startDateStr = new Date(
|
|
31051
|
-
const endDateStr = new Date(
|
|
31052
|
-
const statusText =
|
|
31053
|
-
const statusColor =
|
|
31054
|
-
const rangeText =
|
|
31055
|
-
const startDateInput = new Date(
|
|
31056
|
-
const endDateInput = new Date(
|
|
31056
|
+
function renderModal(container, state6, modalId, error) {
|
|
31057
|
+
const colors = getThemeColors(state6.theme);
|
|
31058
|
+
const startDateStr = new Date(state6.startTs).toLocaleDateString(state6.locale);
|
|
31059
|
+
const endDateStr = new Date(state6.endTs).toLocaleDateString(state6.locale);
|
|
31060
|
+
const statusText = state6.temperatureStatus === "ok" ? "Dentro da faixa" : state6.temperatureStatus === "above" ? "Acima do limite" : state6.temperatureStatus === "below" ? "Abaixo do limite" : "N/A";
|
|
31061
|
+
const statusColor = state6.temperatureStatus === "ok" ? colors.success : state6.temperatureStatus === "above" ? colors.danger : state6.temperatureStatus === "below" ? colors.primary : colors.textMuted;
|
|
31062
|
+
const rangeText = state6.temperatureMin !== null && state6.temperatureMax !== null ? `${state6.temperatureMin}\xB0C - ${state6.temperatureMax}\xB0C` : "N\xE3o definida";
|
|
31063
|
+
const startDateInput = new Date(state6.startTs).toISOString().slice(0, 16);
|
|
31064
|
+
const endDateInput = new Date(state6.endTs).toISOString().slice(0, 16);
|
|
31057
31065
|
const isMaximized = container.__isMaximized || false;
|
|
31058
31066
|
const contentMaxWidth = isMaximized ? "100%" : "900px";
|
|
31059
31067
|
const contentMaxHeight = isMaximized ? "100vh" : "95vh";
|
|
@@ -31080,7 +31088,7 @@ function renderModal(container, state5, modalId, error) {
|
|
|
31080
31088
|
min-height: 20px;
|
|
31081
31089
|
">
|
|
31082
31090
|
<h2 style="margin: 6px; font-size: 18px; font-weight: 600; color: white; line-height: 2;">
|
|
31083
|
-
\u{1F321}\uFE0F ${
|
|
31091
|
+
\u{1F321}\uFE0F ${state6.label} - Hist\xF3rico de Temperatura
|
|
31084
31092
|
</h2>
|
|
31085
31093
|
<div style="display: flex; gap: 4px; align-items: center;">
|
|
31086
31094
|
<!-- Theme Toggle -->
|
|
@@ -31088,7 +31096,7 @@ function renderModal(container, state5, modalId, error) {
|
|
|
31088
31096
|
background: none; border: none; font-size: 16px; cursor: pointer;
|
|
31089
31097
|
padding: 4px 8px; border-radius: 6px; color: rgba(255,255,255,0.8);
|
|
31090
31098
|
transition: background-color 0.2s;
|
|
31091
|
-
">${
|
|
31099
|
+
">${state6.theme === "dark" ? "\u2600\uFE0F" : "\u{1F319}"}</button>
|
|
31092
31100
|
<!-- Maximize Button -->
|
|
31093
31101
|
<button id="${modalId}-maximize" title="${isMaximized ? "Restaurar" : "Maximizar"}" style="
|
|
31094
31102
|
background: none; border: none; font-size: 16px; cursor: pointer;
|
|
@@ -31110,7 +31118,7 @@ function renderModal(container, state5, modalId, error) {
|
|
|
31110
31118
|
<!-- Controls Row -->
|
|
31111
31119
|
<div style="
|
|
31112
31120
|
display: flex; gap: 16px; flex-wrap: wrap; align-items: flex-end;
|
|
31113
|
-
margin-bottom: 16px; padding: 16px; background: ${
|
|
31121
|
+
margin-bottom: 16px; padding: 16px; background: ${state6.theme === "dark" ? "rgba(255,255,255,0.05)" : "#f7f7f7"};
|
|
31114
31122
|
border-radius: 6px; border: 1px solid ${colors.border};
|
|
31115
31123
|
">
|
|
31116
31124
|
<!-- Granularity Select -->
|
|
@@ -31123,8 +31131,8 @@ function renderModal(container, state5, modalId, error) {
|
|
|
31123
31131
|
font-size: 14px; color: ${colors.text}; background: ${colors.surface};
|
|
31124
31132
|
cursor: pointer; min-width: 130px;
|
|
31125
31133
|
">
|
|
31126
|
-
<option value="hour" ${
|
|
31127
|
-
<option value="day" ${
|
|
31134
|
+
<option value="hour" ${state6.granularity === "hour" ? "selected" : ""}>Hora (30 min)</option>
|
|
31135
|
+
<option value="day" ${state6.granularity === "day" ? "selected" : ""}>Dia (m\xE9dia)</option>
|
|
31128
31136
|
</select>
|
|
31129
31137
|
</div>
|
|
31130
31138
|
<!-- Day Period Filter (Multiselect) -->
|
|
@@ -31138,7 +31146,7 @@ function renderModal(container, state5, modalId, error) {
|
|
|
31138
31146
|
cursor: pointer; min-width: 180px; text-align: left;
|
|
31139
31147
|
display: flex; align-items: center; justify-content: space-between; gap: 8px;
|
|
31140
31148
|
">
|
|
31141
|
-
<span>${getSelectedPeriodsLabel(
|
|
31149
|
+
<span>${getSelectedPeriodsLabel(state6.selectedPeriods)}</span>
|
|
31142
31150
|
<span style="font-size: 10px;">\u25BC</span>
|
|
31143
31151
|
</button>
|
|
31144
31152
|
<div id="${modalId}-period-dropdown" style="
|
|
@@ -31151,12 +31159,12 @@ function renderModal(container, state5, modalId, error) {
|
|
|
31151
31159
|
<label style="
|
|
31152
31160
|
display: flex; align-items: center; gap: 8px; padding: 8px 12px;
|
|
31153
31161
|
cursor: pointer; font-size: 13px; color: ${colors.text};
|
|
31154
|
-
" onmouseover="this.style.background='${
|
|
31162
|
+
" onmouseover="this.style.background='${state6.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"}'"
|
|
31155
31163
|
onmouseout="this.style.background='transparent'">
|
|
31156
31164
|
<input type="checkbox"
|
|
31157
31165
|
name="${modalId}-period"
|
|
31158
31166
|
value="${period.id}"
|
|
31159
|
-
${
|
|
31167
|
+
${state6.selectedPeriods.includes(period.id) ? "checked" : ""}
|
|
31160
31168
|
style="width: 16px; height: 16px; cursor: pointer; accent-color: #3e1a7d;">
|
|
31161
31169
|
${period.label}
|
|
31162
31170
|
</label>
|
|
@@ -31164,13 +31172,13 @@ function renderModal(container, state5, modalId, error) {
|
|
|
31164
31172
|
<div style="border-top: 1px solid ${colors.border}; margin-top: 8px; padding-top: 8px;">
|
|
31165
31173
|
<button id="${modalId}-period-select-all" type="button" style="
|
|
31166
31174
|
width: calc(100% - 16px); margin: 0 8px 4px; padding: 6px;
|
|
31167
|
-
background: ${
|
|
31175
|
+
background: ${state6.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"};
|
|
31168
31176
|
border: none; border-radius: 4px; cursor: pointer;
|
|
31169
31177
|
font-size: 12px; color: ${colors.text};
|
|
31170
31178
|
">Selecionar Todos</button>
|
|
31171
31179
|
<button id="${modalId}-period-clear" type="button" style="
|
|
31172
31180
|
width: calc(100% - 16px); margin: 0 8px; padding: 6px;
|
|
31173
|
-
background: ${
|
|
31181
|
+
background: ${state6.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"};
|
|
31174
31182
|
border: none; border-radius: 4px; cursor: pointer;
|
|
31175
31183
|
font-size: 12px; color: ${colors.text};
|
|
31176
31184
|
">Limpar Sele\xE7\xE3o</button>
|
|
@@ -31195,8 +31203,8 @@ function renderModal(container, state5, modalId, error) {
|
|
|
31195
31203
|
font-size: 14px; font-weight: 500; height: 38px;
|
|
31196
31204
|
display: flex; align-items: center; gap: 8px;
|
|
31197
31205
|
font-family: 'Roboto', Arial, sans-serif;
|
|
31198
|
-
" ${
|
|
31199
|
-
${
|
|
31206
|
+
" ${state6.isLoading ? "disabled" : ""}>
|
|
31207
|
+
${state6.isLoading ? '<span style="animation: spin 1s linear infinite; display: inline-block;">\u21BB</span> Carregando...' : "Carregar"}
|
|
31200
31208
|
</button>
|
|
31201
31209
|
</div>
|
|
31202
31210
|
|
|
@@ -31207,40 +31215,40 @@ function renderModal(container, state5, modalId, error) {
|
|
|
31207
31215
|
">
|
|
31208
31216
|
<!-- Current Temperature -->
|
|
31209
31217
|
<div style="
|
|
31210
|
-
padding: 16px; background: ${
|
|
31218
|
+
padding: 16px; background: ${state6.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
|
|
31211
31219
|
border-radius: 12px; border: 1px solid ${colors.border};
|
|
31212
31220
|
">
|
|
31213
31221
|
<span style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500;">Temperatura Atual</span>
|
|
31214
31222
|
<div style="font-weight: 700; font-size: 24px; color: ${statusColor}; margin-top: 4px;">
|
|
31215
|
-
${
|
|
31223
|
+
${state6.currentTemperature !== null ? formatTemperature(state6.currentTemperature) : "N/A"}
|
|
31216
31224
|
</div>
|
|
31217
31225
|
<div style="font-size: 11px; color: ${statusColor}; margin-top: 2px;">${statusText}</div>
|
|
31218
31226
|
</div>
|
|
31219
31227
|
<!-- Average -->
|
|
31220
31228
|
<div style="
|
|
31221
|
-
padding: 16px; background: ${
|
|
31229
|
+
padding: 16px; background: ${state6.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
|
|
31222
31230
|
border-radius: 12px; border: 1px solid ${colors.border};
|
|
31223
31231
|
">
|
|
31224
31232
|
<span style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500;">M\xE9dia do Per\xEDodo</span>
|
|
31225
31233
|
<div id="${modalId}-avg" style="font-weight: 600; font-size: 20px; color: ${colors.text}; margin-top: 4px;">
|
|
31226
|
-
${
|
|
31234
|
+
${state6.stats.count > 0 ? formatTemperature(state6.stats.avg) : "N/A"}
|
|
31227
31235
|
</div>
|
|
31228
31236
|
<div style="font-size: 11px; color: ${colors.textMuted}; margin-top: 2px;">${startDateStr} - ${endDateStr}</div>
|
|
31229
31237
|
</div>
|
|
31230
31238
|
<!-- Min/Max -->
|
|
31231
31239
|
<div style="
|
|
31232
|
-
padding: 16px; background: ${
|
|
31240
|
+
padding: 16px; background: ${state6.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
|
|
31233
31241
|
border-radius: 12px; border: 1px solid ${colors.border};
|
|
31234
31242
|
">
|
|
31235
31243
|
<span style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500;">Min / Max</span>
|
|
31236
31244
|
<div id="${modalId}-minmax" style="font-weight: 600; font-size: 20px; color: ${colors.text}; margin-top: 4px;">
|
|
31237
|
-
${
|
|
31245
|
+
${state6.stats.count > 0 ? `${formatTemperature(state6.stats.min)} / ${formatTemperature(state6.stats.max)}` : "N/A"}
|
|
31238
31246
|
</div>
|
|
31239
|
-
<div style="font-size: 11px; color: ${colors.textMuted}; margin-top: 2px;">${
|
|
31247
|
+
<div style="font-size: 11px; color: ${colors.textMuted}; margin-top: 2px;">${state6.stats.count} leituras</div>
|
|
31240
31248
|
</div>
|
|
31241
31249
|
<!-- Ideal Range -->
|
|
31242
31250
|
<div style="
|
|
31243
|
-
padding: 16px; background: ${
|
|
31251
|
+
padding: 16px; background: ${state6.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
|
|
31244
31252
|
border-radius: 12px; border: 1px solid ${colors.border};
|
|
31245
31253
|
">
|
|
31246
31254
|
<span style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500;">Faixa Ideal</span>
|
|
@@ -31256,18 +31264,18 @@ function renderModal(container, state5, modalId, error) {
|
|
|
31256
31264
|
Hist\xF3rico de Temperatura
|
|
31257
31265
|
</h3>
|
|
31258
31266
|
<div id="${modalId}-chart" style="
|
|
31259
|
-
height: 320px; background: ${
|
|
31267
|
+
height: 320px; background: ${state6.theme === "dark" ? "rgba(255,255,255,0.03)" : "#fafafa"};
|
|
31260
31268
|
border-radius: 12px; display: flex; justify-content: center; align-items: center;
|
|
31261
31269
|
border: 1px solid ${colors.border}; position: relative;
|
|
31262
31270
|
">
|
|
31263
|
-
${
|
|
31271
|
+
${state6.isLoading ? `<div style="text-align: center; color: ${colors.textMuted};">
|
|
31264
31272
|
<div style="animation: spin 1s linear infinite; font-size: 32px; margin-bottom: 8px;">\u21BB</div>
|
|
31265
31273
|
<div>Carregando dados...</div>
|
|
31266
31274
|
</div>` : error ? `<div style="text-align: center; color: ${colors.danger};">
|
|
31267
31275
|
<div style="font-size: 32px; margin-bottom: 8px;">\u26A0\uFE0F</div>
|
|
31268
31276
|
<div>Erro ao carregar dados</div>
|
|
31269
31277
|
<div style="font-size: 12px; margin-top: 4px;">${error.message}</div>
|
|
31270
|
-
</div>` :
|
|
31278
|
+
</div>` : state6.data.length === 0 ? `<div style="text-align: center; color: ${colors.textMuted};">
|
|
31271
31279
|
<div style="font-size: 32px; margin-bottom: 8px;">\u{1F4ED}</div>
|
|
31272
31280
|
<div>Sem dados para o per\xEDodo selecionado</div>
|
|
31273
31281
|
</div>` : `<canvas id="${modalId}-canvas" style="width: 100%; height: 100%;"></canvas>`}
|
|
@@ -31277,12 +31285,12 @@ function renderModal(container, state5, modalId, error) {
|
|
|
31277
31285
|
<!-- Actions -->
|
|
31278
31286
|
<div style="display: flex; justify-content: flex-end; gap: 12px;">
|
|
31279
31287
|
<button id="${modalId}-export" style="
|
|
31280
|
-
background: ${
|
|
31288
|
+
background: ${state6.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f7f7f7"};
|
|
31281
31289
|
color: ${colors.text}; border: 1px solid ${colors.border};
|
|
31282
31290
|
padding: 8px 16px; border-radius: 6px; cursor: pointer;
|
|
31283
31291
|
font-size: 14px; display: flex; align-items: center; gap: 8px;
|
|
31284
31292
|
font-family: 'Roboto', Arial, sans-serif;
|
|
31285
|
-
" ${
|
|
31293
|
+
" ${state6.data.length === 0 ? "disabled" : ""}>
|
|
31286
31294
|
\u{1F4E5} Exportar CSV
|
|
31287
31295
|
</button>
|
|
31288
31296
|
<button id="${modalId}-close-btn" style="
|
|
@@ -31333,14 +31341,14 @@ function renderModal(container, state5, modalId, error) {
|
|
|
31333
31341
|
</style>
|
|
31334
31342
|
`;
|
|
31335
31343
|
}
|
|
31336
|
-
function drawChart(modalId,
|
|
31344
|
+
function drawChart(modalId, state6) {
|
|
31337
31345
|
const chartContainer = document.getElementById(`${modalId}-chart`);
|
|
31338
31346
|
const canvas = document.getElementById(`${modalId}-canvas`);
|
|
31339
|
-
if (!chartContainer || !canvas ||
|
|
31347
|
+
if (!chartContainer || !canvas || state6.data.length === 0) return;
|
|
31340
31348
|
const ctx = canvas.getContext("2d");
|
|
31341
31349
|
if (!ctx) return;
|
|
31342
|
-
const colors = getThemeColors(
|
|
31343
|
-
const filteredData = filterByDayPeriods(
|
|
31350
|
+
const colors = getThemeColors(state6.theme);
|
|
31351
|
+
const filteredData = filterByDayPeriods(state6.data, state6.selectedPeriods);
|
|
31344
31352
|
if (filteredData.length === 0) {
|
|
31345
31353
|
canvas.width = chartContainer.clientWidth;
|
|
31346
31354
|
canvas.height = chartContainer.clientHeight;
|
|
@@ -31351,14 +31359,14 @@ function drawChart(modalId, state5) {
|
|
|
31351
31359
|
return;
|
|
31352
31360
|
}
|
|
31353
31361
|
let chartData;
|
|
31354
|
-
if (
|
|
31362
|
+
if (state6.granularity === "hour") {
|
|
31355
31363
|
const interpolated = interpolateTemperature(filteredData, {
|
|
31356
31364
|
intervalMinutes: 30,
|
|
31357
|
-
startTs:
|
|
31358
|
-
endTs:
|
|
31359
|
-
clampRange:
|
|
31365
|
+
startTs: state6.startTs,
|
|
31366
|
+
endTs: state6.endTs,
|
|
31367
|
+
clampRange: state6.clampRange
|
|
31360
31368
|
});
|
|
31361
|
-
const filteredInterpolated = filterByDayPeriods(interpolated,
|
|
31369
|
+
const filteredInterpolated = filterByDayPeriods(interpolated, state6.selectedPeriods);
|
|
31362
31370
|
chartData = filteredInterpolated.map((item) => ({
|
|
31363
31371
|
x: item.ts,
|
|
31364
31372
|
y: Number(item.value),
|
|
@@ -31366,7 +31374,7 @@ function drawChart(modalId, state5) {
|
|
|
31366
31374
|
screenY: 0
|
|
31367
31375
|
}));
|
|
31368
31376
|
} else {
|
|
31369
|
-
const daily = aggregateByDay(filteredData,
|
|
31377
|
+
const daily = aggregateByDay(filteredData, state6.clampRange);
|
|
31370
31378
|
chartData = daily.map((item) => ({
|
|
31371
31379
|
x: item.dateTs,
|
|
31372
31380
|
y: item.avg,
|
|
@@ -31384,12 +31392,12 @@ function drawChart(modalId, state5) {
|
|
|
31384
31392
|
const paddingRight = 20;
|
|
31385
31393
|
const paddingTop = 20;
|
|
31386
31394
|
const paddingBottom = 55;
|
|
31387
|
-
const isPeriodsFiltered =
|
|
31395
|
+
const isPeriodsFiltered = state6.selectedPeriods.length < 4 && state6.selectedPeriods.length > 0;
|
|
31388
31396
|
const values = chartData.map((d) => d.y);
|
|
31389
31397
|
const dataMin = Math.min(...values);
|
|
31390
31398
|
const dataMax = Math.max(...values);
|
|
31391
|
-
const thresholdMin =
|
|
31392
|
-
const thresholdMax =
|
|
31399
|
+
const thresholdMin = state6.temperatureMin !== null ? state6.temperatureMin : dataMin;
|
|
31400
|
+
const thresholdMax = state6.temperatureMax !== null ? state6.temperatureMax : dataMax;
|
|
31393
31401
|
const minY = Math.min(dataMin, thresholdMin) - 1;
|
|
31394
31402
|
const maxY = Math.max(dataMax, thresholdMax) + 1;
|
|
31395
31403
|
const chartWidth = width - paddingLeft - paddingRight;
|
|
@@ -31421,9 +31429,9 @@ function drawChart(modalId, state5) {
|
|
|
31421
31429
|
ctx.lineTo(width - paddingRight, y);
|
|
31422
31430
|
ctx.stroke();
|
|
31423
31431
|
}
|
|
31424
|
-
if (
|
|
31425
|
-
const rangeMinY = height - paddingBottom - (
|
|
31426
|
-
const rangeMaxY = height - paddingBottom - (
|
|
31432
|
+
if (state6.temperatureMin !== null && state6.temperatureMax !== null) {
|
|
31433
|
+
const rangeMinY = height - paddingBottom - (state6.temperatureMin - minY) * scaleY;
|
|
31434
|
+
const rangeMaxY = height - paddingBottom - (state6.temperatureMax - minY) * scaleY;
|
|
31427
31435
|
ctx.fillStyle = "rgba(76, 175, 80, 0.1)";
|
|
31428
31436
|
ctx.fillRect(paddingLeft, rangeMaxY, chartWidth, rangeMinY - rangeMaxY);
|
|
31429
31437
|
ctx.strokeStyle = colors.success;
|
|
@@ -31465,10 +31473,10 @@ function drawChart(modalId, state5) {
|
|
|
31465
31473
|
const point = chartData[i];
|
|
31466
31474
|
const date = new Date(point.x);
|
|
31467
31475
|
let label;
|
|
31468
|
-
if (
|
|
31469
|
-
label = date.toLocaleTimeString(
|
|
31476
|
+
if (state6.granularity === "hour") {
|
|
31477
|
+
label = date.toLocaleTimeString(state6.locale, { hour: "2-digit", minute: "2-digit" });
|
|
31470
31478
|
} else {
|
|
31471
|
-
label = date.toLocaleDateString(
|
|
31479
|
+
label = date.toLocaleDateString(state6.locale, { day: "2-digit", month: "2-digit" });
|
|
31472
31480
|
}
|
|
31473
31481
|
ctx.strokeStyle = colors.chartGrid;
|
|
31474
31482
|
ctx.lineWidth = 1;
|
|
@@ -31486,16 +31494,16 @@ function drawChart(modalId, state5) {
|
|
|
31486
31494
|
ctx.lineTo(paddingLeft, height - paddingBottom);
|
|
31487
31495
|
ctx.lineTo(width - paddingRight, height - paddingBottom);
|
|
31488
31496
|
ctx.stroke();
|
|
31489
|
-
setupChartTooltip(canvas, chartContainer, chartData,
|
|
31497
|
+
setupChartTooltip(canvas, chartContainer, chartData, state6, colors);
|
|
31490
31498
|
}
|
|
31491
|
-
function setupChartTooltip(canvas, container, chartData,
|
|
31499
|
+
function setupChartTooltip(canvas, container, chartData, state6, colors) {
|
|
31492
31500
|
const existingTooltip = container.querySelector(".myio-chart-tooltip");
|
|
31493
31501
|
if (existingTooltip) existingTooltip.remove();
|
|
31494
31502
|
const tooltip = document.createElement("div");
|
|
31495
31503
|
tooltip.className = "myio-chart-tooltip";
|
|
31496
31504
|
tooltip.style.cssText = `
|
|
31497
31505
|
position: absolute;
|
|
31498
|
-
background: ${
|
|
31506
|
+
background: ${state6.theme === "dark" ? "rgba(30, 30, 40, 0.95)" : "rgba(255, 255, 255, 0.98)"};
|
|
31499
31507
|
border: 1px solid ${colors.border};
|
|
31500
31508
|
border-radius: 8px;
|
|
31501
31509
|
padding: 10px 14px;
|
|
@@ -31532,17 +31540,17 @@ function setupChartTooltip(canvas, container, chartData, state5, colors) {
|
|
|
31532
31540
|
if (point) {
|
|
31533
31541
|
const date = new Date(point.x);
|
|
31534
31542
|
let dateStr;
|
|
31535
|
-
if (
|
|
31536
|
-
dateStr = date.toLocaleDateString(
|
|
31543
|
+
if (state6.granularity === "hour") {
|
|
31544
|
+
dateStr = date.toLocaleDateString(state6.locale, {
|
|
31537
31545
|
day: "2-digit",
|
|
31538
31546
|
month: "2-digit",
|
|
31539
31547
|
year: "numeric"
|
|
31540
|
-
}) + " " + date.toLocaleTimeString(
|
|
31548
|
+
}) + " " + date.toLocaleTimeString(state6.locale, {
|
|
31541
31549
|
hour: "2-digit",
|
|
31542
31550
|
minute: "2-digit"
|
|
31543
31551
|
});
|
|
31544
31552
|
} else {
|
|
31545
|
-
dateStr = date.toLocaleDateString(
|
|
31553
|
+
dateStr = date.toLocaleDateString(state6.locale, {
|
|
31546
31554
|
day: "2-digit",
|
|
31547
31555
|
month: "2-digit",
|
|
31548
31556
|
year: "numeric"
|
|
@@ -31580,7 +31588,7 @@ function setupChartTooltip(canvas, container, chartData, state5, colors) {
|
|
|
31580
31588
|
canvas.style.cursor = "default";
|
|
31581
31589
|
});
|
|
31582
31590
|
}
|
|
31583
|
-
async function setupEventListeners(container,
|
|
31591
|
+
async function setupEventListeners(container, state6, modalId, onClose) {
|
|
31584
31592
|
const closeModal = () => {
|
|
31585
31593
|
container.remove();
|
|
31586
31594
|
onClose?.();
|
|
@@ -31591,19 +31599,19 @@ async function setupEventListeners(container, state5, modalId, onClose) {
|
|
|
31591
31599
|
document.getElementById(`${modalId}-close`)?.addEventListener("click", closeModal);
|
|
31592
31600
|
document.getElementById(`${modalId}-close-btn`)?.addEventListener("click", closeModal);
|
|
31593
31601
|
const dateRangeInput = document.getElementById(`${modalId}-date-range`);
|
|
31594
|
-
if (dateRangeInput && !
|
|
31602
|
+
if (dateRangeInput && !state6.dateRangePicker) {
|
|
31595
31603
|
try {
|
|
31596
|
-
|
|
31597
|
-
presetStart: new Date(
|
|
31598
|
-
presetEnd: new Date(
|
|
31604
|
+
state6.dateRangePicker = await createDateRangePicker2(dateRangeInput, {
|
|
31605
|
+
presetStart: new Date(state6.startTs).toISOString(),
|
|
31606
|
+
presetEnd: new Date(state6.endTs).toISOString(),
|
|
31599
31607
|
includeTime: true,
|
|
31600
31608
|
timePrecision: "minute",
|
|
31601
31609
|
maxRangeDays: 90,
|
|
31602
|
-
locale:
|
|
31610
|
+
locale: state6.locale,
|
|
31603
31611
|
parentEl: container.querySelector(".myio-temp-modal-content"),
|
|
31604
31612
|
onApply: (result) => {
|
|
31605
|
-
|
|
31606
|
-
|
|
31613
|
+
state6.startTs = new Date(result.startISO).getTime();
|
|
31614
|
+
state6.endTs = new Date(result.endISO).getTime();
|
|
31607
31615
|
console.log("[TemperatureModal] Date range applied:", result);
|
|
31608
31616
|
}
|
|
31609
31617
|
});
|
|
@@ -31612,19 +31620,19 @@ async function setupEventListeners(container, state5, modalId, onClose) {
|
|
|
31612
31620
|
}
|
|
31613
31621
|
}
|
|
31614
31622
|
document.getElementById(`${modalId}-theme-toggle`)?.addEventListener("click", async () => {
|
|
31615
|
-
|
|
31616
|
-
localStorage.setItem("myio-temp-modal-theme",
|
|
31617
|
-
|
|
31618
|
-
renderModal(container,
|
|
31619
|
-
if (
|
|
31620
|
-
await setupEventListeners(container,
|
|
31623
|
+
state6.theme = state6.theme === "dark" ? "light" : "dark";
|
|
31624
|
+
localStorage.setItem("myio-temp-modal-theme", state6.theme);
|
|
31625
|
+
state6.dateRangePicker = null;
|
|
31626
|
+
renderModal(container, state6, modalId);
|
|
31627
|
+
if (state6.data.length > 0) drawChart(modalId, state6);
|
|
31628
|
+
await setupEventListeners(container, state6, modalId, onClose);
|
|
31621
31629
|
});
|
|
31622
31630
|
document.getElementById(`${modalId}-maximize`)?.addEventListener("click", async () => {
|
|
31623
31631
|
container.__isMaximized = !container.__isMaximized;
|
|
31624
|
-
|
|
31625
|
-
renderModal(container,
|
|
31626
|
-
if (
|
|
31627
|
-
await setupEventListeners(container,
|
|
31632
|
+
state6.dateRangePicker = null;
|
|
31633
|
+
renderModal(container, state6, modalId);
|
|
31634
|
+
if (state6.data.length > 0) drawChart(modalId, state6);
|
|
31635
|
+
await setupEventListeners(container, state6, modalId, onClose);
|
|
31628
31636
|
});
|
|
31629
31637
|
const periodBtn = document.getElementById(`${modalId}-period-btn`);
|
|
31630
31638
|
const periodDropdown = document.getElementById(`${modalId}-period-dropdown`);
|
|
@@ -31643,71 +31651,71 @@ async function setupEventListeners(container, state5, modalId, onClose) {
|
|
|
31643
31651
|
periodCheckboxes.forEach((checkbox) => {
|
|
31644
31652
|
checkbox.addEventListener("change", () => {
|
|
31645
31653
|
const checked = Array.from(periodCheckboxes).filter((cb) => cb.checked).map((cb) => cb.value);
|
|
31646
|
-
|
|
31654
|
+
state6.selectedPeriods = checked;
|
|
31647
31655
|
const btnLabel = periodBtn?.querySelector("span:first-child");
|
|
31648
31656
|
if (btnLabel) {
|
|
31649
|
-
btnLabel.textContent = getSelectedPeriodsLabel(
|
|
31657
|
+
btnLabel.textContent = getSelectedPeriodsLabel(state6.selectedPeriods);
|
|
31650
31658
|
}
|
|
31651
|
-
if (
|
|
31659
|
+
if (state6.data.length > 0) drawChart(modalId, state6);
|
|
31652
31660
|
});
|
|
31653
31661
|
});
|
|
31654
31662
|
document.getElementById(`${modalId}-period-select-all`)?.addEventListener("click", () => {
|
|
31655
31663
|
periodCheckboxes.forEach((cb) => {
|
|
31656
31664
|
cb.checked = true;
|
|
31657
31665
|
});
|
|
31658
|
-
|
|
31666
|
+
state6.selectedPeriods = ["madrugada", "manha", "tarde", "noite"];
|
|
31659
31667
|
const btnLabel = periodBtn?.querySelector("span:first-child");
|
|
31660
31668
|
if (btnLabel) {
|
|
31661
|
-
btnLabel.textContent = getSelectedPeriodsLabel(
|
|
31669
|
+
btnLabel.textContent = getSelectedPeriodsLabel(state6.selectedPeriods);
|
|
31662
31670
|
}
|
|
31663
|
-
if (
|
|
31671
|
+
if (state6.data.length > 0) drawChart(modalId, state6);
|
|
31664
31672
|
});
|
|
31665
31673
|
document.getElementById(`${modalId}-period-clear`)?.addEventListener("click", () => {
|
|
31666
31674
|
periodCheckboxes.forEach((cb) => {
|
|
31667
31675
|
cb.checked = false;
|
|
31668
31676
|
});
|
|
31669
|
-
|
|
31677
|
+
state6.selectedPeriods = [];
|
|
31670
31678
|
const btnLabel = periodBtn?.querySelector("span:first-child");
|
|
31671
31679
|
if (btnLabel) {
|
|
31672
|
-
btnLabel.textContent = getSelectedPeriodsLabel(
|
|
31680
|
+
btnLabel.textContent = getSelectedPeriodsLabel(state6.selectedPeriods);
|
|
31673
31681
|
}
|
|
31674
|
-
if (
|
|
31682
|
+
if (state6.data.length > 0) drawChart(modalId, state6);
|
|
31675
31683
|
});
|
|
31676
31684
|
document.getElementById(`${modalId}-granularity`)?.addEventListener("change", (e) => {
|
|
31677
|
-
|
|
31678
|
-
localStorage.setItem("myio-temp-modal-granularity",
|
|
31679
|
-
if (
|
|
31685
|
+
state6.granularity = e.target.value;
|
|
31686
|
+
localStorage.setItem("myio-temp-modal-granularity", state6.granularity);
|
|
31687
|
+
if (state6.data.length > 0) drawChart(modalId, state6);
|
|
31680
31688
|
});
|
|
31681
31689
|
document.getElementById(`${modalId}-query`)?.addEventListener("click", async () => {
|
|
31682
|
-
if (
|
|
31690
|
+
if (state6.startTs >= state6.endTs) {
|
|
31683
31691
|
alert("Por favor, selecione um per\xEDodo v\xE1lido");
|
|
31684
31692
|
return;
|
|
31685
31693
|
}
|
|
31686
|
-
|
|
31687
|
-
|
|
31688
|
-
renderModal(container,
|
|
31694
|
+
state6.isLoading = true;
|
|
31695
|
+
state6.dateRangePicker = null;
|
|
31696
|
+
renderModal(container, state6, modalId);
|
|
31689
31697
|
try {
|
|
31690
|
-
|
|
31691
|
-
|
|
31692
|
-
|
|
31693
|
-
renderModal(container,
|
|
31694
|
-
drawChart(modalId,
|
|
31695
|
-
await setupEventListeners(container,
|
|
31698
|
+
state6.data = await fetchTemperatureData(state6.token, state6.deviceId, state6.startTs, state6.endTs);
|
|
31699
|
+
state6.stats = calculateStats(state6.data, state6.clampRange);
|
|
31700
|
+
state6.isLoading = false;
|
|
31701
|
+
renderModal(container, state6, modalId);
|
|
31702
|
+
drawChart(modalId, state6);
|
|
31703
|
+
await setupEventListeners(container, state6, modalId, onClose);
|
|
31696
31704
|
} catch (error) {
|
|
31697
31705
|
console.error("[TemperatureModal] Error fetching data:", error);
|
|
31698
|
-
|
|
31699
|
-
renderModal(container,
|
|
31700
|
-
await setupEventListeners(container,
|
|
31706
|
+
state6.isLoading = false;
|
|
31707
|
+
renderModal(container, state6, modalId, error);
|
|
31708
|
+
await setupEventListeners(container, state6, modalId, onClose);
|
|
31701
31709
|
}
|
|
31702
31710
|
});
|
|
31703
31711
|
document.getElementById(`${modalId}-export`)?.addEventListener("click", () => {
|
|
31704
|
-
if (
|
|
31705
|
-
const startDateStr = new Date(
|
|
31706
|
-
const endDateStr = new Date(
|
|
31712
|
+
if (state6.data.length === 0) return;
|
|
31713
|
+
const startDateStr = new Date(state6.startTs).toLocaleDateString(state6.locale).replace(/\//g, "-");
|
|
31714
|
+
const endDateStr = new Date(state6.endTs).toLocaleDateString(state6.locale).replace(/\//g, "-");
|
|
31707
31715
|
exportTemperatureCSV(
|
|
31708
|
-
|
|
31709
|
-
|
|
31710
|
-
|
|
31716
|
+
state6.data,
|
|
31717
|
+
state6.label,
|
|
31718
|
+
state6.stats,
|
|
31711
31719
|
startDateStr,
|
|
31712
31720
|
endDateStr
|
|
31713
31721
|
);
|
|
@@ -31720,7 +31728,7 @@ async function openTemperatureComparisonModal(params) {
|
|
|
31720
31728
|
const defaultDateRange = getTodaySoFar();
|
|
31721
31729
|
const startTs = params.startDate ? new Date(params.startDate).getTime() : defaultDateRange.startTs;
|
|
31722
31730
|
const endTs = params.endDate ? new Date(params.endDate).getTime() : defaultDateRange.endTs;
|
|
31723
|
-
const
|
|
31731
|
+
const state6 = {
|
|
31724
31732
|
token: params.token,
|
|
31725
31733
|
devices: params.devices,
|
|
31726
31734
|
startTs,
|
|
@@ -31739,44 +31747,44 @@ async function openTemperatureComparisonModal(params) {
|
|
|
31739
31747
|
};
|
|
31740
31748
|
const savedGranularity = localStorage.getItem("myio-temp-comparison-granularity");
|
|
31741
31749
|
const savedTheme = localStorage.getItem("myio-temp-comparison-theme");
|
|
31742
|
-
if (savedGranularity)
|
|
31743
|
-
if (savedTheme)
|
|
31750
|
+
if (savedGranularity) state6.granularity = savedGranularity;
|
|
31751
|
+
if (savedTheme) state6.theme = savedTheme;
|
|
31744
31752
|
const modalContainer = document.createElement("div");
|
|
31745
31753
|
modalContainer.id = modalId;
|
|
31746
31754
|
document.body.appendChild(modalContainer);
|
|
31747
|
-
renderModal2(modalContainer,
|
|
31748
|
-
await fetchAllDevicesData(
|
|
31749
|
-
renderModal2(modalContainer,
|
|
31750
|
-
drawComparisonChart(modalId,
|
|
31751
|
-
await setupEventListeners2(modalContainer,
|
|
31755
|
+
renderModal2(modalContainer, state6, modalId);
|
|
31756
|
+
await fetchAllDevicesData(state6);
|
|
31757
|
+
renderModal2(modalContainer, state6, modalId);
|
|
31758
|
+
drawComparisonChart(modalId, state6);
|
|
31759
|
+
await setupEventListeners2(modalContainer, state6, modalId, params.onClose);
|
|
31752
31760
|
return {
|
|
31753
31761
|
destroy: () => {
|
|
31754
31762
|
modalContainer.remove();
|
|
31755
31763
|
params.onClose?.();
|
|
31756
31764
|
},
|
|
31757
31765
|
updateData: async (startDate, endDate, granularity) => {
|
|
31758
|
-
|
|
31759
|
-
|
|
31760
|
-
if (granularity)
|
|
31761
|
-
|
|
31762
|
-
renderModal2(modalContainer,
|
|
31763
|
-
await fetchAllDevicesData(
|
|
31764
|
-
renderModal2(modalContainer,
|
|
31765
|
-
drawComparisonChart(modalId,
|
|
31766
|
-
setupEventListeners2(modalContainer,
|
|
31766
|
+
state6.startTs = new Date(startDate).getTime();
|
|
31767
|
+
state6.endTs = new Date(endDate).getTime();
|
|
31768
|
+
if (granularity) state6.granularity = granularity;
|
|
31769
|
+
state6.isLoading = true;
|
|
31770
|
+
renderModal2(modalContainer, state6, modalId);
|
|
31771
|
+
await fetchAllDevicesData(state6);
|
|
31772
|
+
renderModal2(modalContainer, state6, modalId);
|
|
31773
|
+
drawComparisonChart(modalId, state6);
|
|
31774
|
+
setupEventListeners2(modalContainer, state6, modalId, params.onClose);
|
|
31767
31775
|
}
|
|
31768
31776
|
};
|
|
31769
31777
|
}
|
|
31770
|
-
async function fetchAllDevicesData(
|
|
31771
|
-
|
|
31772
|
-
|
|
31778
|
+
async function fetchAllDevicesData(state6) {
|
|
31779
|
+
state6.isLoading = true;
|
|
31780
|
+
state6.deviceData = [];
|
|
31773
31781
|
try {
|
|
31774
31782
|
const results = await Promise.all(
|
|
31775
|
-
|
|
31783
|
+
state6.devices.map(async (device, index) => {
|
|
31776
31784
|
const deviceId = device.tbId || device.id;
|
|
31777
31785
|
try {
|
|
31778
|
-
const data = await fetchTemperatureData(
|
|
31779
|
-
const stats = calculateStats(data,
|
|
31786
|
+
const data = await fetchTemperatureData(state6.token, deviceId, state6.startTs, state6.endTs);
|
|
31787
|
+
const stats = calculateStats(data, state6.clampRange);
|
|
31780
31788
|
return {
|
|
31781
31789
|
device,
|
|
31782
31790
|
data,
|
|
@@ -31794,21 +31802,21 @@ async function fetchAllDevicesData(state5) {
|
|
|
31794
31802
|
}
|
|
31795
31803
|
})
|
|
31796
31804
|
);
|
|
31797
|
-
|
|
31805
|
+
state6.deviceData = results;
|
|
31798
31806
|
} catch (error) {
|
|
31799
31807
|
console.error("[TemperatureComparisonModal] Error fetching data:", error);
|
|
31800
31808
|
}
|
|
31801
|
-
|
|
31809
|
+
state6.isLoading = false;
|
|
31802
31810
|
}
|
|
31803
|
-
function renderModal2(container,
|
|
31804
|
-
const colors = getThemeColors(
|
|
31805
|
-
const startDateStr = new Date(
|
|
31806
|
-
const endDateStr = new Date(
|
|
31807
|
-
const startDateInput = new Date(
|
|
31808
|
-
const endDateInput = new Date(
|
|
31809
|
-
const legendHTML =
|
|
31811
|
+
function renderModal2(container, state6, modalId) {
|
|
31812
|
+
const colors = getThemeColors(state6.theme);
|
|
31813
|
+
const startDateStr = new Date(state6.startTs).toLocaleDateString(state6.locale);
|
|
31814
|
+
const endDateStr = new Date(state6.endTs).toLocaleDateString(state6.locale);
|
|
31815
|
+
const startDateInput = new Date(state6.startTs).toISOString().slice(0, 16);
|
|
31816
|
+
const endDateInput = new Date(state6.endTs).toISOString().slice(0, 16);
|
|
31817
|
+
const legendHTML = state6.deviceData.map((dd) => `
|
|
31810
31818
|
<div style="display: flex; align-items: center; gap: 8px; padding: 8px 12px;
|
|
31811
|
-
background: ${
|
|
31819
|
+
background: ${state6.theme === "dark" ? "rgba(255,255,255,0.05)" : "rgba(0,0,0,0.03)"};
|
|
31812
31820
|
border-radius: 8px;">
|
|
31813
31821
|
<span style="width: 12px; height: 12px; border-radius: 50%; background: ${dd.color};"></span>
|
|
31814
31822
|
<span style="color: ${colors.text}; font-size: 13px;">${dd.device.label}</span>
|
|
@@ -31817,9 +31825,9 @@ function renderModal2(container, state5, modalId) {
|
|
|
31817
31825
|
</span>
|
|
31818
31826
|
</div>
|
|
31819
31827
|
`).join("");
|
|
31820
|
-
const statsHTML =
|
|
31828
|
+
const statsHTML = state6.deviceData.map((dd) => `
|
|
31821
31829
|
<div style="
|
|
31822
|
-
padding: 12px; background: ${
|
|
31830
|
+
padding: 12px; background: ${state6.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
|
|
31823
31831
|
border-radius: 10px; border-left: 4px solid ${dd.color};
|
|
31824
31832
|
min-width: 150px;
|
|
31825
31833
|
">
|
|
@@ -31870,7 +31878,7 @@ function renderModal2(container, state5, modalId) {
|
|
|
31870
31878
|
min-height: 20px;
|
|
31871
31879
|
">
|
|
31872
31880
|
<h2 style="margin: 6px; font-size: 18px; font-weight: 600; color: white; line-height: 2;">
|
|
31873
|
-
\u{1F321}\uFE0F Compara\xE7\xE3o de Temperatura - ${
|
|
31881
|
+
\u{1F321}\uFE0F Compara\xE7\xE3o de Temperatura - ${state6.devices.length} sensores
|
|
31874
31882
|
</h2>
|
|
31875
31883
|
<div style="display: flex; gap: 4px; align-items: center;">
|
|
31876
31884
|
<!-- Theme Toggle -->
|
|
@@ -31878,7 +31886,7 @@ function renderModal2(container, state5, modalId) {
|
|
|
31878
31886
|
background: none; border: none; font-size: 16px; cursor: pointer;
|
|
31879
31887
|
padding: 4px 8px; border-radius: 6px; color: rgba(255,255,255,0.8);
|
|
31880
31888
|
transition: background-color 0.2s;
|
|
31881
|
-
">${
|
|
31889
|
+
">${state6.theme === "dark" ? "\u2600\uFE0F" : "\u{1F319}"}</button>
|
|
31882
31890
|
<!-- Maximize Button -->
|
|
31883
31891
|
<button id="${modalId}-maximize" title="${isMaximized ? "Restaurar" : "Maximizar"}" style="
|
|
31884
31892
|
background: none; border: none; font-size: 16px; cursor: pointer;
|
|
@@ -31901,7 +31909,7 @@ function renderModal2(container, state5, modalId) {
|
|
|
31901
31909
|
<div style="
|
|
31902
31910
|
display: flex; gap: 16px; flex-wrap: wrap; align-items: flex-end;
|
|
31903
31911
|
margin-bottom: 16px; padding: 16px;
|
|
31904
|
-
background: ${
|
|
31912
|
+
background: ${state6.theme === "dark" ? "rgba(255,255,255,0.05)" : "#f7f7f7"};
|
|
31905
31913
|
border-radius: 6px; border: 1px solid ${colors.border};
|
|
31906
31914
|
">
|
|
31907
31915
|
<!-- Granularity Select -->
|
|
@@ -31914,8 +31922,8 @@ function renderModal2(container, state5, modalId) {
|
|
|
31914
31922
|
font-size: 14px; color: ${colors.text}; background: ${colors.surface};
|
|
31915
31923
|
cursor: pointer; min-width: 130px;
|
|
31916
31924
|
">
|
|
31917
|
-
<option value="hour" ${
|
|
31918
|
-
<option value="day" ${
|
|
31925
|
+
<option value="hour" ${state6.granularity === "hour" ? "selected" : ""}>Hora (30 min)</option>
|
|
31926
|
+
<option value="day" ${state6.granularity === "day" ? "selected" : ""}>Dia (m\xE9dia)</option>
|
|
31919
31927
|
</select>
|
|
31920
31928
|
</div>
|
|
31921
31929
|
<!-- Day Period Filter (Multiselect) -->
|
|
@@ -31929,7 +31937,7 @@ function renderModal2(container, state5, modalId) {
|
|
|
31929
31937
|
cursor: pointer; min-width: 180px; text-align: left;
|
|
31930
31938
|
display: flex; align-items: center; justify-content: space-between; gap: 8px;
|
|
31931
31939
|
">
|
|
31932
|
-
<span>${getSelectedPeriodsLabel(
|
|
31940
|
+
<span>${getSelectedPeriodsLabel(state6.selectedPeriods)}</span>
|
|
31933
31941
|
<span style="font-size: 10px;">\u25BC</span>
|
|
31934
31942
|
</button>
|
|
31935
31943
|
<div id="${modalId}-period-dropdown" style="
|
|
@@ -31942,12 +31950,12 @@ function renderModal2(container, state5, modalId) {
|
|
|
31942
31950
|
<label style="
|
|
31943
31951
|
display: flex; align-items: center; gap: 8px; padding: 8px 12px;
|
|
31944
31952
|
cursor: pointer; font-size: 13px; color: ${colors.text};
|
|
31945
|
-
" onmouseover="this.style.background='${
|
|
31953
|
+
" onmouseover="this.style.background='${state6.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"}'"
|
|
31946
31954
|
onmouseout="this.style.background='transparent'">
|
|
31947
31955
|
<input type="checkbox"
|
|
31948
31956
|
name="${modalId}-period"
|
|
31949
31957
|
value="${period.id}"
|
|
31950
|
-
${
|
|
31958
|
+
${state6.selectedPeriods.includes(period.id) ? "checked" : ""}
|
|
31951
31959
|
style="width: 16px; height: 16px; cursor: pointer; accent-color: #3e1a7d;">
|
|
31952
31960
|
${period.label}
|
|
31953
31961
|
</label>
|
|
@@ -31955,13 +31963,13 @@ function renderModal2(container, state5, modalId) {
|
|
|
31955
31963
|
<div style="border-top: 1px solid ${colors.border}; margin-top: 8px; padding-top: 8px;">
|
|
31956
31964
|
<button id="${modalId}-period-select-all" type="button" style="
|
|
31957
31965
|
width: calc(100% - 16px); margin: 0 8px 4px; padding: 6px;
|
|
31958
|
-
background: ${
|
|
31966
|
+
background: ${state6.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"};
|
|
31959
31967
|
border: none; border-radius: 4px; cursor: pointer;
|
|
31960
31968
|
font-size: 12px; color: ${colors.text};
|
|
31961
31969
|
">Selecionar Todos</button>
|
|
31962
31970
|
<button id="${modalId}-period-clear" type="button" style="
|
|
31963
31971
|
width: calc(100% - 16px); margin: 0 8px; padding: 6px;
|
|
31964
|
-
background: ${
|
|
31972
|
+
background: ${state6.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"};
|
|
31965
31973
|
border: none; border-radius: 4px; cursor: pointer;
|
|
31966
31974
|
font-size: 12px; color: ${colors.text};
|
|
31967
31975
|
">Limpar Sele\xE7\xE3o</button>
|
|
@@ -31986,8 +31994,8 @@ function renderModal2(container, state5, modalId) {
|
|
|
31986
31994
|
font-size: 14px; font-weight: 500; height: 38px;
|
|
31987
31995
|
display: flex; align-items: center; gap: 8px;
|
|
31988
31996
|
font-family: 'Roboto', Arial, sans-serif;
|
|
31989
|
-
" ${
|
|
31990
|
-
${
|
|
31997
|
+
" ${state6.isLoading ? "disabled" : ""}>
|
|
31998
|
+
${state6.isLoading ? '<span style="animation: spin 1s linear infinite; display: inline-block;">\u21BB</span> Carregando...' : "Carregar"}
|
|
31991
31999
|
</button>
|
|
31992
32000
|
</div>
|
|
31993
32001
|
|
|
@@ -32003,14 +32011,14 @@ function renderModal2(container, state5, modalId) {
|
|
|
32003
32011
|
<div style="margin-bottom: 24px;">
|
|
32004
32012
|
<div id="${modalId}-chart" style="
|
|
32005
32013
|
height: 380px;
|
|
32006
|
-
background: ${
|
|
32014
|
+
background: ${state6.theme === "dark" ? "rgba(255,255,255,0.03)" : "#fafafa"};
|
|
32007
32015
|
border-radius: 14px; display: flex; justify-content: center; align-items: center;
|
|
32008
32016
|
border: 1px solid ${colors.border}; position: relative;
|
|
32009
32017
|
">
|
|
32010
|
-
${
|
|
32018
|
+
${state6.isLoading ? `<div style="text-align: center; color: ${colors.textMuted};">
|
|
32011
32019
|
<div style="animation: spin 1s linear infinite; font-size: 36px; margin-bottom: 12px;">\u21BB</div>
|
|
32012
|
-
<div style="font-size: 15px;">Carregando dados de ${
|
|
32013
|
-
</div>` :
|
|
32020
|
+
<div style="font-size: 15px;">Carregando dados de ${state6.devices.length} sensores...</div>
|
|
32021
|
+
</div>` : state6.deviceData.every((dd) => dd.data.length === 0) ? `<div style="text-align: center; color: ${colors.textMuted};">
|
|
32014
32022
|
<div style="font-size: 48px; margin-bottom: 12px;">\u{1F4ED}</div>
|
|
32015
32023
|
<div style="font-size: 16px;">Sem dados para o per\xEDodo selecionado</div>
|
|
32016
32024
|
</div>` : `<canvas id="${modalId}-canvas" style="width: 100%; height: 100%;"></canvas>`}
|
|
@@ -32028,12 +32036,12 @@ function renderModal2(container, state5, modalId) {
|
|
|
32028
32036
|
<!-- Actions -->
|
|
32029
32037
|
<div style="display: flex; justify-content: flex-end; gap: 12px;">
|
|
32030
32038
|
<button id="${modalId}-export" style="
|
|
32031
|
-
background: ${
|
|
32039
|
+
background: ${state6.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f7f7f7"};
|
|
32032
32040
|
color: ${colors.text}; border: 1px solid ${colors.border};
|
|
32033
32041
|
padding: 8px 16px; border-radius: 6px; cursor: pointer;
|
|
32034
32042
|
font-size: 14px; display: flex; align-items: center; gap: 8px;
|
|
32035
32043
|
font-family: 'Roboto', Arial, sans-serif;
|
|
32036
|
-
" ${
|
|
32044
|
+
" ${state6.deviceData.every((dd) => dd.data.length === 0) ? "disabled" : ""}>
|
|
32037
32045
|
\u{1F4E5} Exportar CSV
|
|
32038
32046
|
</button>
|
|
32039
32047
|
<button id="${modalId}-close-btn" style="
|
|
@@ -32068,15 +32076,15 @@ function renderModal2(container, state5, modalId) {
|
|
|
32068
32076
|
</style>
|
|
32069
32077
|
`;
|
|
32070
32078
|
}
|
|
32071
|
-
function drawComparisonChart(modalId,
|
|
32079
|
+
function drawComparisonChart(modalId, state6) {
|
|
32072
32080
|
const chartContainer = document.getElementById(`${modalId}-chart`);
|
|
32073
32081
|
const canvas = document.getElementById(`${modalId}-canvas`);
|
|
32074
32082
|
if (!chartContainer || !canvas) return;
|
|
32075
|
-
const hasData =
|
|
32083
|
+
const hasData = state6.deviceData.some((dd) => dd.data.length > 0);
|
|
32076
32084
|
if (!hasData) return;
|
|
32077
32085
|
const ctx = canvas.getContext("2d");
|
|
32078
32086
|
if (!ctx) return;
|
|
32079
|
-
const colors = getThemeColors(
|
|
32087
|
+
const colors = getThemeColors(state6.theme);
|
|
32080
32088
|
const width = chartContainer.clientWidth - 2;
|
|
32081
32089
|
const height = 380;
|
|
32082
32090
|
canvas.width = width;
|
|
@@ -32087,19 +32095,19 @@ function drawComparisonChart(modalId, state5) {
|
|
|
32087
32095
|
const paddingBottom = 55;
|
|
32088
32096
|
ctx.clearRect(0, 0, width, height);
|
|
32089
32097
|
const processedData = [];
|
|
32090
|
-
|
|
32098
|
+
state6.deviceData.forEach((dd) => {
|
|
32091
32099
|
if (dd.data.length === 0) return;
|
|
32092
|
-
const filteredData = filterByDayPeriods(dd.data,
|
|
32100
|
+
const filteredData = filterByDayPeriods(dd.data, state6.selectedPeriods);
|
|
32093
32101
|
if (filteredData.length === 0) return;
|
|
32094
32102
|
let points;
|
|
32095
|
-
if (
|
|
32103
|
+
if (state6.granularity === "hour") {
|
|
32096
32104
|
const interpolated = interpolateTemperature(filteredData, {
|
|
32097
32105
|
intervalMinutes: 30,
|
|
32098
|
-
startTs:
|
|
32099
|
-
endTs:
|
|
32100
|
-
clampRange:
|
|
32106
|
+
startTs: state6.startTs,
|
|
32107
|
+
endTs: state6.endTs,
|
|
32108
|
+
clampRange: state6.clampRange
|
|
32101
32109
|
});
|
|
32102
|
-
const filteredInterpolated = filterByDayPeriods(interpolated,
|
|
32110
|
+
const filteredInterpolated = filterByDayPeriods(interpolated, state6.selectedPeriods);
|
|
32103
32111
|
points = filteredInterpolated.map((item) => ({
|
|
32104
32112
|
x: item.ts,
|
|
32105
32113
|
y: Number(item.value),
|
|
@@ -32109,7 +32117,7 @@ function drawComparisonChart(modalId, state5) {
|
|
|
32109
32117
|
deviceColor: dd.color
|
|
32110
32118
|
}));
|
|
32111
32119
|
} else {
|
|
32112
|
-
const daily = aggregateByDay(filteredData,
|
|
32120
|
+
const daily = aggregateByDay(filteredData, state6.clampRange);
|
|
32113
32121
|
points = daily.map((item) => ({
|
|
32114
32122
|
x: item.dateTs,
|
|
32115
32123
|
y: item.avg,
|
|
@@ -32130,7 +32138,7 @@ function drawComparisonChart(modalId, state5) {
|
|
|
32130
32138
|
ctx.fillText("Nenhum dado para os per\xEDodos selecionados", width / 2, height / 2);
|
|
32131
32139
|
return;
|
|
32132
32140
|
}
|
|
32133
|
-
const isPeriodsFiltered =
|
|
32141
|
+
const isPeriodsFiltered = state6.selectedPeriods.length < 4 && state6.selectedPeriods.length > 0;
|
|
32134
32142
|
let dataMinY = Infinity;
|
|
32135
32143
|
let dataMaxY = -Infinity;
|
|
32136
32144
|
processedData.forEach(({ points }) => {
|
|
@@ -32140,7 +32148,7 @@ function drawComparisonChart(modalId, state5) {
|
|
|
32140
32148
|
});
|
|
32141
32149
|
});
|
|
32142
32150
|
const rangeMap = /* @__PURE__ */ new Map();
|
|
32143
|
-
|
|
32151
|
+
state6.deviceData.forEach((dd, index) => {
|
|
32144
32152
|
const device = dd.device;
|
|
32145
32153
|
const min = device.temperatureMin;
|
|
32146
32154
|
const max = device.temperatureMax;
|
|
@@ -32159,10 +32167,10 @@ function drawComparisonChart(modalId, state5) {
|
|
|
32159
32167
|
}
|
|
32160
32168
|
}
|
|
32161
32169
|
});
|
|
32162
|
-
if (rangeMap.size === 0 &&
|
|
32170
|
+
if (rangeMap.size === 0 && state6.temperatureMin !== null && state6.temperatureMax !== null) {
|
|
32163
32171
|
rangeMap.set("global", {
|
|
32164
|
-
min:
|
|
32165
|
-
max:
|
|
32172
|
+
min: state6.temperatureMin,
|
|
32173
|
+
max: state6.temperatureMax,
|
|
32166
32174
|
customerName: "Global",
|
|
32167
32175
|
color: colors.success,
|
|
32168
32176
|
deviceLabels: []
|
|
@@ -32283,10 +32291,10 @@ function drawComparisonChart(modalId, state5) {
|
|
|
32283
32291
|
const point = xAxisPoints[i];
|
|
32284
32292
|
const date = new Date(point.x);
|
|
32285
32293
|
let label;
|
|
32286
|
-
if (
|
|
32287
|
-
label = date.toLocaleTimeString(
|
|
32294
|
+
if (state6.granularity === "hour") {
|
|
32295
|
+
label = date.toLocaleTimeString(state6.locale, { hour: "2-digit", minute: "2-digit" });
|
|
32288
32296
|
} else {
|
|
32289
|
-
label = date.toLocaleDateString(
|
|
32297
|
+
label = date.toLocaleDateString(state6.locale, { day: "2-digit", month: "2-digit" });
|
|
32290
32298
|
}
|
|
32291
32299
|
ctx.strokeStyle = colors.chartGrid;
|
|
32292
32300
|
ctx.lineWidth = 1;
|
|
@@ -32305,16 +32313,16 @@ function drawComparisonChart(modalId, state5) {
|
|
|
32305
32313
|
ctx.lineTo(width - paddingRight, height - paddingBottom);
|
|
32306
32314
|
ctx.stroke();
|
|
32307
32315
|
const allChartPoints = processedData.flatMap((pd) => pd.points);
|
|
32308
|
-
setupComparisonChartTooltip(canvas, chartContainer, allChartPoints,
|
|
32316
|
+
setupComparisonChartTooltip(canvas, chartContainer, allChartPoints, state6, colors);
|
|
32309
32317
|
}
|
|
32310
|
-
function setupComparisonChartTooltip(canvas, container, chartData,
|
|
32318
|
+
function setupComparisonChartTooltip(canvas, container, chartData, state6, colors) {
|
|
32311
32319
|
const existingTooltip = container.querySelector(".myio-chart-tooltip");
|
|
32312
32320
|
if (existingTooltip) existingTooltip.remove();
|
|
32313
32321
|
const tooltip = document.createElement("div");
|
|
32314
32322
|
tooltip.className = "myio-chart-tooltip";
|
|
32315
32323
|
tooltip.style.cssText = `
|
|
32316
32324
|
position: absolute;
|
|
32317
|
-
background: ${
|
|
32325
|
+
background: ${state6.theme === "dark" ? "rgba(30, 30, 40, 0.95)" : "rgba(255, 255, 255, 0.98)"};
|
|
32318
32326
|
border: 1px solid ${colors.border};
|
|
32319
32327
|
border-radius: 8px;
|
|
32320
32328
|
padding: 10px 14px;
|
|
@@ -32351,17 +32359,17 @@ function setupComparisonChartTooltip(canvas, container, chartData, state5, color
|
|
|
32351
32359
|
if (point) {
|
|
32352
32360
|
const date = new Date(point.x);
|
|
32353
32361
|
let dateStr;
|
|
32354
|
-
if (
|
|
32355
|
-
dateStr = date.toLocaleDateString(
|
|
32362
|
+
if (state6.granularity === "hour") {
|
|
32363
|
+
dateStr = date.toLocaleDateString(state6.locale, {
|
|
32356
32364
|
day: "2-digit",
|
|
32357
32365
|
month: "2-digit",
|
|
32358
32366
|
year: "numeric"
|
|
32359
|
-
}) + " " + date.toLocaleTimeString(
|
|
32367
|
+
}) + " " + date.toLocaleTimeString(state6.locale, {
|
|
32360
32368
|
hour: "2-digit",
|
|
32361
32369
|
minute: "2-digit"
|
|
32362
32370
|
});
|
|
32363
32371
|
} else {
|
|
32364
|
-
dateStr = date.toLocaleDateString(
|
|
32372
|
+
dateStr = date.toLocaleDateString(state6.locale, {
|
|
32365
32373
|
day: "2-digit",
|
|
32366
32374
|
month: "2-digit",
|
|
32367
32375
|
year: "numeric"
|
|
@@ -32403,7 +32411,7 @@ function setupComparisonChartTooltip(canvas, container, chartData, state5, color
|
|
|
32403
32411
|
canvas.style.cursor = "default";
|
|
32404
32412
|
});
|
|
32405
32413
|
}
|
|
32406
|
-
async function setupEventListeners2(container,
|
|
32414
|
+
async function setupEventListeners2(container, state6, modalId, onClose) {
|
|
32407
32415
|
const closeModal = () => {
|
|
32408
32416
|
container.remove();
|
|
32409
32417
|
onClose?.();
|
|
@@ -32414,19 +32422,19 @@ async function setupEventListeners2(container, state5, modalId, onClose) {
|
|
|
32414
32422
|
document.getElementById(`${modalId}-close`)?.addEventListener("click", closeModal);
|
|
32415
32423
|
document.getElementById(`${modalId}-close-btn`)?.addEventListener("click", closeModal);
|
|
32416
32424
|
const dateRangeInput = document.getElementById(`${modalId}-date-range`);
|
|
32417
|
-
if (dateRangeInput && !
|
|
32425
|
+
if (dateRangeInput && !state6.dateRangePicker) {
|
|
32418
32426
|
try {
|
|
32419
|
-
|
|
32420
|
-
presetStart: new Date(
|
|
32421
|
-
presetEnd: new Date(
|
|
32427
|
+
state6.dateRangePicker = await createDateRangePicker2(dateRangeInput, {
|
|
32428
|
+
presetStart: new Date(state6.startTs).toISOString(),
|
|
32429
|
+
presetEnd: new Date(state6.endTs).toISOString(),
|
|
32422
32430
|
includeTime: true,
|
|
32423
32431
|
timePrecision: "minute",
|
|
32424
32432
|
maxRangeDays: 90,
|
|
32425
|
-
locale:
|
|
32433
|
+
locale: state6.locale,
|
|
32426
32434
|
parentEl: container.querySelector(".myio-temp-comparison-content"),
|
|
32427
32435
|
onApply: (result) => {
|
|
32428
|
-
|
|
32429
|
-
|
|
32436
|
+
state6.startTs = new Date(result.startISO).getTime();
|
|
32437
|
+
state6.endTs = new Date(result.endISO).getTime();
|
|
32430
32438
|
console.log("[TemperatureComparisonModal] Date range applied:", result);
|
|
32431
32439
|
}
|
|
32432
32440
|
});
|
|
@@ -32435,23 +32443,23 @@ async function setupEventListeners2(container, state5, modalId, onClose) {
|
|
|
32435
32443
|
}
|
|
32436
32444
|
}
|
|
32437
32445
|
document.getElementById(`${modalId}-theme-toggle`)?.addEventListener("click", async () => {
|
|
32438
|
-
|
|
32439
|
-
localStorage.setItem("myio-temp-comparison-theme",
|
|
32440
|
-
|
|
32441
|
-
renderModal2(container,
|
|
32442
|
-
if (
|
|
32443
|
-
drawComparisonChart(modalId,
|
|
32444
|
-
}
|
|
32445
|
-
await setupEventListeners2(container,
|
|
32446
|
+
state6.theme = state6.theme === "dark" ? "light" : "dark";
|
|
32447
|
+
localStorage.setItem("myio-temp-comparison-theme", state6.theme);
|
|
32448
|
+
state6.dateRangePicker = null;
|
|
32449
|
+
renderModal2(container, state6, modalId);
|
|
32450
|
+
if (state6.deviceData.some((dd) => dd.data.length > 0)) {
|
|
32451
|
+
drawComparisonChart(modalId, state6);
|
|
32452
|
+
}
|
|
32453
|
+
await setupEventListeners2(container, state6, modalId, onClose);
|
|
32446
32454
|
});
|
|
32447
32455
|
document.getElementById(`${modalId}-maximize`)?.addEventListener("click", async () => {
|
|
32448
32456
|
container.__isMaximized = !container.__isMaximized;
|
|
32449
|
-
|
|
32450
|
-
renderModal2(container,
|
|
32451
|
-
if (
|
|
32452
|
-
drawComparisonChart(modalId,
|
|
32457
|
+
state6.dateRangePicker = null;
|
|
32458
|
+
renderModal2(container, state6, modalId);
|
|
32459
|
+
if (state6.deviceData.some((dd) => dd.data.length > 0)) {
|
|
32460
|
+
drawComparisonChart(modalId, state6);
|
|
32453
32461
|
}
|
|
32454
|
-
await setupEventListeners2(container,
|
|
32462
|
+
await setupEventListeners2(container, state6, modalId, onClose);
|
|
32455
32463
|
});
|
|
32456
32464
|
const periodBtn = document.getElementById(`${modalId}-period-btn`);
|
|
32457
32465
|
const periodDropdown = document.getElementById(`${modalId}-period-dropdown`);
|
|
@@ -32470,13 +32478,13 @@ async function setupEventListeners2(container, state5, modalId, onClose) {
|
|
|
32470
32478
|
periodCheckboxes.forEach((checkbox) => {
|
|
32471
32479
|
checkbox.addEventListener("change", () => {
|
|
32472
32480
|
const checked = Array.from(periodCheckboxes).filter((cb) => cb.checked).map((cb) => cb.value);
|
|
32473
|
-
|
|
32481
|
+
state6.selectedPeriods = checked;
|
|
32474
32482
|
const btnLabel = periodBtn?.querySelector("span:first-child");
|
|
32475
32483
|
if (btnLabel) {
|
|
32476
|
-
btnLabel.textContent = getSelectedPeriodsLabel(
|
|
32484
|
+
btnLabel.textContent = getSelectedPeriodsLabel(state6.selectedPeriods);
|
|
32477
32485
|
}
|
|
32478
|
-
if (
|
|
32479
|
-
drawComparisonChart(modalId,
|
|
32486
|
+
if (state6.deviceData.some((dd) => dd.data.length > 0)) {
|
|
32487
|
+
drawComparisonChart(modalId, state6);
|
|
32480
32488
|
}
|
|
32481
32489
|
});
|
|
32482
32490
|
});
|
|
@@ -32484,77 +32492,77 @@ async function setupEventListeners2(container, state5, modalId, onClose) {
|
|
|
32484
32492
|
periodCheckboxes.forEach((cb) => {
|
|
32485
32493
|
cb.checked = true;
|
|
32486
32494
|
});
|
|
32487
|
-
|
|
32495
|
+
state6.selectedPeriods = ["madrugada", "manha", "tarde", "noite"];
|
|
32488
32496
|
const btnLabel = periodBtn?.querySelector("span:first-child");
|
|
32489
32497
|
if (btnLabel) {
|
|
32490
|
-
btnLabel.textContent = getSelectedPeriodsLabel(
|
|
32498
|
+
btnLabel.textContent = getSelectedPeriodsLabel(state6.selectedPeriods);
|
|
32491
32499
|
}
|
|
32492
|
-
if (
|
|
32493
|
-
drawComparisonChart(modalId,
|
|
32500
|
+
if (state6.deviceData.some((dd) => dd.data.length > 0)) {
|
|
32501
|
+
drawComparisonChart(modalId, state6);
|
|
32494
32502
|
}
|
|
32495
32503
|
});
|
|
32496
32504
|
document.getElementById(`${modalId}-period-clear`)?.addEventListener("click", () => {
|
|
32497
32505
|
periodCheckboxes.forEach((cb) => {
|
|
32498
32506
|
cb.checked = false;
|
|
32499
32507
|
});
|
|
32500
|
-
|
|
32508
|
+
state6.selectedPeriods = [];
|
|
32501
32509
|
const btnLabel = periodBtn?.querySelector("span:first-child");
|
|
32502
32510
|
if (btnLabel) {
|
|
32503
|
-
btnLabel.textContent = getSelectedPeriodsLabel(
|
|
32511
|
+
btnLabel.textContent = getSelectedPeriodsLabel(state6.selectedPeriods);
|
|
32504
32512
|
}
|
|
32505
|
-
if (
|
|
32506
|
-
drawComparisonChart(modalId,
|
|
32513
|
+
if (state6.deviceData.some((dd) => dd.data.length > 0)) {
|
|
32514
|
+
drawComparisonChart(modalId, state6);
|
|
32507
32515
|
}
|
|
32508
32516
|
});
|
|
32509
32517
|
document.getElementById(`${modalId}-granularity`)?.addEventListener("change", (e) => {
|
|
32510
|
-
|
|
32511
|
-
localStorage.setItem("myio-temp-comparison-granularity",
|
|
32512
|
-
if (
|
|
32513
|
-
drawComparisonChart(modalId,
|
|
32518
|
+
state6.granularity = e.target.value;
|
|
32519
|
+
localStorage.setItem("myio-temp-comparison-granularity", state6.granularity);
|
|
32520
|
+
if (state6.deviceData.some((dd) => dd.data.length > 0)) {
|
|
32521
|
+
drawComparisonChart(modalId, state6);
|
|
32514
32522
|
}
|
|
32515
32523
|
});
|
|
32516
32524
|
document.getElementById(`${modalId}-query`)?.addEventListener("click", async () => {
|
|
32517
|
-
if (
|
|
32525
|
+
if (state6.startTs >= state6.endTs) {
|
|
32518
32526
|
alert("Por favor, selecione um per\xEDodo v\xE1lido");
|
|
32519
32527
|
return;
|
|
32520
32528
|
}
|
|
32521
|
-
|
|
32522
|
-
|
|
32523
|
-
renderModal2(container,
|
|
32524
|
-
await fetchAllDevicesData(
|
|
32525
|
-
renderModal2(container,
|
|
32526
|
-
drawComparisonChart(modalId,
|
|
32527
|
-
await setupEventListeners2(container,
|
|
32529
|
+
state6.isLoading = true;
|
|
32530
|
+
state6.dateRangePicker = null;
|
|
32531
|
+
renderModal2(container, state6, modalId);
|
|
32532
|
+
await fetchAllDevicesData(state6);
|
|
32533
|
+
renderModal2(container, state6, modalId);
|
|
32534
|
+
drawComparisonChart(modalId, state6);
|
|
32535
|
+
await setupEventListeners2(container, state6, modalId, onClose);
|
|
32528
32536
|
});
|
|
32529
32537
|
document.getElementById(`${modalId}-export`)?.addEventListener("click", () => {
|
|
32530
|
-
if (
|
|
32531
|
-
exportComparisonCSV(
|
|
32538
|
+
if (state6.deviceData.every((dd) => dd.data.length === 0)) return;
|
|
32539
|
+
exportComparisonCSV(state6);
|
|
32532
32540
|
});
|
|
32533
32541
|
}
|
|
32534
|
-
function exportComparisonCSV(
|
|
32535
|
-
const startDateStr = new Date(
|
|
32536
|
-
const endDateStr = new Date(
|
|
32542
|
+
function exportComparisonCSV(state6) {
|
|
32543
|
+
const startDateStr = new Date(state6.startTs).toLocaleDateString(state6.locale).replace(/\//g, "-");
|
|
32544
|
+
const endDateStr = new Date(state6.endTs).toLocaleDateString(state6.locale).replace(/\//g, "-");
|
|
32537
32545
|
const BOM = "\uFEFF";
|
|
32538
32546
|
let csvContent = BOM;
|
|
32539
32547
|
csvContent += `Compara\xE7\xE3o de Temperatura
|
|
32540
32548
|
`;
|
|
32541
32549
|
csvContent += `Per\xEDodo: ${startDateStr} at\xE9 ${endDateStr}
|
|
32542
32550
|
`;
|
|
32543
|
-
csvContent += `Sensores: ${
|
|
32551
|
+
csvContent += `Sensores: ${state6.devices.map((d) => d.label).join(", ")}
|
|
32544
32552
|
`;
|
|
32545
32553
|
csvContent += "\n";
|
|
32546
32554
|
csvContent += "Estat\xEDsticas por Sensor:\n";
|
|
32547
32555
|
csvContent += "Sensor,M\xE9dia (\xB0C),Min (\xB0C),Max (\xB0C),Leituras\n";
|
|
32548
|
-
|
|
32556
|
+
state6.deviceData.forEach((dd) => {
|
|
32549
32557
|
csvContent += `"${dd.device.label}",${dd.stats.avg.toFixed(2)},${dd.stats.min.toFixed(2)},${dd.stats.max.toFixed(2)},${dd.stats.count}
|
|
32550
32558
|
`;
|
|
32551
32559
|
});
|
|
32552
32560
|
csvContent += "\n";
|
|
32553
32561
|
csvContent += "Dados Detalhados:\n";
|
|
32554
32562
|
csvContent += "Data/Hora,Sensor,Temperatura (\xB0C)\n";
|
|
32555
|
-
|
|
32563
|
+
state6.deviceData.forEach((dd) => {
|
|
32556
32564
|
dd.data.forEach((item) => {
|
|
32557
|
-
const date = new Date(item.ts).toLocaleString(
|
|
32565
|
+
const date = new Date(item.ts).toLocaleString(state6.locale);
|
|
32558
32566
|
const temp = Number(item.value).toFixed(2);
|
|
32559
32567
|
csvContent += `"${date}","${dd.device.label}",${temp}
|
|
32560
32568
|
`;
|
|
@@ -32662,10 +32670,10 @@ async function saveCustomerAttributes(customerId, token, minTemperature, maxTemp
|
|
|
32662
32670
|
throw new Error(`Failed to save attributes: ${response.status}`);
|
|
32663
32671
|
}
|
|
32664
32672
|
}
|
|
32665
|
-
function renderModal3(container,
|
|
32666
|
-
const colors = getColors(
|
|
32667
|
-
const minValue =
|
|
32668
|
-
const maxValue =
|
|
32673
|
+
function renderModal3(container, state6, modalId, onClose, onSave) {
|
|
32674
|
+
const colors = getColors(state6.theme);
|
|
32675
|
+
const minValue = state6.minTemperature !== null ? state6.minTemperature : "";
|
|
32676
|
+
const maxValue = state6.maxTemperature !== null ? state6.maxTemperature : "";
|
|
32669
32677
|
container.innerHTML = `
|
|
32670
32678
|
<style>
|
|
32671
32679
|
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
|
|
@@ -32930,23 +32938,23 @@ function renderModal3(container, state5, modalId, onClose, onSave) {
|
|
|
32930
32938
|
</div>
|
|
32931
32939
|
|
|
32932
32940
|
<div class="modal-body">
|
|
32933
|
-
${
|
|
32941
|
+
${state6.isLoading ? `
|
|
32934
32942
|
<div class="loading-overlay">
|
|
32935
32943
|
<div class="loading-spinner"></div>
|
|
32936
32944
|
<div>Carregando configura\xE7\xF5es...</div>
|
|
32937
32945
|
</div>
|
|
32938
32946
|
` : `
|
|
32939
|
-
${
|
|
32940
|
-
<div class="message message-error">${
|
|
32947
|
+
${state6.error ? `
|
|
32948
|
+
<div class="message message-error">${state6.error}</div>
|
|
32941
32949
|
` : ""}
|
|
32942
32950
|
|
|
32943
|
-
${
|
|
32944
|
-
<div class="message message-success">${
|
|
32951
|
+
${state6.successMessage ? `
|
|
32952
|
+
<div class="message message-success">${state6.successMessage}</div>
|
|
32945
32953
|
` : ""}
|
|
32946
32954
|
|
|
32947
32955
|
<div class="customer-info">
|
|
32948
32956
|
<div class="customer-label">Shopping / Cliente</div>
|
|
32949
|
-
<div class="customer-name">${
|
|
32957
|
+
<div class="customer-name">${state6.customerName || "N\xE3o identificado"}</div>
|
|
32950
32958
|
</div>
|
|
32951
32959
|
|
|
32952
32960
|
<div class="form-group">
|
|
@@ -32990,11 +32998,11 @@ function renderModal3(container, state5, modalId, onClose, onSave) {
|
|
|
32990
32998
|
`}
|
|
32991
32999
|
</div>
|
|
32992
33000
|
|
|
32993
|
-
${!
|
|
33001
|
+
${!state6.isLoading ? `
|
|
32994
33002
|
<div class="modal-footer">
|
|
32995
33003
|
<button class="btn btn-secondary" id="${modalId}-cancel">Cancelar</button>
|
|
32996
|
-
<button class="btn btn-primary" id="${modalId}-save" ${
|
|
32997
|
-
${
|
|
33004
|
+
<button class="btn btn-primary" id="${modalId}-save" ${state6.isSaving ? "disabled" : ""}>
|
|
33005
|
+
${state6.isSaving ? '<div class="spinner"></div> Salvando...' : "Salvar"}
|
|
32998
33006
|
</button>
|
|
32999
33007
|
</div>
|
|
33000
33008
|
` : ""}
|
|
@@ -33031,18 +33039,18 @@ function renderModal3(container, state5, modalId, onClose, onSave) {
|
|
|
33031
33039
|
const min = parseFloat(minInput.value);
|
|
33032
33040
|
const max = parseFloat(maxInput.value);
|
|
33033
33041
|
if (isNaN(min) || isNaN(max)) {
|
|
33034
|
-
|
|
33035
|
-
renderModal3(container,
|
|
33042
|
+
state6.error = "Por favor, preencha ambos os valores.";
|
|
33043
|
+
renderModal3(container, state6, modalId, onClose, onSave);
|
|
33036
33044
|
return;
|
|
33037
33045
|
}
|
|
33038
33046
|
if (min >= max) {
|
|
33039
|
-
|
|
33040
|
-
renderModal3(container,
|
|
33047
|
+
state6.error = "A temperatura m\xEDnima deve ser menor que a m\xE1xima.";
|
|
33048
|
+
renderModal3(container, state6, modalId, onClose, onSave);
|
|
33041
33049
|
return;
|
|
33042
33050
|
}
|
|
33043
33051
|
if (min < 0 || max > 50) {
|
|
33044
|
-
|
|
33045
|
-
renderModal3(container,
|
|
33052
|
+
state6.error = "Os valores devem estar entre 0\xB0C e 50\xB0C.";
|
|
33053
|
+
renderModal3(container, state6, modalId, onClose, onSave);
|
|
33046
33054
|
return;
|
|
33047
33055
|
}
|
|
33048
33056
|
await onSave(min, max);
|
|
@@ -33050,7 +33058,7 @@ function renderModal3(container, state5, modalId, onClose, onSave) {
|
|
|
33050
33058
|
}
|
|
33051
33059
|
function openTemperatureSettingsModal(params) {
|
|
33052
33060
|
const modalId = `myio-temp-settings-${Date.now()}`;
|
|
33053
|
-
const
|
|
33061
|
+
const state6 = {
|
|
33054
33062
|
customerId: params.customerId,
|
|
33055
33063
|
customerName: params.customerName || "",
|
|
33056
33064
|
token: params.token,
|
|
@@ -33070,37 +33078,37 @@ function openTemperatureSettingsModal(params) {
|
|
|
33070
33078
|
params.onClose?.();
|
|
33071
33079
|
};
|
|
33072
33080
|
const handleSave = async (min, max) => {
|
|
33073
|
-
|
|
33074
|
-
|
|
33075
|
-
|
|
33076
|
-
renderModal3(container,
|
|
33081
|
+
state6.isSaving = true;
|
|
33082
|
+
state6.error = null;
|
|
33083
|
+
state6.successMessage = null;
|
|
33084
|
+
renderModal3(container, state6, modalId, destroy, handleSave);
|
|
33077
33085
|
try {
|
|
33078
|
-
await saveCustomerAttributes(
|
|
33079
|
-
|
|
33080
|
-
|
|
33081
|
-
|
|
33082
|
-
|
|
33083
|
-
renderModal3(container,
|
|
33086
|
+
await saveCustomerAttributes(state6.customerId, state6.token, min, max, params.onError);
|
|
33087
|
+
state6.minTemperature = min;
|
|
33088
|
+
state6.maxTemperature = max;
|
|
33089
|
+
state6.isSaving = false;
|
|
33090
|
+
state6.successMessage = "Configura\xE7\xF5es salvas com sucesso!";
|
|
33091
|
+
renderModal3(container, state6, modalId, destroy, handleSave);
|
|
33084
33092
|
params.onSave?.({ minTemperature: min, maxTemperature: max });
|
|
33085
33093
|
setTimeout(() => {
|
|
33086
33094
|
destroy();
|
|
33087
33095
|
}, 1500);
|
|
33088
33096
|
} catch (error) {
|
|
33089
|
-
|
|
33090
|
-
|
|
33091
|
-
renderModal3(container,
|
|
33097
|
+
state6.isSaving = false;
|
|
33098
|
+
state6.error = `Erro ao salvar: ${error.message}`;
|
|
33099
|
+
renderModal3(container, state6, modalId, destroy, handleSave);
|
|
33092
33100
|
}
|
|
33093
33101
|
};
|
|
33094
|
-
renderModal3(container,
|
|
33095
|
-
fetchCustomerAttributes(
|
|
33096
|
-
|
|
33097
|
-
|
|
33098
|
-
|
|
33099
|
-
renderModal3(container,
|
|
33102
|
+
renderModal3(container, state6, modalId, destroy, handleSave);
|
|
33103
|
+
fetchCustomerAttributes(state6.customerId, state6.token, params.onError).then(({ minTemperature, maxTemperature }) => {
|
|
33104
|
+
state6.minTemperature = minTemperature;
|
|
33105
|
+
state6.maxTemperature = maxTemperature;
|
|
33106
|
+
state6.isLoading = false;
|
|
33107
|
+
renderModal3(container, state6, modalId, destroy, handleSave);
|
|
33100
33108
|
}).catch((error) => {
|
|
33101
|
-
|
|
33102
|
-
|
|
33103
|
-
renderModal3(container,
|
|
33109
|
+
state6.isLoading = false;
|
|
33110
|
+
state6.error = `Erro ao carregar: ${error.message}`;
|
|
33111
|
+
renderModal3(container, state6, modalId, destroy, handleSave);
|
|
33104
33112
|
});
|
|
33105
33113
|
return { destroy };
|
|
33106
33114
|
}
|
|
@@ -34370,7 +34378,7 @@ var EnergySummaryTooltip = {
|
|
|
34370
34378
|
* RFC-0105 Enhancement: Now fetches device lists from MyIOOrchestratorData
|
|
34371
34379
|
* to populate device lists for status popup display
|
|
34372
34380
|
*/
|
|
34373
|
-
buildSummaryFromState(
|
|
34381
|
+
buildSummaryFromState(state6, receivedData, domain = "energy") {
|
|
34374
34382
|
const summary = {
|
|
34375
34383
|
totalDevices: 0,
|
|
34376
34384
|
totalConsumption: 0,
|
|
@@ -34393,22 +34401,22 @@ var EnergySummaryTooltip = {
|
|
|
34393
34401
|
},
|
|
34394
34402
|
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
34395
34403
|
};
|
|
34396
|
-
if (!
|
|
34404
|
+
if (!state6) return summary;
|
|
34397
34405
|
const entrada = {
|
|
34398
34406
|
id: "entrada",
|
|
34399
34407
|
name: "Entrada",
|
|
34400
34408
|
icon: CATEGORY_ICONS.entrada,
|
|
34401
|
-
deviceCount:
|
|
34402
|
-
consumption:
|
|
34409
|
+
deviceCount: state6.entrada?.devices?.length || (receivedData?.entrada_total?.device_count || 0),
|
|
34410
|
+
consumption: state6.entrada?.total || 0,
|
|
34403
34411
|
percentage: 100
|
|
34404
34412
|
};
|
|
34405
34413
|
const lojas = {
|
|
34406
34414
|
id: "lojas",
|
|
34407
34415
|
name: "Lojas",
|
|
34408
34416
|
icon: CATEGORY_ICONS.lojas,
|
|
34409
|
-
deviceCount:
|
|
34410
|
-
consumption:
|
|
34411
|
-
percentage:
|
|
34417
|
+
deviceCount: state6.consumidores?.lojas?.devices?.length || (receivedData?.lojas_total?.device_count || 0),
|
|
34418
|
+
consumption: state6.consumidores?.lojas?.total || 0,
|
|
34419
|
+
percentage: state6.consumidores?.lojas?.perc || 0
|
|
34412
34420
|
};
|
|
34413
34421
|
const climatizacaoData = receivedData?.climatizacao || {};
|
|
34414
34422
|
const elevadoresData = receivedData?.elevadores || {};
|
|
@@ -34419,9 +34427,9 @@ var EnergySummaryTooltip = {
|
|
|
34419
34427
|
id: "climatizacao",
|
|
34420
34428
|
name: "Climatizacao",
|
|
34421
34429
|
icon: CATEGORY_ICONS.climatizacao,
|
|
34422
|
-
deviceCount: climatizacaoData.count ||
|
|
34423
|
-
consumption:
|
|
34424
|
-
percentage:
|
|
34430
|
+
deviceCount: climatizacaoData.count || state6.consumidores?.climatizacao?.devices?.length || 0,
|
|
34431
|
+
consumption: state6.consumidores?.climatizacao?.total || 0,
|
|
34432
|
+
percentage: state6.consumidores?.climatizacao?.perc || 0
|
|
34425
34433
|
};
|
|
34426
34434
|
if (climatizacaoData.subcategories) {
|
|
34427
34435
|
climatizacao.children = [];
|
|
@@ -34472,40 +34480,40 @@ var EnergySummaryTooltip = {
|
|
|
34472
34480
|
id: "elevadores",
|
|
34473
34481
|
name: "Elevadores",
|
|
34474
34482
|
icon: CATEGORY_ICONS.elevadores,
|
|
34475
|
-
deviceCount: elevadoresData.count ||
|
|
34476
|
-
consumption:
|
|
34477
|
-
percentage:
|
|
34483
|
+
deviceCount: elevadoresData.count || state6.consumidores?.elevadores?.devices?.length || 0,
|
|
34484
|
+
consumption: state6.consumidores?.elevadores?.total || 0,
|
|
34485
|
+
percentage: state6.consumidores?.elevadores?.perc || 0
|
|
34478
34486
|
});
|
|
34479
34487
|
areaComumChildren.push({
|
|
34480
34488
|
id: "escadasRolantes",
|
|
34481
34489
|
name: "Esc. Rolantes",
|
|
34482
34490
|
icon: CATEGORY_ICONS.escadas,
|
|
34483
|
-
deviceCount: escadasData.count ||
|
|
34484
|
-
consumption:
|
|
34485
|
-
percentage:
|
|
34491
|
+
deviceCount: escadasData.count || state6.consumidores?.escadasRolantes?.devices?.length || 0,
|
|
34492
|
+
consumption: state6.consumidores?.escadasRolantes?.total || 0,
|
|
34493
|
+
percentage: state6.consumidores?.escadasRolantes?.perc || 0
|
|
34486
34494
|
});
|
|
34487
34495
|
areaComumChildren.push({
|
|
34488
34496
|
id: "outros",
|
|
34489
34497
|
name: "Outros",
|
|
34490
34498
|
icon: CATEGORY_ICONS.outros,
|
|
34491
|
-
deviceCount: outrosData.count ||
|
|
34492
|
-
consumption:
|
|
34493
|
-
percentage:
|
|
34499
|
+
deviceCount: outrosData.count || state6.consumidores?.outros?.devices?.length || 0,
|
|
34500
|
+
consumption: state6.consumidores?.outros?.total || 0,
|
|
34501
|
+
percentage: state6.consumidores?.outros?.perc || 0
|
|
34494
34502
|
});
|
|
34495
34503
|
const areaComumDeviceCount = areaComumChildren.reduce((sum, c) => sum + c.deviceCount, 0);
|
|
34496
|
-
const areaComumConsumption =
|
|
34504
|
+
const areaComumConsumption = state6.consumidores?.areaComum?.total || areaComumChildren.reduce((sum, c) => sum + c.consumption, 0);
|
|
34497
34505
|
const areaComum = {
|
|
34498
34506
|
id: "areaComum",
|
|
34499
34507
|
name: "Area Comum",
|
|
34500
34508
|
icon: CATEGORY_ICONS.areaComum,
|
|
34501
34509
|
deviceCount: areaComumDeviceCount,
|
|
34502
34510
|
consumption: areaComumConsumption,
|
|
34503
|
-
percentage:
|
|
34511
|
+
percentage: state6.consumidores?.areaComum?.perc || 0,
|
|
34504
34512
|
children: areaComumChildren
|
|
34505
34513
|
};
|
|
34506
34514
|
summary.byCategory = [entrada, lojas, areaComum];
|
|
34507
34515
|
summary.totalDevices = entrada.deviceCount + lojas.deviceCount + areaComumDeviceCount;
|
|
34508
|
-
summary.totalConsumption =
|
|
34516
|
+
summary.totalConsumption = state6.grandTotal || entrada.consumption;
|
|
34509
34517
|
const widgetAggregation = receivedData?.deviceStatusAggregation;
|
|
34510
34518
|
if (widgetAggregation && widgetAggregation.hasData) {
|
|
34511
34519
|
summary.byStatus = {
|
|
@@ -35793,7 +35801,7 @@ var WaterSummaryTooltip = {
|
|
|
35793
35801
|
* RFC-0105 Enhancement: Now fetches device lists from MyIOOrchestratorData
|
|
35794
35802
|
* to populate device lists for status popup display
|
|
35795
35803
|
*/
|
|
35796
|
-
buildSummaryFromState(
|
|
35804
|
+
buildSummaryFromState(state6, receivedData, includeBathrooms = false, domain = "water") {
|
|
35797
35805
|
const summary = {
|
|
35798
35806
|
totalDevices: 0,
|
|
35799
35807
|
totalConsumption: 0,
|
|
@@ -35817,22 +35825,22 @@ var WaterSummaryTooltip = {
|
|
|
35817
35825
|
lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
|
|
35818
35826
|
includeBathrooms
|
|
35819
35827
|
};
|
|
35820
|
-
if (!
|
|
35828
|
+
if (!state6) return summary;
|
|
35821
35829
|
const entrada = {
|
|
35822
35830
|
id: "entrada",
|
|
35823
35831
|
name: "Entrada",
|
|
35824
35832
|
icon: WATER_CATEGORY_ICONS.entrada,
|
|
35825
|
-
deviceCount:
|
|
35826
|
-
consumption:
|
|
35833
|
+
deviceCount: state6.entrada?.devices?.length || (receivedData?.entrada_total?.device_count || 0),
|
|
35834
|
+
consumption: state6.entrada?.total || 0,
|
|
35827
35835
|
percentage: 100
|
|
35828
35836
|
};
|
|
35829
35837
|
const lojas = {
|
|
35830
35838
|
id: "lojas",
|
|
35831
35839
|
name: "Lojas",
|
|
35832
35840
|
icon: WATER_CATEGORY_ICONS.lojas,
|
|
35833
|
-
deviceCount:
|
|
35834
|
-
consumption:
|
|
35835
|
-
percentage:
|
|
35841
|
+
deviceCount: state6.lojas?.devices?.length || (receivedData?.lojas_total?.device_count || 0),
|
|
35842
|
+
consumption: state6.lojas?.total || 0,
|
|
35843
|
+
percentage: state6.lojas?.perc || 0
|
|
35836
35844
|
};
|
|
35837
35845
|
summary.byCategory = [entrada, lojas];
|
|
35838
35846
|
if (includeBathrooms) {
|
|
@@ -35840,9 +35848,9 @@ var WaterSummaryTooltip = {
|
|
|
35840
35848
|
id: "banheiros",
|
|
35841
35849
|
name: "Banheiros",
|
|
35842
35850
|
icon: WATER_CATEGORY_ICONS.banheiros,
|
|
35843
|
-
deviceCount:
|
|
35844
|
-
consumption:
|
|
35845
|
-
percentage:
|
|
35851
|
+
deviceCount: state6.banheiros?.devices?.length || (receivedData?.banheiros_total?.device_count || 0),
|
|
35852
|
+
consumption: state6.banheiros?.total || 0,
|
|
35853
|
+
percentage: state6.banheiros?.perc || 0
|
|
35846
35854
|
};
|
|
35847
35855
|
summary.byCategory.push(banheiros);
|
|
35848
35856
|
}
|
|
@@ -35850,24 +35858,24 @@ var WaterSummaryTooltip = {
|
|
|
35850
35858
|
id: "areaComum",
|
|
35851
35859
|
name: "\xC1rea Comum",
|
|
35852
35860
|
icon: WATER_CATEGORY_ICONS.areaComum,
|
|
35853
|
-
deviceCount:
|
|
35854
|
-
consumption:
|
|
35855
|
-
percentage:
|
|
35861
|
+
deviceCount: state6.areaComum?.devices?.length || (receivedData?.area_comum_total?.device_count || 0),
|
|
35862
|
+
consumption: state6.areaComum?.total || 0,
|
|
35863
|
+
percentage: state6.areaComum?.perc || 0
|
|
35856
35864
|
};
|
|
35857
35865
|
summary.byCategory.push(areaComum);
|
|
35858
|
-
if (
|
|
35866
|
+
if (state6.pontosNaoMapeados && state6.pontosNaoMapeados.total > 0) {
|
|
35859
35867
|
const pontosNaoMapeados = {
|
|
35860
35868
|
id: "pontosNaoMapeados",
|
|
35861
35869
|
name: "Pontos N\xE3o Mapeados",
|
|
35862
35870
|
icon: WATER_CATEGORY_ICONS.pontosNaoMapeados,
|
|
35863
|
-
deviceCount:
|
|
35864
|
-
consumption:
|
|
35865
|
-
percentage:
|
|
35871
|
+
deviceCount: state6.pontosNaoMapeados?.devices?.length || 0,
|
|
35872
|
+
consumption: state6.pontosNaoMapeados?.total || 0,
|
|
35873
|
+
percentage: state6.pontosNaoMapeados?.perc || 0
|
|
35866
35874
|
};
|
|
35867
35875
|
summary.byCategory.push(pontosNaoMapeados);
|
|
35868
35876
|
}
|
|
35869
35877
|
summary.totalDevices = summary.byCategory.reduce((sum, cat) => sum + cat.deviceCount, 0);
|
|
35870
|
-
summary.totalConsumption =
|
|
35878
|
+
summary.totalConsumption = state6.entrada?.total || 0;
|
|
35871
35879
|
const widgetAggregation = receivedData?.deviceStatusAggregation;
|
|
35872
35880
|
if (widgetAggregation && widgetAggregation.hasData) {
|
|
35873
35881
|
summary.byStatus = {
|
|
@@ -37404,6 +37412,996 @@ var TempSensorSummaryTooltip = {
|
|
|
37404
37412
|
}
|
|
37405
37413
|
};
|
|
37406
37414
|
|
|
37415
|
+
// src/utils/ContractSummaryTooltip.ts
|
|
37416
|
+
var CONTRACT_SUMMARY_TOOLTIP_CSS = `
|
|
37417
|
+
/* ============================================
|
|
37418
|
+
Contract Summary Tooltip (RFC-0107)
|
|
37419
|
+
Premium draggable tooltip with dark theme
|
|
37420
|
+
============================================ */
|
|
37421
|
+
|
|
37422
|
+
.myio-contract-summary-tooltip {
|
|
37423
|
+
position: fixed;
|
|
37424
|
+
z-index: 99999;
|
|
37425
|
+
pointer-events: none;
|
|
37426
|
+
opacity: 0;
|
|
37427
|
+
transition: opacity 0.25s ease, transform 0.25s ease;
|
|
37428
|
+
transform: translateY(5px);
|
|
37429
|
+
}
|
|
37430
|
+
|
|
37431
|
+
.myio-contract-summary-tooltip.visible {
|
|
37432
|
+
opacity: 1;
|
|
37433
|
+
pointer-events: auto;
|
|
37434
|
+
transform: translateY(0);
|
|
37435
|
+
}
|
|
37436
|
+
|
|
37437
|
+
.myio-contract-summary-tooltip.closing {
|
|
37438
|
+
opacity: 0;
|
|
37439
|
+
transform: translateY(8px);
|
|
37440
|
+
transition: opacity 0.4s ease, transform 0.4s ease;
|
|
37441
|
+
}
|
|
37442
|
+
|
|
37443
|
+
.myio-contract-summary-tooltip.pinned {
|
|
37444
|
+
box-shadow: 0 0 0 2px #9684B5, 0 10px 40px rgba(0, 0, 0, 0.3);
|
|
37445
|
+
border-radius: 16px;
|
|
37446
|
+
}
|
|
37447
|
+
|
|
37448
|
+
.myio-contract-summary-tooltip.dragging {
|
|
37449
|
+
transition: none !important;
|
|
37450
|
+
cursor: move;
|
|
37451
|
+
}
|
|
37452
|
+
|
|
37453
|
+
.myio-contract-summary-tooltip.maximized {
|
|
37454
|
+
top: 20px !important;
|
|
37455
|
+
left: 20px !important;
|
|
37456
|
+
right: 20px !important;
|
|
37457
|
+
bottom: 20px !important;
|
|
37458
|
+
width: auto !important;
|
|
37459
|
+
max-width: none !important;
|
|
37460
|
+
}
|
|
37461
|
+
|
|
37462
|
+
.myio-contract-summary-tooltip.maximized .myio-contract-summary-tooltip__panel {
|
|
37463
|
+
width: 100%;
|
|
37464
|
+
height: 100%;
|
|
37465
|
+
max-width: none;
|
|
37466
|
+
display: flex;
|
|
37467
|
+
flex-direction: column;
|
|
37468
|
+
}
|
|
37469
|
+
|
|
37470
|
+
.myio-contract-summary-tooltip.maximized .myio-contract-summary-tooltip__body {
|
|
37471
|
+
flex: 1;
|
|
37472
|
+
overflow-y: auto;
|
|
37473
|
+
}
|
|
37474
|
+
|
|
37475
|
+
.myio-contract-summary-tooltip__panel {
|
|
37476
|
+
background: #2d1458;
|
|
37477
|
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
37478
|
+
border-radius: 16px;
|
|
37479
|
+
box-shadow:
|
|
37480
|
+
0 20px 60px rgba(0, 0, 0, 0.4),
|
|
37481
|
+
0 8px 20px rgba(0, 0, 0, 0.25),
|
|
37482
|
+
0 0 0 1px rgba(255, 255, 255, 0.05);
|
|
37483
|
+
min-width: 320px;
|
|
37484
|
+
max-width: 380px;
|
|
37485
|
+
font-family: Inter, system-ui, -apple-system, sans-serif;
|
|
37486
|
+
font-size: 12px;
|
|
37487
|
+
color: #ffffff;
|
|
37488
|
+
overflow: hidden;
|
|
37489
|
+
}
|
|
37490
|
+
|
|
37491
|
+
/* Header */
|
|
37492
|
+
.myio-contract-summary-tooltip__header {
|
|
37493
|
+
display: flex;
|
|
37494
|
+
align-items: center;
|
|
37495
|
+
gap: 10px;
|
|
37496
|
+
padding: 14px 16px;
|
|
37497
|
+
background: linear-gradient(135deg, #9684B5 0%, #2d1458 100%);
|
|
37498
|
+
border-radius: 16px 16px 0 0;
|
|
37499
|
+
position: relative;
|
|
37500
|
+
overflow: hidden;
|
|
37501
|
+
cursor: move;
|
|
37502
|
+
user-select: none;
|
|
37503
|
+
}
|
|
37504
|
+
|
|
37505
|
+
.myio-contract-summary-tooltip__header::before {
|
|
37506
|
+
content: '';
|
|
37507
|
+
position: absolute;
|
|
37508
|
+
top: 0;
|
|
37509
|
+
left: 0;
|
|
37510
|
+
right: 0;
|
|
37511
|
+
bottom: 0;
|
|
37512
|
+
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");
|
|
37513
|
+
opacity: 0.3;
|
|
37514
|
+
}
|
|
37515
|
+
|
|
37516
|
+
.myio-contract-summary-tooltip__icon {
|
|
37517
|
+
width: 40px;
|
|
37518
|
+
height: 40px;
|
|
37519
|
+
background: rgba(255, 255, 255, 0.15);
|
|
37520
|
+
border-radius: 12px;
|
|
37521
|
+
display: flex;
|
|
37522
|
+
align-items: center;
|
|
37523
|
+
justify-content: center;
|
|
37524
|
+
font-size: 20px;
|
|
37525
|
+
backdrop-filter: blur(10px);
|
|
37526
|
+
position: relative;
|
|
37527
|
+
z-index: 1;
|
|
37528
|
+
}
|
|
37529
|
+
|
|
37530
|
+
.myio-contract-summary-tooltip__icon.valid {
|
|
37531
|
+
background: rgba(76, 175, 80, 0.3);
|
|
37532
|
+
}
|
|
37533
|
+
|
|
37534
|
+
.myio-contract-summary-tooltip__icon.invalid {
|
|
37535
|
+
background: rgba(244, 67, 54, 0.3);
|
|
37536
|
+
}
|
|
37537
|
+
|
|
37538
|
+
.myio-contract-summary-tooltip__header-info {
|
|
37539
|
+
flex: 1;
|
|
37540
|
+
position: relative;
|
|
37541
|
+
z-index: 1;
|
|
37542
|
+
}
|
|
37543
|
+
|
|
37544
|
+
.myio-contract-summary-tooltip__title {
|
|
37545
|
+
font-weight: 700;
|
|
37546
|
+
font-size: 15px;
|
|
37547
|
+
color: #ffffff;
|
|
37548
|
+
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
|
|
37549
|
+
margin-bottom: 2px;
|
|
37550
|
+
}
|
|
37551
|
+
|
|
37552
|
+
.myio-contract-summary-tooltip__subtitle {
|
|
37553
|
+
font-size: 11px;
|
|
37554
|
+
color: rgba(255, 255, 255, 0.7);
|
|
37555
|
+
}
|
|
37556
|
+
|
|
37557
|
+
.myio-contract-summary-tooltip__header-actions {
|
|
37558
|
+
display: flex;
|
|
37559
|
+
align-items: center;
|
|
37560
|
+
gap: 4px;
|
|
37561
|
+
position: relative;
|
|
37562
|
+
z-index: 1;
|
|
37563
|
+
}
|
|
37564
|
+
|
|
37565
|
+
.myio-contract-summary-tooltip__header-btn {
|
|
37566
|
+
width: 28px;
|
|
37567
|
+
height: 28px;
|
|
37568
|
+
border: none;
|
|
37569
|
+
background: rgba(255, 255, 255, 0.15);
|
|
37570
|
+
border-radius: 8px;
|
|
37571
|
+
cursor: pointer;
|
|
37572
|
+
display: flex;
|
|
37573
|
+
align-items: center;
|
|
37574
|
+
justify-content: center;
|
|
37575
|
+
transition: all 0.2s ease;
|
|
37576
|
+
color: rgba(255, 255, 255, 0.8);
|
|
37577
|
+
}
|
|
37578
|
+
|
|
37579
|
+
.myio-contract-summary-tooltip__header-btn:hover {
|
|
37580
|
+
background: rgba(255, 255, 255, 0.25);
|
|
37581
|
+
color: #ffffff;
|
|
37582
|
+
transform: scale(1.05);
|
|
37583
|
+
}
|
|
37584
|
+
|
|
37585
|
+
.myio-contract-summary-tooltip__header-btn.pinned {
|
|
37586
|
+
background: rgba(255, 255, 255, 0.9);
|
|
37587
|
+
color: #9684B5;
|
|
37588
|
+
}
|
|
37589
|
+
|
|
37590
|
+
.myio-contract-summary-tooltip__header-btn.pinned:hover {
|
|
37591
|
+
background: #ffffff;
|
|
37592
|
+
color: #2d1458;
|
|
37593
|
+
}
|
|
37594
|
+
|
|
37595
|
+
.myio-contract-summary-tooltip__header-btn svg {
|
|
37596
|
+
width: 14px;
|
|
37597
|
+
height: 14px;
|
|
37598
|
+
}
|
|
37599
|
+
|
|
37600
|
+
/* Body */
|
|
37601
|
+
.myio-contract-summary-tooltip__body {
|
|
37602
|
+
padding: 16px;
|
|
37603
|
+
}
|
|
37604
|
+
|
|
37605
|
+
/* Domain Section */
|
|
37606
|
+
.myio-contract-summary-tooltip__domain {
|
|
37607
|
+
margin-bottom: 14px;
|
|
37608
|
+
background: rgba(255, 255, 255, 0.05);
|
|
37609
|
+
border-radius: 12px;
|
|
37610
|
+
overflow: hidden;
|
|
37611
|
+
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
37612
|
+
}
|
|
37613
|
+
|
|
37614
|
+
.myio-contract-summary-tooltip__domain:last-child {
|
|
37615
|
+
margin-bottom: 0;
|
|
37616
|
+
}
|
|
37617
|
+
|
|
37618
|
+
.myio-contract-summary-tooltip__domain-header {
|
|
37619
|
+
display: flex;
|
|
37620
|
+
align-items: center;
|
|
37621
|
+
justify-content: space-between;
|
|
37622
|
+
padding: 10px 14px;
|
|
37623
|
+
cursor: pointer;
|
|
37624
|
+
transition: background 0.2s ease;
|
|
37625
|
+
}
|
|
37626
|
+
|
|
37627
|
+
.myio-contract-summary-tooltip__domain-header:hover {
|
|
37628
|
+
background: rgba(255, 255, 255, 0.05);
|
|
37629
|
+
}
|
|
37630
|
+
|
|
37631
|
+
.myio-contract-summary-tooltip__domain-info {
|
|
37632
|
+
display: flex;
|
|
37633
|
+
align-items: center;
|
|
37634
|
+
gap: 8px;
|
|
37635
|
+
}
|
|
37636
|
+
|
|
37637
|
+
.myio-contract-summary-tooltip__domain-icon {
|
|
37638
|
+
font-size: 18px;
|
|
37639
|
+
}
|
|
37640
|
+
|
|
37641
|
+
.myio-contract-summary-tooltip__domain-name {
|
|
37642
|
+
font-weight: 600;
|
|
37643
|
+
font-size: 13px;
|
|
37644
|
+
}
|
|
37645
|
+
|
|
37646
|
+
.myio-contract-summary-tooltip__domain-count {
|
|
37647
|
+
font-size: 12px;
|
|
37648
|
+
color: #81c784;
|
|
37649
|
+
font-weight: 600;
|
|
37650
|
+
}
|
|
37651
|
+
|
|
37652
|
+
.myio-contract-summary-tooltip__expand-icon {
|
|
37653
|
+
font-size: 10px;
|
|
37654
|
+
opacity: 0.6;
|
|
37655
|
+
transition: transform 0.3s ease;
|
|
37656
|
+
}
|
|
37657
|
+
|
|
37658
|
+
.myio-contract-summary-tooltip__domain.expanded .myio-contract-summary-tooltip__expand-icon {
|
|
37659
|
+
transform: rotate(180deg);
|
|
37660
|
+
}
|
|
37661
|
+
|
|
37662
|
+
/* Domain Details */
|
|
37663
|
+
.myio-contract-summary-tooltip__domain-details {
|
|
37664
|
+
max-height: 0;
|
|
37665
|
+
overflow: hidden;
|
|
37666
|
+
transition: max-height 0.3s ease;
|
|
37667
|
+
background: rgba(0, 0, 0, 0.15);
|
|
37668
|
+
}
|
|
37669
|
+
|
|
37670
|
+
.myio-contract-summary-tooltip__domain.expanded .myio-contract-summary-tooltip__domain-details {
|
|
37671
|
+
max-height: 150px;
|
|
37672
|
+
}
|
|
37673
|
+
|
|
37674
|
+
.myio-contract-summary-tooltip__detail-row {
|
|
37675
|
+
display: flex;
|
|
37676
|
+
align-items: center;
|
|
37677
|
+
justify-content: space-between;
|
|
37678
|
+
padding: 6px 14px 6px 40px;
|
|
37679
|
+
font-size: 12px;
|
|
37680
|
+
border-top: 1px solid rgba(255, 255, 255, 0.05);
|
|
37681
|
+
}
|
|
37682
|
+
|
|
37683
|
+
.myio-contract-summary-tooltip__detail-row:first-child {
|
|
37684
|
+
border-top: none;
|
|
37685
|
+
}
|
|
37686
|
+
|
|
37687
|
+
.myio-contract-summary-tooltip__detail-label {
|
|
37688
|
+
opacity: 0.7;
|
|
37689
|
+
display: flex;
|
|
37690
|
+
align-items: center;
|
|
37691
|
+
gap: 6px;
|
|
37692
|
+
}
|
|
37693
|
+
|
|
37694
|
+
.myio-contract-summary-tooltip__detail-label::before {
|
|
37695
|
+
content: '';
|
|
37696
|
+
width: 4px;
|
|
37697
|
+
height: 4px;
|
|
37698
|
+
border-radius: 50%;
|
|
37699
|
+
background: currentColor;
|
|
37700
|
+
opacity: 0.5;
|
|
37701
|
+
}
|
|
37702
|
+
|
|
37703
|
+
.myio-contract-summary-tooltip__detail-count {
|
|
37704
|
+
font-weight: 500;
|
|
37705
|
+
color: #81c784;
|
|
37706
|
+
}
|
|
37707
|
+
|
|
37708
|
+
/* Status Banner */
|
|
37709
|
+
.myio-contract-summary-tooltip__status {
|
|
37710
|
+
display: flex;
|
|
37711
|
+
align-items: center;
|
|
37712
|
+
justify-content: center;
|
|
37713
|
+
gap: 8px;
|
|
37714
|
+
padding: 10px 14px;
|
|
37715
|
+
border-radius: 10px;
|
|
37716
|
+
margin-bottom: 14px;
|
|
37717
|
+
font-size: 12px;
|
|
37718
|
+
font-weight: 600;
|
|
37719
|
+
}
|
|
37720
|
+
|
|
37721
|
+
.myio-contract-summary-tooltip__status.valid {
|
|
37722
|
+
background: rgba(76, 175, 80, 0.2);
|
|
37723
|
+
color: #81c784;
|
|
37724
|
+
border: 1px solid rgba(76, 175, 80, 0.3);
|
|
37725
|
+
}
|
|
37726
|
+
|
|
37727
|
+
.myio-contract-summary-tooltip__status.invalid {
|
|
37728
|
+
background: rgba(244, 67, 54, 0.2);
|
|
37729
|
+
color: #ef5350;
|
|
37730
|
+
border: 1px solid rgba(244, 67, 54, 0.3);
|
|
37731
|
+
}
|
|
37732
|
+
|
|
37733
|
+
.myio-contract-summary-tooltip__status-icon {
|
|
37734
|
+
font-size: 14px;
|
|
37735
|
+
}
|
|
37736
|
+
|
|
37737
|
+
/* Discrepancies */
|
|
37738
|
+
.myio-contract-summary-tooltip__discrepancies {
|
|
37739
|
+
background: rgba(244, 67, 54, 0.15);
|
|
37740
|
+
border: 1px solid rgba(244, 67, 54, 0.3);
|
|
37741
|
+
border-radius: 10px;
|
|
37742
|
+
padding: 10px 14px;
|
|
37743
|
+
margin-bottom: 14px;
|
|
37744
|
+
}
|
|
37745
|
+
|
|
37746
|
+
.myio-contract-summary-tooltip__discrepancies-title {
|
|
37747
|
+
font-size: 11px;
|
|
37748
|
+
font-weight: 600;
|
|
37749
|
+
color: #ef5350;
|
|
37750
|
+
margin-bottom: 6px;
|
|
37751
|
+
text-transform: uppercase;
|
|
37752
|
+
letter-spacing: 0.5px;
|
|
37753
|
+
}
|
|
37754
|
+
|
|
37755
|
+
.myio-contract-summary-tooltip__discrepancy-item {
|
|
37756
|
+
font-size: 11px;
|
|
37757
|
+
color: rgba(255, 255, 255, 0.8);
|
|
37758
|
+
padding: 3px 0;
|
|
37759
|
+
}
|
|
37760
|
+
|
|
37761
|
+
/* Footer */
|
|
37762
|
+
.myio-contract-summary-tooltip__footer {
|
|
37763
|
+
display: flex;
|
|
37764
|
+
justify-content: space-between;
|
|
37765
|
+
align-items: center;
|
|
37766
|
+
padding: 12px 16px;
|
|
37767
|
+
background: rgba(0, 0, 0, 0.2);
|
|
37768
|
+
border-top: 1px solid rgba(255, 255, 255, 0.05);
|
|
37769
|
+
border-radius: 0 0 16px 16px;
|
|
37770
|
+
}
|
|
37771
|
+
|
|
37772
|
+
.myio-contract-summary-tooltip__footer-label {
|
|
37773
|
+
font-size: 10px;
|
|
37774
|
+
color: rgba(255, 255, 255, 0.5);
|
|
37775
|
+
}
|
|
37776
|
+
|
|
37777
|
+
.myio-contract-summary-tooltip__footer-value {
|
|
37778
|
+
font-size: 11px;
|
|
37779
|
+
font-weight: 600;
|
|
37780
|
+
color: rgba(255, 255, 255, 0.8);
|
|
37781
|
+
}
|
|
37782
|
+
|
|
37783
|
+
/* Total Devices Badge */
|
|
37784
|
+
.myio-contract-summary-tooltip__total {
|
|
37785
|
+
display: flex;
|
|
37786
|
+
align-items: center;
|
|
37787
|
+
justify-content: center;
|
|
37788
|
+
gap: 8px;
|
|
37789
|
+
padding: 12px;
|
|
37790
|
+
background: rgba(255, 255, 255, 0.08);
|
|
37791
|
+
border-radius: 10px;
|
|
37792
|
+
margin-bottom: 14px;
|
|
37793
|
+
}
|
|
37794
|
+
|
|
37795
|
+
.myio-contract-summary-tooltip__total-label {
|
|
37796
|
+
font-size: 12px;
|
|
37797
|
+
opacity: 0.8;
|
|
37798
|
+
}
|
|
37799
|
+
|
|
37800
|
+
.myio-contract-summary-tooltip__total-value {
|
|
37801
|
+
font-size: 18px;
|
|
37802
|
+
font-weight: 700;
|
|
37803
|
+
color: #81c784;
|
|
37804
|
+
}
|
|
37805
|
+
|
|
37806
|
+
/* Responsive */
|
|
37807
|
+
@media (max-width: 400px) {
|
|
37808
|
+
.myio-contract-summary-tooltip__panel {
|
|
37809
|
+
min-width: 280px;
|
|
37810
|
+
max-width: 95vw;
|
|
37811
|
+
}
|
|
37812
|
+
}
|
|
37813
|
+
`;
|
|
37814
|
+
var cssInjected9 = false;
|
|
37815
|
+
function injectCSS9() {
|
|
37816
|
+
if (cssInjected9) return;
|
|
37817
|
+
if (typeof document === "undefined") return;
|
|
37818
|
+
const styleId = "myio-contract-summary-tooltip-styles";
|
|
37819
|
+
if (document.getElementById(styleId)) {
|
|
37820
|
+
cssInjected9 = true;
|
|
37821
|
+
return;
|
|
37822
|
+
}
|
|
37823
|
+
const style = document.createElement("style");
|
|
37824
|
+
style.id = styleId;
|
|
37825
|
+
style.textContent = CONTRACT_SUMMARY_TOOLTIP_CSS;
|
|
37826
|
+
document.head.appendChild(style);
|
|
37827
|
+
cssInjected9 = true;
|
|
37828
|
+
}
|
|
37829
|
+
var state5 = {
|
|
37830
|
+
hideTimer: null,
|
|
37831
|
+
isMouseOverTooltip: false,
|
|
37832
|
+
isMaximized: false,
|
|
37833
|
+
isDragging: false,
|
|
37834
|
+
dragOffset: { x: 0, y: 0 },
|
|
37835
|
+
savedPosition: null,
|
|
37836
|
+
pinnedCounter: 0,
|
|
37837
|
+
expandedDomains: /* @__PURE__ */ new Set(["energy", "water", "temperature"])
|
|
37838
|
+
};
|
|
37839
|
+
function formatTimestamp5(isoString) {
|
|
37840
|
+
if (!isoString) return "Agora";
|
|
37841
|
+
try {
|
|
37842
|
+
const date = new Date(isoString);
|
|
37843
|
+
return date.toLocaleString("pt-BR", {
|
|
37844
|
+
hour: "2-digit",
|
|
37845
|
+
minute: "2-digit",
|
|
37846
|
+
day: "2-digit",
|
|
37847
|
+
month: "2-digit"
|
|
37848
|
+
});
|
|
37849
|
+
} catch {
|
|
37850
|
+
return "Agora";
|
|
37851
|
+
}
|
|
37852
|
+
}
|
|
37853
|
+
function calculateTotalDevices(data) {
|
|
37854
|
+
return data.energy.total + data.water.total + data.temperature.total;
|
|
37855
|
+
}
|
|
37856
|
+
function generateHeaderHTML5(data) {
|
|
37857
|
+
const iconClass = data.isValid ? "valid" : "invalid";
|
|
37858
|
+
const iconSymbol = data.isValid ? "\u2713" : "!";
|
|
37859
|
+
const totalDevices = calculateTotalDevices(data);
|
|
37860
|
+
return `
|
|
37861
|
+
<div class="myio-contract-summary-tooltip__header" data-drag-handle>
|
|
37862
|
+
<div class="myio-contract-summary-tooltip__icon ${iconClass}">${iconSymbol}</div>
|
|
37863
|
+
<div class="myio-contract-summary-tooltip__header-info">
|
|
37864
|
+
<div class="myio-contract-summary-tooltip__title">Contract Summary</div>
|
|
37865
|
+
<div class="myio-contract-summary-tooltip__subtitle">${totalDevices} devices loaded</div>
|
|
37866
|
+
</div>
|
|
37867
|
+
<div class="myio-contract-summary-tooltip__header-actions">
|
|
37868
|
+
<button class="myio-contract-summary-tooltip__header-btn" data-action="pin" title="Pin to screen">
|
|
37869
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
37870
|
+
<path d="M9 4v6l-2 4v2h10v-2l-2-4V4"/>
|
|
37871
|
+
<line x1="12" y1="16" x2="12" y2="21"/>
|
|
37872
|
+
<line x1="8" y1="4" x2="16" y2="4"/>
|
|
37873
|
+
</svg>
|
|
37874
|
+
</button>
|
|
37875
|
+
<button class="myio-contract-summary-tooltip__header-btn" data-action="maximize" title="Maximize">
|
|
37876
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
37877
|
+
<rect x="3" y="3" width="18" height="18" rx="2"/>
|
|
37878
|
+
</svg>
|
|
37879
|
+
</button>
|
|
37880
|
+
<button class="myio-contract-summary-tooltip__header-btn" data-action="close" title="Close">
|
|
37881
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
37882
|
+
<path d="M18 6L6 18M6 6l12 12"/>
|
|
37883
|
+
</svg>
|
|
37884
|
+
</button>
|
|
37885
|
+
</div>
|
|
37886
|
+
</div>
|
|
37887
|
+
`;
|
|
37888
|
+
}
|
|
37889
|
+
function generateDomainHTML(domain, icon, name, counts, isTemperature = false) {
|
|
37890
|
+
const isExpanded = state5.expandedDomains.has(domain);
|
|
37891
|
+
const expandedClass = isExpanded ? "expanded" : "";
|
|
37892
|
+
let detailsHTML = "";
|
|
37893
|
+
if (isTemperature) {
|
|
37894
|
+
const tempCounts = counts;
|
|
37895
|
+
detailsHTML = `
|
|
37896
|
+
<div class="myio-contract-summary-tooltip__detail-row">
|
|
37897
|
+
<span class="myio-contract-summary-tooltip__detail-label">Internal (Climate)</span>
|
|
37898
|
+
<span class="myio-contract-summary-tooltip__detail-count">${tempCounts.internal}</span>
|
|
37899
|
+
</div>
|
|
37900
|
+
<div class="myio-contract-summary-tooltip__detail-row">
|
|
37901
|
+
<span class="myio-contract-summary-tooltip__detail-label">Stores (Non-Climate)</span>
|
|
37902
|
+
<span class="myio-contract-summary-tooltip__detail-count">${tempCounts.stores}</span>
|
|
37903
|
+
</div>
|
|
37904
|
+
`;
|
|
37905
|
+
} else {
|
|
37906
|
+
const domainCounts = counts;
|
|
37907
|
+
detailsHTML = `
|
|
37908
|
+
<div class="myio-contract-summary-tooltip__detail-row">
|
|
37909
|
+
<span class="myio-contract-summary-tooltip__detail-label">Entries</span>
|
|
37910
|
+
<span class="myio-contract-summary-tooltip__detail-count">${domainCounts.entries}</span>
|
|
37911
|
+
</div>
|
|
37912
|
+
<div class="myio-contract-summary-tooltip__detail-row">
|
|
37913
|
+
<span class="myio-contract-summary-tooltip__detail-label">Common Area</span>
|
|
37914
|
+
<span class="myio-contract-summary-tooltip__detail-count">${domainCounts.commonArea}</span>
|
|
37915
|
+
</div>
|
|
37916
|
+
<div class="myio-contract-summary-tooltip__detail-row">
|
|
37917
|
+
<span class="myio-contract-summary-tooltip__detail-label">Stores</span>
|
|
37918
|
+
<span class="myio-contract-summary-tooltip__detail-count">${domainCounts.stores}</span>
|
|
37919
|
+
</div>
|
|
37920
|
+
`;
|
|
37921
|
+
}
|
|
37922
|
+
return `
|
|
37923
|
+
<div class="myio-contract-summary-tooltip__domain ${expandedClass}" data-domain="${domain}">
|
|
37924
|
+
<div class="myio-contract-summary-tooltip__domain-header" data-toggle-domain="${domain}">
|
|
37925
|
+
<div class="myio-contract-summary-tooltip__domain-info">
|
|
37926
|
+
<span class="myio-contract-summary-tooltip__domain-icon">${icon}</span>
|
|
37927
|
+
<span class="myio-contract-summary-tooltip__domain-name">${name}</span>
|
|
37928
|
+
</div>
|
|
37929
|
+
<div style="display: flex; align-items: center; gap: 8px;">
|
|
37930
|
+
<span class="myio-contract-summary-tooltip__domain-count">${counts.total} devices</span>
|
|
37931
|
+
<span class="myio-contract-summary-tooltip__expand-icon">\u25BC</span>
|
|
37932
|
+
</div>
|
|
37933
|
+
</div>
|
|
37934
|
+
<div class="myio-contract-summary-tooltip__domain-details">
|
|
37935
|
+
${detailsHTML}
|
|
37936
|
+
</div>
|
|
37937
|
+
</div>
|
|
37938
|
+
`;
|
|
37939
|
+
}
|
|
37940
|
+
function generateBodyHTML3(data) {
|
|
37941
|
+
const totalDevices = calculateTotalDevices(data);
|
|
37942
|
+
const timestamp = formatTimestamp5(data.timestamp);
|
|
37943
|
+
const statusClass = data.isValid ? "valid" : "invalid";
|
|
37944
|
+
const statusIcon = data.isValid ? "\u2713" : "\u26A0";
|
|
37945
|
+
const statusText = data.isValid ? "Contract validated successfully" : "Validation issues detected";
|
|
37946
|
+
let discrepanciesHTML = "";
|
|
37947
|
+
if (data.discrepancies && data.discrepancies.length > 0) {
|
|
37948
|
+
const items = data.discrepancies.map((d) => `<div class="myio-contract-summary-tooltip__discrepancy-item">${d.domain}: expected ${d.expected}, found ${d.actual}</div>`).join("");
|
|
37949
|
+
discrepanciesHTML = `
|
|
37950
|
+
<div class="myio-contract-summary-tooltip__discrepancies">
|
|
37951
|
+
<div class="myio-contract-summary-tooltip__discrepancies-title">Discrepancies</div>
|
|
37952
|
+
${items}
|
|
37953
|
+
</div>
|
|
37954
|
+
`;
|
|
37955
|
+
}
|
|
37956
|
+
return `
|
|
37957
|
+
<div class="myio-contract-summary-tooltip__body">
|
|
37958
|
+
<!-- Status Banner -->
|
|
37959
|
+
<div class="myio-contract-summary-tooltip__status ${statusClass}">
|
|
37960
|
+
<span class="myio-contract-summary-tooltip__status-icon">${statusIcon}</span>
|
|
37961
|
+
<span>${statusText}</span>
|
|
37962
|
+
</div>
|
|
37963
|
+
|
|
37964
|
+
${discrepanciesHTML}
|
|
37965
|
+
|
|
37966
|
+
<!-- Total Devices -->
|
|
37967
|
+
<div class="myio-contract-summary-tooltip__total">
|
|
37968
|
+
<span class="myio-contract-summary-tooltip__total-label">Total Devices:</span>
|
|
37969
|
+
<span class="myio-contract-summary-tooltip__total-value">${totalDevices}</span>
|
|
37970
|
+
</div>
|
|
37971
|
+
|
|
37972
|
+
<!-- Energy -->
|
|
37973
|
+
${generateDomainHTML("energy", "\u26A1", "Energy", data.energy)}
|
|
37974
|
+
|
|
37975
|
+
<!-- Water -->
|
|
37976
|
+
${generateDomainHTML("water", "\u{1F4A7}", "Water", data.water)}
|
|
37977
|
+
|
|
37978
|
+
<!-- Temperature -->
|
|
37979
|
+
${generateDomainHTML("temperature", "\u{1F321}\uFE0F", "Temperature", data.temperature, true)}
|
|
37980
|
+
</div>
|
|
37981
|
+
|
|
37982
|
+
<!-- Footer -->
|
|
37983
|
+
<div class="myio-contract-summary-tooltip__footer">
|
|
37984
|
+
<span class="myio-contract-summary-tooltip__footer-label">Loaded at</span>
|
|
37985
|
+
<span class="myio-contract-summary-tooltip__footer-value">${timestamp}</span>
|
|
37986
|
+
</div>
|
|
37987
|
+
`;
|
|
37988
|
+
}
|
|
37989
|
+
function setupHoverListeners5(container) {
|
|
37990
|
+
container.onmouseenter = () => {
|
|
37991
|
+
state5.isMouseOverTooltip = true;
|
|
37992
|
+
if (state5.hideTimer) {
|
|
37993
|
+
clearTimeout(state5.hideTimer);
|
|
37994
|
+
state5.hideTimer = null;
|
|
37995
|
+
}
|
|
37996
|
+
};
|
|
37997
|
+
container.onmouseleave = () => {
|
|
37998
|
+
state5.isMouseOverTooltip = false;
|
|
37999
|
+
startDelayedHide5();
|
|
38000
|
+
};
|
|
38001
|
+
}
|
|
38002
|
+
function setupDomainToggleListeners(container) {
|
|
38003
|
+
const toggles = container.querySelectorAll("[data-toggle-domain]");
|
|
38004
|
+
toggles.forEach((toggle) => {
|
|
38005
|
+
toggle.onclick = (e) => {
|
|
38006
|
+
e.stopPropagation();
|
|
38007
|
+
const domain = toggle.dataset.toggleDomain;
|
|
38008
|
+
if (!domain) return;
|
|
38009
|
+
const domainEl = container.querySelector(`[data-domain="${domain}"]`);
|
|
38010
|
+
if (!domainEl) return;
|
|
38011
|
+
if (state5.expandedDomains.has(domain)) {
|
|
38012
|
+
state5.expandedDomains.delete(domain);
|
|
38013
|
+
domainEl.classList.remove("expanded");
|
|
38014
|
+
} else {
|
|
38015
|
+
state5.expandedDomains.add(domain);
|
|
38016
|
+
domainEl.classList.add("expanded");
|
|
38017
|
+
}
|
|
38018
|
+
};
|
|
38019
|
+
});
|
|
38020
|
+
}
|
|
38021
|
+
function setupButtonListeners5(container) {
|
|
38022
|
+
const buttons = container.querySelectorAll("[data-action]");
|
|
38023
|
+
buttons.forEach((btn) => {
|
|
38024
|
+
btn.onclick = (e) => {
|
|
38025
|
+
e.stopPropagation();
|
|
38026
|
+
const action = btn.dataset.action;
|
|
38027
|
+
switch (action) {
|
|
38028
|
+
case "pin":
|
|
38029
|
+
createPinnedClone5(container);
|
|
38030
|
+
break;
|
|
38031
|
+
case "maximize":
|
|
38032
|
+
toggleMaximize5(container);
|
|
38033
|
+
break;
|
|
38034
|
+
case "close":
|
|
38035
|
+
ContractSummaryTooltip.close();
|
|
38036
|
+
break;
|
|
38037
|
+
}
|
|
38038
|
+
};
|
|
38039
|
+
});
|
|
38040
|
+
}
|
|
38041
|
+
function setupDragListeners5(container) {
|
|
38042
|
+
const header = container.querySelector("[data-drag-handle]");
|
|
38043
|
+
if (!header) return;
|
|
38044
|
+
header.onmousedown = (e) => {
|
|
38045
|
+
if (e.target.closest("[data-action]")) return;
|
|
38046
|
+
if (e.target.closest("[data-toggle-domain]")) return;
|
|
38047
|
+
if (state5.isMaximized) return;
|
|
38048
|
+
state5.isDragging = true;
|
|
38049
|
+
container.classList.add("dragging");
|
|
38050
|
+
const rect = container.getBoundingClientRect();
|
|
38051
|
+
state5.dragOffset = {
|
|
38052
|
+
x: e.clientX - rect.left,
|
|
38053
|
+
y: e.clientY - rect.top
|
|
38054
|
+
};
|
|
38055
|
+
const onMouseMove = (e2) => {
|
|
38056
|
+
if (!state5.isDragging) return;
|
|
38057
|
+
const newLeft = e2.clientX - state5.dragOffset.x;
|
|
38058
|
+
const newTop = e2.clientY - state5.dragOffset.y;
|
|
38059
|
+
const maxLeft = window.innerWidth - container.offsetWidth;
|
|
38060
|
+
const maxTop = window.innerHeight - container.offsetHeight;
|
|
38061
|
+
container.style.left = Math.max(0, Math.min(newLeft, maxLeft)) + "px";
|
|
38062
|
+
container.style.top = Math.max(0, Math.min(newTop, maxTop)) + "px";
|
|
38063
|
+
};
|
|
38064
|
+
const onMouseUp = () => {
|
|
38065
|
+
state5.isDragging = false;
|
|
38066
|
+
container.classList.remove("dragging");
|
|
38067
|
+
document.removeEventListener("mousemove", onMouseMove);
|
|
38068
|
+
document.removeEventListener("mouseup", onMouseUp);
|
|
38069
|
+
};
|
|
38070
|
+
document.addEventListener("mousemove", onMouseMove);
|
|
38071
|
+
document.addEventListener("mouseup", onMouseUp);
|
|
38072
|
+
};
|
|
38073
|
+
}
|
|
38074
|
+
function createPinnedClone5(container) {
|
|
38075
|
+
state5.pinnedCounter++;
|
|
38076
|
+
const pinnedId = `myio-contract-summary-tooltip-pinned-${state5.pinnedCounter}`;
|
|
38077
|
+
const clone = container.cloneNode(true);
|
|
38078
|
+
clone.id = pinnedId;
|
|
38079
|
+
clone.classList.add("pinned");
|
|
38080
|
+
clone.classList.remove("closing");
|
|
38081
|
+
const pinBtn = clone.querySelector('[data-action="pin"]');
|
|
38082
|
+
if (pinBtn) {
|
|
38083
|
+
pinBtn.classList.add("pinned");
|
|
38084
|
+
pinBtn.setAttribute("title", "Unpin");
|
|
38085
|
+
pinBtn.innerHTML = `
|
|
38086
|
+
<svg viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="1">
|
|
38087
|
+
<path d="M9 4v6l-2 4v2h10v-2l-2-4V4"/>
|
|
38088
|
+
<line x1="12" y1="16" x2="12" y2="21"/>
|
|
38089
|
+
<line x1="8" y1="4" x2="16" y2="4"/>
|
|
38090
|
+
</svg>
|
|
38091
|
+
`;
|
|
38092
|
+
}
|
|
38093
|
+
document.body.appendChild(clone);
|
|
38094
|
+
setupPinnedCloneListeners5(clone, pinnedId);
|
|
38095
|
+
ContractSummaryTooltip.hide();
|
|
38096
|
+
}
|
|
38097
|
+
function setupPinnedCloneListeners5(clone, cloneId) {
|
|
38098
|
+
let isMaximized = false;
|
|
38099
|
+
let savedPosition = null;
|
|
38100
|
+
const cloneExpandedDomains = new Set(state5.expandedDomains);
|
|
38101
|
+
const toggles = clone.querySelectorAll("[data-toggle-domain]");
|
|
38102
|
+
toggles.forEach((toggle) => {
|
|
38103
|
+
toggle.onclick = (e) => {
|
|
38104
|
+
e.stopPropagation();
|
|
38105
|
+
const domain = toggle.dataset.toggleDomain;
|
|
38106
|
+
if (!domain) return;
|
|
38107
|
+
const domainEl = clone.querySelector(`[data-domain="${domain}"]`);
|
|
38108
|
+
if (!domainEl) return;
|
|
38109
|
+
if (cloneExpandedDomains.has(domain)) {
|
|
38110
|
+
cloneExpandedDomains.delete(domain);
|
|
38111
|
+
domainEl.classList.remove("expanded");
|
|
38112
|
+
} else {
|
|
38113
|
+
cloneExpandedDomains.add(domain);
|
|
38114
|
+
domainEl.classList.add("expanded");
|
|
38115
|
+
}
|
|
38116
|
+
};
|
|
38117
|
+
});
|
|
38118
|
+
const pinBtn = clone.querySelector('[data-action="pin"]');
|
|
38119
|
+
if (pinBtn) {
|
|
38120
|
+
pinBtn.onclick = (e) => {
|
|
38121
|
+
e.stopPropagation();
|
|
38122
|
+
closePinnedClone5(cloneId);
|
|
38123
|
+
};
|
|
38124
|
+
}
|
|
38125
|
+
const closeBtn = clone.querySelector('[data-action="close"]');
|
|
38126
|
+
if (closeBtn) {
|
|
38127
|
+
closeBtn.onclick = (e) => {
|
|
38128
|
+
e.stopPropagation();
|
|
38129
|
+
closePinnedClone5(cloneId);
|
|
38130
|
+
};
|
|
38131
|
+
}
|
|
38132
|
+
const maxBtn = clone.querySelector('[data-action="maximize"]');
|
|
38133
|
+
if (maxBtn) {
|
|
38134
|
+
maxBtn.onclick = (e) => {
|
|
38135
|
+
e.stopPropagation();
|
|
38136
|
+
isMaximized = !isMaximized;
|
|
38137
|
+
if (isMaximized) {
|
|
38138
|
+
savedPosition = { left: clone.style.left, top: clone.style.top };
|
|
38139
|
+
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>`;
|
|
38140
|
+
maxBtn.setAttribute("title", "Restore");
|
|
38141
|
+
} else {
|
|
38142
|
+
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>`;
|
|
38143
|
+
maxBtn.setAttribute("title", "Maximize");
|
|
38144
|
+
if (savedPosition) {
|
|
38145
|
+
clone.style.left = savedPosition.left;
|
|
38146
|
+
clone.style.top = savedPosition.top;
|
|
38147
|
+
}
|
|
38148
|
+
}
|
|
38149
|
+
clone.classList.toggle("maximized", isMaximized);
|
|
38150
|
+
};
|
|
38151
|
+
}
|
|
38152
|
+
const header = clone.querySelector("[data-drag-handle]");
|
|
38153
|
+
if (header) {
|
|
38154
|
+
let isDragging = false;
|
|
38155
|
+
let dragOffset = { x: 0, y: 0 };
|
|
38156
|
+
header.onmousedown = (e) => {
|
|
38157
|
+
if (e.target.closest("[data-action]")) return;
|
|
38158
|
+
if (e.target.closest("[data-toggle-domain]")) return;
|
|
38159
|
+
if (isMaximized) return;
|
|
38160
|
+
isDragging = true;
|
|
38161
|
+
clone.classList.add("dragging");
|
|
38162
|
+
const rect = clone.getBoundingClientRect();
|
|
38163
|
+
dragOffset = { x: e.clientX - rect.left, y: e.clientY - rect.top };
|
|
38164
|
+
const onMouseMove = (e2) => {
|
|
38165
|
+
if (!isDragging) return;
|
|
38166
|
+
const newLeft = e2.clientX - dragOffset.x;
|
|
38167
|
+
const newTop = e2.clientY - dragOffset.y;
|
|
38168
|
+
const maxLeft = window.innerWidth - clone.offsetWidth;
|
|
38169
|
+
const maxTop = window.innerHeight - clone.offsetHeight;
|
|
38170
|
+
clone.style.left = Math.max(0, Math.min(newLeft, maxLeft)) + "px";
|
|
38171
|
+
clone.style.top = Math.max(0, Math.min(newTop, maxTop)) + "px";
|
|
38172
|
+
};
|
|
38173
|
+
const onMouseUp = () => {
|
|
38174
|
+
isDragging = false;
|
|
38175
|
+
clone.classList.remove("dragging");
|
|
38176
|
+
document.removeEventListener("mousemove", onMouseMove);
|
|
38177
|
+
document.removeEventListener("mouseup", onMouseUp);
|
|
38178
|
+
};
|
|
38179
|
+
document.addEventListener("mousemove", onMouseMove);
|
|
38180
|
+
document.addEventListener("mouseup", onMouseUp);
|
|
38181
|
+
};
|
|
38182
|
+
}
|
|
38183
|
+
}
|
|
38184
|
+
function closePinnedClone5(cloneId) {
|
|
38185
|
+
const clone = document.getElementById(cloneId);
|
|
38186
|
+
if (clone) {
|
|
38187
|
+
clone.classList.add("closing");
|
|
38188
|
+
setTimeout(() => clone.remove(), 400);
|
|
38189
|
+
}
|
|
38190
|
+
}
|
|
38191
|
+
function toggleMaximize5(container) {
|
|
38192
|
+
state5.isMaximized = !state5.isMaximized;
|
|
38193
|
+
if (state5.isMaximized) {
|
|
38194
|
+
state5.savedPosition = {
|
|
38195
|
+
left: container.style.left,
|
|
38196
|
+
top: container.style.top
|
|
38197
|
+
};
|
|
38198
|
+
}
|
|
38199
|
+
container.classList.toggle("maximized", state5.isMaximized);
|
|
38200
|
+
const maxBtn = container.querySelector('[data-action="maximize"]');
|
|
38201
|
+
if (maxBtn) {
|
|
38202
|
+
if (state5.isMaximized) {
|
|
38203
|
+
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>`;
|
|
38204
|
+
maxBtn.setAttribute("title", "Restore");
|
|
38205
|
+
} else {
|
|
38206
|
+
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>`;
|
|
38207
|
+
maxBtn.setAttribute("title", "Maximize");
|
|
38208
|
+
if (state5.savedPosition) {
|
|
38209
|
+
container.style.left = state5.savedPosition.left;
|
|
38210
|
+
container.style.top = state5.savedPosition.top;
|
|
38211
|
+
}
|
|
38212
|
+
}
|
|
38213
|
+
}
|
|
38214
|
+
}
|
|
38215
|
+
function startDelayedHide5() {
|
|
38216
|
+
if (state5.isMouseOverTooltip) return;
|
|
38217
|
+
if (state5.hideTimer) {
|
|
38218
|
+
clearTimeout(state5.hideTimer);
|
|
38219
|
+
}
|
|
38220
|
+
state5.hideTimer = setTimeout(() => {
|
|
38221
|
+
hideWithAnimation5();
|
|
38222
|
+
}, 1500);
|
|
38223
|
+
}
|
|
38224
|
+
function hideWithAnimation5() {
|
|
38225
|
+
const container = document.getElementById("myio-contract-summary-tooltip");
|
|
38226
|
+
if (container && container.classList.contains("visible")) {
|
|
38227
|
+
container.classList.add("closing");
|
|
38228
|
+
setTimeout(() => {
|
|
38229
|
+
container.classList.remove("visible", "closing");
|
|
38230
|
+
}, 400);
|
|
38231
|
+
}
|
|
38232
|
+
}
|
|
38233
|
+
function positionTooltip5(container, triggerElement) {
|
|
38234
|
+
const rect = triggerElement.getBoundingClientRect();
|
|
38235
|
+
let left = rect.left;
|
|
38236
|
+
let top = rect.bottom + 8;
|
|
38237
|
+
const tooltipWidth = 360;
|
|
38238
|
+
if (left + tooltipWidth > window.innerWidth - 20) {
|
|
38239
|
+
left = window.innerWidth - tooltipWidth - 20;
|
|
38240
|
+
}
|
|
38241
|
+
if (left < 10) left = 10;
|
|
38242
|
+
if (top + 500 > window.innerHeight) {
|
|
38243
|
+
top = rect.top - 8 - 500;
|
|
38244
|
+
if (top < 10) top = 10;
|
|
38245
|
+
}
|
|
38246
|
+
container.style.left = left + "px";
|
|
38247
|
+
container.style.top = top + "px";
|
|
38248
|
+
}
|
|
38249
|
+
var ContractSummaryTooltip = {
|
|
38250
|
+
containerId: "myio-contract-summary-tooltip",
|
|
38251
|
+
/**
|
|
38252
|
+
* Get or create container
|
|
38253
|
+
*/
|
|
38254
|
+
getContainer() {
|
|
38255
|
+
injectCSS9();
|
|
38256
|
+
let container = document.getElementById(this.containerId);
|
|
38257
|
+
if (!container) {
|
|
38258
|
+
container = document.createElement("div");
|
|
38259
|
+
container.id = this.containerId;
|
|
38260
|
+
container.className = "myio-contract-summary-tooltip";
|
|
38261
|
+
document.body.appendChild(container);
|
|
38262
|
+
}
|
|
38263
|
+
return container;
|
|
38264
|
+
},
|
|
38265
|
+
/**
|
|
38266
|
+
* Show tooltip
|
|
38267
|
+
*/
|
|
38268
|
+
show(triggerElement, data) {
|
|
38269
|
+
if (state5.hideTimer) {
|
|
38270
|
+
clearTimeout(state5.hideTimer);
|
|
38271
|
+
state5.hideTimer = null;
|
|
38272
|
+
}
|
|
38273
|
+
const container = this.getContainer();
|
|
38274
|
+
container.classList.remove("closing");
|
|
38275
|
+
container.innerHTML = `
|
|
38276
|
+
<div class="myio-contract-summary-tooltip__panel">
|
|
38277
|
+
${generateHeaderHTML5(data)}
|
|
38278
|
+
${generateBodyHTML3(data)}
|
|
38279
|
+
</div>
|
|
38280
|
+
`;
|
|
38281
|
+
positionTooltip5(container, triggerElement);
|
|
38282
|
+
container.classList.add("visible");
|
|
38283
|
+
setupHoverListeners5(container);
|
|
38284
|
+
setupButtonListeners5(container);
|
|
38285
|
+
setupDragListeners5(container);
|
|
38286
|
+
setupDomainToggleListeners(container);
|
|
38287
|
+
},
|
|
38288
|
+
/**
|
|
38289
|
+
* Start delayed hide
|
|
38290
|
+
*/
|
|
38291
|
+
startDelayedHide() {
|
|
38292
|
+
startDelayedHide5();
|
|
38293
|
+
},
|
|
38294
|
+
/**
|
|
38295
|
+
* Hide immediately
|
|
38296
|
+
*/
|
|
38297
|
+
hide() {
|
|
38298
|
+
if (state5.hideTimer) {
|
|
38299
|
+
clearTimeout(state5.hideTimer);
|
|
38300
|
+
state5.hideTimer = null;
|
|
38301
|
+
}
|
|
38302
|
+
state5.isMouseOverTooltip = false;
|
|
38303
|
+
const container = document.getElementById(this.containerId);
|
|
38304
|
+
if (container) {
|
|
38305
|
+
container.classList.remove("visible", "closing");
|
|
38306
|
+
}
|
|
38307
|
+
},
|
|
38308
|
+
/**
|
|
38309
|
+
* Close and reset all states
|
|
38310
|
+
*/
|
|
38311
|
+
close() {
|
|
38312
|
+
state5.isMaximized = false;
|
|
38313
|
+
state5.isDragging = false;
|
|
38314
|
+
state5.savedPosition = null;
|
|
38315
|
+
if (state5.hideTimer) {
|
|
38316
|
+
clearTimeout(state5.hideTimer);
|
|
38317
|
+
state5.hideTimer = null;
|
|
38318
|
+
}
|
|
38319
|
+
state5.isMouseOverTooltip = false;
|
|
38320
|
+
const container = document.getElementById(this.containerId);
|
|
38321
|
+
if (container) {
|
|
38322
|
+
container.classList.remove("visible", "pinned", "maximized", "dragging", "closing");
|
|
38323
|
+
}
|
|
38324
|
+
},
|
|
38325
|
+
/**
|
|
38326
|
+
* Attach tooltip to trigger element with click behavior
|
|
38327
|
+
*/
|
|
38328
|
+
attach(triggerElement, getDataFn) {
|
|
38329
|
+
const self = this;
|
|
38330
|
+
const handleClick = () => {
|
|
38331
|
+
if (state5.hideTimer) {
|
|
38332
|
+
clearTimeout(state5.hideTimer);
|
|
38333
|
+
state5.hideTimer = null;
|
|
38334
|
+
}
|
|
38335
|
+
const data = getDataFn();
|
|
38336
|
+
if (data) {
|
|
38337
|
+
self.show(triggerElement, data);
|
|
38338
|
+
}
|
|
38339
|
+
};
|
|
38340
|
+
triggerElement.addEventListener("click", handleClick);
|
|
38341
|
+
return () => {
|
|
38342
|
+
triggerElement.removeEventListener("click", handleClick);
|
|
38343
|
+
self.hide();
|
|
38344
|
+
};
|
|
38345
|
+
},
|
|
38346
|
+
/**
|
|
38347
|
+
* Attach tooltip with hover behavior
|
|
38348
|
+
*/
|
|
38349
|
+
attachHover(triggerElement, getDataFn) {
|
|
38350
|
+
const self = this;
|
|
38351
|
+
const handleMouseEnter = () => {
|
|
38352
|
+
if (state5.hideTimer) {
|
|
38353
|
+
clearTimeout(state5.hideTimer);
|
|
38354
|
+
state5.hideTimer = null;
|
|
38355
|
+
}
|
|
38356
|
+
const data = getDataFn();
|
|
38357
|
+
if (data) {
|
|
38358
|
+
self.show(triggerElement, data);
|
|
38359
|
+
}
|
|
38360
|
+
};
|
|
38361
|
+
const handleMouseLeave = () => {
|
|
38362
|
+
startDelayedHide5();
|
|
38363
|
+
};
|
|
38364
|
+
triggerElement.addEventListener("mouseenter", handleMouseEnter);
|
|
38365
|
+
triggerElement.addEventListener("mouseleave", handleMouseLeave);
|
|
38366
|
+
return () => {
|
|
38367
|
+
triggerElement.removeEventListener("mouseenter", handleMouseEnter);
|
|
38368
|
+
triggerElement.removeEventListener("mouseleave", handleMouseLeave);
|
|
38369
|
+
self.hide();
|
|
38370
|
+
};
|
|
38371
|
+
},
|
|
38372
|
+
/**
|
|
38373
|
+
* Build contract data from window.CONTRACT_STATE
|
|
38374
|
+
* Helper method to build the data structure from global state
|
|
38375
|
+
*/
|
|
38376
|
+
buildFromGlobalState() {
|
|
38377
|
+
const globalState = window.CONTRACT_STATE;
|
|
38378
|
+
if (!globalState) return null;
|
|
38379
|
+
return {
|
|
38380
|
+
isLoaded: globalState.isLoaded ?? false,
|
|
38381
|
+
isValid: globalState.isValid ?? false,
|
|
38382
|
+
timestamp: globalState.timestamp ?? null,
|
|
38383
|
+
energy: {
|
|
38384
|
+
total: globalState.energy?.total ?? 0,
|
|
38385
|
+
entries: globalState.energy?.entries ?? 0,
|
|
38386
|
+
commonArea: globalState.energy?.commonArea ?? 0,
|
|
38387
|
+
stores: globalState.energy?.stores ?? 0
|
|
38388
|
+
},
|
|
38389
|
+
water: {
|
|
38390
|
+
total: globalState.water?.total ?? 0,
|
|
38391
|
+
entries: globalState.water?.entries ?? 0,
|
|
38392
|
+
commonArea: globalState.water?.commonArea ?? 0,
|
|
38393
|
+
stores: globalState.water?.stores ?? 0
|
|
38394
|
+
},
|
|
38395
|
+
temperature: {
|
|
38396
|
+
total: globalState.temperature?.total ?? 0,
|
|
38397
|
+
internal: globalState.temperature?.internal ?? 0,
|
|
38398
|
+
stores: globalState.temperature?.stores ?? 0
|
|
38399
|
+
},
|
|
38400
|
+
discrepancies: globalState.discrepancies
|
|
38401
|
+
};
|
|
38402
|
+
}
|
|
38403
|
+
};
|
|
38404
|
+
|
|
37407
38405
|
// src/components/ModalHeader/index.ts
|
|
37408
38406
|
var DEFAULT_BG_COLOR = "#3e1a7d";
|
|
37409
38407
|
var DEFAULT_TEXT_COLOR = "white";
|
|
@@ -41168,6 +42166,7 @@ function createDistributionChartWidget(config) {
|
|
|
41168
42166
|
CONSUMPTION_CHART_DEFAULTS,
|
|
41169
42167
|
CONSUMPTION_THEME_COLORS,
|
|
41170
42168
|
ConnectionStatusType,
|
|
42169
|
+
ContractSummaryTooltip,
|
|
41171
42170
|
DEFAULT_CLAMP_RANGE,
|
|
41172
42171
|
DEFAULT_ENERGY_GROUP_COLORS,
|
|
41173
42172
|
DEFAULT_GAS_GROUP_COLORS,
|