myio-js-library 0.1.197 → 0.1.199
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.js
CHANGED
|
@@ -8875,8 +8875,8 @@ function getStatusDotClass(deviceStatus) {
|
|
|
8875
8875
|
return "dot--offline";
|
|
8876
8876
|
}
|
|
8877
8877
|
}
|
|
8878
|
-
function buildDOM(
|
|
8879
|
-
const { entityObject, i18n, enableSelection, enableDragDrop } =
|
|
8878
|
+
function buildDOM(state6) {
|
|
8879
|
+
const { entityObject, i18n, enableSelection, enableDragDrop } = state6;
|
|
8880
8880
|
const root = document.createElement("div");
|
|
8881
8881
|
root.className = "myio-ho-card";
|
|
8882
8882
|
root.setAttribute("role", "group");
|
|
@@ -9032,7 +9032,7 @@ function buildDOM(state5) {
|
|
|
9032
9032
|
return root;
|
|
9033
9033
|
}
|
|
9034
9034
|
function buildDebugTooltipInfo(entityObject, statusInfo, stateClass, statusDecisionSource, delayTimeConnectionInMins) {
|
|
9035
|
-
const
|
|
9035
|
+
const formatTimestamp6 = (ts) => {
|
|
9036
9036
|
if (!ts) return "N/A";
|
|
9037
9037
|
const d = new Date(ts);
|
|
9038
9038
|
return d.toLocaleString("pt-BR", {
|
|
@@ -9059,8 +9059,8 @@ function buildDebugTooltipInfo(entityObject, statusInfo, stateClass, statusDecis
|
|
|
9059
9059
|
chipClass: statusInfo.chipClass,
|
|
9060
9060
|
chipLabel: statusInfo.label,
|
|
9061
9061
|
// Connection timestamps
|
|
9062
|
-
lastConnectTime:
|
|
9063
|
-
lastDisconnectTime:
|
|
9062
|
+
lastConnectTime: formatTimestamp6(entityObject.lastConnectTime),
|
|
9063
|
+
lastDisconnectTime: formatTimestamp6(entityObject.lastDisconnectTime),
|
|
9064
9064
|
delayTimeConnectionInMins,
|
|
9065
9065
|
// Raw values
|
|
9066
9066
|
val: entityObject.val,
|
|
@@ -9202,8 +9202,8 @@ function verifyOfflineStatus(entityObject, delayTimeInMins = 15, LogHelper2) {
|
|
|
9202
9202
|
}
|
|
9203
9203
|
return isOffline;
|
|
9204
9204
|
}
|
|
9205
|
-
function paint(root,
|
|
9206
|
-
const { entityObject, i18n, delayTimeConnectionInMins, isSelected, LogHelper: LogHelper2, activeTooltipDebug } =
|
|
9205
|
+
function paint(root, state6) {
|
|
9206
|
+
const { entityObject, i18n, delayTimeConnectionInMins, isSelected, LogHelper: LogHelper2, activeTooltipDebug } = state6;
|
|
9207
9207
|
let statusDecisionSource = "unknown";
|
|
9208
9208
|
if (entityObject.connectionStatus) {
|
|
9209
9209
|
if (entityObject.connectionStatus === "offline") {
|
|
@@ -9255,7 +9255,7 @@ function paint(root, state5) {
|
|
|
9255
9255
|
numSpan.textContent = primaryValue;
|
|
9256
9256
|
const barContainer = root.querySelector(".bar");
|
|
9257
9257
|
const effContainer = root.querySelector(".myio-ho-card__eff");
|
|
9258
|
-
if (
|
|
9258
|
+
if (state6.enableSelection) {
|
|
9259
9259
|
const checkbox = root.querySelector('.myio-ho-card__select input[type="checkbox"]');
|
|
9260
9260
|
if (checkbox) {
|
|
9261
9261
|
checkbox.checked = !!isSelected;
|
|
@@ -9303,8 +9303,8 @@ function paint(root, state5) {
|
|
|
9303
9303
|
statusDot.className = `status-dot ${dotClass}`;
|
|
9304
9304
|
}
|
|
9305
9305
|
}
|
|
9306
|
-
function bindEvents(root,
|
|
9307
|
-
const { entityObject } =
|
|
9306
|
+
function bindEvents(root, state6, callbacks) {
|
|
9307
|
+
const { entityObject } = state6;
|
|
9308
9308
|
const kebabBtn = root.querySelector(".myio-ho-card__kebab");
|
|
9309
9309
|
const menu = root.querySelector(".myio-ho-card__menu");
|
|
9310
9310
|
function toggleMenu() {
|
|
@@ -9360,9 +9360,9 @@ function bindEvents(root, state5, callbacks) {
|
|
|
9360
9360
|
const onSelectionChange = () => {
|
|
9361
9361
|
const selectedIds = MyIOSelectionStore2.getSelectedIds();
|
|
9362
9362
|
const isSelected = selectedIds.includes(entityObject.entityId);
|
|
9363
|
-
if (
|
|
9364
|
-
|
|
9365
|
-
paint(root,
|
|
9363
|
+
if (state6.isSelected !== isSelected) {
|
|
9364
|
+
state6.isSelected = isSelected;
|
|
9365
|
+
paint(root, state6);
|
|
9366
9366
|
}
|
|
9367
9367
|
};
|
|
9368
9368
|
MyIOSelectionStore2.on("selection:change", onSelectionChange);
|
|
@@ -9375,7 +9375,7 @@ function bindEvents(root, state5, callbacks) {
|
|
|
9375
9375
|
clearTimeout(TempRangeTooltip._hideTimer);
|
|
9376
9376
|
TempRangeTooltip._hideTimer = null;
|
|
9377
9377
|
}
|
|
9378
|
-
TempRangeTooltip.show(root,
|
|
9378
|
+
TempRangeTooltip.show(root, state6.entityObject, e);
|
|
9379
9379
|
};
|
|
9380
9380
|
const hideTooltip = () => {
|
|
9381
9381
|
TempRangeTooltip._startDelayedHide();
|
|
@@ -9393,7 +9393,7 @@ function bindEvents(root, state5, callbacks) {
|
|
|
9393
9393
|
clearTimeout(EnergyRangeTooltip._hideTimer);
|
|
9394
9394
|
EnergyRangeTooltip._hideTimer = null;
|
|
9395
9395
|
}
|
|
9396
|
-
EnergyRangeTooltip.show(root,
|
|
9396
|
+
EnergyRangeTooltip.show(root, state6.entityObject, e);
|
|
9397
9397
|
};
|
|
9398
9398
|
const hideEnergyTooltip = () => {
|
|
9399
9399
|
EnergyRangeTooltip._startDelayedHide();
|
|
@@ -9445,7 +9445,7 @@ function bindEvents(root, state5, callbacks) {
|
|
|
9445
9445
|
}
|
|
9446
9446
|
});
|
|
9447
9447
|
}
|
|
9448
|
-
if (
|
|
9448
|
+
if (state6.enableDragDrop) {
|
|
9449
9449
|
root.addEventListener("dragstart", (e) => {
|
|
9450
9450
|
root.classList.add("is-dragging");
|
|
9451
9451
|
e.dataTransfer.setData("text/plain", entityObject.entityId);
|
|
@@ -9487,17 +9487,17 @@ function renderCardComponentHeadOffice(containerEl, params) {
|
|
|
9487
9487
|
throw new Error("renderCardComponentHeadOffice: containerEl is required");
|
|
9488
9488
|
}
|
|
9489
9489
|
ensureCss();
|
|
9490
|
-
const
|
|
9491
|
-
const root = buildDOM(
|
|
9492
|
-
|
|
9490
|
+
const state6 = normalizeParams(params);
|
|
9491
|
+
const root = buildDOM(state6);
|
|
9492
|
+
state6.isSelected = params.isSelected || false;
|
|
9493
9493
|
containerEl.appendChild(root);
|
|
9494
|
-
bindEvents(root,
|
|
9495
|
-
paint(root,
|
|
9494
|
+
bindEvents(root, state6, state6.callbacks);
|
|
9495
|
+
paint(root, state6);
|
|
9496
9496
|
return {
|
|
9497
9497
|
update(next) {
|
|
9498
9498
|
if (next) {
|
|
9499
|
-
Object.assign(
|
|
9500
|
-
paint(root,
|
|
9499
|
+
Object.assign(state6.entityObject, next);
|
|
9500
|
+
paint(root, state6);
|
|
9501
9501
|
}
|
|
9502
9502
|
},
|
|
9503
9503
|
destroy() {
|
|
@@ -11227,6 +11227,15 @@ function calculateTempStatus(currentTemp, avgTemp) {
|
|
|
11227
11227
|
return { deviation: absDeviation, sign: "-", status: "below", statusText: "Abaixo da media", statusIcon: "\u{1F53B}" };
|
|
11228
11228
|
}
|
|
11229
11229
|
}
|
|
11230
|
+
function calculateRangeStatus(currentTemp, minTemp, maxTemp) {
|
|
11231
|
+
if (currentTemp >= minTemp && currentTemp <= maxTemp) {
|
|
11232
|
+
return { status: "ok", statusText: "Dentro da faixa", statusIcon: "\u2713" };
|
|
11233
|
+
} else if (currentTemp > maxTemp) {
|
|
11234
|
+
return { status: "above", statusText: "Acima da faixa", statusIcon: "\u{1F53A}" };
|
|
11235
|
+
} else {
|
|
11236
|
+
return { status: "below", statusText: "Abaixo da faixa", statusIcon: "\u{1F53B}" };
|
|
11237
|
+
}
|
|
11238
|
+
}
|
|
11230
11239
|
function calcTempBarPosition(temp, minRange, maxRange) {
|
|
11231
11240
|
const rangeSize = maxRange - minRange;
|
|
11232
11241
|
const extendedMin = minRange - rangeSize * 0.3;
|
|
@@ -11269,8 +11278,10 @@ function generateBodyHTML2(data) {
|
|
|
11269
11278
|
const { device, average, lastUpdated } = data;
|
|
11270
11279
|
const timestamp = formatTimestamp2(lastUpdated);
|
|
11271
11280
|
const tempStatus = calculateTempStatus(device.currentTemp, average.value);
|
|
11281
|
+
const rangeStatus = calculateRangeStatus(device.currentTemp, device.minTemp, device.maxTemp);
|
|
11272
11282
|
const deviceBarPos = calcTempBarPosition(device.currentTemp, device.minTemp, device.maxTemp);
|
|
11273
11283
|
const avgBarPos = calcTempBarPosition(average.value, device.minTemp, device.maxTemp);
|
|
11284
|
+
const rangeStatusClass = rangeStatus.status === "ok" ? "normal" : rangeStatus.status;
|
|
11274
11285
|
return `
|
|
11275
11286
|
<div class="myio-temp-comparison-tooltip__body">
|
|
11276
11287
|
<!-- Main Stats -->
|
|
@@ -11285,12 +11296,18 @@ function generateBodyHTML2(data) {
|
|
|
11285
11296
|
</div>
|
|
11286
11297
|
</div>
|
|
11287
11298
|
|
|
11288
|
-
<!-- Configured Range -->
|
|
11299
|
+
<!-- Configured Range with Status -->
|
|
11289
11300
|
<div class="myio-temp-comparison-tooltip__range">
|
|
11290
11301
|
<span class="myio-temp-comparison-tooltip__range-label">Faixa Ideal:</span>
|
|
11291
11302
|
<span class="myio-temp-comparison-tooltip__range-value">${formatTemp(device.minTemp)} - ${formatTemp(device.maxTemp)}</span>
|
|
11292
11303
|
</div>
|
|
11293
11304
|
|
|
11305
|
+
<!-- Range Status Indicator -->
|
|
11306
|
+
<div class="myio-temp-comparison-tooltip__status ${rangeStatusClass}">
|
|
11307
|
+
<span class="myio-temp-comparison-tooltip__status-icon">${rangeStatus.statusIcon}</span>
|
|
11308
|
+
<span>${rangeStatus.statusText}</span>
|
|
11309
|
+
</div>
|
|
11310
|
+
|
|
11294
11311
|
<!-- Section: Average Comparison -->
|
|
11295
11312
|
<div class="myio-temp-comparison-tooltip__section-title">
|
|
11296
11313
|
<span class="myio-temp-comparison-tooltip__section-icon">\u{1F4CA}</span>
|
|
@@ -30800,7 +30817,7 @@ async function openTemperatureModal(params) {
|
|
|
30800
30817
|
const defaultDateRange = getTodaySoFar();
|
|
30801
30818
|
const startTs = params.startDate ? new Date(params.startDate).getTime() : defaultDateRange.startTs;
|
|
30802
30819
|
const endTs = params.endDate ? new Date(params.endDate).getTime() : defaultDateRange.endTs;
|
|
30803
|
-
const
|
|
30820
|
+
const state6 = {
|
|
30804
30821
|
token: params.token,
|
|
30805
30822
|
deviceId: params.deviceId,
|
|
30806
30823
|
label: params.label || "Sensor de Temperatura",
|
|
@@ -30823,58 +30840,58 @@ async function openTemperatureModal(params) {
|
|
|
30823
30840
|
};
|
|
30824
30841
|
const savedGranularity = localStorage.getItem("myio-temp-modal-granularity");
|
|
30825
30842
|
const savedTheme = localStorage.getItem("myio-temp-modal-theme");
|
|
30826
|
-
if (savedGranularity)
|
|
30827
|
-
if (savedTheme)
|
|
30843
|
+
if (savedGranularity) state6.granularity = savedGranularity;
|
|
30844
|
+
if (savedTheme) state6.theme = savedTheme;
|
|
30828
30845
|
const modalContainer = document.createElement("div");
|
|
30829
30846
|
modalContainer.id = modalId;
|
|
30830
30847
|
document.body.appendChild(modalContainer);
|
|
30831
|
-
renderModal(modalContainer,
|
|
30848
|
+
renderModal(modalContainer, state6, modalId);
|
|
30832
30849
|
try {
|
|
30833
|
-
|
|
30834
|
-
|
|
30835
|
-
|
|
30836
|
-
renderModal(modalContainer,
|
|
30837
|
-
drawChart(modalId,
|
|
30850
|
+
state6.data = await fetchTemperatureData(state6.token, state6.deviceId, state6.startTs, state6.endTs);
|
|
30851
|
+
state6.stats = calculateStats(state6.data, state6.clampRange);
|
|
30852
|
+
state6.isLoading = false;
|
|
30853
|
+
renderModal(modalContainer, state6, modalId);
|
|
30854
|
+
drawChart(modalId, state6);
|
|
30838
30855
|
} catch (error) {
|
|
30839
30856
|
console.error("[TemperatureModal] Error fetching data:", error);
|
|
30840
|
-
|
|
30841
|
-
renderModal(modalContainer,
|
|
30857
|
+
state6.isLoading = false;
|
|
30858
|
+
renderModal(modalContainer, state6, modalId, error);
|
|
30842
30859
|
}
|
|
30843
|
-
await setupEventListeners(modalContainer,
|
|
30860
|
+
await setupEventListeners(modalContainer, state6, modalId, params.onClose);
|
|
30844
30861
|
return {
|
|
30845
30862
|
destroy: () => {
|
|
30846
30863
|
modalContainer.remove();
|
|
30847
30864
|
params.onClose?.();
|
|
30848
30865
|
},
|
|
30849
30866
|
updateData: async (startDate, endDate, granularity) => {
|
|
30850
|
-
|
|
30851
|
-
|
|
30852
|
-
if (granularity)
|
|
30853
|
-
|
|
30854
|
-
renderModal(modalContainer,
|
|
30867
|
+
state6.startTs = new Date(startDate).getTime();
|
|
30868
|
+
state6.endTs = new Date(endDate).getTime();
|
|
30869
|
+
if (granularity) state6.granularity = granularity;
|
|
30870
|
+
state6.isLoading = true;
|
|
30871
|
+
renderModal(modalContainer, state6, modalId);
|
|
30855
30872
|
try {
|
|
30856
|
-
|
|
30857
|
-
|
|
30858
|
-
|
|
30859
|
-
renderModal(modalContainer,
|
|
30860
|
-
drawChart(modalId,
|
|
30873
|
+
state6.data = await fetchTemperatureData(state6.token, state6.deviceId, state6.startTs, state6.endTs);
|
|
30874
|
+
state6.stats = calculateStats(state6.data, state6.clampRange);
|
|
30875
|
+
state6.isLoading = false;
|
|
30876
|
+
renderModal(modalContainer, state6, modalId);
|
|
30877
|
+
drawChart(modalId, state6);
|
|
30861
30878
|
} catch (error) {
|
|
30862
30879
|
console.error("[TemperatureModal] Error updating data:", error);
|
|
30863
|
-
|
|
30864
|
-
renderModal(modalContainer,
|
|
30880
|
+
state6.isLoading = false;
|
|
30881
|
+
renderModal(modalContainer, state6, modalId, error);
|
|
30865
30882
|
}
|
|
30866
30883
|
}
|
|
30867
30884
|
};
|
|
30868
30885
|
}
|
|
30869
|
-
function renderModal(container,
|
|
30870
|
-
const colors = getThemeColors(
|
|
30871
|
-
const startDateStr = new Date(
|
|
30872
|
-
const endDateStr = new Date(
|
|
30873
|
-
const statusText =
|
|
30874
|
-
const statusColor =
|
|
30875
|
-
const rangeText =
|
|
30876
|
-
const startDateInput = new Date(
|
|
30877
|
-
const endDateInput = new Date(
|
|
30886
|
+
function renderModal(container, state6, modalId, error) {
|
|
30887
|
+
const colors = getThemeColors(state6.theme);
|
|
30888
|
+
const startDateStr = new Date(state6.startTs).toLocaleDateString(state6.locale);
|
|
30889
|
+
const endDateStr = new Date(state6.endTs).toLocaleDateString(state6.locale);
|
|
30890
|
+
const statusText = state6.temperatureStatus === "ok" ? "Dentro da faixa" : state6.temperatureStatus === "above" ? "Acima do limite" : state6.temperatureStatus === "below" ? "Abaixo do limite" : "N/A";
|
|
30891
|
+
const statusColor = state6.temperatureStatus === "ok" ? colors.success : state6.temperatureStatus === "above" ? colors.danger : state6.temperatureStatus === "below" ? colors.primary : colors.textMuted;
|
|
30892
|
+
const rangeText = state6.temperatureMin !== null && state6.temperatureMax !== null ? `${state6.temperatureMin}\xB0C - ${state6.temperatureMax}\xB0C` : "N\xE3o definida";
|
|
30893
|
+
const startDateInput = new Date(state6.startTs).toISOString().slice(0, 16);
|
|
30894
|
+
const endDateInput = new Date(state6.endTs).toISOString().slice(0, 16);
|
|
30878
30895
|
const isMaximized = container.__isMaximized || false;
|
|
30879
30896
|
const contentMaxWidth = isMaximized ? "100%" : "900px";
|
|
30880
30897
|
const contentMaxHeight = isMaximized ? "100vh" : "95vh";
|
|
@@ -30901,7 +30918,7 @@ function renderModal(container, state5, modalId, error) {
|
|
|
30901
30918
|
min-height: 20px;
|
|
30902
30919
|
">
|
|
30903
30920
|
<h2 style="margin: 6px; font-size: 18px; font-weight: 600; color: white; line-height: 2;">
|
|
30904
|
-
\u{1F321}\uFE0F ${
|
|
30921
|
+
\u{1F321}\uFE0F ${state6.label} - Hist\xF3rico de Temperatura
|
|
30905
30922
|
</h2>
|
|
30906
30923
|
<div style="display: flex; gap: 4px; align-items: center;">
|
|
30907
30924
|
<!-- Theme Toggle -->
|
|
@@ -30909,7 +30926,7 @@ function renderModal(container, state5, modalId, error) {
|
|
|
30909
30926
|
background: none; border: none; font-size: 16px; cursor: pointer;
|
|
30910
30927
|
padding: 4px 8px; border-radius: 6px; color: rgba(255,255,255,0.8);
|
|
30911
30928
|
transition: background-color 0.2s;
|
|
30912
|
-
">${
|
|
30929
|
+
">${state6.theme === "dark" ? "\u2600\uFE0F" : "\u{1F319}"}</button>
|
|
30913
30930
|
<!-- Maximize Button -->
|
|
30914
30931
|
<button id="${modalId}-maximize" title="${isMaximized ? "Restaurar" : "Maximizar"}" style="
|
|
30915
30932
|
background: none; border: none; font-size: 16px; cursor: pointer;
|
|
@@ -30931,7 +30948,7 @@ function renderModal(container, state5, modalId, error) {
|
|
|
30931
30948
|
<!-- Controls Row -->
|
|
30932
30949
|
<div style="
|
|
30933
30950
|
display: flex; gap: 16px; flex-wrap: wrap; align-items: flex-end;
|
|
30934
|
-
margin-bottom: 16px; padding: 16px; background: ${
|
|
30951
|
+
margin-bottom: 16px; padding: 16px; background: ${state6.theme === "dark" ? "rgba(255,255,255,0.05)" : "#f7f7f7"};
|
|
30935
30952
|
border-radius: 6px; border: 1px solid ${colors.border};
|
|
30936
30953
|
">
|
|
30937
30954
|
<!-- Granularity Select -->
|
|
@@ -30944,8 +30961,8 @@ function renderModal(container, state5, modalId, error) {
|
|
|
30944
30961
|
font-size: 14px; color: ${colors.text}; background: ${colors.surface};
|
|
30945
30962
|
cursor: pointer; min-width: 130px;
|
|
30946
30963
|
">
|
|
30947
|
-
<option value="hour" ${
|
|
30948
|
-
<option value="day" ${
|
|
30964
|
+
<option value="hour" ${state6.granularity === "hour" ? "selected" : ""}>Hora (30 min)</option>
|
|
30965
|
+
<option value="day" ${state6.granularity === "day" ? "selected" : ""}>Dia (m\xE9dia)</option>
|
|
30949
30966
|
</select>
|
|
30950
30967
|
</div>
|
|
30951
30968
|
<!-- Day Period Filter (Multiselect) -->
|
|
@@ -30959,7 +30976,7 @@ function renderModal(container, state5, modalId, error) {
|
|
|
30959
30976
|
cursor: pointer; min-width: 180px; text-align: left;
|
|
30960
30977
|
display: flex; align-items: center; justify-content: space-between; gap: 8px;
|
|
30961
30978
|
">
|
|
30962
|
-
<span>${getSelectedPeriodsLabel(
|
|
30979
|
+
<span>${getSelectedPeriodsLabel(state6.selectedPeriods)}</span>
|
|
30963
30980
|
<span style="font-size: 10px;">\u25BC</span>
|
|
30964
30981
|
</button>
|
|
30965
30982
|
<div id="${modalId}-period-dropdown" style="
|
|
@@ -30972,12 +30989,12 @@ function renderModal(container, state5, modalId, error) {
|
|
|
30972
30989
|
<label style="
|
|
30973
30990
|
display: flex; align-items: center; gap: 8px; padding: 8px 12px;
|
|
30974
30991
|
cursor: pointer; font-size: 13px; color: ${colors.text};
|
|
30975
|
-
" onmouseover="this.style.background='${
|
|
30992
|
+
" onmouseover="this.style.background='${state6.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"}'"
|
|
30976
30993
|
onmouseout="this.style.background='transparent'">
|
|
30977
30994
|
<input type="checkbox"
|
|
30978
30995
|
name="${modalId}-period"
|
|
30979
30996
|
value="${period.id}"
|
|
30980
|
-
${
|
|
30997
|
+
${state6.selectedPeriods.includes(period.id) ? "checked" : ""}
|
|
30981
30998
|
style="width: 16px; height: 16px; cursor: pointer; accent-color: #3e1a7d;">
|
|
30982
30999
|
${period.label}
|
|
30983
31000
|
</label>
|
|
@@ -30985,13 +31002,13 @@ function renderModal(container, state5, modalId, error) {
|
|
|
30985
31002
|
<div style="border-top: 1px solid ${colors.border}; margin-top: 8px; padding-top: 8px;">
|
|
30986
31003
|
<button id="${modalId}-period-select-all" type="button" style="
|
|
30987
31004
|
width: calc(100% - 16px); margin: 0 8px 4px; padding: 6px;
|
|
30988
|
-
background: ${
|
|
31005
|
+
background: ${state6.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"};
|
|
30989
31006
|
border: none; border-radius: 4px; cursor: pointer;
|
|
30990
31007
|
font-size: 12px; color: ${colors.text};
|
|
30991
31008
|
">Selecionar Todos</button>
|
|
30992
31009
|
<button id="${modalId}-period-clear" type="button" style="
|
|
30993
31010
|
width: calc(100% - 16px); margin: 0 8px; padding: 6px;
|
|
30994
|
-
background: ${
|
|
31011
|
+
background: ${state6.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"};
|
|
30995
31012
|
border: none; border-radius: 4px; cursor: pointer;
|
|
30996
31013
|
font-size: 12px; color: ${colors.text};
|
|
30997
31014
|
">Limpar Sele\xE7\xE3o</button>
|
|
@@ -31016,8 +31033,8 @@ function renderModal(container, state5, modalId, error) {
|
|
|
31016
31033
|
font-size: 14px; font-weight: 500; height: 38px;
|
|
31017
31034
|
display: flex; align-items: center; gap: 8px;
|
|
31018
31035
|
font-family: 'Roboto', Arial, sans-serif;
|
|
31019
|
-
" ${
|
|
31020
|
-
${
|
|
31036
|
+
" ${state6.isLoading ? "disabled" : ""}>
|
|
31037
|
+
${state6.isLoading ? '<span style="animation: spin 1s linear infinite; display: inline-block;">\u21BB</span> Carregando...' : "Carregar"}
|
|
31021
31038
|
</button>
|
|
31022
31039
|
</div>
|
|
31023
31040
|
|
|
@@ -31028,40 +31045,40 @@ function renderModal(container, state5, modalId, error) {
|
|
|
31028
31045
|
">
|
|
31029
31046
|
<!-- Current Temperature -->
|
|
31030
31047
|
<div style="
|
|
31031
|
-
padding: 16px; background: ${
|
|
31048
|
+
padding: 16px; background: ${state6.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
|
|
31032
31049
|
border-radius: 12px; border: 1px solid ${colors.border};
|
|
31033
31050
|
">
|
|
31034
31051
|
<span style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500;">Temperatura Atual</span>
|
|
31035
31052
|
<div style="font-weight: 700; font-size: 24px; color: ${statusColor}; margin-top: 4px;">
|
|
31036
|
-
${
|
|
31053
|
+
${state6.currentTemperature !== null ? formatTemperature(state6.currentTemperature) : "N/A"}
|
|
31037
31054
|
</div>
|
|
31038
31055
|
<div style="font-size: 11px; color: ${statusColor}; margin-top: 2px;">${statusText}</div>
|
|
31039
31056
|
</div>
|
|
31040
31057
|
<!-- Average -->
|
|
31041
31058
|
<div style="
|
|
31042
|
-
padding: 16px; background: ${
|
|
31059
|
+
padding: 16px; background: ${state6.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
|
|
31043
31060
|
border-radius: 12px; border: 1px solid ${colors.border};
|
|
31044
31061
|
">
|
|
31045
31062
|
<span style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500;">M\xE9dia do Per\xEDodo</span>
|
|
31046
31063
|
<div id="${modalId}-avg" style="font-weight: 600; font-size: 20px; color: ${colors.text}; margin-top: 4px;">
|
|
31047
|
-
${
|
|
31064
|
+
${state6.stats.count > 0 ? formatTemperature(state6.stats.avg) : "N/A"}
|
|
31048
31065
|
</div>
|
|
31049
31066
|
<div style="font-size: 11px; color: ${colors.textMuted}; margin-top: 2px;">${startDateStr} - ${endDateStr}</div>
|
|
31050
31067
|
</div>
|
|
31051
31068
|
<!-- Min/Max -->
|
|
31052
31069
|
<div style="
|
|
31053
|
-
padding: 16px; background: ${
|
|
31070
|
+
padding: 16px; background: ${state6.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
|
|
31054
31071
|
border-radius: 12px; border: 1px solid ${colors.border};
|
|
31055
31072
|
">
|
|
31056
31073
|
<span style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500;">Min / Max</span>
|
|
31057
31074
|
<div id="${modalId}-minmax" style="font-weight: 600; font-size: 20px; color: ${colors.text}; margin-top: 4px;">
|
|
31058
|
-
${
|
|
31075
|
+
${state6.stats.count > 0 ? `${formatTemperature(state6.stats.min)} / ${formatTemperature(state6.stats.max)}` : "N/A"}
|
|
31059
31076
|
</div>
|
|
31060
|
-
<div style="font-size: 11px; color: ${colors.textMuted}; margin-top: 2px;">${
|
|
31077
|
+
<div style="font-size: 11px; color: ${colors.textMuted}; margin-top: 2px;">${state6.stats.count} leituras</div>
|
|
31061
31078
|
</div>
|
|
31062
31079
|
<!-- Ideal Range -->
|
|
31063
31080
|
<div style="
|
|
31064
|
-
padding: 16px; background: ${
|
|
31081
|
+
padding: 16px; background: ${state6.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
|
|
31065
31082
|
border-radius: 12px; border: 1px solid ${colors.border};
|
|
31066
31083
|
">
|
|
31067
31084
|
<span style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500;">Faixa Ideal</span>
|
|
@@ -31077,18 +31094,18 @@ function renderModal(container, state5, modalId, error) {
|
|
|
31077
31094
|
Hist\xF3rico de Temperatura
|
|
31078
31095
|
</h3>
|
|
31079
31096
|
<div id="${modalId}-chart" style="
|
|
31080
|
-
height: 320px; background: ${
|
|
31097
|
+
height: 320px; background: ${state6.theme === "dark" ? "rgba(255,255,255,0.03)" : "#fafafa"};
|
|
31081
31098
|
border-radius: 12px; display: flex; justify-content: center; align-items: center;
|
|
31082
31099
|
border: 1px solid ${colors.border}; position: relative;
|
|
31083
31100
|
">
|
|
31084
|
-
${
|
|
31101
|
+
${state6.isLoading ? `<div style="text-align: center; color: ${colors.textMuted};">
|
|
31085
31102
|
<div style="animation: spin 1s linear infinite; font-size: 32px; margin-bottom: 8px;">\u21BB</div>
|
|
31086
31103
|
<div>Carregando dados...</div>
|
|
31087
31104
|
</div>` : error ? `<div style="text-align: center; color: ${colors.danger};">
|
|
31088
31105
|
<div style="font-size: 32px; margin-bottom: 8px;">\u26A0\uFE0F</div>
|
|
31089
31106
|
<div>Erro ao carregar dados</div>
|
|
31090
31107
|
<div style="font-size: 12px; margin-top: 4px;">${error.message}</div>
|
|
31091
|
-
</div>` :
|
|
31108
|
+
</div>` : state6.data.length === 0 ? `<div style="text-align: center; color: ${colors.textMuted};">
|
|
31092
31109
|
<div style="font-size: 32px; margin-bottom: 8px;">\u{1F4ED}</div>
|
|
31093
31110
|
<div>Sem dados para o per\xEDodo selecionado</div>
|
|
31094
31111
|
</div>` : `<canvas id="${modalId}-canvas" style="width: 100%; height: 100%;"></canvas>`}
|
|
@@ -31098,12 +31115,12 @@ function renderModal(container, state5, modalId, error) {
|
|
|
31098
31115
|
<!-- Actions -->
|
|
31099
31116
|
<div style="display: flex; justify-content: flex-end; gap: 12px;">
|
|
31100
31117
|
<button id="${modalId}-export" style="
|
|
31101
|
-
background: ${
|
|
31118
|
+
background: ${state6.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f7f7f7"};
|
|
31102
31119
|
color: ${colors.text}; border: 1px solid ${colors.border};
|
|
31103
31120
|
padding: 8px 16px; border-radius: 6px; cursor: pointer;
|
|
31104
31121
|
font-size: 14px; display: flex; align-items: center; gap: 8px;
|
|
31105
31122
|
font-family: 'Roboto', Arial, sans-serif;
|
|
31106
|
-
" ${
|
|
31123
|
+
" ${state6.data.length === 0 ? "disabled" : ""}>
|
|
31107
31124
|
\u{1F4E5} Exportar CSV
|
|
31108
31125
|
</button>
|
|
31109
31126
|
<button id="${modalId}-close-btn" style="
|
|
@@ -31154,14 +31171,14 @@ function renderModal(container, state5, modalId, error) {
|
|
|
31154
31171
|
</style>
|
|
31155
31172
|
`;
|
|
31156
31173
|
}
|
|
31157
|
-
function drawChart(modalId,
|
|
31174
|
+
function drawChart(modalId, state6) {
|
|
31158
31175
|
const chartContainer = document.getElementById(`${modalId}-chart`);
|
|
31159
31176
|
const canvas = document.getElementById(`${modalId}-canvas`);
|
|
31160
|
-
if (!chartContainer || !canvas ||
|
|
31177
|
+
if (!chartContainer || !canvas || state6.data.length === 0) return;
|
|
31161
31178
|
const ctx = canvas.getContext("2d");
|
|
31162
31179
|
if (!ctx) return;
|
|
31163
|
-
const colors = getThemeColors(
|
|
31164
|
-
const filteredData = filterByDayPeriods(
|
|
31180
|
+
const colors = getThemeColors(state6.theme);
|
|
31181
|
+
const filteredData = filterByDayPeriods(state6.data, state6.selectedPeriods);
|
|
31165
31182
|
if (filteredData.length === 0) {
|
|
31166
31183
|
canvas.width = chartContainer.clientWidth;
|
|
31167
31184
|
canvas.height = chartContainer.clientHeight;
|
|
@@ -31172,14 +31189,14 @@ function drawChart(modalId, state5) {
|
|
|
31172
31189
|
return;
|
|
31173
31190
|
}
|
|
31174
31191
|
let chartData;
|
|
31175
|
-
if (
|
|
31192
|
+
if (state6.granularity === "hour") {
|
|
31176
31193
|
const interpolated = interpolateTemperature(filteredData, {
|
|
31177
31194
|
intervalMinutes: 30,
|
|
31178
|
-
startTs:
|
|
31179
|
-
endTs:
|
|
31180
|
-
clampRange:
|
|
31195
|
+
startTs: state6.startTs,
|
|
31196
|
+
endTs: state6.endTs,
|
|
31197
|
+
clampRange: state6.clampRange
|
|
31181
31198
|
});
|
|
31182
|
-
const filteredInterpolated = filterByDayPeriods(interpolated,
|
|
31199
|
+
const filteredInterpolated = filterByDayPeriods(interpolated, state6.selectedPeriods);
|
|
31183
31200
|
chartData = filteredInterpolated.map((item) => ({
|
|
31184
31201
|
x: item.ts,
|
|
31185
31202
|
y: Number(item.value),
|
|
@@ -31187,7 +31204,7 @@ function drawChart(modalId, state5) {
|
|
|
31187
31204
|
screenY: 0
|
|
31188
31205
|
}));
|
|
31189
31206
|
} else {
|
|
31190
|
-
const daily = aggregateByDay(filteredData,
|
|
31207
|
+
const daily = aggregateByDay(filteredData, state6.clampRange);
|
|
31191
31208
|
chartData = daily.map((item) => ({
|
|
31192
31209
|
x: item.dateTs,
|
|
31193
31210
|
y: item.avg,
|
|
@@ -31205,12 +31222,12 @@ function drawChart(modalId, state5) {
|
|
|
31205
31222
|
const paddingRight = 20;
|
|
31206
31223
|
const paddingTop = 20;
|
|
31207
31224
|
const paddingBottom = 55;
|
|
31208
|
-
const isPeriodsFiltered =
|
|
31225
|
+
const isPeriodsFiltered = state6.selectedPeriods.length < 4 && state6.selectedPeriods.length > 0;
|
|
31209
31226
|
const values = chartData.map((d) => d.y);
|
|
31210
31227
|
const dataMin = Math.min(...values);
|
|
31211
31228
|
const dataMax = Math.max(...values);
|
|
31212
|
-
const thresholdMin =
|
|
31213
|
-
const thresholdMax =
|
|
31229
|
+
const thresholdMin = state6.temperatureMin !== null ? state6.temperatureMin : dataMin;
|
|
31230
|
+
const thresholdMax = state6.temperatureMax !== null ? state6.temperatureMax : dataMax;
|
|
31214
31231
|
const minY = Math.min(dataMin, thresholdMin) - 1;
|
|
31215
31232
|
const maxY = Math.max(dataMax, thresholdMax) + 1;
|
|
31216
31233
|
const chartWidth = width - paddingLeft - paddingRight;
|
|
@@ -31242,9 +31259,9 @@ function drawChart(modalId, state5) {
|
|
|
31242
31259
|
ctx.lineTo(width - paddingRight, y);
|
|
31243
31260
|
ctx.stroke();
|
|
31244
31261
|
}
|
|
31245
|
-
if (
|
|
31246
|
-
const rangeMinY = height - paddingBottom - (
|
|
31247
|
-
const rangeMaxY = height - paddingBottom - (
|
|
31262
|
+
if (state6.temperatureMin !== null && state6.temperatureMax !== null) {
|
|
31263
|
+
const rangeMinY = height - paddingBottom - (state6.temperatureMin - minY) * scaleY;
|
|
31264
|
+
const rangeMaxY = height - paddingBottom - (state6.temperatureMax - minY) * scaleY;
|
|
31248
31265
|
ctx.fillStyle = "rgba(76, 175, 80, 0.1)";
|
|
31249
31266
|
ctx.fillRect(paddingLeft, rangeMaxY, chartWidth, rangeMinY - rangeMaxY);
|
|
31250
31267
|
ctx.strokeStyle = colors.success;
|
|
@@ -31286,10 +31303,10 @@ function drawChart(modalId, state5) {
|
|
|
31286
31303
|
const point = chartData[i];
|
|
31287
31304
|
const date = new Date(point.x);
|
|
31288
31305
|
let label;
|
|
31289
|
-
if (
|
|
31290
|
-
label = date.toLocaleTimeString(
|
|
31306
|
+
if (state6.granularity === "hour") {
|
|
31307
|
+
label = date.toLocaleTimeString(state6.locale, { hour: "2-digit", minute: "2-digit" });
|
|
31291
31308
|
} else {
|
|
31292
|
-
label = date.toLocaleDateString(
|
|
31309
|
+
label = date.toLocaleDateString(state6.locale, { day: "2-digit", month: "2-digit" });
|
|
31293
31310
|
}
|
|
31294
31311
|
ctx.strokeStyle = colors.chartGrid;
|
|
31295
31312
|
ctx.lineWidth = 1;
|
|
@@ -31307,16 +31324,16 @@ function drawChart(modalId, state5) {
|
|
|
31307
31324
|
ctx.lineTo(paddingLeft, height - paddingBottom);
|
|
31308
31325
|
ctx.lineTo(width - paddingRight, height - paddingBottom);
|
|
31309
31326
|
ctx.stroke();
|
|
31310
|
-
setupChartTooltip(canvas, chartContainer, chartData,
|
|
31327
|
+
setupChartTooltip(canvas, chartContainer, chartData, state6, colors);
|
|
31311
31328
|
}
|
|
31312
|
-
function setupChartTooltip(canvas, container, chartData,
|
|
31329
|
+
function setupChartTooltip(canvas, container, chartData, state6, colors) {
|
|
31313
31330
|
const existingTooltip = container.querySelector(".myio-chart-tooltip");
|
|
31314
31331
|
if (existingTooltip) existingTooltip.remove();
|
|
31315
31332
|
const tooltip = document.createElement("div");
|
|
31316
31333
|
tooltip.className = "myio-chart-tooltip";
|
|
31317
31334
|
tooltip.style.cssText = `
|
|
31318
31335
|
position: absolute;
|
|
31319
|
-
background: ${
|
|
31336
|
+
background: ${state6.theme === "dark" ? "rgba(30, 30, 40, 0.95)" : "rgba(255, 255, 255, 0.98)"};
|
|
31320
31337
|
border: 1px solid ${colors.border};
|
|
31321
31338
|
border-radius: 8px;
|
|
31322
31339
|
padding: 10px 14px;
|
|
@@ -31353,17 +31370,17 @@ function setupChartTooltip(canvas, container, chartData, state5, colors) {
|
|
|
31353
31370
|
if (point) {
|
|
31354
31371
|
const date = new Date(point.x);
|
|
31355
31372
|
let dateStr;
|
|
31356
|
-
if (
|
|
31357
|
-
dateStr = date.toLocaleDateString(
|
|
31373
|
+
if (state6.granularity === "hour") {
|
|
31374
|
+
dateStr = date.toLocaleDateString(state6.locale, {
|
|
31358
31375
|
day: "2-digit",
|
|
31359
31376
|
month: "2-digit",
|
|
31360
31377
|
year: "numeric"
|
|
31361
|
-
}) + " " + date.toLocaleTimeString(
|
|
31378
|
+
}) + " " + date.toLocaleTimeString(state6.locale, {
|
|
31362
31379
|
hour: "2-digit",
|
|
31363
31380
|
minute: "2-digit"
|
|
31364
31381
|
});
|
|
31365
31382
|
} else {
|
|
31366
|
-
dateStr = date.toLocaleDateString(
|
|
31383
|
+
dateStr = date.toLocaleDateString(state6.locale, {
|
|
31367
31384
|
day: "2-digit",
|
|
31368
31385
|
month: "2-digit",
|
|
31369
31386
|
year: "numeric"
|
|
@@ -31401,7 +31418,7 @@ function setupChartTooltip(canvas, container, chartData, state5, colors) {
|
|
|
31401
31418
|
canvas.style.cursor = "default";
|
|
31402
31419
|
});
|
|
31403
31420
|
}
|
|
31404
|
-
async function setupEventListeners(container,
|
|
31421
|
+
async function setupEventListeners(container, state6, modalId, onClose) {
|
|
31405
31422
|
const closeModal = () => {
|
|
31406
31423
|
container.remove();
|
|
31407
31424
|
onClose?.();
|
|
@@ -31412,19 +31429,19 @@ async function setupEventListeners(container, state5, modalId, onClose) {
|
|
|
31412
31429
|
document.getElementById(`${modalId}-close`)?.addEventListener("click", closeModal);
|
|
31413
31430
|
document.getElementById(`${modalId}-close-btn`)?.addEventListener("click", closeModal);
|
|
31414
31431
|
const dateRangeInput = document.getElementById(`${modalId}-date-range`);
|
|
31415
|
-
if (dateRangeInput && !
|
|
31432
|
+
if (dateRangeInput && !state6.dateRangePicker) {
|
|
31416
31433
|
try {
|
|
31417
|
-
|
|
31418
|
-
presetStart: new Date(
|
|
31419
|
-
presetEnd: new Date(
|
|
31434
|
+
state6.dateRangePicker = await createDateRangePicker2(dateRangeInput, {
|
|
31435
|
+
presetStart: new Date(state6.startTs).toISOString(),
|
|
31436
|
+
presetEnd: new Date(state6.endTs).toISOString(),
|
|
31420
31437
|
includeTime: true,
|
|
31421
31438
|
timePrecision: "minute",
|
|
31422
31439
|
maxRangeDays: 90,
|
|
31423
|
-
locale:
|
|
31440
|
+
locale: state6.locale,
|
|
31424
31441
|
parentEl: container.querySelector(".myio-temp-modal-content"),
|
|
31425
31442
|
onApply: (result) => {
|
|
31426
|
-
|
|
31427
|
-
|
|
31443
|
+
state6.startTs = new Date(result.startISO).getTime();
|
|
31444
|
+
state6.endTs = new Date(result.endISO).getTime();
|
|
31428
31445
|
console.log("[TemperatureModal] Date range applied:", result);
|
|
31429
31446
|
}
|
|
31430
31447
|
});
|
|
@@ -31433,19 +31450,19 @@ async function setupEventListeners(container, state5, modalId, onClose) {
|
|
|
31433
31450
|
}
|
|
31434
31451
|
}
|
|
31435
31452
|
document.getElementById(`${modalId}-theme-toggle`)?.addEventListener("click", async () => {
|
|
31436
|
-
|
|
31437
|
-
localStorage.setItem("myio-temp-modal-theme",
|
|
31438
|
-
|
|
31439
|
-
renderModal(container,
|
|
31440
|
-
if (
|
|
31441
|
-
await setupEventListeners(container,
|
|
31453
|
+
state6.theme = state6.theme === "dark" ? "light" : "dark";
|
|
31454
|
+
localStorage.setItem("myio-temp-modal-theme", state6.theme);
|
|
31455
|
+
state6.dateRangePicker = null;
|
|
31456
|
+
renderModal(container, state6, modalId);
|
|
31457
|
+
if (state6.data.length > 0) drawChart(modalId, state6);
|
|
31458
|
+
await setupEventListeners(container, state6, modalId, onClose);
|
|
31442
31459
|
});
|
|
31443
31460
|
document.getElementById(`${modalId}-maximize`)?.addEventListener("click", async () => {
|
|
31444
31461
|
container.__isMaximized = !container.__isMaximized;
|
|
31445
|
-
|
|
31446
|
-
renderModal(container,
|
|
31447
|
-
if (
|
|
31448
|
-
await setupEventListeners(container,
|
|
31462
|
+
state6.dateRangePicker = null;
|
|
31463
|
+
renderModal(container, state6, modalId);
|
|
31464
|
+
if (state6.data.length > 0) drawChart(modalId, state6);
|
|
31465
|
+
await setupEventListeners(container, state6, modalId, onClose);
|
|
31449
31466
|
});
|
|
31450
31467
|
const periodBtn = document.getElementById(`${modalId}-period-btn`);
|
|
31451
31468
|
const periodDropdown = document.getElementById(`${modalId}-period-dropdown`);
|
|
@@ -31464,71 +31481,71 @@ async function setupEventListeners(container, state5, modalId, onClose) {
|
|
|
31464
31481
|
periodCheckboxes.forEach((checkbox) => {
|
|
31465
31482
|
checkbox.addEventListener("change", () => {
|
|
31466
31483
|
const checked = Array.from(periodCheckboxes).filter((cb) => cb.checked).map((cb) => cb.value);
|
|
31467
|
-
|
|
31484
|
+
state6.selectedPeriods = checked;
|
|
31468
31485
|
const btnLabel = periodBtn?.querySelector("span:first-child");
|
|
31469
31486
|
if (btnLabel) {
|
|
31470
|
-
btnLabel.textContent = getSelectedPeriodsLabel(
|
|
31487
|
+
btnLabel.textContent = getSelectedPeriodsLabel(state6.selectedPeriods);
|
|
31471
31488
|
}
|
|
31472
|
-
if (
|
|
31489
|
+
if (state6.data.length > 0) drawChart(modalId, state6);
|
|
31473
31490
|
});
|
|
31474
31491
|
});
|
|
31475
31492
|
document.getElementById(`${modalId}-period-select-all`)?.addEventListener("click", () => {
|
|
31476
31493
|
periodCheckboxes.forEach((cb) => {
|
|
31477
31494
|
cb.checked = true;
|
|
31478
31495
|
});
|
|
31479
|
-
|
|
31496
|
+
state6.selectedPeriods = ["madrugada", "manha", "tarde", "noite"];
|
|
31480
31497
|
const btnLabel = periodBtn?.querySelector("span:first-child");
|
|
31481
31498
|
if (btnLabel) {
|
|
31482
|
-
btnLabel.textContent = getSelectedPeriodsLabel(
|
|
31499
|
+
btnLabel.textContent = getSelectedPeriodsLabel(state6.selectedPeriods);
|
|
31483
31500
|
}
|
|
31484
|
-
if (
|
|
31501
|
+
if (state6.data.length > 0) drawChart(modalId, state6);
|
|
31485
31502
|
});
|
|
31486
31503
|
document.getElementById(`${modalId}-period-clear`)?.addEventListener("click", () => {
|
|
31487
31504
|
periodCheckboxes.forEach((cb) => {
|
|
31488
31505
|
cb.checked = false;
|
|
31489
31506
|
});
|
|
31490
|
-
|
|
31507
|
+
state6.selectedPeriods = [];
|
|
31491
31508
|
const btnLabel = periodBtn?.querySelector("span:first-child");
|
|
31492
31509
|
if (btnLabel) {
|
|
31493
|
-
btnLabel.textContent = getSelectedPeriodsLabel(
|
|
31510
|
+
btnLabel.textContent = getSelectedPeriodsLabel(state6.selectedPeriods);
|
|
31494
31511
|
}
|
|
31495
|
-
if (
|
|
31512
|
+
if (state6.data.length > 0) drawChart(modalId, state6);
|
|
31496
31513
|
});
|
|
31497
31514
|
document.getElementById(`${modalId}-granularity`)?.addEventListener("change", (e) => {
|
|
31498
|
-
|
|
31499
|
-
localStorage.setItem("myio-temp-modal-granularity",
|
|
31500
|
-
if (
|
|
31515
|
+
state6.granularity = e.target.value;
|
|
31516
|
+
localStorage.setItem("myio-temp-modal-granularity", state6.granularity);
|
|
31517
|
+
if (state6.data.length > 0) drawChart(modalId, state6);
|
|
31501
31518
|
});
|
|
31502
31519
|
document.getElementById(`${modalId}-query`)?.addEventListener("click", async () => {
|
|
31503
|
-
if (
|
|
31520
|
+
if (state6.startTs >= state6.endTs) {
|
|
31504
31521
|
alert("Por favor, selecione um per\xEDodo v\xE1lido");
|
|
31505
31522
|
return;
|
|
31506
31523
|
}
|
|
31507
|
-
|
|
31508
|
-
|
|
31509
|
-
renderModal(container,
|
|
31524
|
+
state6.isLoading = true;
|
|
31525
|
+
state6.dateRangePicker = null;
|
|
31526
|
+
renderModal(container, state6, modalId);
|
|
31510
31527
|
try {
|
|
31511
|
-
|
|
31512
|
-
|
|
31513
|
-
|
|
31514
|
-
renderModal(container,
|
|
31515
|
-
drawChart(modalId,
|
|
31516
|
-
await setupEventListeners(container,
|
|
31528
|
+
state6.data = await fetchTemperatureData(state6.token, state6.deviceId, state6.startTs, state6.endTs);
|
|
31529
|
+
state6.stats = calculateStats(state6.data, state6.clampRange);
|
|
31530
|
+
state6.isLoading = false;
|
|
31531
|
+
renderModal(container, state6, modalId);
|
|
31532
|
+
drawChart(modalId, state6);
|
|
31533
|
+
await setupEventListeners(container, state6, modalId, onClose);
|
|
31517
31534
|
} catch (error) {
|
|
31518
31535
|
console.error("[TemperatureModal] Error fetching data:", error);
|
|
31519
|
-
|
|
31520
|
-
renderModal(container,
|
|
31521
|
-
await setupEventListeners(container,
|
|
31536
|
+
state6.isLoading = false;
|
|
31537
|
+
renderModal(container, state6, modalId, error);
|
|
31538
|
+
await setupEventListeners(container, state6, modalId, onClose);
|
|
31522
31539
|
}
|
|
31523
31540
|
});
|
|
31524
31541
|
document.getElementById(`${modalId}-export`)?.addEventListener("click", () => {
|
|
31525
|
-
if (
|
|
31526
|
-
const startDateStr = new Date(
|
|
31527
|
-
const endDateStr = new Date(
|
|
31542
|
+
if (state6.data.length === 0) return;
|
|
31543
|
+
const startDateStr = new Date(state6.startTs).toLocaleDateString(state6.locale).replace(/\//g, "-");
|
|
31544
|
+
const endDateStr = new Date(state6.endTs).toLocaleDateString(state6.locale).replace(/\//g, "-");
|
|
31528
31545
|
exportTemperatureCSV(
|
|
31529
|
-
|
|
31530
|
-
|
|
31531
|
-
|
|
31546
|
+
state6.data,
|
|
31547
|
+
state6.label,
|
|
31548
|
+
state6.stats,
|
|
31532
31549
|
startDateStr,
|
|
31533
31550
|
endDateStr
|
|
31534
31551
|
);
|
|
@@ -31541,7 +31558,7 @@ async function openTemperatureComparisonModal(params) {
|
|
|
31541
31558
|
const defaultDateRange = getTodaySoFar();
|
|
31542
31559
|
const startTs = params.startDate ? new Date(params.startDate).getTime() : defaultDateRange.startTs;
|
|
31543
31560
|
const endTs = params.endDate ? new Date(params.endDate).getTime() : defaultDateRange.endTs;
|
|
31544
|
-
const
|
|
31561
|
+
const state6 = {
|
|
31545
31562
|
token: params.token,
|
|
31546
31563
|
devices: params.devices,
|
|
31547
31564
|
startTs,
|
|
@@ -31560,44 +31577,44 @@ async function openTemperatureComparisonModal(params) {
|
|
|
31560
31577
|
};
|
|
31561
31578
|
const savedGranularity = localStorage.getItem("myio-temp-comparison-granularity");
|
|
31562
31579
|
const savedTheme = localStorage.getItem("myio-temp-comparison-theme");
|
|
31563
|
-
if (savedGranularity)
|
|
31564
|
-
if (savedTheme)
|
|
31580
|
+
if (savedGranularity) state6.granularity = savedGranularity;
|
|
31581
|
+
if (savedTheme) state6.theme = savedTheme;
|
|
31565
31582
|
const modalContainer = document.createElement("div");
|
|
31566
31583
|
modalContainer.id = modalId;
|
|
31567
31584
|
document.body.appendChild(modalContainer);
|
|
31568
|
-
renderModal2(modalContainer,
|
|
31569
|
-
await fetchAllDevicesData(
|
|
31570
|
-
renderModal2(modalContainer,
|
|
31571
|
-
drawComparisonChart(modalId,
|
|
31572
|
-
await setupEventListeners2(modalContainer,
|
|
31585
|
+
renderModal2(modalContainer, state6, modalId);
|
|
31586
|
+
await fetchAllDevicesData(state6);
|
|
31587
|
+
renderModal2(modalContainer, state6, modalId);
|
|
31588
|
+
drawComparisonChart(modalId, state6);
|
|
31589
|
+
await setupEventListeners2(modalContainer, state6, modalId, params.onClose);
|
|
31573
31590
|
return {
|
|
31574
31591
|
destroy: () => {
|
|
31575
31592
|
modalContainer.remove();
|
|
31576
31593
|
params.onClose?.();
|
|
31577
31594
|
},
|
|
31578
31595
|
updateData: async (startDate, endDate, granularity) => {
|
|
31579
|
-
|
|
31580
|
-
|
|
31581
|
-
if (granularity)
|
|
31582
|
-
|
|
31583
|
-
renderModal2(modalContainer,
|
|
31584
|
-
await fetchAllDevicesData(
|
|
31585
|
-
renderModal2(modalContainer,
|
|
31586
|
-
drawComparisonChart(modalId,
|
|
31587
|
-
setupEventListeners2(modalContainer,
|
|
31596
|
+
state6.startTs = new Date(startDate).getTime();
|
|
31597
|
+
state6.endTs = new Date(endDate).getTime();
|
|
31598
|
+
if (granularity) state6.granularity = granularity;
|
|
31599
|
+
state6.isLoading = true;
|
|
31600
|
+
renderModal2(modalContainer, state6, modalId);
|
|
31601
|
+
await fetchAllDevicesData(state6);
|
|
31602
|
+
renderModal2(modalContainer, state6, modalId);
|
|
31603
|
+
drawComparisonChart(modalId, state6);
|
|
31604
|
+
setupEventListeners2(modalContainer, state6, modalId, params.onClose);
|
|
31588
31605
|
}
|
|
31589
31606
|
};
|
|
31590
31607
|
}
|
|
31591
|
-
async function fetchAllDevicesData(
|
|
31592
|
-
|
|
31593
|
-
|
|
31608
|
+
async function fetchAllDevicesData(state6) {
|
|
31609
|
+
state6.isLoading = true;
|
|
31610
|
+
state6.deviceData = [];
|
|
31594
31611
|
try {
|
|
31595
31612
|
const results = await Promise.all(
|
|
31596
|
-
|
|
31613
|
+
state6.devices.map(async (device, index) => {
|
|
31597
31614
|
const deviceId = device.tbId || device.id;
|
|
31598
31615
|
try {
|
|
31599
|
-
const data = await fetchTemperatureData(
|
|
31600
|
-
const stats = calculateStats(data,
|
|
31616
|
+
const data = await fetchTemperatureData(state6.token, deviceId, state6.startTs, state6.endTs);
|
|
31617
|
+
const stats = calculateStats(data, state6.clampRange);
|
|
31601
31618
|
return {
|
|
31602
31619
|
device,
|
|
31603
31620
|
data,
|
|
@@ -31615,21 +31632,21 @@ async function fetchAllDevicesData(state5) {
|
|
|
31615
31632
|
}
|
|
31616
31633
|
})
|
|
31617
31634
|
);
|
|
31618
|
-
|
|
31635
|
+
state6.deviceData = results;
|
|
31619
31636
|
} catch (error) {
|
|
31620
31637
|
console.error("[TemperatureComparisonModal] Error fetching data:", error);
|
|
31621
31638
|
}
|
|
31622
|
-
|
|
31639
|
+
state6.isLoading = false;
|
|
31623
31640
|
}
|
|
31624
|
-
function renderModal2(container,
|
|
31625
|
-
const colors = getThemeColors(
|
|
31626
|
-
const startDateStr = new Date(
|
|
31627
|
-
const endDateStr = new Date(
|
|
31628
|
-
const startDateInput = new Date(
|
|
31629
|
-
const endDateInput = new Date(
|
|
31630
|
-
const legendHTML =
|
|
31641
|
+
function renderModal2(container, state6, modalId) {
|
|
31642
|
+
const colors = getThemeColors(state6.theme);
|
|
31643
|
+
const startDateStr = new Date(state6.startTs).toLocaleDateString(state6.locale);
|
|
31644
|
+
const endDateStr = new Date(state6.endTs).toLocaleDateString(state6.locale);
|
|
31645
|
+
const startDateInput = new Date(state6.startTs).toISOString().slice(0, 16);
|
|
31646
|
+
const endDateInput = new Date(state6.endTs).toISOString().slice(0, 16);
|
|
31647
|
+
const legendHTML = state6.deviceData.map((dd) => `
|
|
31631
31648
|
<div style="display: flex; align-items: center; gap: 8px; padding: 8px 12px;
|
|
31632
|
-
background: ${
|
|
31649
|
+
background: ${state6.theme === "dark" ? "rgba(255,255,255,0.05)" : "rgba(0,0,0,0.03)"};
|
|
31633
31650
|
border-radius: 8px;">
|
|
31634
31651
|
<span style="width: 12px; height: 12px; border-radius: 50%; background: ${dd.color};"></span>
|
|
31635
31652
|
<span style="color: ${colors.text}; font-size: 13px;">${dd.device.label}</span>
|
|
@@ -31638,9 +31655,9 @@ function renderModal2(container, state5, modalId) {
|
|
|
31638
31655
|
</span>
|
|
31639
31656
|
</div>
|
|
31640
31657
|
`).join("");
|
|
31641
|
-
const statsHTML =
|
|
31658
|
+
const statsHTML = state6.deviceData.map((dd) => `
|
|
31642
31659
|
<div style="
|
|
31643
|
-
padding: 12px; background: ${
|
|
31660
|
+
padding: 12px; background: ${state6.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
|
|
31644
31661
|
border-radius: 10px; border-left: 4px solid ${dd.color};
|
|
31645
31662
|
min-width: 150px;
|
|
31646
31663
|
">
|
|
@@ -31691,7 +31708,7 @@ function renderModal2(container, state5, modalId) {
|
|
|
31691
31708
|
min-height: 20px;
|
|
31692
31709
|
">
|
|
31693
31710
|
<h2 style="margin: 6px; font-size: 18px; font-weight: 600; color: white; line-height: 2;">
|
|
31694
|
-
\u{1F321}\uFE0F Compara\xE7\xE3o de Temperatura - ${
|
|
31711
|
+
\u{1F321}\uFE0F Compara\xE7\xE3o de Temperatura - ${state6.devices.length} sensores
|
|
31695
31712
|
</h2>
|
|
31696
31713
|
<div style="display: flex; gap: 4px; align-items: center;">
|
|
31697
31714
|
<!-- Theme Toggle -->
|
|
@@ -31699,7 +31716,7 @@ function renderModal2(container, state5, modalId) {
|
|
|
31699
31716
|
background: none; border: none; font-size: 16px; cursor: pointer;
|
|
31700
31717
|
padding: 4px 8px; border-radius: 6px; color: rgba(255,255,255,0.8);
|
|
31701
31718
|
transition: background-color 0.2s;
|
|
31702
|
-
">${
|
|
31719
|
+
">${state6.theme === "dark" ? "\u2600\uFE0F" : "\u{1F319}"}</button>
|
|
31703
31720
|
<!-- Maximize Button -->
|
|
31704
31721
|
<button id="${modalId}-maximize" title="${isMaximized ? "Restaurar" : "Maximizar"}" style="
|
|
31705
31722
|
background: none; border: none; font-size: 16px; cursor: pointer;
|
|
@@ -31722,7 +31739,7 @@ function renderModal2(container, state5, modalId) {
|
|
|
31722
31739
|
<div style="
|
|
31723
31740
|
display: flex; gap: 16px; flex-wrap: wrap; align-items: flex-end;
|
|
31724
31741
|
margin-bottom: 16px; padding: 16px;
|
|
31725
|
-
background: ${
|
|
31742
|
+
background: ${state6.theme === "dark" ? "rgba(255,255,255,0.05)" : "#f7f7f7"};
|
|
31726
31743
|
border-radius: 6px; border: 1px solid ${colors.border};
|
|
31727
31744
|
">
|
|
31728
31745
|
<!-- Granularity Select -->
|
|
@@ -31735,8 +31752,8 @@ function renderModal2(container, state5, modalId) {
|
|
|
31735
31752
|
font-size: 14px; color: ${colors.text}; background: ${colors.surface};
|
|
31736
31753
|
cursor: pointer; min-width: 130px;
|
|
31737
31754
|
">
|
|
31738
|
-
<option value="hour" ${
|
|
31739
|
-
<option value="day" ${
|
|
31755
|
+
<option value="hour" ${state6.granularity === "hour" ? "selected" : ""}>Hora (30 min)</option>
|
|
31756
|
+
<option value="day" ${state6.granularity === "day" ? "selected" : ""}>Dia (m\xE9dia)</option>
|
|
31740
31757
|
</select>
|
|
31741
31758
|
</div>
|
|
31742
31759
|
<!-- Day Period Filter (Multiselect) -->
|
|
@@ -31750,7 +31767,7 @@ function renderModal2(container, state5, modalId) {
|
|
|
31750
31767
|
cursor: pointer; min-width: 180px; text-align: left;
|
|
31751
31768
|
display: flex; align-items: center; justify-content: space-between; gap: 8px;
|
|
31752
31769
|
">
|
|
31753
|
-
<span>${getSelectedPeriodsLabel(
|
|
31770
|
+
<span>${getSelectedPeriodsLabel(state6.selectedPeriods)}</span>
|
|
31754
31771
|
<span style="font-size: 10px;">\u25BC</span>
|
|
31755
31772
|
</button>
|
|
31756
31773
|
<div id="${modalId}-period-dropdown" style="
|
|
@@ -31763,12 +31780,12 @@ function renderModal2(container, state5, modalId) {
|
|
|
31763
31780
|
<label style="
|
|
31764
31781
|
display: flex; align-items: center; gap: 8px; padding: 8px 12px;
|
|
31765
31782
|
cursor: pointer; font-size: 13px; color: ${colors.text};
|
|
31766
|
-
" onmouseover="this.style.background='${
|
|
31783
|
+
" onmouseover="this.style.background='${state6.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"}'"
|
|
31767
31784
|
onmouseout="this.style.background='transparent'">
|
|
31768
31785
|
<input type="checkbox"
|
|
31769
31786
|
name="${modalId}-period"
|
|
31770
31787
|
value="${period.id}"
|
|
31771
|
-
${
|
|
31788
|
+
${state6.selectedPeriods.includes(period.id) ? "checked" : ""}
|
|
31772
31789
|
style="width: 16px; height: 16px; cursor: pointer; accent-color: #3e1a7d;">
|
|
31773
31790
|
${period.label}
|
|
31774
31791
|
</label>
|
|
@@ -31776,13 +31793,13 @@ function renderModal2(container, state5, modalId) {
|
|
|
31776
31793
|
<div style="border-top: 1px solid ${colors.border}; margin-top: 8px; padding-top: 8px;">
|
|
31777
31794
|
<button id="${modalId}-period-select-all" type="button" style="
|
|
31778
31795
|
width: calc(100% - 16px); margin: 0 8px 4px; padding: 6px;
|
|
31779
|
-
background: ${
|
|
31796
|
+
background: ${state6.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"};
|
|
31780
31797
|
border: none; border-radius: 4px; cursor: pointer;
|
|
31781
31798
|
font-size: 12px; color: ${colors.text};
|
|
31782
31799
|
">Selecionar Todos</button>
|
|
31783
31800
|
<button id="${modalId}-period-clear" type="button" style="
|
|
31784
31801
|
width: calc(100% - 16px); margin: 0 8px; padding: 6px;
|
|
31785
|
-
background: ${
|
|
31802
|
+
background: ${state6.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"};
|
|
31786
31803
|
border: none; border-radius: 4px; cursor: pointer;
|
|
31787
31804
|
font-size: 12px; color: ${colors.text};
|
|
31788
31805
|
">Limpar Sele\xE7\xE3o</button>
|
|
@@ -31807,8 +31824,8 @@ function renderModal2(container, state5, modalId) {
|
|
|
31807
31824
|
font-size: 14px; font-weight: 500; height: 38px;
|
|
31808
31825
|
display: flex; align-items: center; gap: 8px;
|
|
31809
31826
|
font-family: 'Roboto', Arial, sans-serif;
|
|
31810
|
-
" ${
|
|
31811
|
-
${
|
|
31827
|
+
" ${state6.isLoading ? "disabled" : ""}>
|
|
31828
|
+
${state6.isLoading ? '<span style="animation: spin 1s linear infinite; display: inline-block;">\u21BB</span> Carregando...' : "Carregar"}
|
|
31812
31829
|
</button>
|
|
31813
31830
|
</div>
|
|
31814
31831
|
|
|
@@ -31824,14 +31841,14 @@ function renderModal2(container, state5, modalId) {
|
|
|
31824
31841
|
<div style="margin-bottom: 24px;">
|
|
31825
31842
|
<div id="${modalId}-chart" style="
|
|
31826
31843
|
height: 380px;
|
|
31827
|
-
background: ${
|
|
31844
|
+
background: ${state6.theme === "dark" ? "rgba(255,255,255,0.03)" : "#fafafa"};
|
|
31828
31845
|
border-radius: 14px; display: flex; justify-content: center; align-items: center;
|
|
31829
31846
|
border: 1px solid ${colors.border}; position: relative;
|
|
31830
31847
|
">
|
|
31831
|
-
${
|
|
31848
|
+
${state6.isLoading ? `<div style="text-align: center; color: ${colors.textMuted};">
|
|
31832
31849
|
<div style="animation: spin 1s linear infinite; font-size: 36px; margin-bottom: 12px;">\u21BB</div>
|
|
31833
|
-
<div style="font-size: 15px;">Carregando dados de ${
|
|
31834
|
-
</div>` :
|
|
31850
|
+
<div style="font-size: 15px;">Carregando dados de ${state6.devices.length} sensores...</div>
|
|
31851
|
+
</div>` : state6.deviceData.every((dd) => dd.data.length === 0) ? `<div style="text-align: center; color: ${colors.textMuted};">
|
|
31835
31852
|
<div style="font-size: 48px; margin-bottom: 12px;">\u{1F4ED}</div>
|
|
31836
31853
|
<div style="font-size: 16px;">Sem dados para o per\xEDodo selecionado</div>
|
|
31837
31854
|
</div>` : `<canvas id="${modalId}-canvas" style="width: 100%; height: 100%;"></canvas>`}
|
|
@@ -31849,12 +31866,12 @@ function renderModal2(container, state5, modalId) {
|
|
|
31849
31866
|
<!-- Actions -->
|
|
31850
31867
|
<div style="display: flex; justify-content: flex-end; gap: 12px;">
|
|
31851
31868
|
<button id="${modalId}-export" style="
|
|
31852
|
-
background: ${
|
|
31869
|
+
background: ${state6.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f7f7f7"};
|
|
31853
31870
|
color: ${colors.text}; border: 1px solid ${colors.border};
|
|
31854
31871
|
padding: 8px 16px; border-radius: 6px; cursor: pointer;
|
|
31855
31872
|
font-size: 14px; display: flex; align-items: center; gap: 8px;
|
|
31856
31873
|
font-family: 'Roboto', Arial, sans-serif;
|
|
31857
|
-
" ${
|
|
31874
|
+
" ${state6.deviceData.every((dd) => dd.data.length === 0) ? "disabled" : ""}>
|
|
31858
31875
|
\u{1F4E5} Exportar CSV
|
|
31859
31876
|
</button>
|
|
31860
31877
|
<button id="${modalId}-close-btn" style="
|
|
@@ -31889,15 +31906,15 @@ function renderModal2(container, state5, modalId) {
|
|
|
31889
31906
|
</style>
|
|
31890
31907
|
`;
|
|
31891
31908
|
}
|
|
31892
|
-
function drawComparisonChart(modalId,
|
|
31909
|
+
function drawComparisonChart(modalId, state6) {
|
|
31893
31910
|
const chartContainer = document.getElementById(`${modalId}-chart`);
|
|
31894
31911
|
const canvas = document.getElementById(`${modalId}-canvas`);
|
|
31895
31912
|
if (!chartContainer || !canvas) return;
|
|
31896
|
-
const hasData =
|
|
31913
|
+
const hasData = state6.deviceData.some((dd) => dd.data.length > 0);
|
|
31897
31914
|
if (!hasData) return;
|
|
31898
31915
|
const ctx = canvas.getContext("2d");
|
|
31899
31916
|
if (!ctx) return;
|
|
31900
|
-
const colors = getThemeColors(
|
|
31917
|
+
const colors = getThemeColors(state6.theme);
|
|
31901
31918
|
const width = chartContainer.clientWidth - 2;
|
|
31902
31919
|
const height = 380;
|
|
31903
31920
|
canvas.width = width;
|
|
@@ -31908,19 +31925,19 @@ function drawComparisonChart(modalId, state5) {
|
|
|
31908
31925
|
const paddingBottom = 55;
|
|
31909
31926
|
ctx.clearRect(0, 0, width, height);
|
|
31910
31927
|
const processedData = [];
|
|
31911
|
-
|
|
31928
|
+
state6.deviceData.forEach((dd) => {
|
|
31912
31929
|
if (dd.data.length === 0) return;
|
|
31913
|
-
const filteredData = filterByDayPeriods(dd.data,
|
|
31930
|
+
const filteredData = filterByDayPeriods(dd.data, state6.selectedPeriods);
|
|
31914
31931
|
if (filteredData.length === 0) return;
|
|
31915
31932
|
let points;
|
|
31916
|
-
if (
|
|
31933
|
+
if (state6.granularity === "hour") {
|
|
31917
31934
|
const interpolated = interpolateTemperature(filteredData, {
|
|
31918
31935
|
intervalMinutes: 30,
|
|
31919
|
-
startTs:
|
|
31920
|
-
endTs:
|
|
31921
|
-
clampRange:
|
|
31936
|
+
startTs: state6.startTs,
|
|
31937
|
+
endTs: state6.endTs,
|
|
31938
|
+
clampRange: state6.clampRange
|
|
31922
31939
|
});
|
|
31923
|
-
const filteredInterpolated = filterByDayPeriods(interpolated,
|
|
31940
|
+
const filteredInterpolated = filterByDayPeriods(interpolated, state6.selectedPeriods);
|
|
31924
31941
|
points = filteredInterpolated.map((item) => ({
|
|
31925
31942
|
x: item.ts,
|
|
31926
31943
|
y: Number(item.value),
|
|
@@ -31930,7 +31947,7 @@ function drawComparisonChart(modalId, state5) {
|
|
|
31930
31947
|
deviceColor: dd.color
|
|
31931
31948
|
}));
|
|
31932
31949
|
} else {
|
|
31933
|
-
const daily = aggregateByDay(filteredData,
|
|
31950
|
+
const daily = aggregateByDay(filteredData, state6.clampRange);
|
|
31934
31951
|
points = daily.map((item) => ({
|
|
31935
31952
|
x: item.dateTs,
|
|
31936
31953
|
y: item.avg,
|
|
@@ -31951,7 +31968,7 @@ function drawComparisonChart(modalId, state5) {
|
|
|
31951
31968
|
ctx.fillText("Nenhum dado para os per\xEDodos selecionados", width / 2, height / 2);
|
|
31952
31969
|
return;
|
|
31953
31970
|
}
|
|
31954
|
-
const isPeriodsFiltered =
|
|
31971
|
+
const isPeriodsFiltered = state6.selectedPeriods.length < 4 && state6.selectedPeriods.length > 0;
|
|
31955
31972
|
let dataMinY = Infinity;
|
|
31956
31973
|
let dataMaxY = -Infinity;
|
|
31957
31974
|
processedData.forEach(({ points }) => {
|
|
@@ -31961,7 +31978,7 @@ function drawComparisonChart(modalId, state5) {
|
|
|
31961
31978
|
});
|
|
31962
31979
|
});
|
|
31963
31980
|
const rangeMap = /* @__PURE__ */ new Map();
|
|
31964
|
-
|
|
31981
|
+
state6.deviceData.forEach((dd, index) => {
|
|
31965
31982
|
const device = dd.device;
|
|
31966
31983
|
const min = device.temperatureMin;
|
|
31967
31984
|
const max = device.temperatureMax;
|
|
@@ -31980,10 +31997,10 @@ function drawComparisonChart(modalId, state5) {
|
|
|
31980
31997
|
}
|
|
31981
31998
|
}
|
|
31982
31999
|
});
|
|
31983
|
-
if (rangeMap.size === 0 &&
|
|
32000
|
+
if (rangeMap.size === 0 && state6.temperatureMin !== null && state6.temperatureMax !== null) {
|
|
31984
32001
|
rangeMap.set("global", {
|
|
31985
|
-
min:
|
|
31986
|
-
max:
|
|
32002
|
+
min: state6.temperatureMin,
|
|
32003
|
+
max: state6.temperatureMax,
|
|
31987
32004
|
customerName: "Global",
|
|
31988
32005
|
color: colors.success,
|
|
31989
32006
|
deviceLabels: []
|
|
@@ -32104,10 +32121,10 @@ function drawComparisonChart(modalId, state5) {
|
|
|
32104
32121
|
const point = xAxisPoints[i];
|
|
32105
32122
|
const date = new Date(point.x);
|
|
32106
32123
|
let label;
|
|
32107
|
-
if (
|
|
32108
|
-
label = date.toLocaleTimeString(
|
|
32124
|
+
if (state6.granularity === "hour") {
|
|
32125
|
+
label = date.toLocaleTimeString(state6.locale, { hour: "2-digit", minute: "2-digit" });
|
|
32109
32126
|
} else {
|
|
32110
|
-
label = date.toLocaleDateString(
|
|
32127
|
+
label = date.toLocaleDateString(state6.locale, { day: "2-digit", month: "2-digit" });
|
|
32111
32128
|
}
|
|
32112
32129
|
ctx.strokeStyle = colors.chartGrid;
|
|
32113
32130
|
ctx.lineWidth = 1;
|
|
@@ -32126,16 +32143,16 @@ function drawComparisonChart(modalId, state5) {
|
|
|
32126
32143
|
ctx.lineTo(width - paddingRight, height - paddingBottom);
|
|
32127
32144
|
ctx.stroke();
|
|
32128
32145
|
const allChartPoints = processedData.flatMap((pd) => pd.points);
|
|
32129
|
-
setupComparisonChartTooltip(canvas, chartContainer, allChartPoints,
|
|
32146
|
+
setupComparisonChartTooltip(canvas, chartContainer, allChartPoints, state6, colors);
|
|
32130
32147
|
}
|
|
32131
|
-
function setupComparisonChartTooltip(canvas, container, chartData,
|
|
32148
|
+
function setupComparisonChartTooltip(canvas, container, chartData, state6, colors) {
|
|
32132
32149
|
const existingTooltip = container.querySelector(".myio-chart-tooltip");
|
|
32133
32150
|
if (existingTooltip) existingTooltip.remove();
|
|
32134
32151
|
const tooltip = document.createElement("div");
|
|
32135
32152
|
tooltip.className = "myio-chart-tooltip";
|
|
32136
32153
|
tooltip.style.cssText = `
|
|
32137
32154
|
position: absolute;
|
|
32138
|
-
background: ${
|
|
32155
|
+
background: ${state6.theme === "dark" ? "rgba(30, 30, 40, 0.95)" : "rgba(255, 255, 255, 0.98)"};
|
|
32139
32156
|
border: 1px solid ${colors.border};
|
|
32140
32157
|
border-radius: 8px;
|
|
32141
32158
|
padding: 10px 14px;
|
|
@@ -32172,17 +32189,17 @@ function setupComparisonChartTooltip(canvas, container, chartData, state5, color
|
|
|
32172
32189
|
if (point) {
|
|
32173
32190
|
const date = new Date(point.x);
|
|
32174
32191
|
let dateStr;
|
|
32175
|
-
if (
|
|
32176
|
-
dateStr = date.toLocaleDateString(
|
|
32192
|
+
if (state6.granularity === "hour") {
|
|
32193
|
+
dateStr = date.toLocaleDateString(state6.locale, {
|
|
32177
32194
|
day: "2-digit",
|
|
32178
32195
|
month: "2-digit",
|
|
32179
32196
|
year: "numeric"
|
|
32180
|
-
}) + " " + date.toLocaleTimeString(
|
|
32197
|
+
}) + " " + date.toLocaleTimeString(state6.locale, {
|
|
32181
32198
|
hour: "2-digit",
|
|
32182
32199
|
minute: "2-digit"
|
|
32183
32200
|
});
|
|
32184
32201
|
} else {
|
|
32185
|
-
dateStr = date.toLocaleDateString(
|
|
32202
|
+
dateStr = date.toLocaleDateString(state6.locale, {
|
|
32186
32203
|
day: "2-digit",
|
|
32187
32204
|
month: "2-digit",
|
|
32188
32205
|
year: "numeric"
|
|
@@ -32224,7 +32241,7 @@ function setupComparisonChartTooltip(canvas, container, chartData, state5, color
|
|
|
32224
32241
|
canvas.style.cursor = "default";
|
|
32225
32242
|
});
|
|
32226
32243
|
}
|
|
32227
|
-
async function setupEventListeners2(container,
|
|
32244
|
+
async function setupEventListeners2(container, state6, modalId, onClose) {
|
|
32228
32245
|
const closeModal = () => {
|
|
32229
32246
|
container.remove();
|
|
32230
32247
|
onClose?.();
|
|
@@ -32235,19 +32252,19 @@ async function setupEventListeners2(container, state5, modalId, onClose) {
|
|
|
32235
32252
|
document.getElementById(`${modalId}-close`)?.addEventListener("click", closeModal);
|
|
32236
32253
|
document.getElementById(`${modalId}-close-btn`)?.addEventListener("click", closeModal);
|
|
32237
32254
|
const dateRangeInput = document.getElementById(`${modalId}-date-range`);
|
|
32238
|
-
if (dateRangeInput && !
|
|
32255
|
+
if (dateRangeInput && !state6.dateRangePicker) {
|
|
32239
32256
|
try {
|
|
32240
|
-
|
|
32241
|
-
presetStart: new Date(
|
|
32242
|
-
presetEnd: new Date(
|
|
32257
|
+
state6.dateRangePicker = await createDateRangePicker2(dateRangeInput, {
|
|
32258
|
+
presetStart: new Date(state6.startTs).toISOString(),
|
|
32259
|
+
presetEnd: new Date(state6.endTs).toISOString(),
|
|
32243
32260
|
includeTime: true,
|
|
32244
32261
|
timePrecision: "minute",
|
|
32245
32262
|
maxRangeDays: 90,
|
|
32246
|
-
locale:
|
|
32263
|
+
locale: state6.locale,
|
|
32247
32264
|
parentEl: container.querySelector(".myio-temp-comparison-content"),
|
|
32248
32265
|
onApply: (result) => {
|
|
32249
|
-
|
|
32250
|
-
|
|
32266
|
+
state6.startTs = new Date(result.startISO).getTime();
|
|
32267
|
+
state6.endTs = new Date(result.endISO).getTime();
|
|
32251
32268
|
console.log("[TemperatureComparisonModal] Date range applied:", result);
|
|
32252
32269
|
}
|
|
32253
32270
|
});
|
|
@@ -32256,23 +32273,23 @@ async function setupEventListeners2(container, state5, modalId, onClose) {
|
|
|
32256
32273
|
}
|
|
32257
32274
|
}
|
|
32258
32275
|
document.getElementById(`${modalId}-theme-toggle`)?.addEventListener("click", async () => {
|
|
32259
|
-
|
|
32260
|
-
localStorage.setItem("myio-temp-comparison-theme",
|
|
32261
|
-
|
|
32262
|
-
renderModal2(container,
|
|
32263
|
-
if (
|
|
32264
|
-
drawComparisonChart(modalId,
|
|
32265
|
-
}
|
|
32266
|
-
await setupEventListeners2(container,
|
|
32276
|
+
state6.theme = state6.theme === "dark" ? "light" : "dark";
|
|
32277
|
+
localStorage.setItem("myio-temp-comparison-theme", state6.theme);
|
|
32278
|
+
state6.dateRangePicker = null;
|
|
32279
|
+
renderModal2(container, state6, modalId);
|
|
32280
|
+
if (state6.deviceData.some((dd) => dd.data.length > 0)) {
|
|
32281
|
+
drawComparisonChart(modalId, state6);
|
|
32282
|
+
}
|
|
32283
|
+
await setupEventListeners2(container, state6, modalId, onClose);
|
|
32267
32284
|
});
|
|
32268
32285
|
document.getElementById(`${modalId}-maximize`)?.addEventListener("click", async () => {
|
|
32269
32286
|
container.__isMaximized = !container.__isMaximized;
|
|
32270
|
-
|
|
32271
|
-
renderModal2(container,
|
|
32272
|
-
if (
|
|
32273
|
-
drawComparisonChart(modalId,
|
|
32287
|
+
state6.dateRangePicker = null;
|
|
32288
|
+
renderModal2(container, state6, modalId);
|
|
32289
|
+
if (state6.deviceData.some((dd) => dd.data.length > 0)) {
|
|
32290
|
+
drawComparisonChart(modalId, state6);
|
|
32274
32291
|
}
|
|
32275
|
-
await setupEventListeners2(container,
|
|
32292
|
+
await setupEventListeners2(container, state6, modalId, onClose);
|
|
32276
32293
|
});
|
|
32277
32294
|
const periodBtn = document.getElementById(`${modalId}-period-btn`);
|
|
32278
32295
|
const periodDropdown = document.getElementById(`${modalId}-period-dropdown`);
|
|
@@ -32291,13 +32308,13 @@ async function setupEventListeners2(container, state5, modalId, onClose) {
|
|
|
32291
32308
|
periodCheckboxes.forEach((checkbox) => {
|
|
32292
32309
|
checkbox.addEventListener("change", () => {
|
|
32293
32310
|
const checked = Array.from(periodCheckboxes).filter((cb) => cb.checked).map((cb) => cb.value);
|
|
32294
|
-
|
|
32311
|
+
state6.selectedPeriods = checked;
|
|
32295
32312
|
const btnLabel = periodBtn?.querySelector("span:first-child");
|
|
32296
32313
|
if (btnLabel) {
|
|
32297
|
-
btnLabel.textContent = getSelectedPeriodsLabel(
|
|
32314
|
+
btnLabel.textContent = getSelectedPeriodsLabel(state6.selectedPeriods);
|
|
32298
32315
|
}
|
|
32299
|
-
if (
|
|
32300
|
-
drawComparisonChart(modalId,
|
|
32316
|
+
if (state6.deviceData.some((dd) => dd.data.length > 0)) {
|
|
32317
|
+
drawComparisonChart(modalId, state6);
|
|
32301
32318
|
}
|
|
32302
32319
|
});
|
|
32303
32320
|
});
|
|
@@ -32305,77 +32322,77 @@ async function setupEventListeners2(container, state5, modalId, onClose) {
|
|
|
32305
32322
|
periodCheckboxes.forEach((cb) => {
|
|
32306
32323
|
cb.checked = true;
|
|
32307
32324
|
});
|
|
32308
|
-
|
|
32325
|
+
state6.selectedPeriods = ["madrugada", "manha", "tarde", "noite"];
|
|
32309
32326
|
const btnLabel = periodBtn?.querySelector("span:first-child");
|
|
32310
32327
|
if (btnLabel) {
|
|
32311
|
-
btnLabel.textContent = getSelectedPeriodsLabel(
|
|
32328
|
+
btnLabel.textContent = getSelectedPeriodsLabel(state6.selectedPeriods);
|
|
32312
32329
|
}
|
|
32313
|
-
if (
|
|
32314
|
-
drawComparisonChart(modalId,
|
|
32330
|
+
if (state6.deviceData.some((dd) => dd.data.length > 0)) {
|
|
32331
|
+
drawComparisonChart(modalId, state6);
|
|
32315
32332
|
}
|
|
32316
32333
|
});
|
|
32317
32334
|
document.getElementById(`${modalId}-period-clear`)?.addEventListener("click", () => {
|
|
32318
32335
|
periodCheckboxes.forEach((cb) => {
|
|
32319
32336
|
cb.checked = false;
|
|
32320
32337
|
});
|
|
32321
|
-
|
|
32338
|
+
state6.selectedPeriods = [];
|
|
32322
32339
|
const btnLabel = periodBtn?.querySelector("span:first-child");
|
|
32323
32340
|
if (btnLabel) {
|
|
32324
|
-
btnLabel.textContent = getSelectedPeriodsLabel(
|
|
32341
|
+
btnLabel.textContent = getSelectedPeriodsLabel(state6.selectedPeriods);
|
|
32325
32342
|
}
|
|
32326
|
-
if (
|
|
32327
|
-
drawComparisonChart(modalId,
|
|
32343
|
+
if (state6.deviceData.some((dd) => dd.data.length > 0)) {
|
|
32344
|
+
drawComparisonChart(modalId, state6);
|
|
32328
32345
|
}
|
|
32329
32346
|
});
|
|
32330
32347
|
document.getElementById(`${modalId}-granularity`)?.addEventListener("change", (e) => {
|
|
32331
|
-
|
|
32332
|
-
localStorage.setItem("myio-temp-comparison-granularity",
|
|
32333
|
-
if (
|
|
32334
|
-
drawComparisonChart(modalId,
|
|
32348
|
+
state6.granularity = e.target.value;
|
|
32349
|
+
localStorage.setItem("myio-temp-comparison-granularity", state6.granularity);
|
|
32350
|
+
if (state6.deviceData.some((dd) => dd.data.length > 0)) {
|
|
32351
|
+
drawComparisonChart(modalId, state6);
|
|
32335
32352
|
}
|
|
32336
32353
|
});
|
|
32337
32354
|
document.getElementById(`${modalId}-query`)?.addEventListener("click", async () => {
|
|
32338
|
-
if (
|
|
32355
|
+
if (state6.startTs >= state6.endTs) {
|
|
32339
32356
|
alert("Por favor, selecione um per\xEDodo v\xE1lido");
|
|
32340
32357
|
return;
|
|
32341
32358
|
}
|
|
32342
|
-
|
|
32343
|
-
|
|
32344
|
-
renderModal2(container,
|
|
32345
|
-
await fetchAllDevicesData(
|
|
32346
|
-
renderModal2(container,
|
|
32347
|
-
drawComparisonChart(modalId,
|
|
32348
|
-
await setupEventListeners2(container,
|
|
32359
|
+
state6.isLoading = true;
|
|
32360
|
+
state6.dateRangePicker = null;
|
|
32361
|
+
renderModal2(container, state6, modalId);
|
|
32362
|
+
await fetchAllDevicesData(state6);
|
|
32363
|
+
renderModal2(container, state6, modalId);
|
|
32364
|
+
drawComparisonChart(modalId, state6);
|
|
32365
|
+
await setupEventListeners2(container, state6, modalId, onClose);
|
|
32349
32366
|
});
|
|
32350
32367
|
document.getElementById(`${modalId}-export`)?.addEventListener("click", () => {
|
|
32351
|
-
if (
|
|
32352
|
-
exportComparisonCSV(
|
|
32368
|
+
if (state6.deviceData.every((dd) => dd.data.length === 0)) return;
|
|
32369
|
+
exportComparisonCSV(state6);
|
|
32353
32370
|
});
|
|
32354
32371
|
}
|
|
32355
|
-
function exportComparisonCSV(
|
|
32356
|
-
const startDateStr = new Date(
|
|
32357
|
-
const endDateStr = new Date(
|
|
32372
|
+
function exportComparisonCSV(state6) {
|
|
32373
|
+
const startDateStr = new Date(state6.startTs).toLocaleDateString(state6.locale).replace(/\//g, "-");
|
|
32374
|
+
const endDateStr = new Date(state6.endTs).toLocaleDateString(state6.locale).replace(/\//g, "-");
|
|
32358
32375
|
const BOM = "\uFEFF";
|
|
32359
32376
|
let csvContent = BOM;
|
|
32360
32377
|
csvContent += `Compara\xE7\xE3o de Temperatura
|
|
32361
32378
|
`;
|
|
32362
32379
|
csvContent += `Per\xEDodo: ${startDateStr} at\xE9 ${endDateStr}
|
|
32363
32380
|
`;
|
|
32364
|
-
csvContent += `Sensores: ${
|
|
32381
|
+
csvContent += `Sensores: ${state6.devices.map((d) => d.label).join(", ")}
|
|
32365
32382
|
`;
|
|
32366
32383
|
csvContent += "\n";
|
|
32367
32384
|
csvContent += "Estat\xEDsticas por Sensor:\n";
|
|
32368
32385
|
csvContent += "Sensor,M\xE9dia (\xB0C),Min (\xB0C),Max (\xB0C),Leituras\n";
|
|
32369
|
-
|
|
32386
|
+
state6.deviceData.forEach((dd) => {
|
|
32370
32387
|
csvContent += `"${dd.device.label}",${dd.stats.avg.toFixed(2)},${dd.stats.min.toFixed(2)},${dd.stats.max.toFixed(2)},${dd.stats.count}
|
|
32371
32388
|
`;
|
|
32372
32389
|
});
|
|
32373
32390
|
csvContent += "\n";
|
|
32374
32391
|
csvContent += "Dados Detalhados:\n";
|
|
32375
32392
|
csvContent += "Data/Hora,Sensor,Temperatura (\xB0C)\n";
|
|
32376
|
-
|
|
32393
|
+
state6.deviceData.forEach((dd) => {
|
|
32377
32394
|
dd.data.forEach((item) => {
|
|
32378
|
-
const date = new Date(item.ts).toLocaleString(
|
|
32395
|
+
const date = new Date(item.ts).toLocaleString(state6.locale);
|
|
32379
32396
|
const temp = Number(item.value).toFixed(2);
|
|
32380
32397
|
csvContent += `"${date}","${dd.device.label}",${temp}
|
|
32381
32398
|
`;
|
|
@@ -32483,10 +32500,10 @@ async function saveCustomerAttributes(customerId, token, minTemperature, maxTemp
|
|
|
32483
32500
|
throw new Error(`Failed to save attributes: ${response.status}`);
|
|
32484
32501
|
}
|
|
32485
32502
|
}
|
|
32486
|
-
function renderModal3(container,
|
|
32487
|
-
const colors = getColors(
|
|
32488
|
-
const minValue =
|
|
32489
|
-
const maxValue =
|
|
32503
|
+
function renderModal3(container, state6, modalId, onClose, onSave) {
|
|
32504
|
+
const colors = getColors(state6.theme);
|
|
32505
|
+
const minValue = state6.minTemperature !== null ? state6.minTemperature : "";
|
|
32506
|
+
const maxValue = state6.maxTemperature !== null ? state6.maxTemperature : "";
|
|
32490
32507
|
container.innerHTML = `
|
|
32491
32508
|
<style>
|
|
32492
32509
|
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
|
|
@@ -32751,23 +32768,23 @@ function renderModal3(container, state5, modalId, onClose, onSave) {
|
|
|
32751
32768
|
</div>
|
|
32752
32769
|
|
|
32753
32770
|
<div class="modal-body">
|
|
32754
|
-
${
|
|
32771
|
+
${state6.isLoading ? `
|
|
32755
32772
|
<div class="loading-overlay">
|
|
32756
32773
|
<div class="loading-spinner"></div>
|
|
32757
32774
|
<div>Carregando configura\xE7\xF5es...</div>
|
|
32758
32775
|
</div>
|
|
32759
32776
|
` : `
|
|
32760
|
-
${
|
|
32761
|
-
<div class="message message-error">${
|
|
32777
|
+
${state6.error ? `
|
|
32778
|
+
<div class="message message-error">${state6.error}</div>
|
|
32762
32779
|
` : ""}
|
|
32763
32780
|
|
|
32764
|
-
${
|
|
32765
|
-
<div class="message message-success">${
|
|
32781
|
+
${state6.successMessage ? `
|
|
32782
|
+
<div class="message message-success">${state6.successMessage}</div>
|
|
32766
32783
|
` : ""}
|
|
32767
32784
|
|
|
32768
32785
|
<div class="customer-info">
|
|
32769
32786
|
<div class="customer-label">Shopping / Cliente</div>
|
|
32770
|
-
<div class="customer-name">${
|
|
32787
|
+
<div class="customer-name">${state6.customerName || "N\xE3o identificado"}</div>
|
|
32771
32788
|
</div>
|
|
32772
32789
|
|
|
32773
32790
|
<div class="form-group">
|
|
@@ -32811,11 +32828,11 @@ function renderModal3(container, state5, modalId, onClose, onSave) {
|
|
|
32811
32828
|
`}
|
|
32812
32829
|
</div>
|
|
32813
32830
|
|
|
32814
|
-
${!
|
|
32831
|
+
${!state6.isLoading ? `
|
|
32815
32832
|
<div class="modal-footer">
|
|
32816
32833
|
<button class="btn btn-secondary" id="${modalId}-cancel">Cancelar</button>
|
|
32817
|
-
<button class="btn btn-primary" id="${modalId}-save" ${
|
|
32818
|
-
${
|
|
32834
|
+
<button class="btn btn-primary" id="${modalId}-save" ${state6.isSaving ? "disabled" : ""}>
|
|
32835
|
+
${state6.isSaving ? '<div class="spinner"></div> Salvando...' : "Salvar"}
|
|
32819
32836
|
</button>
|
|
32820
32837
|
</div>
|
|
32821
32838
|
` : ""}
|
|
@@ -32852,18 +32869,18 @@ function renderModal3(container, state5, modalId, onClose, onSave) {
|
|
|
32852
32869
|
const min = parseFloat(minInput.value);
|
|
32853
32870
|
const max = parseFloat(maxInput.value);
|
|
32854
32871
|
if (isNaN(min) || isNaN(max)) {
|
|
32855
|
-
|
|
32856
|
-
renderModal3(container,
|
|
32872
|
+
state6.error = "Por favor, preencha ambos os valores.";
|
|
32873
|
+
renderModal3(container, state6, modalId, onClose, onSave);
|
|
32857
32874
|
return;
|
|
32858
32875
|
}
|
|
32859
32876
|
if (min >= max) {
|
|
32860
|
-
|
|
32861
|
-
renderModal3(container,
|
|
32877
|
+
state6.error = "A temperatura m\xEDnima deve ser menor que a m\xE1xima.";
|
|
32878
|
+
renderModal3(container, state6, modalId, onClose, onSave);
|
|
32862
32879
|
return;
|
|
32863
32880
|
}
|
|
32864
32881
|
if (min < 0 || max > 50) {
|
|
32865
|
-
|
|
32866
|
-
renderModal3(container,
|
|
32882
|
+
state6.error = "Os valores devem estar entre 0\xB0C e 50\xB0C.";
|
|
32883
|
+
renderModal3(container, state6, modalId, onClose, onSave);
|
|
32867
32884
|
return;
|
|
32868
32885
|
}
|
|
32869
32886
|
await onSave(min, max);
|
|
@@ -32871,7 +32888,7 @@ function renderModal3(container, state5, modalId, onClose, onSave) {
|
|
|
32871
32888
|
}
|
|
32872
32889
|
function openTemperatureSettingsModal(params) {
|
|
32873
32890
|
const modalId = `myio-temp-settings-${Date.now()}`;
|
|
32874
|
-
const
|
|
32891
|
+
const state6 = {
|
|
32875
32892
|
customerId: params.customerId,
|
|
32876
32893
|
customerName: params.customerName || "",
|
|
32877
32894
|
token: params.token,
|
|
@@ -32891,37 +32908,37 @@ function openTemperatureSettingsModal(params) {
|
|
|
32891
32908
|
params.onClose?.();
|
|
32892
32909
|
};
|
|
32893
32910
|
const handleSave = async (min, max) => {
|
|
32894
|
-
|
|
32895
|
-
|
|
32896
|
-
|
|
32897
|
-
renderModal3(container,
|
|
32911
|
+
state6.isSaving = true;
|
|
32912
|
+
state6.error = null;
|
|
32913
|
+
state6.successMessage = null;
|
|
32914
|
+
renderModal3(container, state6, modalId, destroy, handleSave);
|
|
32898
32915
|
try {
|
|
32899
|
-
await saveCustomerAttributes(
|
|
32900
|
-
|
|
32901
|
-
|
|
32902
|
-
|
|
32903
|
-
|
|
32904
|
-
renderModal3(container,
|
|
32916
|
+
await saveCustomerAttributes(state6.customerId, state6.token, min, max, params.onError);
|
|
32917
|
+
state6.minTemperature = min;
|
|
32918
|
+
state6.maxTemperature = max;
|
|
32919
|
+
state6.isSaving = false;
|
|
32920
|
+
state6.successMessage = "Configura\xE7\xF5es salvas com sucesso!";
|
|
32921
|
+
renderModal3(container, state6, modalId, destroy, handleSave);
|
|
32905
32922
|
params.onSave?.({ minTemperature: min, maxTemperature: max });
|
|
32906
32923
|
setTimeout(() => {
|
|
32907
32924
|
destroy();
|
|
32908
32925
|
}, 1500);
|
|
32909
32926
|
} catch (error) {
|
|
32910
|
-
|
|
32911
|
-
|
|
32912
|
-
renderModal3(container,
|
|
32927
|
+
state6.isSaving = false;
|
|
32928
|
+
state6.error = `Erro ao salvar: ${error.message}`;
|
|
32929
|
+
renderModal3(container, state6, modalId, destroy, handleSave);
|
|
32913
32930
|
}
|
|
32914
32931
|
};
|
|
32915
|
-
renderModal3(container,
|
|
32916
|
-
fetchCustomerAttributes(
|
|
32917
|
-
|
|
32918
|
-
|
|
32919
|
-
|
|
32920
|
-
renderModal3(container,
|
|
32932
|
+
renderModal3(container, state6, modalId, destroy, handleSave);
|
|
32933
|
+
fetchCustomerAttributes(state6.customerId, state6.token, params.onError).then(({ minTemperature, maxTemperature }) => {
|
|
32934
|
+
state6.minTemperature = minTemperature;
|
|
32935
|
+
state6.maxTemperature = maxTemperature;
|
|
32936
|
+
state6.isLoading = false;
|
|
32937
|
+
renderModal3(container, state6, modalId, destroy, handleSave);
|
|
32921
32938
|
}).catch((error) => {
|
|
32922
|
-
|
|
32923
|
-
|
|
32924
|
-
renderModal3(container,
|
|
32939
|
+
state6.isLoading = false;
|
|
32940
|
+
state6.error = `Erro ao carregar: ${error.message}`;
|
|
32941
|
+
renderModal3(container, state6, modalId, destroy, handleSave);
|
|
32925
32942
|
});
|
|
32926
32943
|
return { destroy };
|
|
32927
32944
|
}
|
|
@@ -34191,7 +34208,7 @@ var EnergySummaryTooltip = {
|
|
|
34191
34208
|
* RFC-0105 Enhancement: Now fetches device lists from MyIOOrchestratorData
|
|
34192
34209
|
* to populate device lists for status popup display
|
|
34193
34210
|
*/
|
|
34194
|
-
buildSummaryFromState(
|
|
34211
|
+
buildSummaryFromState(state6, receivedData, domain = "energy") {
|
|
34195
34212
|
const summary = {
|
|
34196
34213
|
totalDevices: 0,
|
|
34197
34214
|
totalConsumption: 0,
|
|
@@ -34214,22 +34231,22 @@ var EnergySummaryTooltip = {
|
|
|
34214
34231
|
},
|
|
34215
34232
|
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
34216
34233
|
};
|
|
34217
|
-
if (!
|
|
34234
|
+
if (!state6) return summary;
|
|
34218
34235
|
const entrada = {
|
|
34219
34236
|
id: "entrada",
|
|
34220
34237
|
name: "Entrada",
|
|
34221
34238
|
icon: CATEGORY_ICONS.entrada,
|
|
34222
|
-
deviceCount:
|
|
34223
|
-
consumption:
|
|
34239
|
+
deviceCount: state6.entrada?.devices?.length || (receivedData?.entrada_total?.device_count || 0),
|
|
34240
|
+
consumption: state6.entrada?.total || 0,
|
|
34224
34241
|
percentage: 100
|
|
34225
34242
|
};
|
|
34226
34243
|
const lojas = {
|
|
34227
34244
|
id: "lojas",
|
|
34228
34245
|
name: "Lojas",
|
|
34229
34246
|
icon: CATEGORY_ICONS.lojas,
|
|
34230
|
-
deviceCount:
|
|
34231
|
-
consumption:
|
|
34232
|
-
percentage:
|
|
34247
|
+
deviceCount: state6.consumidores?.lojas?.devices?.length || (receivedData?.lojas_total?.device_count || 0),
|
|
34248
|
+
consumption: state6.consumidores?.lojas?.total || 0,
|
|
34249
|
+
percentage: state6.consumidores?.lojas?.perc || 0
|
|
34233
34250
|
};
|
|
34234
34251
|
const climatizacaoData = receivedData?.climatizacao || {};
|
|
34235
34252
|
const elevadoresData = receivedData?.elevadores || {};
|
|
@@ -34240,9 +34257,9 @@ var EnergySummaryTooltip = {
|
|
|
34240
34257
|
id: "climatizacao",
|
|
34241
34258
|
name: "Climatizacao",
|
|
34242
34259
|
icon: CATEGORY_ICONS.climatizacao,
|
|
34243
|
-
deviceCount: climatizacaoData.count ||
|
|
34244
|
-
consumption:
|
|
34245
|
-
percentage:
|
|
34260
|
+
deviceCount: climatizacaoData.count || state6.consumidores?.climatizacao?.devices?.length || 0,
|
|
34261
|
+
consumption: state6.consumidores?.climatizacao?.total || 0,
|
|
34262
|
+
percentage: state6.consumidores?.climatizacao?.perc || 0
|
|
34246
34263
|
};
|
|
34247
34264
|
if (climatizacaoData.subcategories) {
|
|
34248
34265
|
climatizacao.children = [];
|
|
@@ -34293,40 +34310,40 @@ var EnergySummaryTooltip = {
|
|
|
34293
34310
|
id: "elevadores",
|
|
34294
34311
|
name: "Elevadores",
|
|
34295
34312
|
icon: CATEGORY_ICONS.elevadores,
|
|
34296
|
-
deviceCount: elevadoresData.count ||
|
|
34297
|
-
consumption:
|
|
34298
|
-
percentage:
|
|
34313
|
+
deviceCount: elevadoresData.count || state6.consumidores?.elevadores?.devices?.length || 0,
|
|
34314
|
+
consumption: state6.consumidores?.elevadores?.total || 0,
|
|
34315
|
+
percentage: state6.consumidores?.elevadores?.perc || 0
|
|
34299
34316
|
});
|
|
34300
34317
|
areaComumChildren.push({
|
|
34301
34318
|
id: "escadasRolantes",
|
|
34302
34319
|
name: "Esc. Rolantes",
|
|
34303
34320
|
icon: CATEGORY_ICONS.escadas,
|
|
34304
|
-
deviceCount: escadasData.count ||
|
|
34305
|
-
consumption:
|
|
34306
|
-
percentage:
|
|
34321
|
+
deviceCount: escadasData.count || state6.consumidores?.escadasRolantes?.devices?.length || 0,
|
|
34322
|
+
consumption: state6.consumidores?.escadasRolantes?.total || 0,
|
|
34323
|
+
percentage: state6.consumidores?.escadasRolantes?.perc || 0
|
|
34307
34324
|
});
|
|
34308
34325
|
areaComumChildren.push({
|
|
34309
34326
|
id: "outros",
|
|
34310
34327
|
name: "Outros",
|
|
34311
34328
|
icon: CATEGORY_ICONS.outros,
|
|
34312
|
-
deviceCount: outrosData.count ||
|
|
34313
|
-
consumption:
|
|
34314
|
-
percentage:
|
|
34329
|
+
deviceCount: outrosData.count || state6.consumidores?.outros?.devices?.length || 0,
|
|
34330
|
+
consumption: state6.consumidores?.outros?.total || 0,
|
|
34331
|
+
percentage: state6.consumidores?.outros?.perc || 0
|
|
34315
34332
|
});
|
|
34316
34333
|
const areaComumDeviceCount = areaComumChildren.reduce((sum, c) => sum + c.deviceCount, 0);
|
|
34317
|
-
const areaComumConsumption =
|
|
34334
|
+
const areaComumConsumption = state6.consumidores?.areaComum?.total || areaComumChildren.reduce((sum, c) => sum + c.consumption, 0);
|
|
34318
34335
|
const areaComum = {
|
|
34319
34336
|
id: "areaComum",
|
|
34320
34337
|
name: "Area Comum",
|
|
34321
34338
|
icon: CATEGORY_ICONS.areaComum,
|
|
34322
34339
|
deviceCount: areaComumDeviceCount,
|
|
34323
34340
|
consumption: areaComumConsumption,
|
|
34324
|
-
percentage:
|
|
34341
|
+
percentage: state6.consumidores?.areaComum?.perc || 0,
|
|
34325
34342
|
children: areaComumChildren
|
|
34326
34343
|
};
|
|
34327
34344
|
summary.byCategory = [entrada, lojas, areaComum];
|
|
34328
34345
|
summary.totalDevices = entrada.deviceCount + lojas.deviceCount + areaComumDeviceCount;
|
|
34329
|
-
summary.totalConsumption =
|
|
34346
|
+
summary.totalConsumption = state6.grandTotal || entrada.consumption;
|
|
34330
34347
|
const widgetAggregation = receivedData?.deviceStatusAggregation;
|
|
34331
34348
|
if (widgetAggregation && widgetAggregation.hasData) {
|
|
34332
34349
|
summary.byStatus = {
|
|
@@ -35614,7 +35631,7 @@ var WaterSummaryTooltip = {
|
|
|
35614
35631
|
* RFC-0105 Enhancement: Now fetches device lists from MyIOOrchestratorData
|
|
35615
35632
|
* to populate device lists for status popup display
|
|
35616
35633
|
*/
|
|
35617
|
-
buildSummaryFromState(
|
|
35634
|
+
buildSummaryFromState(state6, receivedData, includeBathrooms = false, domain = "water") {
|
|
35618
35635
|
const summary = {
|
|
35619
35636
|
totalDevices: 0,
|
|
35620
35637
|
totalConsumption: 0,
|
|
@@ -35638,22 +35655,22 @@ var WaterSummaryTooltip = {
|
|
|
35638
35655
|
lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
|
|
35639
35656
|
includeBathrooms
|
|
35640
35657
|
};
|
|
35641
|
-
if (!
|
|
35658
|
+
if (!state6) return summary;
|
|
35642
35659
|
const entrada = {
|
|
35643
35660
|
id: "entrada",
|
|
35644
35661
|
name: "Entrada",
|
|
35645
35662
|
icon: WATER_CATEGORY_ICONS.entrada,
|
|
35646
|
-
deviceCount:
|
|
35647
|
-
consumption:
|
|
35663
|
+
deviceCount: state6.entrada?.devices?.length || (receivedData?.entrada_total?.device_count || 0),
|
|
35664
|
+
consumption: state6.entrada?.total || 0,
|
|
35648
35665
|
percentage: 100
|
|
35649
35666
|
};
|
|
35650
35667
|
const lojas = {
|
|
35651
35668
|
id: "lojas",
|
|
35652
35669
|
name: "Lojas",
|
|
35653
35670
|
icon: WATER_CATEGORY_ICONS.lojas,
|
|
35654
|
-
deviceCount:
|
|
35655
|
-
consumption:
|
|
35656
|
-
percentage:
|
|
35671
|
+
deviceCount: state6.lojas?.devices?.length || (receivedData?.lojas_total?.device_count || 0),
|
|
35672
|
+
consumption: state6.lojas?.total || 0,
|
|
35673
|
+
percentage: state6.lojas?.perc || 0
|
|
35657
35674
|
};
|
|
35658
35675
|
summary.byCategory = [entrada, lojas];
|
|
35659
35676
|
if (includeBathrooms) {
|
|
@@ -35661,9 +35678,9 @@ var WaterSummaryTooltip = {
|
|
|
35661
35678
|
id: "banheiros",
|
|
35662
35679
|
name: "Banheiros",
|
|
35663
35680
|
icon: WATER_CATEGORY_ICONS.banheiros,
|
|
35664
|
-
deviceCount:
|
|
35665
|
-
consumption:
|
|
35666
|
-
percentage:
|
|
35681
|
+
deviceCount: state6.banheiros?.devices?.length || (receivedData?.banheiros_total?.device_count || 0),
|
|
35682
|
+
consumption: state6.banheiros?.total || 0,
|
|
35683
|
+
percentage: state6.banheiros?.perc || 0
|
|
35667
35684
|
};
|
|
35668
35685
|
summary.byCategory.push(banheiros);
|
|
35669
35686
|
}
|
|
@@ -35671,24 +35688,24 @@ var WaterSummaryTooltip = {
|
|
|
35671
35688
|
id: "areaComum",
|
|
35672
35689
|
name: "\xC1rea Comum",
|
|
35673
35690
|
icon: WATER_CATEGORY_ICONS.areaComum,
|
|
35674
|
-
deviceCount:
|
|
35675
|
-
consumption:
|
|
35676
|
-
percentage:
|
|
35691
|
+
deviceCount: state6.areaComum?.devices?.length || (receivedData?.area_comum_total?.device_count || 0),
|
|
35692
|
+
consumption: state6.areaComum?.total || 0,
|
|
35693
|
+
percentage: state6.areaComum?.perc || 0
|
|
35677
35694
|
};
|
|
35678
35695
|
summary.byCategory.push(areaComum);
|
|
35679
|
-
if (
|
|
35696
|
+
if (state6.pontosNaoMapeados && state6.pontosNaoMapeados.total > 0) {
|
|
35680
35697
|
const pontosNaoMapeados = {
|
|
35681
35698
|
id: "pontosNaoMapeados",
|
|
35682
35699
|
name: "Pontos N\xE3o Mapeados",
|
|
35683
35700
|
icon: WATER_CATEGORY_ICONS.pontosNaoMapeados,
|
|
35684
|
-
deviceCount:
|
|
35685
|
-
consumption:
|
|
35686
|
-
percentage:
|
|
35701
|
+
deviceCount: state6.pontosNaoMapeados?.devices?.length || 0,
|
|
35702
|
+
consumption: state6.pontosNaoMapeados?.total || 0,
|
|
35703
|
+
percentage: state6.pontosNaoMapeados?.perc || 0
|
|
35687
35704
|
};
|
|
35688
35705
|
summary.byCategory.push(pontosNaoMapeados);
|
|
35689
35706
|
}
|
|
35690
35707
|
summary.totalDevices = summary.byCategory.reduce((sum, cat) => sum + cat.deviceCount, 0);
|
|
35691
|
-
summary.totalConsumption =
|
|
35708
|
+
summary.totalConsumption = state6.entrada?.total || 0;
|
|
35692
35709
|
const widgetAggregation = receivedData?.deviceStatusAggregation;
|
|
35693
35710
|
if (widgetAggregation && widgetAggregation.hasData) {
|
|
35694
35711
|
summary.byStatus = {
|
|
@@ -37225,6 +37242,996 @@ var TempSensorSummaryTooltip = {
|
|
|
37225
37242
|
}
|
|
37226
37243
|
};
|
|
37227
37244
|
|
|
37245
|
+
// src/utils/ContractSummaryTooltip.ts
|
|
37246
|
+
var CONTRACT_SUMMARY_TOOLTIP_CSS = `
|
|
37247
|
+
/* ============================================
|
|
37248
|
+
Contract Summary Tooltip (RFC-0107)
|
|
37249
|
+
Premium draggable tooltip with dark theme
|
|
37250
|
+
============================================ */
|
|
37251
|
+
|
|
37252
|
+
.myio-contract-summary-tooltip {
|
|
37253
|
+
position: fixed;
|
|
37254
|
+
z-index: 99999;
|
|
37255
|
+
pointer-events: none;
|
|
37256
|
+
opacity: 0;
|
|
37257
|
+
transition: opacity 0.25s ease, transform 0.25s ease;
|
|
37258
|
+
transform: translateY(5px);
|
|
37259
|
+
}
|
|
37260
|
+
|
|
37261
|
+
.myio-contract-summary-tooltip.visible {
|
|
37262
|
+
opacity: 1;
|
|
37263
|
+
pointer-events: auto;
|
|
37264
|
+
transform: translateY(0);
|
|
37265
|
+
}
|
|
37266
|
+
|
|
37267
|
+
.myio-contract-summary-tooltip.closing {
|
|
37268
|
+
opacity: 0;
|
|
37269
|
+
transform: translateY(8px);
|
|
37270
|
+
transition: opacity 0.4s ease, transform 0.4s ease;
|
|
37271
|
+
}
|
|
37272
|
+
|
|
37273
|
+
.myio-contract-summary-tooltip.pinned {
|
|
37274
|
+
box-shadow: 0 0 0 2px #9684B5, 0 10px 40px rgba(0, 0, 0, 0.3);
|
|
37275
|
+
border-radius: 16px;
|
|
37276
|
+
}
|
|
37277
|
+
|
|
37278
|
+
.myio-contract-summary-tooltip.dragging {
|
|
37279
|
+
transition: none !important;
|
|
37280
|
+
cursor: move;
|
|
37281
|
+
}
|
|
37282
|
+
|
|
37283
|
+
.myio-contract-summary-tooltip.maximized {
|
|
37284
|
+
top: 20px !important;
|
|
37285
|
+
left: 20px !important;
|
|
37286
|
+
right: 20px !important;
|
|
37287
|
+
bottom: 20px !important;
|
|
37288
|
+
width: auto !important;
|
|
37289
|
+
max-width: none !important;
|
|
37290
|
+
}
|
|
37291
|
+
|
|
37292
|
+
.myio-contract-summary-tooltip.maximized .myio-contract-summary-tooltip__panel {
|
|
37293
|
+
width: 100%;
|
|
37294
|
+
height: 100%;
|
|
37295
|
+
max-width: none;
|
|
37296
|
+
display: flex;
|
|
37297
|
+
flex-direction: column;
|
|
37298
|
+
}
|
|
37299
|
+
|
|
37300
|
+
.myio-contract-summary-tooltip.maximized .myio-contract-summary-tooltip__body {
|
|
37301
|
+
flex: 1;
|
|
37302
|
+
overflow-y: auto;
|
|
37303
|
+
}
|
|
37304
|
+
|
|
37305
|
+
.myio-contract-summary-tooltip__panel {
|
|
37306
|
+
background: #2d1458;
|
|
37307
|
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
37308
|
+
border-radius: 16px;
|
|
37309
|
+
box-shadow:
|
|
37310
|
+
0 20px 60px rgba(0, 0, 0, 0.4),
|
|
37311
|
+
0 8px 20px rgba(0, 0, 0, 0.25),
|
|
37312
|
+
0 0 0 1px rgba(255, 255, 255, 0.05);
|
|
37313
|
+
min-width: 320px;
|
|
37314
|
+
max-width: 380px;
|
|
37315
|
+
font-family: Inter, system-ui, -apple-system, sans-serif;
|
|
37316
|
+
font-size: 12px;
|
|
37317
|
+
color: #ffffff;
|
|
37318
|
+
overflow: hidden;
|
|
37319
|
+
}
|
|
37320
|
+
|
|
37321
|
+
/* Header */
|
|
37322
|
+
.myio-contract-summary-tooltip__header {
|
|
37323
|
+
display: flex;
|
|
37324
|
+
align-items: center;
|
|
37325
|
+
gap: 10px;
|
|
37326
|
+
padding: 14px 16px;
|
|
37327
|
+
background: linear-gradient(135deg, #9684B5 0%, #2d1458 100%);
|
|
37328
|
+
border-radius: 16px 16px 0 0;
|
|
37329
|
+
position: relative;
|
|
37330
|
+
overflow: hidden;
|
|
37331
|
+
cursor: move;
|
|
37332
|
+
user-select: none;
|
|
37333
|
+
}
|
|
37334
|
+
|
|
37335
|
+
.myio-contract-summary-tooltip__header::before {
|
|
37336
|
+
content: '';
|
|
37337
|
+
position: absolute;
|
|
37338
|
+
top: 0;
|
|
37339
|
+
left: 0;
|
|
37340
|
+
right: 0;
|
|
37341
|
+
bottom: 0;
|
|
37342
|
+
background: url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%23ffffff' fill-opacity='0.05'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
|
|
37343
|
+
opacity: 0.3;
|
|
37344
|
+
}
|
|
37345
|
+
|
|
37346
|
+
.myio-contract-summary-tooltip__icon {
|
|
37347
|
+
width: 40px;
|
|
37348
|
+
height: 40px;
|
|
37349
|
+
background: rgba(255, 255, 255, 0.15);
|
|
37350
|
+
border-radius: 12px;
|
|
37351
|
+
display: flex;
|
|
37352
|
+
align-items: center;
|
|
37353
|
+
justify-content: center;
|
|
37354
|
+
font-size: 20px;
|
|
37355
|
+
backdrop-filter: blur(10px);
|
|
37356
|
+
position: relative;
|
|
37357
|
+
z-index: 1;
|
|
37358
|
+
}
|
|
37359
|
+
|
|
37360
|
+
.myio-contract-summary-tooltip__icon.valid {
|
|
37361
|
+
background: rgba(76, 175, 80, 0.3);
|
|
37362
|
+
}
|
|
37363
|
+
|
|
37364
|
+
.myio-contract-summary-tooltip__icon.invalid {
|
|
37365
|
+
background: rgba(244, 67, 54, 0.3);
|
|
37366
|
+
}
|
|
37367
|
+
|
|
37368
|
+
.myio-contract-summary-tooltip__header-info {
|
|
37369
|
+
flex: 1;
|
|
37370
|
+
position: relative;
|
|
37371
|
+
z-index: 1;
|
|
37372
|
+
}
|
|
37373
|
+
|
|
37374
|
+
.myio-contract-summary-tooltip__title {
|
|
37375
|
+
font-weight: 700;
|
|
37376
|
+
font-size: 15px;
|
|
37377
|
+
color: #ffffff;
|
|
37378
|
+
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
|
|
37379
|
+
margin-bottom: 2px;
|
|
37380
|
+
}
|
|
37381
|
+
|
|
37382
|
+
.myio-contract-summary-tooltip__subtitle {
|
|
37383
|
+
font-size: 11px;
|
|
37384
|
+
color: rgba(255, 255, 255, 0.7);
|
|
37385
|
+
}
|
|
37386
|
+
|
|
37387
|
+
.myio-contract-summary-tooltip__header-actions {
|
|
37388
|
+
display: flex;
|
|
37389
|
+
align-items: center;
|
|
37390
|
+
gap: 4px;
|
|
37391
|
+
position: relative;
|
|
37392
|
+
z-index: 1;
|
|
37393
|
+
}
|
|
37394
|
+
|
|
37395
|
+
.myio-contract-summary-tooltip__header-btn {
|
|
37396
|
+
width: 28px;
|
|
37397
|
+
height: 28px;
|
|
37398
|
+
border: none;
|
|
37399
|
+
background: rgba(255, 255, 255, 0.15);
|
|
37400
|
+
border-radius: 8px;
|
|
37401
|
+
cursor: pointer;
|
|
37402
|
+
display: flex;
|
|
37403
|
+
align-items: center;
|
|
37404
|
+
justify-content: center;
|
|
37405
|
+
transition: all 0.2s ease;
|
|
37406
|
+
color: rgba(255, 255, 255, 0.8);
|
|
37407
|
+
}
|
|
37408
|
+
|
|
37409
|
+
.myio-contract-summary-tooltip__header-btn:hover {
|
|
37410
|
+
background: rgba(255, 255, 255, 0.25);
|
|
37411
|
+
color: #ffffff;
|
|
37412
|
+
transform: scale(1.05);
|
|
37413
|
+
}
|
|
37414
|
+
|
|
37415
|
+
.myio-contract-summary-tooltip__header-btn.pinned {
|
|
37416
|
+
background: rgba(255, 255, 255, 0.9);
|
|
37417
|
+
color: #9684B5;
|
|
37418
|
+
}
|
|
37419
|
+
|
|
37420
|
+
.myio-contract-summary-tooltip__header-btn.pinned:hover {
|
|
37421
|
+
background: #ffffff;
|
|
37422
|
+
color: #2d1458;
|
|
37423
|
+
}
|
|
37424
|
+
|
|
37425
|
+
.myio-contract-summary-tooltip__header-btn svg {
|
|
37426
|
+
width: 14px;
|
|
37427
|
+
height: 14px;
|
|
37428
|
+
}
|
|
37429
|
+
|
|
37430
|
+
/* Body */
|
|
37431
|
+
.myio-contract-summary-tooltip__body {
|
|
37432
|
+
padding: 16px;
|
|
37433
|
+
}
|
|
37434
|
+
|
|
37435
|
+
/* Domain Section */
|
|
37436
|
+
.myio-contract-summary-tooltip__domain {
|
|
37437
|
+
margin-bottom: 14px;
|
|
37438
|
+
background: rgba(255, 255, 255, 0.05);
|
|
37439
|
+
border-radius: 12px;
|
|
37440
|
+
overflow: hidden;
|
|
37441
|
+
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
37442
|
+
}
|
|
37443
|
+
|
|
37444
|
+
.myio-contract-summary-tooltip__domain:last-child {
|
|
37445
|
+
margin-bottom: 0;
|
|
37446
|
+
}
|
|
37447
|
+
|
|
37448
|
+
.myio-contract-summary-tooltip__domain-header {
|
|
37449
|
+
display: flex;
|
|
37450
|
+
align-items: center;
|
|
37451
|
+
justify-content: space-between;
|
|
37452
|
+
padding: 10px 14px;
|
|
37453
|
+
cursor: pointer;
|
|
37454
|
+
transition: background 0.2s ease;
|
|
37455
|
+
}
|
|
37456
|
+
|
|
37457
|
+
.myio-contract-summary-tooltip__domain-header:hover {
|
|
37458
|
+
background: rgba(255, 255, 255, 0.05);
|
|
37459
|
+
}
|
|
37460
|
+
|
|
37461
|
+
.myio-contract-summary-tooltip__domain-info {
|
|
37462
|
+
display: flex;
|
|
37463
|
+
align-items: center;
|
|
37464
|
+
gap: 8px;
|
|
37465
|
+
}
|
|
37466
|
+
|
|
37467
|
+
.myio-contract-summary-tooltip__domain-icon {
|
|
37468
|
+
font-size: 18px;
|
|
37469
|
+
}
|
|
37470
|
+
|
|
37471
|
+
.myio-contract-summary-tooltip__domain-name {
|
|
37472
|
+
font-weight: 600;
|
|
37473
|
+
font-size: 13px;
|
|
37474
|
+
}
|
|
37475
|
+
|
|
37476
|
+
.myio-contract-summary-tooltip__domain-count {
|
|
37477
|
+
font-size: 12px;
|
|
37478
|
+
color: #81c784;
|
|
37479
|
+
font-weight: 600;
|
|
37480
|
+
}
|
|
37481
|
+
|
|
37482
|
+
.myio-contract-summary-tooltip__expand-icon {
|
|
37483
|
+
font-size: 10px;
|
|
37484
|
+
opacity: 0.6;
|
|
37485
|
+
transition: transform 0.3s ease;
|
|
37486
|
+
}
|
|
37487
|
+
|
|
37488
|
+
.myio-contract-summary-tooltip__domain.expanded .myio-contract-summary-tooltip__expand-icon {
|
|
37489
|
+
transform: rotate(180deg);
|
|
37490
|
+
}
|
|
37491
|
+
|
|
37492
|
+
/* Domain Details */
|
|
37493
|
+
.myio-contract-summary-tooltip__domain-details {
|
|
37494
|
+
max-height: 0;
|
|
37495
|
+
overflow: hidden;
|
|
37496
|
+
transition: max-height 0.3s ease;
|
|
37497
|
+
background: rgba(0, 0, 0, 0.15);
|
|
37498
|
+
}
|
|
37499
|
+
|
|
37500
|
+
.myio-contract-summary-tooltip__domain.expanded .myio-contract-summary-tooltip__domain-details {
|
|
37501
|
+
max-height: 150px;
|
|
37502
|
+
}
|
|
37503
|
+
|
|
37504
|
+
.myio-contract-summary-tooltip__detail-row {
|
|
37505
|
+
display: flex;
|
|
37506
|
+
align-items: center;
|
|
37507
|
+
justify-content: space-between;
|
|
37508
|
+
padding: 6px 14px 6px 40px;
|
|
37509
|
+
font-size: 12px;
|
|
37510
|
+
border-top: 1px solid rgba(255, 255, 255, 0.05);
|
|
37511
|
+
}
|
|
37512
|
+
|
|
37513
|
+
.myio-contract-summary-tooltip__detail-row:first-child {
|
|
37514
|
+
border-top: none;
|
|
37515
|
+
}
|
|
37516
|
+
|
|
37517
|
+
.myio-contract-summary-tooltip__detail-label {
|
|
37518
|
+
opacity: 0.7;
|
|
37519
|
+
display: flex;
|
|
37520
|
+
align-items: center;
|
|
37521
|
+
gap: 6px;
|
|
37522
|
+
}
|
|
37523
|
+
|
|
37524
|
+
.myio-contract-summary-tooltip__detail-label::before {
|
|
37525
|
+
content: '';
|
|
37526
|
+
width: 4px;
|
|
37527
|
+
height: 4px;
|
|
37528
|
+
border-radius: 50%;
|
|
37529
|
+
background: currentColor;
|
|
37530
|
+
opacity: 0.5;
|
|
37531
|
+
}
|
|
37532
|
+
|
|
37533
|
+
.myio-contract-summary-tooltip__detail-count {
|
|
37534
|
+
font-weight: 500;
|
|
37535
|
+
color: #81c784;
|
|
37536
|
+
}
|
|
37537
|
+
|
|
37538
|
+
/* Status Banner */
|
|
37539
|
+
.myio-contract-summary-tooltip__status {
|
|
37540
|
+
display: flex;
|
|
37541
|
+
align-items: center;
|
|
37542
|
+
justify-content: center;
|
|
37543
|
+
gap: 8px;
|
|
37544
|
+
padding: 10px 14px;
|
|
37545
|
+
border-radius: 10px;
|
|
37546
|
+
margin-bottom: 14px;
|
|
37547
|
+
font-size: 12px;
|
|
37548
|
+
font-weight: 600;
|
|
37549
|
+
}
|
|
37550
|
+
|
|
37551
|
+
.myio-contract-summary-tooltip__status.valid {
|
|
37552
|
+
background: rgba(76, 175, 80, 0.2);
|
|
37553
|
+
color: #81c784;
|
|
37554
|
+
border: 1px solid rgba(76, 175, 80, 0.3);
|
|
37555
|
+
}
|
|
37556
|
+
|
|
37557
|
+
.myio-contract-summary-tooltip__status.invalid {
|
|
37558
|
+
background: rgba(244, 67, 54, 0.2);
|
|
37559
|
+
color: #ef5350;
|
|
37560
|
+
border: 1px solid rgba(244, 67, 54, 0.3);
|
|
37561
|
+
}
|
|
37562
|
+
|
|
37563
|
+
.myio-contract-summary-tooltip__status-icon {
|
|
37564
|
+
font-size: 14px;
|
|
37565
|
+
}
|
|
37566
|
+
|
|
37567
|
+
/* Discrepancies */
|
|
37568
|
+
.myio-contract-summary-tooltip__discrepancies {
|
|
37569
|
+
background: rgba(244, 67, 54, 0.15);
|
|
37570
|
+
border: 1px solid rgba(244, 67, 54, 0.3);
|
|
37571
|
+
border-radius: 10px;
|
|
37572
|
+
padding: 10px 14px;
|
|
37573
|
+
margin-bottom: 14px;
|
|
37574
|
+
}
|
|
37575
|
+
|
|
37576
|
+
.myio-contract-summary-tooltip__discrepancies-title {
|
|
37577
|
+
font-size: 11px;
|
|
37578
|
+
font-weight: 600;
|
|
37579
|
+
color: #ef5350;
|
|
37580
|
+
margin-bottom: 6px;
|
|
37581
|
+
text-transform: uppercase;
|
|
37582
|
+
letter-spacing: 0.5px;
|
|
37583
|
+
}
|
|
37584
|
+
|
|
37585
|
+
.myio-contract-summary-tooltip__discrepancy-item {
|
|
37586
|
+
font-size: 11px;
|
|
37587
|
+
color: rgba(255, 255, 255, 0.8);
|
|
37588
|
+
padding: 3px 0;
|
|
37589
|
+
}
|
|
37590
|
+
|
|
37591
|
+
/* Footer */
|
|
37592
|
+
.myio-contract-summary-tooltip__footer {
|
|
37593
|
+
display: flex;
|
|
37594
|
+
justify-content: space-between;
|
|
37595
|
+
align-items: center;
|
|
37596
|
+
padding: 12px 16px;
|
|
37597
|
+
background: rgba(0, 0, 0, 0.2);
|
|
37598
|
+
border-top: 1px solid rgba(255, 255, 255, 0.05);
|
|
37599
|
+
border-radius: 0 0 16px 16px;
|
|
37600
|
+
}
|
|
37601
|
+
|
|
37602
|
+
.myio-contract-summary-tooltip__footer-label {
|
|
37603
|
+
font-size: 10px;
|
|
37604
|
+
color: rgba(255, 255, 255, 0.5);
|
|
37605
|
+
}
|
|
37606
|
+
|
|
37607
|
+
.myio-contract-summary-tooltip__footer-value {
|
|
37608
|
+
font-size: 11px;
|
|
37609
|
+
font-weight: 600;
|
|
37610
|
+
color: rgba(255, 255, 255, 0.8);
|
|
37611
|
+
}
|
|
37612
|
+
|
|
37613
|
+
/* Total Devices Badge */
|
|
37614
|
+
.myio-contract-summary-tooltip__total {
|
|
37615
|
+
display: flex;
|
|
37616
|
+
align-items: center;
|
|
37617
|
+
justify-content: center;
|
|
37618
|
+
gap: 8px;
|
|
37619
|
+
padding: 12px;
|
|
37620
|
+
background: rgba(255, 255, 255, 0.08);
|
|
37621
|
+
border-radius: 10px;
|
|
37622
|
+
margin-bottom: 14px;
|
|
37623
|
+
}
|
|
37624
|
+
|
|
37625
|
+
.myio-contract-summary-tooltip__total-label {
|
|
37626
|
+
font-size: 12px;
|
|
37627
|
+
opacity: 0.8;
|
|
37628
|
+
}
|
|
37629
|
+
|
|
37630
|
+
.myio-contract-summary-tooltip__total-value {
|
|
37631
|
+
font-size: 18px;
|
|
37632
|
+
font-weight: 700;
|
|
37633
|
+
color: #81c784;
|
|
37634
|
+
}
|
|
37635
|
+
|
|
37636
|
+
/* Responsive */
|
|
37637
|
+
@media (max-width: 400px) {
|
|
37638
|
+
.myio-contract-summary-tooltip__panel {
|
|
37639
|
+
min-width: 280px;
|
|
37640
|
+
max-width: 95vw;
|
|
37641
|
+
}
|
|
37642
|
+
}
|
|
37643
|
+
`;
|
|
37644
|
+
var cssInjected9 = false;
|
|
37645
|
+
function injectCSS9() {
|
|
37646
|
+
if (cssInjected9) return;
|
|
37647
|
+
if (typeof document === "undefined") return;
|
|
37648
|
+
const styleId = "myio-contract-summary-tooltip-styles";
|
|
37649
|
+
if (document.getElementById(styleId)) {
|
|
37650
|
+
cssInjected9 = true;
|
|
37651
|
+
return;
|
|
37652
|
+
}
|
|
37653
|
+
const style = document.createElement("style");
|
|
37654
|
+
style.id = styleId;
|
|
37655
|
+
style.textContent = CONTRACT_SUMMARY_TOOLTIP_CSS;
|
|
37656
|
+
document.head.appendChild(style);
|
|
37657
|
+
cssInjected9 = true;
|
|
37658
|
+
}
|
|
37659
|
+
var state5 = {
|
|
37660
|
+
hideTimer: null,
|
|
37661
|
+
isMouseOverTooltip: false,
|
|
37662
|
+
isMaximized: false,
|
|
37663
|
+
isDragging: false,
|
|
37664
|
+
dragOffset: { x: 0, y: 0 },
|
|
37665
|
+
savedPosition: null,
|
|
37666
|
+
pinnedCounter: 0,
|
|
37667
|
+
expandedDomains: /* @__PURE__ */ new Set(["energy", "water", "temperature"])
|
|
37668
|
+
};
|
|
37669
|
+
function formatTimestamp5(isoString) {
|
|
37670
|
+
if (!isoString) return "Agora";
|
|
37671
|
+
try {
|
|
37672
|
+
const date = new Date(isoString);
|
|
37673
|
+
return date.toLocaleString("pt-BR", {
|
|
37674
|
+
hour: "2-digit",
|
|
37675
|
+
minute: "2-digit",
|
|
37676
|
+
day: "2-digit",
|
|
37677
|
+
month: "2-digit"
|
|
37678
|
+
});
|
|
37679
|
+
} catch {
|
|
37680
|
+
return "Agora";
|
|
37681
|
+
}
|
|
37682
|
+
}
|
|
37683
|
+
function calculateTotalDevices(data) {
|
|
37684
|
+
return data.energy.total + data.water.total + data.temperature.total;
|
|
37685
|
+
}
|
|
37686
|
+
function generateHeaderHTML5(data) {
|
|
37687
|
+
const iconClass = data.isValid ? "valid" : "invalid";
|
|
37688
|
+
const iconSymbol = data.isValid ? "\u2713" : "!";
|
|
37689
|
+
const totalDevices = calculateTotalDevices(data);
|
|
37690
|
+
return `
|
|
37691
|
+
<div class="myio-contract-summary-tooltip__header" data-drag-handle>
|
|
37692
|
+
<div class="myio-contract-summary-tooltip__icon ${iconClass}">${iconSymbol}</div>
|
|
37693
|
+
<div class="myio-contract-summary-tooltip__header-info">
|
|
37694
|
+
<div class="myio-contract-summary-tooltip__title">Contract Summary</div>
|
|
37695
|
+
<div class="myio-contract-summary-tooltip__subtitle">${totalDevices} devices loaded</div>
|
|
37696
|
+
</div>
|
|
37697
|
+
<div class="myio-contract-summary-tooltip__header-actions">
|
|
37698
|
+
<button class="myio-contract-summary-tooltip__header-btn" data-action="pin" title="Pin to screen">
|
|
37699
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
37700
|
+
<path d="M9 4v6l-2 4v2h10v-2l-2-4V4"/>
|
|
37701
|
+
<line x1="12" y1="16" x2="12" y2="21"/>
|
|
37702
|
+
<line x1="8" y1="4" x2="16" y2="4"/>
|
|
37703
|
+
</svg>
|
|
37704
|
+
</button>
|
|
37705
|
+
<button class="myio-contract-summary-tooltip__header-btn" data-action="maximize" title="Maximize">
|
|
37706
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
37707
|
+
<rect x="3" y="3" width="18" height="18" rx="2"/>
|
|
37708
|
+
</svg>
|
|
37709
|
+
</button>
|
|
37710
|
+
<button class="myio-contract-summary-tooltip__header-btn" data-action="close" title="Close">
|
|
37711
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
37712
|
+
<path d="M18 6L6 18M6 6l12 12"/>
|
|
37713
|
+
</svg>
|
|
37714
|
+
</button>
|
|
37715
|
+
</div>
|
|
37716
|
+
</div>
|
|
37717
|
+
`;
|
|
37718
|
+
}
|
|
37719
|
+
function generateDomainHTML(domain, icon, name, counts, isTemperature = false) {
|
|
37720
|
+
const isExpanded = state5.expandedDomains.has(domain);
|
|
37721
|
+
const expandedClass = isExpanded ? "expanded" : "";
|
|
37722
|
+
let detailsHTML = "";
|
|
37723
|
+
if (isTemperature) {
|
|
37724
|
+
const tempCounts = counts;
|
|
37725
|
+
detailsHTML = `
|
|
37726
|
+
<div class="myio-contract-summary-tooltip__detail-row">
|
|
37727
|
+
<span class="myio-contract-summary-tooltip__detail-label">Internal (Climate)</span>
|
|
37728
|
+
<span class="myio-contract-summary-tooltip__detail-count">${tempCounts.internal}</span>
|
|
37729
|
+
</div>
|
|
37730
|
+
<div class="myio-contract-summary-tooltip__detail-row">
|
|
37731
|
+
<span class="myio-contract-summary-tooltip__detail-label">Stores (Non-Climate)</span>
|
|
37732
|
+
<span class="myio-contract-summary-tooltip__detail-count">${tempCounts.stores}</span>
|
|
37733
|
+
</div>
|
|
37734
|
+
`;
|
|
37735
|
+
} else {
|
|
37736
|
+
const domainCounts = counts;
|
|
37737
|
+
detailsHTML = `
|
|
37738
|
+
<div class="myio-contract-summary-tooltip__detail-row">
|
|
37739
|
+
<span class="myio-contract-summary-tooltip__detail-label">Entries</span>
|
|
37740
|
+
<span class="myio-contract-summary-tooltip__detail-count">${domainCounts.entries}</span>
|
|
37741
|
+
</div>
|
|
37742
|
+
<div class="myio-contract-summary-tooltip__detail-row">
|
|
37743
|
+
<span class="myio-contract-summary-tooltip__detail-label">Common Area</span>
|
|
37744
|
+
<span class="myio-contract-summary-tooltip__detail-count">${domainCounts.commonArea}</span>
|
|
37745
|
+
</div>
|
|
37746
|
+
<div class="myio-contract-summary-tooltip__detail-row">
|
|
37747
|
+
<span class="myio-contract-summary-tooltip__detail-label">Stores</span>
|
|
37748
|
+
<span class="myio-contract-summary-tooltip__detail-count">${domainCounts.stores}</span>
|
|
37749
|
+
</div>
|
|
37750
|
+
`;
|
|
37751
|
+
}
|
|
37752
|
+
return `
|
|
37753
|
+
<div class="myio-contract-summary-tooltip__domain ${expandedClass}" data-domain="${domain}">
|
|
37754
|
+
<div class="myio-contract-summary-tooltip__domain-header" data-toggle-domain="${domain}">
|
|
37755
|
+
<div class="myio-contract-summary-tooltip__domain-info">
|
|
37756
|
+
<span class="myio-contract-summary-tooltip__domain-icon">${icon}</span>
|
|
37757
|
+
<span class="myio-contract-summary-tooltip__domain-name">${name}</span>
|
|
37758
|
+
</div>
|
|
37759
|
+
<div style="display: flex; align-items: center; gap: 8px;">
|
|
37760
|
+
<span class="myio-contract-summary-tooltip__domain-count">${counts.total} devices</span>
|
|
37761
|
+
<span class="myio-contract-summary-tooltip__expand-icon">\u25BC</span>
|
|
37762
|
+
</div>
|
|
37763
|
+
</div>
|
|
37764
|
+
<div class="myio-contract-summary-tooltip__domain-details">
|
|
37765
|
+
${detailsHTML}
|
|
37766
|
+
</div>
|
|
37767
|
+
</div>
|
|
37768
|
+
`;
|
|
37769
|
+
}
|
|
37770
|
+
function generateBodyHTML3(data) {
|
|
37771
|
+
const totalDevices = calculateTotalDevices(data);
|
|
37772
|
+
const timestamp = formatTimestamp5(data.timestamp);
|
|
37773
|
+
const statusClass = data.isValid ? "valid" : "invalid";
|
|
37774
|
+
const statusIcon = data.isValid ? "\u2713" : "\u26A0";
|
|
37775
|
+
const statusText = data.isValid ? "Contract validated successfully" : "Validation issues detected";
|
|
37776
|
+
let discrepanciesHTML = "";
|
|
37777
|
+
if (data.discrepancies && data.discrepancies.length > 0) {
|
|
37778
|
+
const items = data.discrepancies.map((d) => `<div class="myio-contract-summary-tooltip__discrepancy-item">${d.domain}: expected ${d.expected}, found ${d.actual}</div>`).join("");
|
|
37779
|
+
discrepanciesHTML = `
|
|
37780
|
+
<div class="myio-contract-summary-tooltip__discrepancies">
|
|
37781
|
+
<div class="myio-contract-summary-tooltip__discrepancies-title">Discrepancies</div>
|
|
37782
|
+
${items}
|
|
37783
|
+
</div>
|
|
37784
|
+
`;
|
|
37785
|
+
}
|
|
37786
|
+
return `
|
|
37787
|
+
<div class="myio-contract-summary-tooltip__body">
|
|
37788
|
+
<!-- Status Banner -->
|
|
37789
|
+
<div class="myio-contract-summary-tooltip__status ${statusClass}">
|
|
37790
|
+
<span class="myio-contract-summary-tooltip__status-icon">${statusIcon}</span>
|
|
37791
|
+
<span>${statusText}</span>
|
|
37792
|
+
</div>
|
|
37793
|
+
|
|
37794
|
+
${discrepanciesHTML}
|
|
37795
|
+
|
|
37796
|
+
<!-- Total Devices -->
|
|
37797
|
+
<div class="myio-contract-summary-tooltip__total">
|
|
37798
|
+
<span class="myio-contract-summary-tooltip__total-label">Total Devices:</span>
|
|
37799
|
+
<span class="myio-contract-summary-tooltip__total-value">${totalDevices}</span>
|
|
37800
|
+
</div>
|
|
37801
|
+
|
|
37802
|
+
<!-- Energy -->
|
|
37803
|
+
${generateDomainHTML("energy", "\u26A1", "Energy", data.energy)}
|
|
37804
|
+
|
|
37805
|
+
<!-- Water -->
|
|
37806
|
+
${generateDomainHTML("water", "\u{1F4A7}", "Water", data.water)}
|
|
37807
|
+
|
|
37808
|
+
<!-- Temperature -->
|
|
37809
|
+
${generateDomainHTML("temperature", "\u{1F321}\uFE0F", "Temperature", data.temperature, true)}
|
|
37810
|
+
</div>
|
|
37811
|
+
|
|
37812
|
+
<!-- Footer -->
|
|
37813
|
+
<div class="myio-contract-summary-tooltip__footer">
|
|
37814
|
+
<span class="myio-contract-summary-tooltip__footer-label">Loaded at</span>
|
|
37815
|
+
<span class="myio-contract-summary-tooltip__footer-value">${timestamp}</span>
|
|
37816
|
+
</div>
|
|
37817
|
+
`;
|
|
37818
|
+
}
|
|
37819
|
+
function setupHoverListeners5(container) {
|
|
37820
|
+
container.onmouseenter = () => {
|
|
37821
|
+
state5.isMouseOverTooltip = true;
|
|
37822
|
+
if (state5.hideTimer) {
|
|
37823
|
+
clearTimeout(state5.hideTimer);
|
|
37824
|
+
state5.hideTimer = null;
|
|
37825
|
+
}
|
|
37826
|
+
};
|
|
37827
|
+
container.onmouseleave = () => {
|
|
37828
|
+
state5.isMouseOverTooltip = false;
|
|
37829
|
+
startDelayedHide5();
|
|
37830
|
+
};
|
|
37831
|
+
}
|
|
37832
|
+
function setupDomainToggleListeners(container) {
|
|
37833
|
+
const toggles = container.querySelectorAll("[data-toggle-domain]");
|
|
37834
|
+
toggles.forEach((toggle) => {
|
|
37835
|
+
toggle.onclick = (e) => {
|
|
37836
|
+
e.stopPropagation();
|
|
37837
|
+
const domain = toggle.dataset.toggleDomain;
|
|
37838
|
+
if (!domain) return;
|
|
37839
|
+
const domainEl = container.querySelector(`[data-domain="${domain}"]`);
|
|
37840
|
+
if (!domainEl) return;
|
|
37841
|
+
if (state5.expandedDomains.has(domain)) {
|
|
37842
|
+
state5.expandedDomains.delete(domain);
|
|
37843
|
+
domainEl.classList.remove("expanded");
|
|
37844
|
+
} else {
|
|
37845
|
+
state5.expandedDomains.add(domain);
|
|
37846
|
+
domainEl.classList.add("expanded");
|
|
37847
|
+
}
|
|
37848
|
+
};
|
|
37849
|
+
});
|
|
37850
|
+
}
|
|
37851
|
+
function setupButtonListeners5(container) {
|
|
37852
|
+
const buttons = container.querySelectorAll("[data-action]");
|
|
37853
|
+
buttons.forEach((btn) => {
|
|
37854
|
+
btn.onclick = (e) => {
|
|
37855
|
+
e.stopPropagation();
|
|
37856
|
+
const action = btn.dataset.action;
|
|
37857
|
+
switch (action) {
|
|
37858
|
+
case "pin":
|
|
37859
|
+
createPinnedClone5(container);
|
|
37860
|
+
break;
|
|
37861
|
+
case "maximize":
|
|
37862
|
+
toggleMaximize5(container);
|
|
37863
|
+
break;
|
|
37864
|
+
case "close":
|
|
37865
|
+
ContractSummaryTooltip.close();
|
|
37866
|
+
break;
|
|
37867
|
+
}
|
|
37868
|
+
};
|
|
37869
|
+
});
|
|
37870
|
+
}
|
|
37871
|
+
function setupDragListeners5(container) {
|
|
37872
|
+
const header = container.querySelector("[data-drag-handle]");
|
|
37873
|
+
if (!header) return;
|
|
37874
|
+
header.onmousedown = (e) => {
|
|
37875
|
+
if (e.target.closest("[data-action]")) return;
|
|
37876
|
+
if (e.target.closest("[data-toggle-domain]")) return;
|
|
37877
|
+
if (state5.isMaximized) return;
|
|
37878
|
+
state5.isDragging = true;
|
|
37879
|
+
container.classList.add("dragging");
|
|
37880
|
+
const rect = container.getBoundingClientRect();
|
|
37881
|
+
state5.dragOffset = {
|
|
37882
|
+
x: e.clientX - rect.left,
|
|
37883
|
+
y: e.clientY - rect.top
|
|
37884
|
+
};
|
|
37885
|
+
const onMouseMove = (e2) => {
|
|
37886
|
+
if (!state5.isDragging) return;
|
|
37887
|
+
const newLeft = e2.clientX - state5.dragOffset.x;
|
|
37888
|
+
const newTop = e2.clientY - state5.dragOffset.y;
|
|
37889
|
+
const maxLeft = window.innerWidth - container.offsetWidth;
|
|
37890
|
+
const maxTop = window.innerHeight - container.offsetHeight;
|
|
37891
|
+
container.style.left = Math.max(0, Math.min(newLeft, maxLeft)) + "px";
|
|
37892
|
+
container.style.top = Math.max(0, Math.min(newTop, maxTop)) + "px";
|
|
37893
|
+
};
|
|
37894
|
+
const onMouseUp = () => {
|
|
37895
|
+
state5.isDragging = false;
|
|
37896
|
+
container.classList.remove("dragging");
|
|
37897
|
+
document.removeEventListener("mousemove", onMouseMove);
|
|
37898
|
+
document.removeEventListener("mouseup", onMouseUp);
|
|
37899
|
+
};
|
|
37900
|
+
document.addEventListener("mousemove", onMouseMove);
|
|
37901
|
+
document.addEventListener("mouseup", onMouseUp);
|
|
37902
|
+
};
|
|
37903
|
+
}
|
|
37904
|
+
function createPinnedClone5(container) {
|
|
37905
|
+
state5.pinnedCounter++;
|
|
37906
|
+
const pinnedId = `myio-contract-summary-tooltip-pinned-${state5.pinnedCounter}`;
|
|
37907
|
+
const clone = container.cloneNode(true);
|
|
37908
|
+
clone.id = pinnedId;
|
|
37909
|
+
clone.classList.add("pinned");
|
|
37910
|
+
clone.classList.remove("closing");
|
|
37911
|
+
const pinBtn = clone.querySelector('[data-action="pin"]');
|
|
37912
|
+
if (pinBtn) {
|
|
37913
|
+
pinBtn.classList.add("pinned");
|
|
37914
|
+
pinBtn.setAttribute("title", "Unpin");
|
|
37915
|
+
pinBtn.innerHTML = `
|
|
37916
|
+
<svg viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="1">
|
|
37917
|
+
<path d="M9 4v6l-2 4v2h10v-2l-2-4V4"/>
|
|
37918
|
+
<line x1="12" y1="16" x2="12" y2="21"/>
|
|
37919
|
+
<line x1="8" y1="4" x2="16" y2="4"/>
|
|
37920
|
+
</svg>
|
|
37921
|
+
`;
|
|
37922
|
+
}
|
|
37923
|
+
document.body.appendChild(clone);
|
|
37924
|
+
setupPinnedCloneListeners5(clone, pinnedId);
|
|
37925
|
+
ContractSummaryTooltip.hide();
|
|
37926
|
+
}
|
|
37927
|
+
function setupPinnedCloneListeners5(clone, cloneId) {
|
|
37928
|
+
let isMaximized = false;
|
|
37929
|
+
let savedPosition = null;
|
|
37930
|
+
const cloneExpandedDomains = new Set(state5.expandedDomains);
|
|
37931
|
+
const toggles = clone.querySelectorAll("[data-toggle-domain]");
|
|
37932
|
+
toggles.forEach((toggle) => {
|
|
37933
|
+
toggle.onclick = (e) => {
|
|
37934
|
+
e.stopPropagation();
|
|
37935
|
+
const domain = toggle.dataset.toggleDomain;
|
|
37936
|
+
if (!domain) return;
|
|
37937
|
+
const domainEl = clone.querySelector(`[data-domain="${domain}"]`);
|
|
37938
|
+
if (!domainEl) return;
|
|
37939
|
+
if (cloneExpandedDomains.has(domain)) {
|
|
37940
|
+
cloneExpandedDomains.delete(domain);
|
|
37941
|
+
domainEl.classList.remove("expanded");
|
|
37942
|
+
} else {
|
|
37943
|
+
cloneExpandedDomains.add(domain);
|
|
37944
|
+
domainEl.classList.add("expanded");
|
|
37945
|
+
}
|
|
37946
|
+
};
|
|
37947
|
+
});
|
|
37948
|
+
const pinBtn = clone.querySelector('[data-action="pin"]');
|
|
37949
|
+
if (pinBtn) {
|
|
37950
|
+
pinBtn.onclick = (e) => {
|
|
37951
|
+
e.stopPropagation();
|
|
37952
|
+
closePinnedClone5(cloneId);
|
|
37953
|
+
};
|
|
37954
|
+
}
|
|
37955
|
+
const closeBtn = clone.querySelector('[data-action="close"]');
|
|
37956
|
+
if (closeBtn) {
|
|
37957
|
+
closeBtn.onclick = (e) => {
|
|
37958
|
+
e.stopPropagation();
|
|
37959
|
+
closePinnedClone5(cloneId);
|
|
37960
|
+
};
|
|
37961
|
+
}
|
|
37962
|
+
const maxBtn = clone.querySelector('[data-action="maximize"]');
|
|
37963
|
+
if (maxBtn) {
|
|
37964
|
+
maxBtn.onclick = (e) => {
|
|
37965
|
+
e.stopPropagation();
|
|
37966
|
+
isMaximized = !isMaximized;
|
|
37967
|
+
if (isMaximized) {
|
|
37968
|
+
savedPosition = { left: clone.style.left, top: clone.style.top };
|
|
37969
|
+
maxBtn.innerHTML = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="5" y="5" width="14" height="14" rx="2"/><path d="M9 5V3h12v12h-2"/></svg>`;
|
|
37970
|
+
maxBtn.setAttribute("title", "Restore");
|
|
37971
|
+
} else {
|
|
37972
|
+
maxBtn.innerHTML = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/></svg>`;
|
|
37973
|
+
maxBtn.setAttribute("title", "Maximize");
|
|
37974
|
+
if (savedPosition) {
|
|
37975
|
+
clone.style.left = savedPosition.left;
|
|
37976
|
+
clone.style.top = savedPosition.top;
|
|
37977
|
+
}
|
|
37978
|
+
}
|
|
37979
|
+
clone.classList.toggle("maximized", isMaximized);
|
|
37980
|
+
};
|
|
37981
|
+
}
|
|
37982
|
+
const header = clone.querySelector("[data-drag-handle]");
|
|
37983
|
+
if (header) {
|
|
37984
|
+
let isDragging = false;
|
|
37985
|
+
let dragOffset = { x: 0, y: 0 };
|
|
37986
|
+
header.onmousedown = (e) => {
|
|
37987
|
+
if (e.target.closest("[data-action]")) return;
|
|
37988
|
+
if (e.target.closest("[data-toggle-domain]")) return;
|
|
37989
|
+
if (isMaximized) return;
|
|
37990
|
+
isDragging = true;
|
|
37991
|
+
clone.classList.add("dragging");
|
|
37992
|
+
const rect = clone.getBoundingClientRect();
|
|
37993
|
+
dragOffset = { x: e.clientX - rect.left, y: e.clientY - rect.top };
|
|
37994
|
+
const onMouseMove = (e2) => {
|
|
37995
|
+
if (!isDragging) return;
|
|
37996
|
+
const newLeft = e2.clientX - dragOffset.x;
|
|
37997
|
+
const newTop = e2.clientY - dragOffset.y;
|
|
37998
|
+
const maxLeft = window.innerWidth - clone.offsetWidth;
|
|
37999
|
+
const maxTop = window.innerHeight - clone.offsetHeight;
|
|
38000
|
+
clone.style.left = Math.max(0, Math.min(newLeft, maxLeft)) + "px";
|
|
38001
|
+
clone.style.top = Math.max(0, Math.min(newTop, maxTop)) + "px";
|
|
38002
|
+
};
|
|
38003
|
+
const onMouseUp = () => {
|
|
38004
|
+
isDragging = false;
|
|
38005
|
+
clone.classList.remove("dragging");
|
|
38006
|
+
document.removeEventListener("mousemove", onMouseMove);
|
|
38007
|
+
document.removeEventListener("mouseup", onMouseUp);
|
|
38008
|
+
};
|
|
38009
|
+
document.addEventListener("mousemove", onMouseMove);
|
|
38010
|
+
document.addEventListener("mouseup", onMouseUp);
|
|
38011
|
+
};
|
|
38012
|
+
}
|
|
38013
|
+
}
|
|
38014
|
+
function closePinnedClone5(cloneId) {
|
|
38015
|
+
const clone = document.getElementById(cloneId);
|
|
38016
|
+
if (clone) {
|
|
38017
|
+
clone.classList.add("closing");
|
|
38018
|
+
setTimeout(() => clone.remove(), 400);
|
|
38019
|
+
}
|
|
38020
|
+
}
|
|
38021
|
+
function toggleMaximize5(container) {
|
|
38022
|
+
state5.isMaximized = !state5.isMaximized;
|
|
38023
|
+
if (state5.isMaximized) {
|
|
38024
|
+
state5.savedPosition = {
|
|
38025
|
+
left: container.style.left,
|
|
38026
|
+
top: container.style.top
|
|
38027
|
+
};
|
|
38028
|
+
}
|
|
38029
|
+
container.classList.toggle("maximized", state5.isMaximized);
|
|
38030
|
+
const maxBtn = container.querySelector('[data-action="maximize"]');
|
|
38031
|
+
if (maxBtn) {
|
|
38032
|
+
if (state5.isMaximized) {
|
|
38033
|
+
maxBtn.innerHTML = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="5" y="5" width="14" height="14" rx="2"/><path d="M9 5V3h12v12h-2"/></svg>`;
|
|
38034
|
+
maxBtn.setAttribute("title", "Restore");
|
|
38035
|
+
} else {
|
|
38036
|
+
maxBtn.innerHTML = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/></svg>`;
|
|
38037
|
+
maxBtn.setAttribute("title", "Maximize");
|
|
38038
|
+
if (state5.savedPosition) {
|
|
38039
|
+
container.style.left = state5.savedPosition.left;
|
|
38040
|
+
container.style.top = state5.savedPosition.top;
|
|
38041
|
+
}
|
|
38042
|
+
}
|
|
38043
|
+
}
|
|
38044
|
+
}
|
|
38045
|
+
function startDelayedHide5() {
|
|
38046
|
+
if (state5.isMouseOverTooltip) return;
|
|
38047
|
+
if (state5.hideTimer) {
|
|
38048
|
+
clearTimeout(state5.hideTimer);
|
|
38049
|
+
}
|
|
38050
|
+
state5.hideTimer = setTimeout(() => {
|
|
38051
|
+
hideWithAnimation5();
|
|
38052
|
+
}, 1500);
|
|
38053
|
+
}
|
|
38054
|
+
function hideWithAnimation5() {
|
|
38055
|
+
const container = document.getElementById("myio-contract-summary-tooltip");
|
|
38056
|
+
if (container && container.classList.contains("visible")) {
|
|
38057
|
+
container.classList.add("closing");
|
|
38058
|
+
setTimeout(() => {
|
|
38059
|
+
container.classList.remove("visible", "closing");
|
|
38060
|
+
}, 400);
|
|
38061
|
+
}
|
|
38062
|
+
}
|
|
38063
|
+
function positionTooltip5(container, triggerElement) {
|
|
38064
|
+
const rect = triggerElement.getBoundingClientRect();
|
|
38065
|
+
let left = rect.left;
|
|
38066
|
+
let top = rect.bottom + 8;
|
|
38067
|
+
const tooltipWidth = 360;
|
|
38068
|
+
if (left + tooltipWidth > window.innerWidth - 20) {
|
|
38069
|
+
left = window.innerWidth - tooltipWidth - 20;
|
|
38070
|
+
}
|
|
38071
|
+
if (left < 10) left = 10;
|
|
38072
|
+
if (top + 500 > window.innerHeight) {
|
|
38073
|
+
top = rect.top - 8 - 500;
|
|
38074
|
+
if (top < 10) top = 10;
|
|
38075
|
+
}
|
|
38076
|
+
container.style.left = left + "px";
|
|
38077
|
+
container.style.top = top + "px";
|
|
38078
|
+
}
|
|
38079
|
+
var ContractSummaryTooltip = {
|
|
38080
|
+
containerId: "myio-contract-summary-tooltip",
|
|
38081
|
+
/**
|
|
38082
|
+
* Get or create container
|
|
38083
|
+
*/
|
|
38084
|
+
getContainer() {
|
|
38085
|
+
injectCSS9();
|
|
38086
|
+
let container = document.getElementById(this.containerId);
|
|
38087
|
+
if (!container) {
|
|
38088
|
+
container = document.createElement("div");
|
|
38089
|
+
container.id = this.containerId;
|
|
38090
|
+
container.className = "myio-contract-summary-tooltip";
|
|
38091
|
+
document.body.appendChild(container);
|
|
38092
|
+
}
|
|
38093
|
+
return container;
|
|
38094
|
+
},
|
|
38095
|
+
/**
|
|
38096
|
+
* Show tooltip
|
|
38097
|
+
*/
|
|
38098
|
+
show(triggerElement, data) {
|
|
38099
|
+
if (state5.hideTimer) {
|
|
38100
|
+
clearTimeout(state5.hideTimer);
|
|
38101
|
+
state5.hideTimer = null;
|
|
38102
|
+
}
|
|
38103
|
+
const container = this.getContainer();
|
|
38104
|
+
container.classList.remove("closing");
|
|
38105
|
+
container.innerHTML = `
|
|
38106
|
+
<div class="myio-contract-summary-tooltip__panel">
|
|
38107
|
+
${generateHeaderHTML5(data)}
|
|
38108
|
+
${generateBodyHTML3(data)}
|
|
38109
|
+
</div>
|
|
38110
|
+
`;
|
|
38111
|
+
positionTooltip5(container, triggerElement);
|
|
38112
|
+
container.classList.add("visible");
|
|
38113
|
+
setupHoverListeners5(container);
|
|
38114
|
+
setupButtonListeners5(container);
|
|
38115
|
+
setupDragListeners5(container);
|
|
38116
|
+
setupDomainToggleListeners(container);
|
|
38117
|
+
},
|
|
38118
|
+
/**
|
|
38119
|
+
* Start delayed hide
|
|
38120
|
+
*/
|
|
38121
|
+
startDelayedHide() {
|
|
38122
|
+
startDelayedHide5();
|
|
38123
|
+
},
|
|
38124
|
+
/**
|
|
38125
|
+
* Hide immediately
|
|
38126
|
+
*/
|
|
38127
|
+
hide() {
|
|
38128
|
+
if (state5.hideTimer) {
|
|
38129
|
+
clearTimeout(state5.hideTimer);
|
|
38130
|
+
state5.hideTimer = null;
|
|
38131
|
+
}
|
|
38132
|
+
state5.isMouseOverTooltip = false;
|
|
38133
|
+
const container = document.getElementById(this.containerId);
|
|
38134
|
+
if (container) {
|
|
38135
|
+
container.classList.remove("visible", "closing");
|
|
38136
|
+
}
|
|
38137
|
+
},
|
|
38138
|
+
/**
|
|
38139
|
+
* Close and reset all states
|
|
38140
|
+
*/
|
|
38141
|
+
close() {
|
|
38142
|
+
state5.isMaximized = false;
|
|
38143
|
+
state5.isDragging = false;
|
|
38144
|
+
state5.savedPosition = null;
|
|
38145
|
+
if (state5.hideTimer) {
|
|
38146
|
+
clearTimeout(state5.hideTimer);
|
|
38147
|
+
state5.hideTimer = null;
|
|
38148
|
+
}
|
|
38149
|
+
state5.isMouseOverTooltip = false;
|
|
38150
|
+
const container = document.getElementById(this.containerId);
|
|
38151
|
+
if (container) {
|
|
38152
|
+
container.classList.remove("visible", "pinned", "maximized", "dragging", "closing");
|
|
38153
|
+
}
|
|
38154
|
+
},
|
|
38155
|
+
/**
|
|
38156
|
+
* Attach tooltip to trigger element with click behavior
|
|
38157
|
+
*/
|
|
38158
|
+
attach(triggerElement, getDataFn) {
|
|
38159
|
+
const self = this;
|
|
38160
|
+
const handleClick = () => {
|
|
38161
|
+
if (state5.hideTimer) {
|
|
38162
|
+
clearTimeout(state5.hideTimer);
|
|
38163
|
+
state5.hideTimer = null;
|
|
38164
|
+
}
|
|
38165
|
+
const data = getDataFn();
|
|
38166
|
+
if (data) {
|
|
38167
|
+
self.show(triggerElement, data);
|
|
38168
|
+
}
|
|
38169
|
+
};
|
|
38170
|
+
triggerElement.addEventListener("click", handleClick);
|
|
38171
|
+
return () => {
|
|
38172
|
+
triggerElement.removeEventListener("click", handleClick);
|
|
38173
|
+
self.hide();
|
|
38174
|
+
};
|
|
38175
|
+
},
|
|
38176
|
+
/**
|
|
38177
|
+
* Attach tooltip with hover behavior
|
|
38178
|
+
*/
|
|
38179
|
+
attachHover(triggerElement, getDataFn) {
|
|
38180
|
+
const self = this;
|
|
38181
|
+
const handleMouseEnter = () => {
|
|
38182
|
+
if (state5.hideTimer) {
|
|
38183
|
+
clearTimeout(state5.hideTimer);
|
|
38184
|
+
state5.hideTimer = null;
|
|
38185
|
+
}
|
|
38186
|
+
const data = getDataFn();
|
|
38187
|
+
if (data) {
|
|
38188
|
+
self.show(triggerElement, data);
|
|
38189
|
+
}
|
|
38190
|
+
};
|
|
38191
|
+
const handleMouseLeave = () => {
|
|
38192
|
+
startDelayedHide5();
|
|
38193
|
+
};
|
|
38194
|
+
triggerElement.addEventListener("mouseenter", handleMouseEnter);
|
|
38195
|
+
triggerElement.addEventListener("mouseleave", handleMouseLeave);
|
|
38196
|
+
return () => {
|
|
38197
|
+
triggerElement.removeEventListener("mouseenter", handleMouseEnter);
|
|
38198
|
+
triggerElement.removeEventListener("mouseleave", handleMouseLeave);
|
|
38199
|
+
self.hide();
|
|
38200
|
+
};
|
|
38201
|
+
},
|
|
38202
|
+
/**
|
|
38203
|
+
* Build contract data from window.CONTRACT_STATE
|
|
38204
|
+
* Helper method to build the data structure from global state
|
|
38205
|
+
*/
|
|
38206
|
+
buildFromGlobalState() {
|
|
38207
|
+
const globalState = window.CONTRACT_STATE;
|
|
38208
|
+
if (!globalState) return null;
|
|
38209
|
+
return {
|
|
38210
|
+
isLoaded: globalState.isLoaded ?? false,
|
|
38211
|
+
isValid: globalState.isValid ?? false,
|
|
38212
|
+
timestamp: globalState.timestamp ?? null,
|
|
38213
|
+
energy: {
|
|
38214
|
+
total: globalState.energy?.total ?? 0,
|
|
38215
|
+
entries: globalState.energy?.entries ?? 0,
|
|
38216
|
+
commonArea: globalState.energy?.commonArea ?? 0,
|
|
38217
|
+
stores: globalState.energy?.stores ?? 0
|
|
38218
|
+
},
|
|
38219
|
+
water: {
|
|
38220
|
+
total: globalState.water?.total ?? 0,
|
|
38221
|
+
entries: globalState.water?.entries ?? 0,
|
|
38222
|
+
commonArea: globalState.water?.commonArea ?? 0,
|
|
38223
|
+
stores: globalState.water?.stores ?? 0
|
|
38224
|
+
},
|
|
38225
|
+
temperature: {
|
|
38226
|
+
total: globalState.temperature?.total ?? 0,
|
|
38227
|
+
internal: globalState.temperature?.internal ?? 0,
|
|
38228
|
+
stores: globalState.temperature?.stores ?? 0
|
|
38229
|
+
},
|
|
38230
|
+
discrepancies: globalState.discrepancies
|
|
38231
|
+
};
|
|
38232
|
+
}
|
|
38233
|
+
};
|
|
38234
|
+
|
|
37228
38235
|
// src/components/ModalHeader/index.ts
|
|
37229
38236
|
var DEFAULT_BG_COLOR = "#3e1a7d";
|
|
37230
38237
|
var DEFAULT_TEXT_COLOR = "white";
|
|
@@ -40988,6 +41995,7 @@ export {
|
|
|
40988
41995
|
DEFAULT_CONFIG as CONSUMPTION_CHART_DEFAULTS,
|
|
40989
41996
|
THEME_COLORS as CONSUMPTION_THEME_COLORS,
|
|
40990
41997
|
ConnectionStatusType,
|
|
41998
|
+
ContractSummaryTooltip,
|
|
40991
41999
|
DEFAULT_CLAMP_RANGE,
|
|
40992
42000
|
DEFAULT_ENERGY_GROUP_COLORS,
|
|
40993
42001
|
DEFAULT_GAS_GROUP_COLORS,
|