myio-js-library 0.1.197 → 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 +1381 -372
- package/dist/index.d.cts +96 -1
- package/dist/index.js +1380 -372
- package/dist/myio-js-library.umd.js +1380 -372
- 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>
|
|
@@ -30969,7 +30987,7 @@ async function openTemperatureModal(params) {
|
|
|
30969
30987
|
const defaultDateRange = getTodaySoFar();
|
|
30970
30988
|
const startTs = params.startDate ? new Date(params.startDate).getTime() : defaultDateRange.startTs;
|
|
30971
30989
|
const endTs = params.endDate ? new Date(params.endDate).getTime() : defaultDateRange.endTs;
|
|
30972
|
-
const
|
|
30990
|
+
const state6 = {
|
|
30973
30991
|
token: params.token,
|
|
30974
30992
|
deviceId: params.deviceId,
|
|
30975
30993
|
label: params.label || "Sensor de Temperatura",
|
|
@@ -30992,58 +31010,58 @@ async function openTemperatureModal(params) {
|
|
|
30992
31010
|
};
|
|
30993
31011
|
const savedGranularity = localStorage.getItem("myio-temp-modal-granularity");
|
|
30994
31012
|
const savedTheme = localStorage.getItem("myio-temp-modal-theme");
|
|
30995
|
-
if (savedGranularity)
|
|
30996
|
-
if (savedTheme)
|
|
31013
|
+
if (savedGranularity) state6.granularity = savedGranularity;
|
|
31014
|
+
if (savedTheme) state6.theme = savedTheme;
|
|
30997
31015
|
const modalContainer = document.createElement("div");
|
|
30998
31016
|
modalContainer.id = modalId;
|
|
30999
31017
|
document.body.appendChild(modalContainer);
|
|
31000
|
-
renderModal(modalContainer,
|
|
31018
|
+
renderModal(modalContainer, state6, modalId);
|
|
31001
31019
|
try {
|
|
31002
|
-
|
|
31003
|
-
|
|
31004
|
-
|
|
31005
|
-
renderModal(modalContainer,
|
|
31006
|
-
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);
|
|
31007
31025
|
} catch (error) {
|
|
31008
31026
|
console.error("[TemperatureModal] Error fetching data:", error);
|
|
31009
|
-
|
|
31010
|
-
renderModal(modalContainer,
|
|
31027
|
+
state6.isLoading = false;
|
|
31028
|
+
renderModal(modalContainer, state6, modalId, error);
|
|
31011
31029
|
}
|
|
31012
|
-
await setupEventListeners(modalContainer,
|
|
31030
|
+
await setupEventListeners(modalContainer, state6, modalId, params.onClose);
|
|
31013
31031
|
return {
|
|
31014
31032
|
destroy: () => {
|
|
31015
31033
|
modalContainer.remove();
|
|
31016
31034
|
params.onClose?.();
|
|
31017
31035
|
},
|
|
31018
31036
|
updateData: async (startDate, endDate, granularity) => {
|
|
31019
|
-
|
|
31020
|
-
|
|
31021
|
-
if (granularity)
|
|
31022
|
-
|
|
31023
|
-
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);
|
|
31024
31042
|
try {
|
|
31025
|
-
|
|
31026
|
-
|
|
31027
|
-
|
|
31028
|
-
renderModal(modalContainer,
|
|
31029
|
-
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);
|
|
31030
31048
|
} catch (error) {
|
|
31031
31049
|
console.error("[TemperatureModal] Error updating data:", error);
|
|
31032
|
-
|
|
31033
|
-
renderModal(modalContainer,
|
|
31050
|
+
state6.isLoading = false;
|
|
31051
|
+
renderModal(modalContainer, state6, modalId, error);
|
|
31034
31052
|
}
|
|
31035
31053
|
}
|
|
31036
31054
|
};
|
|
31037
31055
|
}
|
|
31038
|
-
function renderModal(container,
|
|
31039
|
-
const colors = getThemeColors(
|
|
31040
|
-
const startDateStr = new Date(
|
|
31041
|
-
const endDateStr = new Date(
|
|
31042
|
-
const statusText =
|
|
31043
|
-
const statusColor =
|
|
31044
|
-
const rangeText =
|
|
31045
|
-
const startDateInput = new Date(
|
|
31046
|
-
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);
|
|
31047
31065
|
const isMaximized = container.__isMaximized || false;
|
|
31048
31066
|
const contentMaxWidth = isMaximized ? "100%" : "900px";
|
|
31049
31067
|
const contentMaxHeight = isMaximized ? "100vh" : "95vh";
|
|
@@ -31070,7 +31088,7 @@ function renderModal(container, state5, modalId, error) {
|
|
|
31070
31088
|
min-height: 20px;
|
|
31071
31089
|
">
|
|
31072
31090
|
<h2 style="margin: 6px; font-size: 18px; font-weight: 600; color: white; line-height: 2;">
|
|
31073
|
-
\u{1F321}\uFE0F ${
|
|
31091
|
+
\u{1F321}\uFE0F ${state6.label} - Hist\xF3rico de Temperatura
|
|
31074
31092
|
</h2>
|
|
31075
31093
|
<div style="display: flex; gap: 4px; align-items: center;">
|
|
31076
31094
|
<!-- Theme Toggle -->
|
|
@@ -31078,7 +31096,7 @@ function renderModal(container, state5, modalId, error) {
|
|
|
31078
31096
|
background: none; border: none; font-size: 16px; cursor: pointer;
|
|
31079
31097
|
padding: 4px 8px; border-radius: 6px; color: rgba(255,255,255,0.8);
|
|
31080
31098
|
transition: background-color 0.2s;
|
|
31081
|
-
">${
|
|
31099
|
+
">${state6.theme === "dark" ? "\u2600\uFE0F" : "\u{1F319}"}</button>
|
|
31082
31100
|
<!-- Maximize Button -->
|
|
31083
31101
|
<button id="${modalId}-maximize" title="${isMaximized ? "Restaurar" : "Maximizar"}" style="
|
|
31084
31102
|
background: none; border: none; font-size: 16px; cursor: pointer;
|
|
@@ -31100,7 +31118,7 @@ function renderModal(container, state5, modalId, error) {
|
|
|
31100
31118
|
<!-- Controls Row -->
|
|
31101
31119
|
<div style="
|
|
31102
31120
|
display: flex; gap: 16px; flex-wrap: wrap; align-items: flex-end;
|
|
31103
|
-
margin-bottom: 16px; padding: 16px; background: ${
|
|
31121
|
+
margin-bottom: 16px; padding: 16px; background: ${state6.theme === "dark" ? "rgba(255,255,255,0.05)" : "#f7f7f7"};
|
|
31104
31122
|
border-radius: 6px; border: 1px solid ${colors.border};
|
|
31105
31123
|
">
|
|
31106
31124
|
<!-- Granularity Select -->
|
|
@@ -31113,8 +31131,8 @@ function renderModal(container, state5, modalId, error) {
|
|
|
31113
31131
|
font-size: 14px; color: ${colors.text}; background: ${colors.surface};
|
|
31114
31132
|
cursor: pointer; min-width: 130px;
|
|
31115
31133
|
">
|
|
31116
|
-
<option value="hour" ${
|
|
31117
|
-
<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>
|
|
31118
31136
|
</select>
|
|
31119
31137
|
</div>
|
|
31120
31138
|
<!-- Day Period Filter (Multiselect) -->
|
|
@@ -31128,7 +31146,7 @@ function renderModal(container, state5, modalId, error) {
|
|
|
31128
31146
|
cursor: pointer; min-width: 180px; text-align: left;
|
|
31129
31147
|
display: flex; align-items: center; justify-content: space-between; gap: 8px;
|
|
31130
31148
|
">
|
|
31131
|
-
<span>${getSelectedPeriodsLabel(
|
|
31149
|
+
<span>${getSelectedPeriodsLabel(state6.selectedPeriods)}</span>
|
|
31132
31150
|
<span style="font-size: 10px;">\u25BC</span>
|
|
31133
31151
|
</button>
|
|
31134
31152
|
<div id="${modalId}-period-dropdown" style="
|
|
@@ -31141,12 +31159,12 @@ function renderModal(container, state5, modalId, error) {
|
|
|
31141
31159
|
<label style="
|
|
31142
31160
|
display: flex; align-items: center; gap: 8px; padding: 8px 12px;
|
|
31143
31161
|
cursor: pointer; font-size: 13px; color: ${colors.text};
|
|
31144
|
-
" onmouseover="this.style.background='${
|
|
31162
|
+
" onmouseover="this.style.background='${state6.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"}'"
|
|
31145
31163
|
onmouseout="this.style.background='transparent'">
|
|
31146
31164
|
<input type="checkbox"
|
|
31147
31165
|
name="${modalId}-period"
|
|
31148
31166
|
value="${period.id}"
|
|
31149
|
-
${
|
|
31167
|
+
${state6.selectedPeriods.includes(period.id) ? "checked" : ""}
|
|
31150
31168
|
style="width: 16px; height: 16px; cursor: pointer; accent-color: #3e1a7d;">
|
|
31151
31169
|
${period.label}
|
|
31152
31170
|
</label>
|
|
@@ -31154,13 +31172,13 @@ function renderModal(container, state5, modalId, error) {
|
|
|
31154
31172
|
<div style="border-top: 1px solid ${colors.border}; margin-top: 8px; padding-top: 8px;">
|
|
31155
31173
|
<button id="${modalId}-period-select-all" type="button" style="
|
|
31156
31174
|
width: calc(100% - 16px); margin: 0 8px 4px; padding: 6px;
|
|
31157
|
-
background: ${
|
|
31175
|
+
background: ${state6.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"};
|
|
31158
31176
|
border: none; border-radius: 4px; cursor: pointer;
|
|
31159
31177
|
font-size: 12px; color: ${colors.text};
|
|
31160
31178
|
">Selecionar Todos</button>
|
|
31161
31179
|
<button id="${modalId}-period-clear" type="button" style="
|
|
31162
31180
|
width: calc(100% - 16px); margin: 0 8px; padding: 6px;
|
|
31163
|
-
background: ${
|
|
31181
|
+
background: ${state6.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"};
|
|
31164
31182
|
border: none; border-radius: 4px; cursor: pointer;
|
|
31165
31183
|
font-size: 12px; color: ${colors.text};
|
|
31166
31184
|
">Limpar Sele\xE7\xE3o</button>
|
|
@@ -31185,8 +31203,8 @@ function renderModal(container, state5, modalId, error) {
|
|
|
31185
31203
|
font-size: 14px; font-weight: 500; height: 38px;
|
|
31186
31204
|
display: flex; align-items: center; gap: 8px;
|
|
31187
31205
|
font-family: 'Roboto', Arial, sans-serif;
|
|
31188
|
-
" ${
|
|
31189
|
-
${
|
|
31206
|
+
" ${state6.isLoading ? "disabled" : ""}>
|
|
31207
|
+
${state6.isLoading ? '<span style="animation: spin 1s linear infinite; display: inline-block;">\u21BB</span> Carregando...' : "Carregar"}
|
|
31190
31208
|
</button>
|
|
31191
31209
|
</div>
|
|
31192
31210
|
|
|
@@ -31197,40 +31215,40 @@ function renderModal(container, state5, modalId, error) {
|
|
|
31197
31215
|
">
|
|
31198
31216
|
<!-- Current Temperature -->
|
|
31199
31217
|
<div style="
|
|
31200
|
-
padding: 16px; background: ${
|
|
31218
|
+
padding: 16px; background: ${state6.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
|
|
31201
31219
|
border-radius: 12px; border: 1px solid ${colors.border};
|
|
31202
31220
|
">
|
|
31203
31221
|
<span style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500;">Temperatura Atual</span>
|
|
31204
31222
|
<div style="font-weight: 700; font-size: 24px; color: ${statusColor}; margin-top: 4px;">
|
|
31205
|
-
${
|
|
31223
|
+
${state6.currentTemperature !== null ? formatTemperature(state6.currentTemperature) : "N/A"}
|
|
31206
31224
|
</div>
|
|
31207
31225
|
<div style="font-size: 11px; color: ${statusColor}; margin-top: 2px;">${statusText}</div>
|
|
31208
31226
|
</div>
|
|
31209
31227
|
<!-- Average -->
|
|
31210
31228
|
<div style="
|
|
31211
|
-
padding: 16px; background: ${
|
|
31229
|
+
padding: 16px; background: ${state6.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
|
|
31212
31230
|
border-radius: 12px; border: 1px solid ${colors.border};
|
|
31213
31231
|
">
|
|
31214
31232
|
<span style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500;">M\xE9dia do Per\xEDodo</span>
|
|
31215
31233
|
<div id="${modalId}-avg" style="font-weight: 600; font-size: 20px; color: ${colors.text}; margin-top: 4px;">
|
|
31216
|
-
${
|
|
31234
|
+
${state6.stats.count > 0 ? formatTemperature(state6.stats.avg) : "N/A"}
|
|
31217
31235
|
</div>
|
|
31218
31236
|
<div style="font-size: 11px; color: ${colors.textMuted}; margin-top: 2px;">${startDateStr} - ${endDateStr}</div>
|
|
31219
31237
|
</div>
|
|
31220
31238
|
<!-- Min/Max -->
|
|
31221
31239
|
<div style="
|
|
31222
|
-
padding: 16px; background: ${
|
|
31240
|
+
padding: 16px; background: ${state6.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
|
|
31223
31241
|
border-radius: 12px; border: 1px solid ${colors.border};
|
|
31224
31242
|
">
|
|
31225
31243
|
<span style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500;">Min / Max</span>
|
|
31226
31244
|
<div id="${modalId}-minmax" style="font-weight: 600; font-size: 20px; color: ${colors.text}; margin-top: 4px;">
|
|
31227
|
-
${
|
|
31245
|
+
${state6.stats.count > 0 ? `${formatTemperature(state6.stats.min)} / ${formatTemperature(state6.stats.max)}` : "N/A"}
|
|
31228
31246
|
</div>
|
|
31229
|
-
<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>
|
|
31230
31248
|
</div>
|
|
31231
31249
|
<!-- Ideal Range -->
|
|
31232
31250
|
<div style="
|
|
31233
|
-
padding: 16px; background: ${
|
|
31251
|
+
padding: 16px; background: ${state6.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
|
|
31234
31252
|
border-radius: 12px; border: 1px solid ${colors.border};
|
|
31235
31253
|
">
|
|
31236
31254
|
<span style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500;">Faixa Ideal</span>
|
|
@@ -31246,18 +31264,18 @@ function renderModal(container, state5, modalId, error) {
|
|
|
31246
31264
|
Hist\xF3rico de Temperatura
|
|
31247
31265
|
</h3>
|
|
31248
31266
|
<div id="${modalId}-chart" style="
|
|
31249
|
-
height: 320px; background: ${
|
|
31267
|
+
height: 320px; background: ${state6.theme === "dark" ? "rgba(255,255,255,0.03)" : "#fafafa"};
|
|
31250
31268
|
border-radius: 12px; display: flex; justify-content: center; align-items: center;
|
|
31251
31269
|
border: 1px solid ${colors.border}; position: relative;
|
|
31252
31270
|
">
|
|
31253
|
-
${
|
|
31271
|
+
${state6.isLoading ? `<div style="text-align: center; color: ${colors.textMuted};">
|
|
31254
31272
|
<div style="animation: spin 1s linear infinite; font-size: 32px; margin-bottom: 8px;">\u21BB</div>
|
|
31255
31273
|
<div>Carregando dados...</div>
|
|
31256
31274
|
</div>` : error ? `<div style="text-align: center; color: ${colors.danger};">
|
|
31257
31275
|
<div style="font-size: 32px; margin-bottom: 8px;">\u26A0\uFE0F</div>
|
|
31258
31276
|
<div>Erro ao carregar dados</div>
|
|
31259
31277
|
<div style="font-size: 12px; margin-top: 4px;">${error.message}</div>
|
|
31260
|
-
</div>` :
|
|
31278
|
+
</div>` : state6.data.length === 0 ? `<div style="text-align: center; color: ${colors.textMuted};">
|
|
31261
31279
|
<div style="font-size: 32px; margin-bottom: 8px;">\u{1F4ED}</div>
|
|
31262
31280
|
<div>Sem dados para o per\xEDodo selecionado</div>
|
|
31263
31281
|
</div>` : `<canvas id="${modalId}-canvas" style="width: 100%; height: 100%;"></canvas>`}
|
|
@@ -31267,12 +31285,12 @@ function renderModal(container, state5, modalId, error) {
|
|
|
31267
31285
|
<!-- Actions -->
|
|
31268
31286
|
<div style="display: flex; justify-content: flex-end; gap: 12px;">
|
|
31269
31287
|
<button id="${modalId}-export" style="
|
|
31270
|
-
background: ${
|
|
31288
|
+
background: ${state6.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f7f7f7"};
|
|
31271
31289
|
color: ${colors.text}; border: 1px solid ${colors.border};
|
|
31272
31290
|
padding: 8px 16px; border-radius: 6px; cursor: pointer;
|
|
31273
31291
|
font-size: 14px; display: flex; align-items: center; gap: 8px;
|
|
31274
31292
|
font-family: 'Roboto', Arial, sans-serif;
|
|
31275
|
-
" ${
|
|
31293
|
+
" ${state6.data.length === 0 ? "disabled" : ""}>
|
|
31276
31294
|
\u{1F4E5} Exportar CSV
|
|
31277
31295
|
</button>
|
|
31278
31296
|
<button id="${modalId}-close-btn" style="
|
|
@@ -31323,14 +31341,14 @@ function renderModal(container, state5, modalId, error) {
|
|
|
31323
31341
|
</style>
|
|
31324
31342
|
`;
|
|
31325
31343
|
}
|
|
31326
|
-
function drawChart(modalId,
|
|
31344
|
+
function drawChart(modalId, state6) {
|
|
31327
31345
|
const chartContainer = document.getElementById(`${modalId}-chart`);
|
|
31328
31346
|
const canvas = document.getElementById(`${modalId}-canvas`);
|
|
31329
|
-
if (!chartContainer || !canvas ||
|
|
31347
|
+
if (!chartContainer || !canvas || state6.data.length === 0) return;
|
|
31330
31348
|
const ctx = canvas.getContext("2d");
|
|
31331
31349
|
if (!ctx) return;
|
|
31332
|
-
const colors = getThemeColors(
|
|
31333
|
-
const filteredData = filterByDayPeriods(
|
|
31350
|
+
const colors = getThemeColors(state6.theme);
|
|
31351
|
+
const filteredData = filterByDayPeriods(state6.data, state6.selectedPeriods);
|
|
31334
31352
|
if (filteredData.length === 0) {
|
|
31335
31353
|
canvas.width = chartContainer.clientWidth;
|
|
31336
31354
|
canvas.height = chartContainer.clientHeight;
|
|
@@ -31341,14 +31359,14 @@ function drawChart(modalId, state5) {
|
|
|
31341
31359
|
return;
|
|
31342
31360
|
}
|
|
31343
31361
|
let chartData;
|
|
31344
|
-
if (
|
|
31362
|
+
if (state6.granularity === "hour") {
|
|
31345
31363
|
const interpolated = interpolateTemperature(filteredData, {
|
|
31346
31364
|
intervalMinutes: 30,
|
|
31347
|
-
startTs:
|
|
31348
|
-
endTs:
|
|
31349
|
-
clampRange:
|
|
31365
|
+
startTs: state6.startTs,
|
|
31366
|
+
endTs: state6.endTs,
|
|
31367
|
+
clampRange: state6.clampRange
|
|
31350
31368
|
});
|
|
31351
|
-
const filteredInterpolated = filterByDayPeriods(interpolated,
|
|
31369
|
+
const filteredInterpolated = filterByDayPeriods(interpolated, state6.selectedPeriods);
|
|
31352
31370
|
chartData = filteredInterpolated.map((item) => ({
|
|
31353
31371
|
x: item.ts,
|
|
31354
31372
|
y: Number(item.value),
|
|
@@ -31356,7 +31374,7 @@ function drawChart(modalId, state5) {
|
|
|
31356
31374
|
screenY: 0
|
|
31357
31375
|
}));
|
|
31358
31376
|
} else {
|
|
31359
|
-
const daily = aggregateByDay(filteredData,
|
|
31377
|
+
const daily = aggregateByDay(filteredData, state6.clampRange);
|
|
31360
31378
|
chartData = daily.map((item) => ({
|
|
31361
31379
|
x: item.dateTs,
|
|
31362
31380
|
y: item.avg,
|
|
@@ -31374,12 +31392,12 @@ function drawChart(modalId, state5) {
|
|
|
31374
31392
|
const paddingRight = 20;
|
|
31375
31393
|
const paddingTop = 20;
|
|
31376
31394
|
const paddingBottom = 55;
|
|
31377
|
-
const isPeriodsFiltered =
|
|
31395
|
+
const isPeriodsFiltered = state6.selectedPeriods.length < 4 && state6.selectedPeriods.length > 0;
|
|
31378
31396
|
const values = chartData.map((d) => d.y);
|
|
31379
31397
|
const dataMin = Math.min(...values);
|
|
31380
31398
|
const dataMax = Math.max(...values);
|
|
31381
|
-
const thresholdMin =
|
|
31382
|
-
const thresholdMax =
|
|
31399
|
+
const thresholdMin = state6.temperatureMin !== null ? state6.temperatureMin : dataMin;
|
|
31400
|
+
const thresholdMax = state6.temperatureMax !== null ? state6.temperatureMax : dataMax;
|
|
31383
31401
|
const minY = Math.min(dataMin, thresholdMin) - 1;
|
|
31384
31402
|
const maxY = Math.max(dataMax, thresholdMax) + 1;
|
|
31385
31403
|
const chartWidth = width - paddingLeft - paddingRight;
|
|
@@ -31411,9 +31429,9 @@ function drawChart(modalId, state5) {
|
|
|
31411
31429
|
ctx.lineTo(width - paddingRight, y);
|
|
31412
31430
|
ctx.stroke();
|
|
31413
31431
|
}
|
|
31414
|
-
if (
|
|
31415
|
-
const rangeMinY = height - paddingBottom - (
|
|
31416
|
-
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;
|
|
31417
31435
|
ctx.fillStyle = "rgba(76, 175, 80, 0.1)";
|
|
31418
31436
|
ctx.fillRect(paddingLeft, rangeMaxY, chartWidth, rangeMinY - rangeMaxY);
|
|
31419
31437
|
ctx.strokeStyle = colors.success;
|
|
@@ -31455,10 +31473,10 @@ function drawChart(modalId, state5) {
|
|
|
31455
31473
|
const point = chartData[i];
|
|
31456
31474
|
const date = new Date(point.x);
|
|
31457
31475
|
let label;
|
|
31458
|
-
if (
|
|
31459
|
-
label = date.toLocaleTimeString(
|
|
31476
|
+
if (state6.granularity === "hour") {
|
|
31477
|
+
label = date.toLocaleTimeString(state6.locale, { hour: "2-digit", minute: "2-digit" });
|
|
31460
31478
|
} else {
|
|
31461
|
-
label = date.toLocaleDateString(
|
|
31479
|
+
label = date.toLocaleDateString(state6.locale, { day: "2-digit", month: "2-digit" });
|
|
31462
31480
|
}
|
|
31463
31481
|
ctx.strokeStyle = colors.chartGrid;
|
|
31464
31482
|
ctx.lineWidth = 1;
|
|
@@ -31476,16 +31494,16 @@ function drawChart(modalId, state5) {
|
|
|
31476
31494
|
ctx.lineTo(paddingLeft, height - paddingBottom);
|
|
31477
31495
|
ctx.lineTo(width - paddingRight, height - paddingBottom);
|
|
31478
31496
|
ctx.stroke();
|
|
31479
|
-
setupChartTooltip(canvas, chartContainer, chartData,
|
|
31497
|
+
setupChartTooltip(canvas, chartContainer, chartData, state6, colors);
|
|
31480
31498
|
}
|
|
31481
|
-
function setupChartTooltip(canvas, container, chartData,
|
|
31499
|
+
function setupChartTooltip(canvas, container, chartData, state6, colors) {
|
|
31482
31500
|
const existingTooltip = container.querySelector(".myio-chart-tooltip");
|
|
31483
31501
|
if (existingTooltip) existingTooltip.remove();
|
|
31484
31502
|
const tooltip = document.createElement("div");
|
|
31485
31503
|
tooltip.className = "myio-chart-tooltip";
|
|
31486
31504
|
tooltip.style.cssText = `
|
|
31487
31505
|
position: absolute;
|
|
31488
|
-
background: ${
|
|
31506
|
+
background: ${state6.theme === "dark" ? "rgba(30, 30, 40, 0.95)" : "rgba(255, 255, 255, 0.98)"};
|
|
31489
31507
|
border: 1px solid ${colors.border};
|
|
31490
31508
|
border-radius: 8px;
|
|
31491
31509
|
padding: 10px 14px;
|
|
@@ -31522,17 +31540,17 @@ function setupChartTooltip(canvas, container, chartData, state5, colors) {
|
|
|
31522
31540
|
if (point) {
|
|
31523
31541
|
const date = new Date(point.x);
|
|
31524
31542
|
let dateStr;
|
|
31525
|
-
if (
|
|
31526
|
-
dateStr = date.toLocaleDateString(
|
|
31543
|
+
if (state6.granularity === "hour") {
|
|
31544
|
+
dateStr = date.toLocaleDateString(state6.locale, {
|
|
31527
31545
|
day: "2-digit",
|
|
31528
31546
|
month: "2-digit",
|
|
31529
31547
|
year: "numeric"
|
|
31530
|
-
}) + " " + date.toLocaleTimeString(
|
|
31548
|
+
}) + " " + date.toLocaleTimeString(state6.locale, {
|
|
31531
31549
|
hour: "2-digit",
|
|
31532
31550
|
minute: "2-digit"
|
|
31533
31551
|
});
|
|
31534
31552
|
} else {
|
|
31535
|
-
dateStr = date.toLocaleDateString(
|
|
31553
|
+
dateStr = date.toLocaleDateString(state6.locale, {
|
|
31536
31554
|
day: "2-digit",
|
|
31537
31555
|
month: "2-digit",
|
|
31538
31556
|
year: "numeric"
|
|
@@ -31570,7 +31588,7 @@ function setupChartTooltip(canvas, container, chartData, state5, colors) {
|
|
|
31570
31588
|
canvas.style.cursor = "default";
|
|
31571
31589
|
});
|
|
31572
31590
|
}
|
|
31573
|
-
async function setupEventListeners(container,
|
|
31591
|
+
async function setupEventListeners(container, state6, modalId, onClose) {
|
|
31574
31592
|
const closeModal = () => {
|
|
31575
31593
|
container.remove();
|
|
31576
31594
|
onClose?.();
|
|
@@ -31581,19 +31599,19 @@ async function setupEventListeners(container, state5, modalId, onClose) {
|
|
|
31581
31599
|
document.getElementById(`${modalId}-close`)?.addEventListener("click", closeModal);
|
|
31582
31600
|
document.getElementById(`${modalId}-close-btn`)?.addEventListener("click", closeModal);
|
|
31583
31601
|
const dateRangeInput = document.getElementById(`${modalId}-date-range`);
|
|
31584
|
-
if (dateRangeInput && !
|
|
31602
|
+
if (dateRangeInput && !state6.dateRangePicker) {
|
|
31585
31603
|
try {
|
|
31586
|
-
|
|
31587
|
-
presetStart: new Date(
|
|
31588
|
-
presetEnd: new Date(
|
|
31604
|
+
state6.dateRangePicker = await createDateRangePicker2(dateRangeInput, {
|
|
31605
|
+
presetStart: new Date(state6.startTs).toISOString(),
|
|
31606
|
+
presetEnd: new Date(state6.endTs).toISOString(),
|
|
31589
31607
|
includeTime: true,
|
|
31590
31608
|
timePrecision: "minute",
|
|
31591
31609
|
maxRangeDays: 90,
|
|
31592
|
-
locale:
|
|
31610
|
+
locale: state6.locale,
|
|
31593
31611
|
parentEl: container.querySelector(".myio-temp-modal-content"),
|
|
31594
31612
|
onApply: (result) => {
|
|
31595
|
-
|
|
31596
|
-
|
|
31613
|
+
state6.startTs = new Date(result.startISO).getTime();
|
|
31614
|
+
state6.endTs = new Date(result.endISO).getTime();
|
|
31597
31615
|
console.log("[TemperatureModal] Date range applied:", result);
|
|
31598
31616
|
}
|
|
31599
31617
|
});
|
|
@@ -31602,19 +31620,19 @@ async function setupEventListeners(container, state5, modalId, onClose) {
|
|
|
31602
31620
|
}
|
|
31603
31621
|
}
|
|
31604
31622
|
document.getElementById(`${modalId}-theme-toggle`)?.addEventListener("click", async () => {
|
|
31605
|
-
|
|
31606
|
-
localStorage.setItem("myio-temp-modal-theme",
|
|
31607
|
-
|
|
31608
|
-
renderModal(container,
|
|
31609
|
-
if (
|
|
31610
|
-
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);
|
|
31611
31629
|
});
|
|
31612
31630
|
document.getElementById(`${modalId}-maximize`)?.addEventListener("click", async () => {
|
|
31613
31631
|
container.__isMaximized = !container.__isMaximized;
|
|
31614
|
-
|
|
31615
|
-
renderModal(container,
|
|
31616
|
-
if (
|
|
31617
|
-
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);
|
|
31618
31636
|
});
|
|
31619
31637
|
const periodBtn = document.getElementById(`${modalId}-period-btn`);
|
|
31620
31638
|
const periodDropdown = document.getElementById(`${modalId}-period-dropdown`);
|
|
@@ -31633,71 +31651,71 @@ async function setupEventListeners(container, state5, modalId, onClose) {
|
|
|
31633
31651
|
periodCheckboxes.forEach((checkbox) => {
|
|
31634
31652
|
checkbox.addEventListener("change", () => {
|
|
31635
31653
|
const checked = Array.from(periodCheckboxes).filter((cb) => cb.checked).map((cb) => cb.value);
|
|
31636
|
-
|
|
31654
|
+
state6.selectedPeriods = checked;
|
|
31637
31655
|
const btnLabel = periodBtn?.querySelector("span:first-child");
|
|
31638
31656
|
if (btnLabel) {
|
|
31639
|
-
btnLabel.textContent = getSelectedPeriodsLabel(
|
|
31657
|
+
btnLabel.textContent = getSelectedPeriodsLabel(state6.selectedPeriods);
|
|
31640
31658
|
}
|
|
31641
|
-
if (
|
|
31659
|
+
if (state6.data.length > 0) drawChart(modalId, state6);
|
|
31642
31660
|
});
|
|
31643
31661
|
});
|
|
31644
31662
|
document.getElementById(`${modalId}-period-select-all`)?.addEventListener("click", () => {
|
|
31645
31663
|
periodCheckboxes.forEach((cb) => {
|
|
31646
31664
|
cb.checked = true;
|
|
31647
31665
|
});
|
|
31648
|
-
|
|
31666
|
+
state6.selectedPeriods = ["madrugada", "manha", "tarde", "noite"];
|
|
31649
31667
|
const btnLabel = periodBtn?.querySelector("span:first-child");
|
|
31650
31668
|
if (btnLabel) {
|
|
31651
|
-
btnLabel.textContent = getSelectedPeriodsLabel(
|
|
31669
|
+
btnLabel.textContent = getSelectedPeriodsLabel(state6.selectedPeriods);
|
|
31652
31670
|
}
|
|
31653
|
-
if (
|
|
31671
|
+
if (state6.data.length > 0) drawChart(modalId, state6);
|
|
31654
31672
|
});
|
|
31655
31673
|
document.getElementById(`${modalId}-period-clear`)?.addEventListener("click", () => {
|
|
31656
31674
|
periodCheckboxes.forEach((cb) => {
|
|
31657
31675
|
cb.checked = false;
|
|
31658
31676
|
});
|
|
31659
|
-
|
|
31677
|
+
state6.selectedPeriods = [];
|
|
31660
31678
|
const btnLabel = periodBtn?.querySelector("span:first-child");
|
|
31661
31679
|
if (btnLabel) {
|
|
31662
|
-
btnLabel.textContent = getSelectedPeriodsLabel(
|
|
31680
|
+
btnLabel.textContent = getSelectedPeriodsLabel(state6.selectedPeriods);
|
|
31663
31681
|
}
|
|
31664
|
-
if (
|
|
31682
|
+
if (state6.data.length > 0) drawChart(modalId, state6);
|
|
31665
31683
|
});
|
|
31666
31684
|
document.getElementById(`${modalId}-granularity`)?.addEventListener("change", (e) => {
|
|
31667
|
-
|
|
31668
|
-
localStorage.setItem("myio-temp-modal-granularity",
|
|
31669
|
-
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);
|
|
31670
31688
|
});
|
|
31671
31689
|
document.getElementById(`${modalId}-query`)?.addEventListener("click", async () => {
|
|
31672
|
-
if (
|
|
31690
|
+
if (state6.startTs >= state6.endTs) {
|
|
31673
31691
|
alert("Por favor, selecione um per\xEDodo v\xE1lido");
|
|
31674
31692
|
return;
|
|
31675
31693
|
}
|
|
31676
|
-
|
|
31677
|
-
|
|
31678
|
-
renderModal(container,
|
|
31694
|
+
state6.isLoading = true;
|
|
31695
|
+
state6.dateRangePicker = null;
|
|
31696
|
+
renderModal(container, state6, modalId);
|
|
31679
31697
|
try {
|
|
31680
|
-
|
|
31681
|
-
|
|
31682
|
-
|
|
31683
|
-
renderModal(container,
|
|
31684
|
-
drawChart(modalId,
|
|
31685
|
-
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);
|
|
31686
31704
|
} catch (error) {
|
|
31687
31705
|
console.error("[TemperatureModal] Error fetching data:", error);
|
|
31688
|
-
|
|
31689
|
-
renderModal(container,
|
|
31690
|
-
await setupEventListeners(container,
|
|
31706
|
+
state6.isLoading = false;
|
|
31707
|
+
renderModal(container, state6, modalId, error);
|
|
31708
|
+
await setupEventListeners(container, state6, modalId, onClose);
|
|
31691
31709
|
}
|
|
31692
31710
|
});
|
|
31693
31711
|
document.getElementById(`${modalId}-export`)?.addEventListener("click", () => {
|
|
31694
|
-
if (
|
|
31695
|
-
const startDateStr = new Date(
|
|
31696
|
-
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, "-");
|
|
31697
31715
|
exportTemperatureCSV(
|
|
31698
|
-
|
|
31699
|
-
|
|
31700
|
-
|
|
31716
|
+
state6.data,
|
|
31717
|
+
state6.label,
|
|
31718
|
+
state6.stats,
|
|
31701
31719
|
startDateStr,
|
|
31702
31720
|
endDateStr
|
|
31703
31721
|
);
|
|
@@ -31710,7 +31728,7 @@ async function openTemperatureComparisonModal(params) {
|
|
|
31710
31728
|
const defaultDateRange = getTodaySoFar();
|
|
31711
31729
|
const startTs = params.startDate ? new Date(params.startDate).getTime() : defaultDateRange.startTs;
|
|
31712
31730
|
const endTs = params.endDate ? new Date(params.endDate).getTime() : defaultDateRange.endTs;
|
|
31713
|
-
const
|
|
31731
|
+
const state6 = {
|
|
31714
31732
|
token: params.token,
|
|
31715
31733
|
devices: params.devices,
|
|
31716
31734
|
startTs,
|
|
@@ -31729,44 +31747,44 @@ async function openTemperatureComparisonModal(params) {
|
|
|
31729
31747
|
};
|
|
31730
31748
|
const savedGranularity = localStorage.getItem("myio-temp-comparison-granularity");
|
|
31731
31749
|
const savedTheme = localStorage.getItem("myio-temp-comparison-theme");
|
|
31732
|
-
if (savedGranularity)
|
|
31733
|
-
if (savedTheme)
|
|
31750
|
+
if (savedGranularity) state6.granularity = savedGranularity;
|
|
31751
|
+
if (savedTheme) state6.theme = savedTheme;
|
|
31734
31752
|
const modalContainer = document.createElement("div");
|
|
31735
31753
|
modalContainer.id = modalId;
|
|
31736
31754
|
document.body.appendChild(modalContainer);
|
|
31737
|
-
renderModal2(modalContainer,
|
|
31738
|
-
await fetchAllDevicesData(
|
|
31739
|
-
renderModal2(modalContainer,
|
|
31740
|
-
drawComparisonChart(modalId,
|
|
31741
|
-
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);
|
|
31742
31760
|
return {
|
|
31743
31761
|
destroy: () => {
|
|
31744
31762
|
modalContainer.remove();
|
|
31745
31763
|
params.onClose?.();
|
|
31746
31764
|
},
|
|
31747
31765
|
updateData: async (startDate, endDate, granularity) => {
|
|
31748
|
-
|
|
31749
|
-
|
|
31750
|
-
if (granularity)
|
|
31751
|
-
|
|
31752
|
-
renderModal2(modalContainer,
|
|
31753
|
-
await fetchAllDevicesData(
|
|
31754
|
-
renderModal2(modalContainer,
|
|
31755
|
-
drawComparisonChart(modalId,
|
|
31756
|
-
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);
|
|
31757
31775
|
}
|
|
31758
31776
|
};
|
|
31759
31777
|
}
|
|
31760
|
-
async function fetchAllDevicesData(
|
|
31761
|
-
|
|
31762
|
-
|
|
31778
|
+
async function fetchAllDevicesData(state6) {
|
|
31779
|
+
state6.isLoading = true;
|
|
31780
|
+
state6.deviceData = [];
|
|
31763
31781
|
try {
|
|
31764
31782
|
const results = await Promise.all(
|
|
31765
|
-
|
|
31783
|
+
state6.devices.map(async (device, index) => {
|
|
31766
31784
|
const deviceId = device.tbId || device.id;
|
|
31767
31785
|
try {
|
|
31768
|
-
const data = await fetchTemperatureData(
|
|
31769
|
-
const stats = calculateStats(data,
|
|
31786
|
+
const data = await fetchTemperatureData(state6.token, deviceId, state6.startTs, state6.endTs);
|
|
31787
|
+
const stats = calculateStats(data, state6.clampRange);
|
|
31770
31788
|
return {
|
|
31771
31789
|
device,
|
|
31772
31790
|
data,
|
|
@@ -31784,21 +31802,21 @@ async function fetchAllDevicesData(state5) {
|
|
|
31784
31802
|
}
|
|
31785
31803
|
})
|
|
31786
31804
|
);
|
|
31787
|
-
|
|
31805
|
+
state6.deviceData = results;
|
|
31788
31806
|
} catch (error) {
|
|
31789
31807
|
console.error("[TemperatureComparisonModal] Error fetching data:", error);
|
|
31790
31808
|
}
|
|
31791
|
-
|
|
31809
|
+
state6.isLoading = false;
|
|
31792
31810
|
}
|
|
31793
|
-
function renderModal2(container,
|
|
31794
|
-
const colors = getThemeColors(
|
|
31795
|
-
const startDateStr = new Date(
|
|
31796
|
-
const endDateStr = new Date(
|
|
31797
|
-
const startDateInput = new Date(
|
|
31798
|
-
const endDateInput = new Date(
|
|
31799
|
-
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) => `
|
|
31800
31818
|
<div style="display: flex; align-items: center; gap: 8px; padding: 8px 12px;
|
|
31801
|
-
background: ${
|
|
31819
|
+
background: ${state6.theme === "dark" ? "rgba(255,255,255,0.05)" : "rgba(0,0,0,0.03)"};
|
|
31802
31820
|
border-radius: 8px;">
|
|
31803
31821
|
<span style="width: 12px; height: 12px; border-radius: 50%; background: ${dd.color};"></span>
|
|
31804
31822
|
<span style="color: ${colors.text}; font-size: 13px;">${dd.device.label}</span>
|
|
@@ -31807,9 +31825,9 @@ function renderModal2(container, state5, modalId) {
|
|
|
31807
31825
|
</span>
|
|
31808
31826
|
</div>
|
|
31809
31827
|
`).join("");
|
|
31810
|
-
const statsHTML =
|
|
31828
|
+
const statsHTML = state6.deviceData.map((dd) => `
|
|
31811
31829
|
<div style="
|
|
31812
|
-
padding: 12px; background: ${
|
|
31830
|
+
padding: 12px; background: ${state6.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
|
|
31813
31831
|
border-radius: 10px; border-left: 4px solid ${dd.color};
|
|
31814
31832
|
min-width: 150px;
|
|
31815
31833
|
">
|
|
@@ -31860,7 +31878,7 @@ function renderModal2(container, state5, modalId) {
|
|
|
31860
31878
|
min-height: 20px;
|
|
31861
31879
|
">
|
|
31862
31880
|
<h2 style="margin: 6px; font-size: 18px; font-weight: 600; color: white; line-height: 2;">
|
|
31863
|
-
\u{1F321}\uFE0F Compara\xE7\xE3o de Temperatura - ${
|
|
31881
|
+
\u{1F321}\uFE0F Compara\xE7\xE3o de Temperatura - ${state6.devices.length} sensores
|
|
31864
31882
|
</h2>
|
|
31865
31883
|
<div style="display: flex; gap: 4px; align-items: center;">
|
|
31866
31884
|
<!-- Theme Toggle -->
|
|
@@ -31868,7 +31886,7 @@ function renderModal2(container, state5, modalId) {
|
|
|
31868
31886
|
background: none; border: none; font-size: 16px; cursor: pointer;
|
|
31869
31887
|
padding: 4px 8px; border-radius: 6px; color: rgba(255,255,255,0.8);
|
|
31870
31888
|
transition: background-color 0.2s;
|
|
31871
|
-
">${
|
|
31889
|
+
">${state6.theme === "dark" ? "\u2600\uFE0F" : "\u{1F319}"}</button>
|
|
31872
31890
|
<!-- Maximize Button -->
|
|
31873
31891
|
<button id="${modalId}-maximize" title="${isMaximized ? "Restaurar" : "Maximizar"}" style="
|
|
31874
31892
|
background: none; border: none; font-size: 16px; cursor: pointer;
|
|
@@ -31891,7 +31909,7 @@ function renderModal2(container, state5, modalId) {
|
|
|
31891
31909
|
<div style="
|
|
31892
31910
|
display: flex; gap: 16px; flex-wrap: wrap; align-items: flex-end;
|
|
31893
31911
|
margin-bottom: 16px; padding: 16px;
|
|
31894
|
-
background: ${
|
|
31912
|
+
background: ${state6.theme === "dark" ? "rgba(255,255,255,0.05)" : "#f7f7f7"};
|
|
31895
31913
|
border-radius: 6px; border: 1px solid ${colors.border};
|
|
31896
31914
|
">
|
|
31897
31915
|
<!-- Granularity Select -->
|
|
@@ -31904,8 +31922,8 @@ function renderModal2(container, state5, modalId) {
|
|
|
31904
31922
|
font-size: 14px; color: ${colors.text}; background: ${colors.surface};
|
|
31905
31923
|
cursor: pointer; min-width: 130px;
|
|
31906
31924
|
">
|
|
31907
|
-
<option value="hour" ${
|
|
31908
|
-
<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>
|
|
31909
31927
|
</select>
|
|
31910
31928
|
</div>
|
|
31911
31929
|
<!-- Day Period Filter (Multiselect) -->
|
|
@@ -31919,7 +31937,7 @@ function renderModal2(container, state5, modalId) {
|
|
|
31919
31937
|
cursor: pointer; min-width: 180px; text-align: left;
|
|
31920
31938
|
display: flex; align-items: center; justify-content: space-between; gap: 8px;
|
|
31921
31939
|
">
|
|
31922
|
-
<span>${getSelectedPeriodsLabel(
|
|
31940
|
+
<span>${getSelectedPeriodsLabel(state6.selectedPeriods)}</span>
|
|
31923
31941
|
<span style="font-size: 10px;">\u25BC</span>
|
|
31924
31942
|
</button>
|
|
31925
31943
|
<div id="${modalId}-period-dropdown" style="
|
|
@@ -31932,12 +31950,12 @@ function renderModal2(container, state5, modalId) {
|
|
|
31932
31950
|
<label style="
|
|
31933
31951
|
display: flex; align-items: center; gap: 8px; padding: 8px 12px;
|
|
31934
31952
|
cursor: pointer; font-size: 13px; color: ${colors.text};
|
|
31935
|
-
" onmouseover="this.style.background='${
|
|
31953
|
+
" onmouseover="this.style.background='${state6.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"}'"
|
|
31936
31954
|
onmouseout="this.style.background='transparent'">
|
|
31937
31955
|
<input type="checkbox"
|
|
31938
31956
|
name="${modalId}-period"
|
|
31939
31957
|
value="${period.id}"
|
|
31940
|
-
${
|
|
31958
|
+
${state6.selectedPeriods.includes(period.id) ? "checked" : ""}
|
|
31941
31959
|
style="width: 16px; height: 16px; cursor: pointer; accent-color: #3e1a7d;">
|
|
31942
31960
|
${period.label}
|
|
31943
31961
|
</label>
|
|
@@ -31945,13 +31963,13 @@ function renderModal2(container, state5, modalId) {
|
|
|
31945
31963
|
<div style="border-top: 1px solid ${colors.border}; margin-top: 8px; padding-top: 8px;">
|
|
31946
31964
|
<button id="${modalId}-period-select-all" type="button" style="
|
|
31947
31965
|
width: calc(100% - 16px); margin: 0 8px 4px; padding: 6px;
|
|
31948
|
-
background: ${
|
|
31966
|
+
background: ${state6.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"};
|
|
31949
31967
|
border: none; border-radius: 4px; cursor: pointer;
|
|
31950
31968
|
font-size: 12px; color: ${colors.text};
|
|
31951
31969
|
">Selecionar Todos</button>
|
|
31952
31970
|
<button id="${modalId}-period-clear" type="button" style="
|
|
31953
31971
|
width: calc(100% - 16px); margin: 0 8px; padding: 6px;
|
|
31954
|
-
background: ${
|
|
31972
|
+
background: ${state6.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"};
|
|
31955
31973
|
border: none; border-radius: 4px; cursor: pointer;
|
|
31956
31974
|
font-size: 12px; color: ${colors.text};
|
|
31957
31975
|
">Limpar Sele\xE7\xE3o</button>
|
|
@@ -31976,8 +31994,8 @@ function renderModal2(container, state5, modalId) {
|
|
|
31976
31994
|
font-size: 14px; font-weight: 500; height: 38px;
|
|
31977
31995
|
display: flex; align-items: center; gap: 8px;
|
|
31978
31996
|
font-family: 'Roboto', Arial, sans-serif;
|
|
31979
|
-
" ${
|
|
31980
|
-
${
|
|
31997
|
+
" ${state6.isLoading ? "disabled" : ""}>
|
|
31998
|
+
${state6.isLoading ? '<span style="animation: spin 1s linear infinite; display: inline-block;">\u21BB</span> Carregando...' : "Carregar"}
|
|
31981
31999
|
</button>
|
|
31982
32000
|
</div>
|
|
31983
32001
|
|
|
@@ -31993,14 +32011,14 @@ function renderModal2(container, state5, modalId) {
|
|
|
31993
32011
|
<div style="margin-bottom: 24px;">
|
|
31994
32012
|
<div id="${modalId}-chart" style="
|
|
31995
32013
|
height: 380px;
|
|
31996
|
-
background: ${
|
|
32014
|
+
background: ${state6.theme === "dark" ? "rgba(255,255,255,0.03)" : "#fafafa"};
|
|
31997
32015
|
border-radius: 14px; display: flex; justify-content: center; align-items: center;
|
|
31998
32016
|
border: 1px solid ${colors.border}; position: relative;
|
|
31999
32017
|
">
|
|
32000
|
-
${
|
|
32018
|
+
${state6.isLoading ? `<div style="text-align: center; color: ${colors.textMuted};">
|
|
32001
32019
|
<div style="animation: spin 1s linear infinite; font-size: 36px; margin-bottom: 12px;">\u21BB</div>
|
|
32002
|
-
<div style="font-size: 15px;">Carregando dados de ${
|
|
32003
|
-
</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};">
|
|
32004
32022
|
<div style="font-size: 48px; margin-bottom: 12px;">\u{1F4ED}</div>
|
|
32005
32023
|
<div style="font-size: 16px;">Sem dados para o per\xEDodo selecionado</div>
|
|
32006
32024
|
</div>` : `<canvas id="${modalId}-canvas" style="width: 100%; height: 100%;"></canvas>`}
|
|
@@ -32018,12 +32036,12 @@ function renderModal2(container, state5, modalId) {
|
|
|
32018
32036
|
<!-- Actions -->
|
|
32019
32037
|
<div style="display: flex; justify-content: flex-end; gap: 12px;">
|
|
32020
32038
|
<button id="${modalId}-export" style="
|
|
32021
|
-
background: ${
|
|
32039
|
+
background: ${state6.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f7f7f7"};
|
|
32022
32040
|
color: ${colors.text}; border: 1px solid ${colors.border};
|
|
32023
32041
|
padding: 8px 16px; border-radius: 6px; cursor: pointer;
|
|
32024
32042
|
font-size: 14px; display: flex; align-items: center; gap: 8px;
|
|
32025
32043
|
font-family: 'Roboto', Arial, sans-serif;
|
|
32026
|
-
" ${
|
|
32044
|
+
" ${state6.deviceData.every((dd) => dd.data.length === 0) ? "disabled" : ""}>
|
|
32027
32045
|
\u{1F4E5} Exportar CSV
|
|
32028
32046
|
</button>
|
|
32029
32047
|
<button id="${modalId}-close-btn" style="
|
|
@@ -32058,15 +32076,15 @@ function renderModal2(container, state5, modalId) {
|
|
|
32058
32076
|
</style>
|
|
32059
32077
|
`;
|
|
32060
32078
|
}
|
|
32061
|
-
function drawComparisonChart(modalId,
|
|
32079
|
+
function drawComparisonChart(modalId, state6) {
|
|
32062
32080
|
const chartContainer = document.getElementById(`${modalId}-chart`);
|
|
32063
32081
|
const canvas = document.getElementById(`${modalId}-canvas`);
|
|
32064
32082
|
if (!chartContainer || !canvas) return;
|
|
32065
|
-
const hasData =
|
|
32083
|
+
const hasData = state6.deviceData.some((dd) => dd.data.length > 0);
|
|
32066
32084
|
if (!hasData) return;
|
|
32067
32085
|
const ctx = canvas.getContext("2d");
|
|
32068
32086
|
if (!ctx) return;
|
|
32069
|
-
const colors = getThemeColors(
|
|
32087
|
+
const colors = getThemeColors(state6.theme);
|
|
32070
32088
|
const width = chartContainer.clientWidth - 2;
|
|
32071
32089
|
const height = 380;
|
|
32072
32090
|
canvas.width = width;
|
|
@@ -32077,19 +32095,19 @@ function drawComparisonChart(modalId, state5) {
|
|
|
32077
32095
|
const paddingBottom = 55;
|
|
32078
32096
|
ctx.clearRect(0, 0, width, height);
|
|
32079
32097
|
const processedData = [];
|
|
32080
|
-
|
|
32098
|
+
state6.deviceData.forEach((dd) => {
|
|
32081
32099
|
if (dd.data.length === 0) return;
|
|
32082
|
-
const filteredData = filterByDayPeriods(dd.data,
|
|
32100
|
+
const filteredData = filterByDayPeriods(dd.data, state6.selectedPeriods);
|
|
32083
32101
|
if (filteredData.length === 0) return;
|
|
32084
32102
|
let points;
|
|
32085
|
-
if (
|
|
32103
|
+
if (state6.granularity === "hour") {
|
|
32086
32104
|
const interpolated = interpolateTemperature(filteredData, {
|
|
32087
32105
|
intervalMinutes: 30,
|
|
32088
|
-
startTs:
|
|
32089
|
-
endTs:
|
|
32090
|
-
clampRange:
|
|
32106
|
+
startTs: state6.startTs,
|
|
32107
|
+
endTs: state6.endTs,
|
|
32108
|
+
clampRange: state6.clampRange
|
|
32091
32109
|
});
|
|
32092
|
-
const filteredInterpolated = filterByDayPeriods(interpolated,
|
|
32110
|
+
const filteredInterpolated = filterByDayPeriods(interpolated, state6.selectedPeriods);
|
|
32093
32111
|
points = filteredInterpolated.map((item) => ({
|
|
32094
32112
|
x: item.ts,
|
|
32095
32113
|
y: Number(item.value),
|
|
@@ -32099,7 +32117,7 @@ function drawComparisonChart(modalId, state5) {
|
|
|
32099
32117
|
deviceColor: dd.color
|
|
32100
32118
|
}));
|
|
32101
32119
|
} else {
|
|
32102
|
-
const daily = aggregateByDay(filteredData,
|
|
32120
|
+
const daily = aggregateByDay(filteredData, state6.clampRange);
|
|
32103
32121
|
points = daily.map((item) => ({
|
|
32104
32122
|
x: item.dateTs,
|
|
32105
32123
|
y: item.avg,
|
|
@@ -32120,7 +32138,7 @@ function drawComparisonChart(modalId, state5) {
|
|
|
32120
32138
|
ctx.fillText("Nenhum dado para os per\xEDodos selecionados", width / 2, height / 2);
|
|
32121
32139
|
return;
|
|
32122
32140
|
}
|
|
32123
|
-
const isPeriodsFiltered =
|
|
32141
|
+
const isPeriodsFiltered = state6.selectedPeriods.length < 4 && state6.selectedPeriods.length > 0;
|
|
32124
32142
|
let dataMinY = Infinity;
|
|
32125
32143
|
let dataMaxY = -Infinity;
|
|
32126
32144
|
processedData.forEach(({ points }) => {
|
|
@@ -32130,7 +32148,7 @@ function drawComparisonChart(modalId, state5) {
|
|
|
32130
32148
|
});
|
|
32131
32149
|
});
|
|
32132
32150
|
const rangeMap = /* @__PURE__ */ new Map();
|
|
32133
|
-
|
|
32151
|
+
state6.deviceData.forEach((dd, index) => {
|
|
32134
32152
|
const device = dd.device;
|
|
32135
32153
|
const min = device.temperatureMin;
|
|
32136
32154
|
const max = device.temperatureMax;
|
|
@@ -32149,10 +32167,10 @@ function drawComparisonChart(modalId, state5) {
|
|
|
32149
32167
|
}
|
|
32150
32168
|
}
|
|
32151
32169
|
});
|
|
32152
|
-
if (rangeMap.size === 0 &&
|
|
32170
|
+
if (rangeMap.size === 0 && state6.temperatureMin !== null && state6.temperatureMax !== null) {
|
|
32153
32171
|
rangeMap.set("global", {
|
|
32154
|
-
min:
|
|
32155
|
-
max:
|
|
32172
|
+
min: state6.temperatureMin,
|
|
32173
|
+
max: state6.temperatureMax,
|
|
32156
32174
|
customerName: "Global",
|
|
32157
32175
|
color: colors.success,
|
|
32158
32176
|
deviceLabels: []
|
|
@@ -32273,10 +32291,10 @@ function drawComparisonChart(modalId, state5) {
|
|
|
32273
32291
|
const point = xAxisPoints[i];
|
|
32274
32292
|
const date = new Date(point.x);
|
|
32275
32293
|
let label;
|
|
32276
|
-
if (
|
|
32277
|
-
label = date.toLocaleTimeString(
|
|
32294
|
+
if (state6.granularity === "hour") {
|
|
32295
|
+
label = date.toLocaleTimeString(state6.locale, { hour: "2-digit", minute: "2-digit" });
|
|
32278
32296
|
} else {
|
|
32279
|
-
label = date.toLocaleDateString(
|
|
32297
|
+
label = date.toLocaleDateString(state6.locale, { day: "2-digit", month: "2-digit" });
|
|
32280
32298
|
}
|
|
32281
32299
|
ctx.strokeStyle = colors.chartGrid;
|
|
32282
32300
|
ctx.lineWidth = 1;
|
|
@@ -32295,16 +32313,16 @@ function drawComparisonChart(modalId, state5) {
|
|
|
32295
32313
|
ctx.lineTo(width - paddingRight, height - paddingBottom);
|
|
32296
32314
|
ctx.stroke();
|
|
32297
32315
|
const allChartPoints = processedData.flatMap((pd) => pd.points);
|
|
32298
|
-
setupComparisonChartTooltip(canvas, chartContainer, allChartPoints,
|
|
32316
|
+
setupComparisonChartTooltip(canvas, chartContainer, allChartPoints, state6, colors);
|
|
32299
32317
|
}
|
|
32300
|
-
function setupComparisonChartTooltip(canvas, container, chartData,
|
|
32318
|
+
function setupComparisonChartTooltip(canvas, container, chartData, state6, colors) {
|
|
32301
32319
|
const existingTooltip = container.querySelector(".myio-chart-tooltip");
|
|
32302
32320
|
if (existingTooltip) existingTooltip.remove();
|
|
32303
32321
|
const tooltip = document.createElement("div");
|
|
32304
32322
|
tooltip.className = "myio-chart-tooltip";
|
|
32305
32323
|
tooltip.style.cssText = `
|
|
32306
32324
|
position: absolute;
|
|
32307
|
-
background: ${
|
|
32325
|
+
background: ${state6.theme === "dark" ? "rgba(30, 30, 40, 0.95)" : "rgba(255, 255, 255, 0.98)"};
|
|
32308
32326
|
border: 1px solid ${colors.border};
|
|
32309
32327
|
border-radius: 8px;
|
|
32310
32328
|
padding: 10px 14px;
|
|
@@ -32341,17 +32359,17 @@ function setupComparisonChartTooltip(canvas, container, chartData, state5, color
|
|
|
32341
32359
|
if (point) {
|
|
32342
32360
|
const date = new Date(point.x);
|
|
32343
32361
|
let dateStr;
|
|
32344
|
-
if (
|
|
32345
|
-
dateStr = date.toLocaleDateString(
|
|
32362
|
+
if (state6.granularity === "hour") {
|
|
32363
|
+
dateStr = date.toLocaleDateString(state6.locale, {
|
|
32346
32364
|
day: "2-digit",
|
|
32347
32365
|
month: "2-digit",
|
|
32348
32366
|
year: "numeric"
|
|
32349
|
-
}) + " " + date.toLocaleTimeString(
|
|
32367
|
+
}) + " " + date.toLocaleTimeString(state6.locale, {
|
|
32350
32368
|
hour: "2-digit",
|
|
32351
32369
|
minute: "2-digit"
|
|
32352
32370
|
});
|
|
32353
32371
|
} else {
|
|
32354
|
-
dateStr = date.toLocaleDateString(
|
|
32372
|
+
dateStr = date.toLocaleDateString(state6.locale, {
|
|
32355
32373
|
day: "2-digit",
|
|
32356
32374
|
month: "2-digit",
|
|
32357
32375
|
year: "numeric"
|
|
@@ -32393,7 +32411,7 @@ function setupComparisonChartTooltip(canvas, container, chartData, state5, color
|
|
|
32393
32411
|
canvas.style.cursor = "default";
|
|
32394
32412
|
});
|
|
32395
32413
|
}
|
|
32396
|
-
async function setupEventListeners2(container,
|
|
32414
|
+
async function setupEventListeners2(container, state6, modalId, onClose) {
|
|
32397
32415
|
const closeModal = () => {
|
|
32398
32416
|
container.remove();
|
|
32399
32417
|
onClose?.();
|
|
@@ -32404,19 +32422,19 @@ async function setupEventListeners2(container, state5, modalId, onClose) {
|
|
|
32404
32422
|
document.getElementById(`${modalId}-close`)?.addEventListener("click", closeModal);
|
|
32405
32423
|
document.getElementById(`${modalId}-close-btn`)?.addEventListener("click", closeModal);
|
|
32406
32424
|
const dateRangeInput = document.getElementById(`${modalId}-date-range`);
|
|
32407
|
-
if (dateRangeInput && !
|
|
32425
|
+
if (dateRangeInput && !state6.dateRangePicker) {
|
|
32408
32426
|
try {
|
|
32409
|
-
|
|
32410
|
-
presetStart: new Date(
|
|
32411
|
-
presetEnd: new Date(
|
|
32427
|
+
state6.dateRangePicker = await createDateRangePicker2(dateRangeInput, {
|
|
32428
|
+
presetStart: new Date(state6.startTs).toISOString(),
|
|
32429
|
+
presetEnd: new Date(state6.endTs).toISOString(),
|
|
32412
32430
|
includeTime: true,
|
|
32413
32431
|
timePrecision: "minute",
|
|
32414
32432
|
maxRangeDays: 90,
|
|
32415
|
-
locale:
|
|
32433
|
+
locale: state6.locale,
|
|
32416
32434
|
parentEl: container.querySelector(".myio-temp-comparison-content"),
|
|
32417
32435
|
onApply: (result) => {
|
|
32418
|
-
|
|
32419
|
-
|
|
32436
|
+
state6.startTs = new Date(result.startISO).getTime();
|
|
32437
|
+
state6.endTs = new Date(result.endISO).getTime();
|
|
32420
32438
|
console.log("[TemperatureComparisonModal] Date range applied:", result);
|
|
32421
32439
|
}
|
|
32422
32440
|
});
|
|
@@ -32425,23 +32443,23 @@ async function setupEventListeners2(container, state5, modalId, onClose) {
|
|
|
32425
32443
|
}
|
|
32426
32444
|
}
|
|
32427
32445
|
document.getElementById(`${modalId}-theme-toggle`)?.addEventListener("click", async () => {
|
|
32428
|
-
|
|
32429
|
-
localStorage.setItem("myio-temp-comparison-theme",
|
|
32430
|
-
|
|
32431
|
-
renderModal2(container,
|
|
32432
|
-
if (
|
|
32433
|
-
drawComparisonChart(modalId,
|
|
32434
|
-
}
|
|
32435
|
-
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);
|
|
32436
32454
|
});
|
|
32437
32455
|
document.getElementById(`${modalId}-maximize`)?.addEventListener("click", async () => {
|
|
32438
32456
|
container.__isMaximized = !container.__isMaximized;
|
|
32439
|
-
|
|
32440
|
-
renderModal2(container,
|
|
32441
|
-
if (
|
|
32442
|
-
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);
|
|
32443
32461
|
}
|
|
32444
|
-
await setupEventListeners2(container,
|
|
32462
|
+
await setupEventListeners2(container, state6, modalId, onClose);
|
|
32445
32463
|
});
|
|
32446
32464
|
const periodBtn = document.getElementById(`${modalId}-period-btn`);
|
|
32447
32465
|
const periodDropdown = document.getElementById(`${modalId}-period-dropdown`);
|
|
@@ -32460,13 +32478,13 @@ async function setupEventListeners2(container, state5, modalId, onClose) {
|
|
|
32460
32478
|
periodCheckboxes.forEach((checkbox) => {
|
|
32461
32479
|
checkbox.addEventListener("change", () => {
|
|
32462
32480
|
const checked = Array.from(periodCheckboxes).filter((cb) => cb.checked).map((cb) => cb.value);
|
|
32463
|
-
|
|
32481
|
+
state6.selectedPeriods = checked;
|
|
32464
32482
|
const btnLabel = periodBtn?.querySelector("span:first-child");
|
|
32465
32483
|
if (btnLabel) {
|
|
32466
|
-
btnLabel.textContent = getSelectedPeriodsLabel(
|
|
32484
|
+
btnLabel.textContent = getSelectedPeriodsLabel(state6.selectedPeriods);
|
|
32467
32485
|
}
|
|
32468
|
-
if (
|
|
32469
|
-
drawComparisonChart(modalId,
|
|
32486
|
+
if (state6.deviceData.some((dd) => dd.data.length > 0)) {
|
|
32487
|
+
drawComparisonChart(modalId, state6);
|
|
32470
32488
|
}
|
|
32471
32489
|
});
|
|
32472
32490
|
});
|
|
@@ -32474,77 +32492,77 @@ async function setupEventListeners2(container, state5, modalId, onClose) {
|
|
|
32474
32492
|
periodCheckboxes.forEach((cb) => {
|
|
32475
32493
|
cb.checked = true;
|
|
32476
32494
|
});
|
|
32477
|
-
|
|
32495
|
+
state6.selectedPeriods = ["madrugada", "manha", "tarde", "noite"];
|
|
32478
32496
|
const btnLabel = periodBtn?.querySelector("span:first-child");
|
|
32479
32497
|
if (btnLabel) {
|
|
32480
|
-
btnLabel.textContent = getSelectedPeriodsLabel(
|
|
32498
|
+
btnLabel.textContent = getSelectedPeriodsLabel(state6.selectedPeriods);
|
|
32481
32499
|
}
|
|
32482
|
-
if (
|
|
32483
|
-
drawComparisonChart(modalId,
|
|
32500
|
+
if (state6.deviceData.some((dd) => dd.data.length > 0)) {
|
|
32501
|
+
drawComparisonChart(modalId, state6);
|
|
32484
32502
|
}
|
|
32485
32503
|
});
|
|
32486
32504
|
document.getElementById(`${modalId}-period-clear`)?.addEventListener("click", () => {
|
|
32487
32505
|
periodCheckboxes.forEach((cb) => {
|
|
32488
32506
|
cb.checked = false;
|
|
32489
32507
|
});
|
|
32490
|
-
|
|
32508
|
+
state6.selectedPeriods = [];
|
|
32491
32509
|
const btnLabel = periodBtn?.querySelector("span:first-child");
|
|
32492
32510
|
if (btnLabel) {
|
|
32493
|
-
btnLabel.textContent = getSelectedPeriodsLabel(
|
|
32511
|
+
btnLabel.textContent = getSelectedPeriodsLabel(state6.selectedPeriods);
|
|
32494
32512
|
}
|
|
32495
|
-
if (
|
|
32496
|
-
drawComparisonChart(modalId,
|
|
32513
|
+
if (state6.deviceData.some((dd) => dd.data.length > 0)) {
|
|
32514
|
+
drawComparisonChart(modalId, state6);
|
|
32497
32515
|
}
|
|
32498
32516
|
});
|
|
32499
32517
|
document.getElementById(`${modalId}-granularity`)?.addEventListener("change", (e) => {
|
|
32500
|
-
|
|
32501
|
-
localStorage.setItem("myio-temp-comparison-granularity",
|
|
32502
|
-
if (
|
|
32503
|
-
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);
|
|
32504
32522
|
}
|
|
32505
32523
|
});
|
|
32506
32524
|
document.getElementById(`${modalId}-query`)?.addEventListener("click", async () => {
|
|
32507
|
-
if (
|
|
32525
|
+
if (state6.startTs >= state6.endTs) {
|
|
32508
32526
|
alert("Por favor, selecione um per\xEDodo v\xE1lido");
|
|
32509
32527
|
return;
|
|
32510
32528
|
}
|
|
32511
|
-
|
|
32512
|
-
|
|
32513
|
-
renderModal2(container,
|
|
32514
|
-
await fetchAllDevicesData(
|
|
32515
|
-
renderModal2(container,
|
|
32516
|
-
drawComparisonChart(modalId,
|
|
32517
|
-
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);
|
|
32518
32536
|
});
|
|
32519
32537
|
document.getElementById(`${modalId}-export`)?.addEventListener("click", () => {
|
|
32520
|
-
if (
|
|
32521
|
-
exportComparisonCSV(
|
|
32538
|
+
if (state6.deviceData.every((dd) => dd.data.length === 0)) return;
|
|
32539
|
+
exportComparisonCSV(state6);
|
|
32522
32540
|
});
|
|
32523
32541
|
}
|
|
32524
|
-
function exportComparisonCSV(
|
|
32525
|
-
const startDateStr = new Date(
|
|
32526
|
-
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, "-");
|
|
32527
32545
|
const BOM = "\uFEFF";
|
|
32528
32546
|
let csvContent = BOM;
|
|
32529
32547
|
csvContent += `Compara\xE7\xE3o de Temperatura
|
|
32530
32548
|
`;
|
|
32531
32549
|
csvContent += `Per\xEDodo: ${startDateStr} at\xE9 ${endDateStr}
|
|
32532
32550
|
`;
|
|
32533
|
-
csvContent += `Sensores: ${
|
|
32551
|
+
csvContent += `Sensores: ${state6.devices.map((d) => d.label).join(", ")}
|
|
32534
32552
|
`;
|
|
32535
32553
|
csvContent += "\n";
|
|
32536
32554
|
csvContent += "Estat\xEDsticas por Sensor:\n";
|
|
32537
32555
|
csvContent += "Sensor,M\xE9dia (\xB0C),Min (\xB0C),Max (\xB0C),Leituras\n";
|
|
32538
|
-
|
|
32556
|
+
state6.deviceData.forEach((dd) => {
|
|
32539
32557
|
csvContent += `"${dd.device.label}",${dd.stats.avg.toFixed(2)},${dd.stats.min.toFixed(2)},${dd.stats.max.toFixed(2)},${dd.stats.count}
|
|
32540
32558
|
`;
|
|
32541
32559
|
});
|
|
32542
32560
|
csvContent += "\n";
|
|
32543
32561
|
csvContent += "Dados Detalhados:\n";
|
|
32544
32562
|
csvContent += "Data/Hora,Sensor,Temperatura (\xB0C)\n";
|
|
32545
|
-
|
|
32563
|
+
state6.deviceData.forEach((dd) => {
|
|
32546
32564
|
dd.data.forEach((item) => {
|
|
32547
|
-
const date = new Date(item.ts).toLocaleString(
|
|
32565
|
+
const date = new Date(item.ts).toLocaleString(state6.locale);
|
|
32548
32566
|
const temp = Number(item.value).toFixed(2);
|
|
32549
32567
|
csvContent += `"${date}","${dd.device.label}",${temp}
|
|
32550
32568
|
`;
|
|
@@ -32652,10 +32670,10 @@ async function saveCustomerAttributes(customerId, token, minTemperature, maxTemp
|
|
|
32652
32670
|
throw new Error(`Failed to save attributes: ${response.status}`);
|
|
32653
32671
|
}
|
|
32654
32672
|
}
|
|
32655
|
-
function renderModal3(container,
|
|
32656
|
-
const colors = getColors(
|
|
32657
|
-
const minValue =
|
|
32658
|
-
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 : "";
|
|
32659
32677
|
container.innerHTML = `
|
|
32660
32678
|
<style>
|
|
32661
32679
|
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
|
|
@@ -32920,23 +32938,23 @@ function renderModal3(container, state5, modalId, onClose, onSave) {
|
|
|
32920
32938
|
</div>
|
|
32921
32939
|
|
|
32922
32940
|
<div class="modal-body">
|
|
32923
|
-
${
|
|
32941
|
+
${state6.isLoading ? `
|
|
32924
32942
|
<div class="loading-overlay">
|
|
32925
32943
|
<div class="loading-spinner"></div>
|
|
32926
32944
|
<div>Carregando configura\xE7\xF5es...</div>
|
|
32927
32945
|
</div>
|
|
32928
32946
|
` : `
|
|
32929
|
-
${
|
|
32930
|
-
<div class="message message-error">${
|
|
32947
|
+
${state6.error ? `
|
|
32948
|
+
<div class="message message-error">${state6.error}</div>
|
|
32931
32949
|
` : ""}
|
|
32932
32950
|
|
|
32933
|
-
${
|
|
32934
|
-
<div class="message message-success">${
|
|
32951
|
+
${state6.successMessage ? `
|
|
32952
|
+
<div class="message message-success">${state6.successMessage}</div>
|
|
32935
32953
|
` : ""}
|
|
32936
32954
|
|
|
32937
32955
|
<div class="customer-info">
|
|
32938
32956
|
<div class="customer-label">Shopping / Cliente</div>
|
|
32939
|
-
<div class="customer-name">${
|
|
32957
|
+
<div class="customer-name">${state6.customerName || "N\xE3o identificado"}</div>
|
|
32940
32958
|
</div>
|
|
32941
32959
|
|
|
32942
32960
|
<div class="form-group">
|
|
@@ -32980,11 +32998,11 @@ function renderModal3(container, state5, modalId, onClose, onSave) {
|
|
|
32980
32998
|
`}
|
|
32981
32999
|
</div>
|
|
32982
33000
|
|
|
32983
|
-
${!
|
|
33001
|
+
${!state6.isLoading ? `
|
|
32984
33002
|
<div class="modal-footer">
|
|
32985
33003
|
<button class="btn btn-secondary" id="${modalId}-cancel">Cancelar</button>
|
|
32986
|
-
<button class="btn btn-primary" id="${modalId}-save" ${
|
|
32987
|
-
${
|
|
33004
|
+
<button class="btn btn-primary" id="${modalId}-save" ${state6.isSaving ? "disabled" : ""}>
|
|
33005
|
+
${state6.isSaving ? '<div class="spinner"></div> Salvando...' : "Salvar"}
|
|
32988
33006
|
</button>
|
|
32989
33007
|
</div>
|
|
32990
33008
|
` : ""}
|
|
@@ -33021,18 +33039,18 @@ function renderModal3(container, state5, modalId, onClose, onSave) {
|
|
|
33021
33039
|
const min = parseFloat(minInput.value);
|
|
33022
33040
|
const max = parseFloat(maxInput.value);
|
|
33023
33041
|
if (isNaN(min) || isNaN(max)) {
|
|
33024
|
-
|
|
33025
|
-
renderModal3(container,
|
|
33042
|
+
state6.error = "Por favor, preencha ambos os valores.";
|
|
33043
|
+
renderModal3(container, state6, modalId, onClose, onSave);
|
|
33026
33044
|
return;
|
|
33027
33045
|
}
|
|
33028
33046
|
if (min >= max) {
|
|
33029
|
-
|
|
33030
|
-
renderModal3(container,
|
|
33047
|
+
state6.error = "A temperatura m\xEDnima deve ser menor que a m\xE1xima.";
|
|
33048
|
+
renderModal3(container, state6, modalId, onClose, onSave);
|
|
33031
33049
|
return;
|
|
33032
33050
|
}
|
|
33033
33051
|
if (min < 0 || max > 50) {
|
|
33034
|
-
|
|
33035
|
-
renderModal3(container,
|
|
33052
|
+
state6.error = "Os valores devem estar entre 0\xB0C e 50\xB0C.";
|
|
33053
|
+
renderModal3(container, state6, modalId, onClose, onSave);
|
|
33036
33054
|
return;
|
|
33037
33055
|
}
|
|
33038
33056
|
await onSave(min, max);
|
|
@@ -33040,7 +33058,7 @@ function renderModal3(container, state5, modalId, onClose, onSave) {
|
|
|
33040
33058
|
}
|
|
33041
33059
|
function openTemperatureSettingsModal(params) {
|
|
33042
33060
|
const modalId = `myio-temp-settings-${Date.now()}`;
|
|
33043
|
-
const
|
|
33061
|
+
const state6 = {
|
|
33044
33062
|
customerId: params.customerId,
|
|
33045
33063
|
customerName: params.customerName || "",
|
|
33046
33064
|
token: params.token,
|
|
@@ -33060,37 +33078,37 @@ function openTemperatureSettingsModal(params) {
|
|
|
33060
33078
|
params.onClose?.();
|
|
33061
33079
|
};
|
|
33062
33080
|
const handleSave = async (min, max) => {
|
|
33063
|
-
|
|
33064
|
-
|
|
33065
|
-
|
|
33066
|
-
renderModal3(container,
|
|
33081
|
+
state6.isSaving = true;
|
|
33082
|
+
state6.error = null;
|
|
33083
|
+
state6.successMessage = null;
|
|
33084
|
+
renderModal3(container, state6, modalId, destroy, handleSave);
|
|
33067
33085
|
try {
|
|
33068
|
-
await saveCustomerAttributes(
|
|
33069
|
-
|
|
33070
|
-
|
|
33071
|
-
|
|
33072
|
-
|
|
33073
|
-
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);
|
|
33074
33092
|
params.onSave?.({ minTemperature: min, maxTemperature: max });
|
|
33075
33093
|
setTimeout(() => {
|
|
33076
33094
|
destroy();
|
|
33077
33095
|
}, 1500);
|
|
33078
33096
|
} catch (error) {
|
|
33079
|
-
|
|
33080
|
-
|
|
33081
|
-
renderModal3(container,
|
|
33097
|
+
state6.isSaving = false;
|
|
33098
|
+
state6.error = `Erro ao salvar: ${error.message}`;
|
|
33099
|
+
renderModal3(container, state6, modalId, destroy, handleSave);
|
|
33082
33100
|
}
|
|
33083
33101
|
};
|
|
33084
|
-
renderModal3(container,
|
|
33085
|
-
fetchCustomerAttributes(
|
|
33086
|
-
|
|
33087
|
-
|
|
33088
|
-
|
|
33089
|
-
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);
|
|
33090
33108
|
}).catch((error) => {
|
|
33091
|
-
|
|
33092
|
-
|
|
33093
|
-
renderModal3(container,
|
|
33109
|
+
state6.isLoading = false;
|
|
33110
|
+
state6.error = `Erro ao carregar: ${error.message}`;
|
|
33111
|
+
renderModal3(container, state6, modalId, destroy, handleSave);
|
|
33094
33112
|
});
|
|
33095
33113
|
return { destroy };
|
|
33096
33114
|
}
|
|
@@ -34360,7 +34378,7 @@ var EnergySummaryTooltip = {
|
|
|
34360
34378
|
* RFC-0105 Enhancement: Now fetches device lists from MyIOOrchestratorData
|
|
34361
34379
|
* to populate device lists for status popup display
|
|
34362
34380
|
*/
|
|
34363
|
-
buildSummaryFromState(
|
|
34381
|
+
buildSummaryFromState(state6, receivedData, domain = "energy") {
|
|
34364
34382
|
const summary = {
|
|
34365
34383
|
totalDevices: 0,
|
|
34366
34384
|
totalConsumption: 0,
|
|
@@ -34383,22 +34401,22 @@ var EnergySummaryTooltip = {
|
|
|
34383
34401
|
},
|
|
34384
34402
|
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
34385
34403
|
};
|
|
34386
|
-
if (!
|
|
34404
|
+
if (!state6) return summary;
|
|
34387
34405
|
const entrada = {
|
|
34388
34406
|
id: "entrada",
|
|
34389
34407
|
name: "Entrada",
|
|
34390
34408
|
icon: CATEGORY_ICONS.entrada,
|
|
34391
|
-
deviceCount:
|
|
34392
|
-
consumption:
|
|
34409
|
+
deviceCount: state6.entrada?.devices?.length || (receivedData?.entrada_total?.device_count || 0),
|
|
34410
|
+
consumption: state6.entrada?.total || 0,
|
|
34393
34411
|
percentage: 100
|
|
34394
34412
|
};
|
|
34395
34413
|
const lojas = {
|
|
34396
34414
|
id: "lojas",
|
|
34397
34415
|
name: "Lojas",
|
|
34398
34416
|
icon: CATEGORY_ICONS.lojas,
|
|
34399
|
-
deviceCount:
|
|
34400
|
-
consumption:
|
|
34401
|
-
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
|
|
34402
34420
|
};
|
|
34403
34421
|
const climatizacaoData = receivedData?.climatizacao || {};
|
|
34404
34422
|
const elevadoresData = receivedData?.elevadores || {};
|
|
@@ -34409,9 +34427,9 @@ var EnergySummaryTooltip = {
|
|
|
34409
34427
|
id: "climatizacao",
|
|
34410
34428
|
name: "Climatizacao",
|
|
34411
34429
|
icon: CATEGORY_ICONS.climatizacao,
|
|
34412
|
-
deviceCount: climatizacaoData.count ||
|
|
34413
|
-
consumption:
|
|
34414
|
-
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
|
|
34415
34433
|
};
|
|
34416
34434
|
if (climatizacaoData.subcategories) {
|
|
34417
34435
|
climatizacao.children = [];
|
|
@@ -34462,40 +34480,40 @@ var EnergySummaryTooltip = {
|
|
|
34462
34480
|
id: "elevadores",
|
|
34463
34481
|
name: "Elevadores",
|
|
34464
34482
|
icon: CATEGORY_ICONS.elevadores,
|
|
34465
|
-
deviceCount: elevadoresData.count ||
|
|
34466
|
-
consumption:
|
|
34467
|
-
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
|
|
34468
34486
|
});
|
|
34469
34487
|
areaComumChildren.push({
|
|
34470
34488
|
id: "escadasRolantes",
|
|
34471
34489
|
name: "Esc. Rolantes",
|
|
34472
34490
|
icon: CATEGORY_ICONS.escadas,
|
|
34473
|
-
deviceCount: escadasData.count ||
|
|
34474
|
-
consumption:
|
|
34475
|
-
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
|
|
34476
34494
|
});
|
|
34477
34495
|
areaComumChildren.push({
|
|
34478
34496
|
id: "outros",
|
|
34479
34497
|
name: "Outros",
|
|
34480
34498
|
icon: CATEGORY_ICONS.outros,
|
|
34481
|
-
deviceCount: outrosData.count ||
|
|
34482
|
-
consumption:
|
|
34483
|
-
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
|
|
34484
34502
|
});
|
|
34485
34503
|
const areaComumDeviceCount = areaComumChildren.reduce((sum, c) => sum + c.deviceCount, 0);
|
|
34486
|
-
const areaComumConsumption =
|
|
34504
|
+
const areaComumConsumption = state6.consumidores?.areaComum?.total || areaComumChildren.reduce((sum, c) => sum + c.consumption, 0);
|
|
34487
34505
|
const areaComum = {
|
|
34488
34506
|
id: "areaComum",
|
|
34489
34507
|
name: "Area Comum",
|
|
34490
34508
|
icon: CATEGORY_ICONS.areaComum,
|
|
34491
34509
|
deviceCount: areaComumDeviceCount,
|
|
34492
34510
|
consumption: areaComumConsumption,
|
|
34493
|
-
percentage:
|
|
34511
|
+
percentage: state6.consumidores?.areaComum?.perc || 0,
|
|
34494
34512
|
children: areaComumChildren
|
|
34495
34513
|
};
|
|
34496
34514
|
summary.byCategory = [entrada, lojas, areaComum];
|
|
34497
34515
|
summary.totalDevices = entrada.deviceCount + lojas.deviceCount + areaComumDeviceCount;
|
|
34498
|
-
summary.totalConsumption =
|
|
34516
|
+
summary.totalConsumption = state6.grandTotal || entrada.consumption;
|
|
34499
34517
|
const widgetAggregation = receivedData?.deviceStatusAggregation;
|
|
34500
34518
|
if (widgetAggregation && widgetAggregation.hasData) {
|
|
34501
34519
|
summary.byStatus = {
|
|
@@ -35783,7 +35801,7 @@ var WaterSummaryTooltip = {
|
|
|
35783
35801
|
* RFC-0105 Enhancement: Now fetches device lists from MyIOOrchestratorData
|
|
35784
35802
|
* to populate device lists for status popup display
|
|
35785
35803
|
*/
|
|
35786
|
-
buildSummaryFromState(
|
|
35804
|
+
buildSummaryFromState(state6, receivedData, includeBathrooms = false, domain = "water") {
|
|
35787
35805
|
const summary = {
|
|
35788
35806
|
totalDevices: 0,
|
|
35789
35807
|
totalConsumption: 0,
|
|
@@ -35807,22 +35825,22 @@ var WaterSummaryTooltip = {
|
|
|
35807
35825
|
lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
|
|
35808
35826
|
includeBathrooms
|
|
35809
35827
|
};
|
|
35810
|
-
if (!
|
|
35828
|
+
if (!state6) return summary;
|
|
35811
35829
|
const entrada = {
|
|
35812
35830
|
id: "entrada",
|
|
35813
35831
|
name: "Entrada",
|
|
35814
35832
|
icon: WATER_CATEGORY_ICONS.entrada,
|
|
35815
|
-
deviceCount:
|
|
35816
|
-
consumption:
|
|
35833
|
+
deviceCount: state6.entrada?.devices?.length || (receivedData?.entrada_total?.device_count || 0),
|
|
35834
|
+
consumption: state6.entrada?.total || 0,
|
|
35817
35835
|
percentage: 100
|
|
35818
35836
|
};
|
|
35819
35837
|
const lojas = {
|
|
35820
35838
|
id: "lojas",
|
|
35821
35839
|
name: "Lojas",
|
|
35822
35840
|
icon: WATER_CATEGORY_ICONS.lojas,
|
|
35823
|
-
deviceCount:
|
|
35824
|
-
consumption:
|
|
35825
|
-
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
|
|
35826
35844
|
};
|
|
35827
35845
|
summary.byCategory = [entrada, lojas];
|
|
35828
35846
|
if (includeBathrooms) {
|
|
@@ -35830,9 +35848,9 @@ var WaterSummaryTooltip = {
|
|
|
35830
35848
|
id: "banheiros",
|
|
35831
35849
|
name: "Banheiros",
|
|
35832
35850
|
icon: WATER_CATEGORY_ICONS.banheiros,
|
|
35833
|
-
deviceCount:
|
|
35834
|
-
consumption:
|
|
35835
|
-
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
|
|
35836
35854
|
};
|
|
35837
35855
|
summary.byCategory.push(banheiros);
|
|
35838
35856
|
}
|
|
@@ -35840,24 +35858,24 @@ var WaterSummaryTooltip = {
|
|
|
35840
35858
|
id: "areaComum",
|
|
35841
35859
|
name: "\xC1rea Comum",
|
|
35842
35860
|
icon: WATER_CATEGORY_ICONS.areaComum,
|
|
35843
|
-
deviceCount:
|
|
35844
|
-
consumption:
|
|
35845
|
-
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
|
|
35846
35864
|
};
|
|
35847
35865
|
summary.byCategory.push(areaComum);
|
|
35848
|
-
if (
|
|
35866
|
+
if (state6.pontosNaoMapeados && state6.pontosNaoMapeados.total > 0) {
|
|
35849
35867
|
const pontosNaoMapeados = {
|
|
35850
35868
|
id: "pontosNaoMapeados",
|
|
35851
35869
|
name: "Pontos N\xE3o Mapeados",
|
|
35852
35870
|
icon: WATER_CATEGORY_ICONS.pontosNaoMapeados,
|
|
35853
|
-
deviceCount:
|
|
35854
|
-
consumption:
|
|
35855
|
-
percentage:
|
|
35871
|
+
deviceCount: state6.pontosNaoMapeados?.devices?.length || 0,
|
|
35872
|
+
consumption: state6.pontosNaoMapeados?.total || 0,
|
|
35873
|
+
percentage: state6.pontosNaoMapeados?.perc || 0
|
|
35856
35874
|
};
|
|
35857
35875
|
summary.byCategory.push(pontosNaoMapeados);
|
|
35858
35876
|
}
|
|
35859
35877
|
summary.totalDevices = summary.byCategory.reduce((sum, cat) => sum + cat.deviceCount, 0);
|
|
35860
|
-
summary.totalConsumption =
|
|
35878
|
+
summary.totalConsumption = state6.entrada?.total || 0;
|
|
35861
35879
|
const widgetAggregation = receivedData?.deviceStatusAggregation;
|
|
35862
35880
|
if (widgetAggregation && widgetAggregation.hasData) {
|
|
35863
35881
|
summary.byStatus = {
|
|
@@ -37394,6 +37412,996 @@ var TempSensorSummaryTooltip = {
|
|
|
37394
37412
|
}
|
|
37395
37413
|
};
|
|
37396
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
|
+
|
|
37397
38405
|
// src/components/ModalHeader/index.ts
|
|
37398
38406
|
var DEFAULT_BG_COLOR = "#3e1a7d";
|
|
37399
38407
|
var DEFAULT_TEXT_COLOR = "white";
|
|
@@ -41158,6 +42166,7 @@ function createDistributionChartWidget(config) {
|
|
|
41158
42166
|
CONSUMPTION_CHART_DEFAULTS,
|
|
41159
42167
|
CONSUMPTION_THEME_COLORS,
|
|
41160
42168
|
ConnectionStatusType,
|
|
42169
|
+
ContractSummaryTooltip,
|
|
41161
42170
|
DEFAULT_CLAMP_RANGE,
|
|
41162
42171
|
DEFAULT_ENERGY_GROUP_COLORS,
|
|
41163
42172
|
DEFAULT_GAS_GROUP_COLORS,
|