myio-js-library 0.1.185 → 0.1.186
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 +1419 -386
- package/dist/index.d.cts +58 -1
- package/dist/index.js +1418 -386
- package/dist/myio-js-library.umd.js +1418 -386
- package/dist/myio-js-library.umd.min.js +1 -1
- package/package.json +1 -1
|
@@ -8863,8 +8863,8 @@
|
|
|
8863
8863
|
return "dot--offline";
|
|
8864
8864
|
}
|
|
8865
8865
|
}
|
|
8866
|
-
function buildDOM(
|
|
8867
|
-
const { entityObject, i18n, enableSelection, enableDragDrop } =
|
|
8866
|
+
function buildDOM(state2) {
|
|
8867
|
+
const { entityObject, i18n, enableSelection, enableDragDrop } = state2;
|
|
8868
8868
|
const root = document.createElement("div");
|
|
8869
8869
|
root.className = "myio-ho-card";
|
|
8870
8870
|
root.setAttribute("role", "group");
|
|
@@ -9190,8 +9190,8 @@
|
|
|
9190
9190
|
}
|
|
9191
9191
|
return isOffline;
|
|
9192
9192
|
}
|
|
9193
|
-
function paint(root,
|
|
9194
|
-
const { entityObject, i18n, delayTimeConnectionInMins, isSelected, LogHelper: LogHelper2, activeTooltipDebug } =
|
|
9193
|
+
function paint(root, state2) {
|
|
9194
|
+
const { entityObject, i18n, delayTimeConnectionInMins, isSelected, LogHelper: LogHelper2, activeTooltipDebug } = state2;
|
|
9195
9195
|
let statusDecisionSource = "unknown";
|
|
9196
9196
|
if (entityObject.connectionStatus) {
|
|
9197
9197
|
if (entityObject.connectionStatus === "offline") {
|
|
@@ -9243,7 +9243,7 @@
|
|
|
9243
9243
|
numSpan.textContent = primaryValue;
|
|
9244
9244
|
const barContainer = root.querySelector(".bar");
|
|
9245
9245
|
const effContainer = root.querySelector(".myio-ho-card__eff");
|
|
9246
|
-
if (
|
|
9246
|
+
if (state2.enableSelection) {
|
|
9247
9247
|
const checkbox = root.querySelector('.myio-ho-card__select input[type="checkbox"]');
|
|
9248
9248
|
if (checkbox) {
|
|
9249
9249
|
checkbox.checked = !!isSelected;
|
|
@@ -9291,8 +9291,8 @@
|
|
|
9291
9291
|
statusDot.className = `status-dot ${dotClass}`;
|
|
9292
9292
|
}
|
|
9293
9293
|
}
|
|
9294
|
-
function bindEvents(root,
|
|
9295
|
-
const { entityObject } =
|
|
9294
|
+
function bindEvents(root, state2, callbacks) {
|
|
9295
|
+
const { entityObject } = state2;
|
|
9296
9296
|
const kebabBtn = root.querySelector(".myio-ho-card__kebab");
|
|
9297
9297
|
const menu = root.querySelector(".myio-ho-card__menu");
|
|
9298
9298
|
function toggleMenu() {
|
|
@@ -9348,9 +9348,9 @@
|
|
|
9348
9348
|
const onSelectionChange = () => {
|
|
9349
9349
|
const selectedIds = MyIOSelectionStore2.getSelectedIds();
|
|
9350
9350
|
const isSelected = selectedIds.includes(entityObject.entityId);
|
|
9351
|
-
if (
|
|
9352
|
-
|
|
9353
|
-
paint(root,
|
|
9351
|
+
if (state2.isSelected !== isSelected) {
|
|
9352
|
+
state2.isSelected = isSelected;
|
|
9353
|
+
paint(root, state2);
|
|
9354
9354
|
}
|
|
9355
9355
|
};
|
|
9356
9356
|
MyIOSelectionStore2.on("selection:change", onSelectionChange);
|
|
@@ -9363,7 +9363,7 @@
|
|
|
9363
9363
|
clearTimeout(TempRangeTooltip._hideTimer);
|
|
9364
9364
|
TempRangeTooltip._hideTimer = null;
|
|
9365
9365
|
}
|
|
9366
|
-
TempRangeTooltip.show(root,
|
|
9366
|
+
TempRangeTooltip.show(root, state2.entityObject, e);
|
|
9367
9367
|
};
|
|
9368
9368
|
const hideTooltip = () => {
|
|
9369
9369
|
TempRangeTooltip._startDelayedHide();
|
|
@@ -9381,7 +9381,7 @@
|
|
|
9381
9381
|
clearTimeout(EnergyRangeTooltip._hideTimer);
|
|
9382
9382
|
EnergyRangeTooltip._hideTimer = null;
|
|
9383
9383
|
}
|
|
9384
|
-
EnergyRangeTooltip.show(root,
|
|
9384
|
+
EnergyRangeTooltip.show(root, state2.entityObject, e);
|
|
9385
9385
|
};
|
|
9386
9386
|
const hideEnergyTooltip = () => {
|
|
9387
9387
|
EnergyRangeTooltip._startDelayedHide();
|
|
@@ -9433,7 +9433,7 @@
|
|
|
9433
9433
|
}
|
|
9434
9434
|
});
|
|
9435
9435
|
}
|
|
9436
|
-
if (
|
|
9436
|
+
if (state2.enableDragDrop) {
|
|
9437
9437
|
root.addEventListener("dragstart", (e) => {
|
|
9438
9438
|
root.classList.add("is-dragging");
|
|
9439
9439
|
e.dataTransfer.setData("text/plain", entityObject.entityId);
|
|
@@ -9475,17 +9475,17 @@
|
|
|
9475
9475
|
throw new Error("renderCardComponentHeadOffice: containerEl is required");
|
|
9476
9476
|
}
|
|
9477
9477
|
ensureCss();
|
|
9478
|
-
const
|
|
9479
|
-
const root = buildDOM(
|
|
9480
|
-
|
|
9478
|
+
const state2 = normalizeParams(params);
|
|
9479
|
+
const root = buildDOM(state2);
|
|
9480
|
+
state2.isSelected = params.isSelected || false;
|
|
9481
9481
|
containerEl.appendChild(root);
|
|
9482
|
-
bindEvents(root,
|
|
9483
|
-
paint(root,
|
|
9482
|
+
bindEvents(root, state2, state2.callbacks);
|
|
9483
|
+
paint(root, state2);
|
|
9484
9484
|
return {
|
|
9485
9485
|
update(next) {
|
|
9486
9486
|
if (next) {
|
|
9487
|
-
Object.assign(
|
|
9488
|
-
paint(root,
|
|
9487
|
+
Object.assign(state2.entityObject, next);
|
|
9488
|
+
paint(root, state2);
|
|
9489
9489
|
}
|
|
9490
9490
|
},
|
|
9491
9491
|
destroy() {
|
|
@@ -20794,15 +20794,45 @@
|
|
|
20794
20794
|
}
|
|
20795
20795
|
}
|
|
20796
20796
|
/**
|
|
20797
|
-
*
|
|
20797
|
+
* Fetch child customer relations (level 1) from a parent customer
|
|
20798
|
+
* Returns array of child customer IDs
|
|
20798
20799
|
*/
|
|
20799
|
-
async
|
|
20800
|
+
async fetchChildCustomerIds(parentCustomerId) {
|
|
20801
|
+
try {
|
|
20802
|
+
const url = `${this.tbBaseUrl}/api/relations/info?fromId=${parentCustomerId}&fromType=CUSTOMER`;
|
|
20803
|
+
const response = await fetch(url, {
|
|
20804
|
+
method: "GET",
|
|
20805
|
+
headers: {
|
|
20806
|
+
"X-Authorization": `Bearer ${this.jwtToken}`,
|
|
20807
|
+
"Content-Type": "application/json"
|
|
20808
|
+
}
|
|
20809
|
+
});
|
|
20810
|
+
if (!response.ok) {
|
|
20811
|
+
console.warn("[PowerLimitsPersister] Failed to fetch relations:", response.status);
|
|
20812
|
+
return [];
|
|
20813
|
+
}
|
|
20814
|
+
const relations = await response.json();
|
|
20815
|
+
if (!Array.isArray(relations) || relations.length === 0) {
|
|
20816
|
+
console.log("[PowerLimitsPersister] No child customer relations found");
|
|
20817
|
+
return [];
|
|
20818
|
+
}
|
|
20819
|
+
const childCustomerIds = relations.filter((rel) => rel.to?.entityType === "CUSTOMER" && rel.to?.id).map((rel) => rel.to.id);
|
|
20820
|
+
console.log(`[PowerLimitsPersister] Found ${childCustomerIds.length} child customer(s)`);
|
|
20821
|
+
return childCustomerIds;
|
|
20822
|
+
} catch (error) {
|
|
20823
|
+
console.error("[PowerLimitsPersister] Error fetching relations:", error);
|
|
20824
|
+
return [];
|
|
20825
|
+
}
|
|
20826
|
+
}
|
|
20827
|
+
/**
|
|
20828
|
+
* Save power limits to a single customer (internal method)
|
|
20829
|
+
*/
|
|
20830
|
+
async saveToSingleCustomer(customerId, limits) {
|
|
20800
20831
|
try {
|
|
20801
20832
|
const url = `${this.tbBaseUrl}/api/plugins/telemetry/CUSTOMER/${customerId}/attributes/SERVER_SCOPE`;
|
|
20802
20833
|
const payload = {
|
|
20803
20834
|
mapInstantaneousPower: limits
|
|
20804
20835
|
};
|
|
20805
|
-
console.log("[PowerLimitsPersister] Saving power limits:", payload);
|
|
20806
20836
|
const response = await fetch(url, {
|
|
20807
20837
|
method: "POST",
|
|
20808
20838
|
headers: {
|
|
@@ -20812,10 +20842,41 @@
|
|
|
20812
20842
|
body: JSON.stringify(payload)
|
|
20813
20843
|
});
|
|
20814
20844
|
if (!response.ok) {
|
|
20815
|
-
|
|
20845
|
+
console.warn(`[PowerLimitsPersister] Failed to save to customer ${customerId}:`, response.status);
|
|
20846
|
+
return false;
|
|
20816
20847
|
}
|
|
20817
|
-
|
|
20818
|
-
|
|
20848
|
+
return true;
|
|
20849
|
+
} catch (error) {
|
|
20850
|
+
console.error(`[PowerLimitsPersister] Error saving to customer ${customerId}:`, error);
|
|
20851
|
+
return false;
|
|
20852
|
+
}
|
|
20853
|
+
}
|
|
20854
|
+
/**
|
|
20855
|
+
* Save mapInstantaneousPower to customer server_scope attributes
|
|
20856
|
+
* Also propagates to all child customers (level 1 relations)
|
|
20857
|
+
*/
|
|
20858
|
+
async saveCustomerPowerLimits(customerId, limits) {
|
|
20859
|
+
try {
|
|
20860
|
+
console.log("[PowerLimitsPersister] Saving power limits:", { customerId, limits });
|
|
20861
|
+
const mainSaveSuccess = await this.saveToSingleCustomer(customerId, limits);
|
|
20862
|
+
if (!mainSaveSuccess) {
|
|
20863
|
+
throw new Error("Failed to save to main customer");
|
|
20864
|
+
}
|
|
20865
|
+
console.log("[PowerLimitsPersister] Successfully saved to main customer");
|
|
20866
|
+
const childCustomerIds = await this.fetchChildCustomerIds(customerId);
|
|
20867
|
+
let successCount = 1;
|
|
20868
|
+
if (childCustomerIds.length > 0) {
|
|
20869
|
+
console.log(`[PowerLimitsPersister] Saving to ${childCustomerIds.length} child customer(s)...`);
|
|
20870
|
+
const savePromises = childCustomerIds.map(
|
|
20871
|
+
(childId) => this.saveToSingleCustomer(childId, limits)
|
|
20872
|
+
);
|
|
20873
|
+
const results = await Promise.all(savePromises);
|
|
20874
|
+
const childSuccessCount = results.filter(Boolean).length;
|
|
20875
|
+
successCount += childSuccessCount;
|
|
20876
|
+
console.log(`[PowerLimitsPersister] Saved to ${childSuccessCount}/${childCustomerIds.length} child customer(s)`);
|
|
20877
|
+
}
|
|
20878
|
+
console.log(`[PowerLimitsPersister] Total: saved to ${successCount} customer(s)`);
|
|
20879
|
+
return { ok: true, savedCount: successCount };
|
|
20819
20880
|
} catch (error) {
|
|
20820
20881
|
console.error("[PowerLimitsPersister] Error saving power limits:", error);
|
|
20821
20882
|
return { ok: false, error: this.mapError(error) };
|
|
@@ -22005,6 +22066,136 @@
|
|
|
22005
22066
|
this.annotations = [];
|
|
22006
22067
|
}
|
|
22007
22068
|
}
|
|
22069
|
+
/**
|
|
22070
|
+
* Show a confirmation modal and return user's choice
|
|
22071
|
+
* Replaces native confirm() for better UX
|
|
22072
|
+
*/
|
|
22073
|
+
showConfirmation(message, title = "Confirmar") {
|
|
22074
|
+
return new Promise((resolve) => {
|
|
22075
|
+
const overlay = document.createElement("div");
|
|
22076
|
+
overlay.className = "annotations-confirm-overlay";
|
|
22077
|
+
overlay.innerHTML = `
|
|
22078
|
+
<div class="annotations-confirm-modal">
|
|
22079
|
+
<div class="annotations-confirm-header">
|
|
22080
|
+
<span class="annotations-confirm-icon">\u26A0\uFE0F</span>
|
|
22081
|
+
<span class="annotations-confirm-title">${title}</span>
|
|
22082
|
+
</div>
|
|
22083
|
+
<div class="annotations-confirm-body">
|
|
22084
|
+
<p>${message}</p>
|
|
22085
|
+
</div>
|
|
22086
|
+
<div class="annotations-confirm-actions">
|
|
22087
|
+
<button class="annotations-confirm-btn annotations-confirm-btn--cancel" data-action="cancel">
|
|
22088
|
+
Cancelar
|
|
22089
|
+
</button>
|
|
22090
|
+
<button class="annotations-confirm-btn annotations-confirm-btn--confirm" data-action="confirm">
|
|
22091
|
+
Confirmar
|
|
22092
|
+
</button>
|
|
22093
|
+
</div>
|
|
22094
|
+
</div>
|
|
22095
|
+
`;
|
|
22096
|
+
if (!document.getElementById("annotations-confirm-styles")) {
|
|
22097
|
+
const style = document.createElement("style");
|
|
22098
|
+
style.id = "annotations-confirm-styles";
|
|
22099
|
+
style.textContent = `
|
|
22100
|
+
.annotations-confirm-overlay {
|
|
22101
|
+
position: fixed;
|
|
22102
|
+
inset: 0;
|
|
22103
|
+
background: rgba(0, 0, 0, 0.5);
|
|
22104
|
+
backdrop-filter: blur(4px);
|
|
22105
|
+
display: flex;
|
|
22106
|
+
align-items: center;
|
|
22107
|
+
justify-content: center;
|
|
22108
|
+
z-index: 100001;
|
|
22109
|
+
animation: confirmFadeIn 0.2s ease;
|
|
22110
|
+
}
|
|
22111
|
+
@keyframes confirmFadeIn {
|
|
22112
|
+
from { opacity: 0; }
|
|
22113
|
+
to { opacity: 1; }
|
|
22114
|
+
}
|
|
22115
|
+
.annotations-confirm-modal {
|
|
22116
|
+
background: white;
|
|
22117
|
+
border-radius: 12px;
|
|
22118
|
+
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
|
22119
|
+
max-width: 400px;
|
|
22120
|
+
width: 90%;
|
|
22121
|
+
overflow: hidden;
|
|
22122
|
+
animation: confirmSlideIn 0.25s ease;
|
|
22123
|
+
}
|
|
22124
|
+
@keyframes confirmSlideIn {
|
|
22125
|
+
from { transform: translateY(-20px) scale(0.95); opacity: 0; }
|
|
22126
|
+
to { transform: translateY(0) scale(1); opacity: 1; }
|
|
22127
|
+
}
|
|
22128
|
+
.annotations-confirm-header {
|
|
22129
|
+
display: flex;
|
|
22130
|
+
align-items: center;
|
|
22131
|
+
gap: 10px;
|
|
22132
|
+
padding: 16px 20px;
|
|
22133
|
+
background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);
|
|
22134
|
+
border-bottom: 1px solid #f59e0b;
|
|
22135
|
+
}
|
|
22136
|
+
.annotations-confirm-icon {
|
|
22137
|
+
font-size: 20px;
|
|
22138
|
+
}
|
|
22139
|
+
.annotations-confirm-title {
|
|
22140
|
+
font-weight: 600;
|
|
22141
|
+
color: #92400e;
|
|
22142
|
+
font-size: 16px;
|
|
22143
|
+
}
|
|
22144
|
+
.annotations-confirm-body {
|
|
22145
|
+
padding: 20px;
|
|
22146
|
+
}
|
|
22147
|
+
.annotations-confirm-body p {
|
|
22148
|
+
margin: 0;
|
|
22149
|
+
color: #374151;
|
|
22150
|
+
font-size: 14px;
|
|
22151
|
+
line-height: 1.5;
|
|
22152
|
+
}
|
|
22153
|
+
.annotations-confirm-actions {
|
|
22154
|
+
display: flex;
|
|
22155
|
+
gap: 12px;
|
|
22156
|
+
padding: 16px 20px;
|
|
22157
|
+
background: #f9fafb;
|
|
22158
|
+
border-top: 1px solid #e5e7eb;
|
|
22159
|
+
justify-content: flex-end;
|
|
22160
|
+
}
|
|
22161
|
+
.annotations-confirm-btn {
|
|
22162
|
+
padding: 10px 20px;
|
|
22163
|
+
border-radius: 8px;
|
|
22164
|
+
font-size: 14px;
|
|
22165
|
+
font-weight: 500;
|
|
22166
|
+
cursor: pointer;
|
|
22167
|
+
transition: all 0.2s ease;
|
|
22168
|
+
border: none;
|
|
22169
|
+
}
|
|
22170
|
+
.annotations-confirm-btn--cancel {
|
|
22171
|
+
background: #e5e7eb;
|
|
22172
|
+
color: #374151;
|
|
22173
|
+
}
|
|
22174
|
+
.annotations-confirm-btn--cancel:hover {
|
|
22175
|
+
background: #d1d5db;
|
|
22176
|
+
}
|
|
22177
|
+
.annotations-confirm-btn--confirm {
|
|
22178
|
+
background: #f59e0b;
|
|
22179
|
+
color: white;
|
|
22180
|
+
}
|
|
22181
|
+
.annotations-confirm-btn--confirm:hover {
|
|
22182
|
+
background: #d97706;
|
|
22183
|
+
}
|
|
22184
|
+
`;
|
|
22185
|
+
document.head.appendChild(style);
|
|
22186
|
+
}
|
|
22187
|
+
document.body.appendChild(overlay);
|
|
22188
|
+
const cleanup = (result) => {
|
|
22189
|
+
overlay.remove();
|
|
22190
|
+
resolve(result);
|
|
22191
|
+
};
|
|
22192
|
+
overlay.querySelector('[data-action="cancel"]')?.addEventListener("click", () => cleanup(false));
|
|
22193
|
+
overlay.querySelector('[data-action="confirm"]')?.addEventListener("click", () => cleanup(true));
|
|
22194
|
+
overlay.addEventListener("click", (e) => {
|
|
22195
|
+
if (e.target === overlay) cleanup(false);
|
|
22196
|
+
});
|
|
22197
|
+
});
|
|
22198
|
+
}
|
|
22008
22199
|
async saveAnnotations() {
|
|
22009
22200
|
try {
|
|
22010
22201
|
const data = {
|
|
@@ -22068,7 +22259,7 @@
|
|
|
22068
22259
|
this.render();
|
|
22069
22260
|
} else {
|
|
22070
22261
|
this.annotations.shift();
|
|
22071
|
-
|
|
22262
|
+
MyIOToast.show("Erro ao salvar anota\xE7\xE3o. Tente novamente.", "error");
|
|
22072
22263
|
}
|
|
22073
22264
|
}
|
|
22074
22265
|
async editAnnotation(id, changes) {
|
|
@@ -22078,9 +22269,10 @@
|
|
|
22078
22269
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
22079
22270
|
const changeRecord = {};
|
|
22080
22271
|
for (const [key, value] of Object.entries(changes)) {
|
|
22081
|
-
|
|
22272
|
+
const annotationAny = annotation;
|
|
22273
|
+
if (annotationAny[key] !== value) {
|
|
22082
22274
|
changeRecord[key] = {
|
|
22083
|
-
from:
|
|
22275
|
+
from: annotationAny[key],
|
|
22084
22276
|
to: value
|
|
22085
22277
|
};
|
|
22086
22278
|
}
|
|
@@ -22105,7 +22297,7 @@
|
|
|
22105
22297
|
const success = await this.saveAnnotations();
|
|
22106
22298
|
if (!success) {
|
|
22107
22299
|
this.annotations[index] = annotation;
|
|
22108
|
-
|
|
22300
|
+
MyIOToast.show("Erro ao atualizar anota\xE7\xE3o. Tente novamente.", "error");
|
|
22109
22301
|
}
|
|
22110
22302
|
}
|
|
22111
22303
|
async archiveAnnotation(id) {
|
|
@@ -22133,7 +22325,7 @@
|
|
|
22133
22325
|
this.render();
|
|
22134
22326
|
} else {
|
|
22135
22327
|
this.annotations[index] = annotation;
|
|
22136
|
-
|
|
22328
|
+
MyIOToast.show("Erro ao arquivar anota\xE7\xE3o. Tente novamente.", "error");
|
|
22137
22329
|
}
|
|
22138
22330
|
}
|
|
22139
22331
|
async acknowledgeAnnotation(id) {
|
|
@@ -22648,8 +22840,8 @@
|
|
|
22648
22840
|
card.querySelector('[data-action="details"]')?.addEventListener("click", () => {
|
|
22649
22841
|
this.showDetailModal(id);
|
|
22650
22842
|
});
|
|
22651
|
-
card.querySelector('[data-action="archive"]')?.addEventListener("click", () => {
|
|
22652
|
-
if (
|
|
22843
|
+
card.querySelector('[data-action="archive"]')?.addEventListener("click", async () => {
|
|
22844
|
+
if (await this.showConfirmation("Tem certeza que deseja arquivar esta anota\xE7\xE3o?", "Arquivar Anota\xE7\xE3o")) {
|
|
22653
22845
|
this.archiveAnnotation(id);
|
|
22654
22846
|
}
|
|
22655
22847
|
});
|
|
@@ -22666,8 +22858,8 @@
|
|
|
22666
22858
|
row.querySelector('[data-action="details"]')?.addEventListener("click", () => {
|
|
22667
22859
|
this.showDetailModal(id);
|
|
22668
22860
|
});
|
|
22669
|
-
row.querySelector('[data-action="archive"]')?.addEventListener("click", () => {
|
|
22670
|
-
if (
|
|
22861
|
+
row.querySelector('[data-action="archive"]')?.addEventListener("click", async () => {
|
|
22862
|
+
if (await this.showConfirmation("Tem certeza que deseja arquivar esta anota\xE7\xE3o?", "Arquivar Anota\xE7\xE3o")) {
|
|
22671
22863
|
this.archiveAnnotation(id);
|
|
22672
22864
|
}
|
|
22673
22865
|
});
|
|
@@ -22676,12 +22868,189 @@
|
|
|
22676
22868
|
// ============================================
|
|
22677
22869
|
// EDIT MODAL
|
|
22678
22870
|
// ============================================
|
|
22679
|
-
|
|
22871
|
+
/**
|
|
22872
|
+
* Show a styled input modal for editing annotation text
|
|
22873
|
+
* Replaces native prompt() for better UX
|
|
22874
|
+
*/
|
|
22875
|
+
showInputModal(title, placeholder, initialValue) {
|
|
22876
|
+
return new Promise((resolve) => {
|
|
22877
|
+
const overlay = document.createElement("div");
|
|
22878
|
+
overlay.className = "annotations-input-overlay";
|
|
22879
|
+
overlay.innerHTML = `
|
|
22880
|
+
<div class="annotations-input-modal">
|
|
22881
|
+
<div class="annotations-input-header">
|
|
22882
|
+
<span class="annotations-input-icon">\u270F\uFE0F</span>
|
|
22883
|
+
<span class="annotations-input-title">${title}</span>
|
|
22884
|
+
</div>
|
|
22885
|
+
<div class="annotations-input-body">
|
|
22886
|
+
<textarea
|
|
22887
|
+
class="annotations-input-textarea"
|
|
22888
|
+
placeholder="${placeholder}"
|
|
22889
|
+
maxlength="255"
|
|
22890
|
+
>${initialValue}</textarea>
|
|
22891
|
+
<div class="annotations-input-char-count">
|
|
22892
|
+
<span id="input-char-count">${initialValue.length}</span> / 255
|
|
22893
|
+
</div>
|
|
22894
|
+
</div>
|
|
22895
|
+
<div class="annotations-input-actions">
|
|
22896
|
+
<button class="annotations-input-btn annotations-input-btn--cancel" data-action="cancel">
|
|
22897
|
+
Cancelar
|
|
22898
|
+
</button>
|
|
22899
|
+
<button class="annotations-input-btn annotations-input-btn--confirm" data-action="confirm">
|
|
22900
|
+
Salvar
|
|
22901
|
+
</button>
|
|
22902
|
+
</div>
|
|
22903
|
+
</div>
|
|
22904
|
+
`;
|
|
22905
|
+
if (!document.getElementById("annotations-input-styles")) {
|
|
22906
|
+
const style = document.createElement("style");
|
|
22907
|
+
style.id = "annotations-input-styles";
|
|
22908
|
+
style.textContent = `
|
|
22909
|
+
.annotations-input-overlay {
|
|
22910
|
+
position: fixed;
|
|
22911
|
+
inset: 0;
|
|
22912
|
+
background: rgba(0, 0, 0, 0.5);
|
|
22913
|
+
backdrop-filter: blur(4px);
|
|
22914
|
+
display: flex;
|
|
22915
|
+
align-items: center;
|
|
22916
|
+
justify-content: center;
|
|
22917
|
+
z-index: 100001;
|
|
22918
|
+
animation: inputFadeIn 0.2s ease;
|
|
22919
|
+
}
|
|
22920
|
+
@keyframes inputFadeIn {
|
|
22921
|
+
from { opacity: 0; }
|
|
22922
|
+
to { opacity: 1; }
|
|
22923
|
+
}
|
|
22924
|
+
.annotations-input-modal {
|
|
22925
|
+
background: white;
|
|
22926
|
+
border-radius: 12px;
|
|
22927
|
+
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
|
22928
|
+
max-width: 500px;
|
|
22929
|
+
width: 90%;
|
|
22930
|
+
overflow: hidden;
|
|
22931
|
+
animation: inputSlideIn 0.25s ease;
|
|
22932
|
+
}
|
|
22933
|
+
@keyframes inputSlideIn {
|
|
22934
|
+
from { transform: translateY(-20px) scale(0.95); opacity: 0; }
|
|
22935
|
+
to { transform: translateY(0) scale(1); opacity: 1; }
|
|
22936
|
+
}
|
|
22937
|
+
.annotations-input-header {
|
|
22938
|
+
display: flex;
|
|
22939
|
+
align-items: center;
|
|
22940
|
+
gap: 10px;
|
|
22941
|
+
padding: 16px 20px;
|
|
22942
|
+
background: linear-gradient(135deg, #6c5ce7 0%, #a29bfe 100%);
|
|
22943
|
+
border-bottom: 1px solid #5b4cdb;
|
|
22944
|
+
}
|
|
22945
|
+
.annotations-input-icon {
|
|
22946
|
+
font-size: 20px;
|
|
22947
|
+
}
|
|
22948
|
+
.annotations-input-title {
|
|
22949
|
+
font-weight: 600;
|
|
22950
|
+
color: white;
|
|
22951
|
+
font-size: 16px;
|
|
22952
|
+
}
|
|
22953
|
+
.annotations-input-body {
|
|
22954
|
+
padding: 20px;
|
|
22955
|
+
}
|
|
22956
|
+
.annotations-input-textarea {
|
|
22957
|
+
width: 100%;
|
|
22958
|
+
min-height: 100px;
|
|
22959
|
+
padding: 12px;
|
|
22960
|
+
border: 1px solid #dee2e6;
|
|
22961
|
+
border-radius: 8px;
|
|
22962
|
+
font-size: 14px;
|
|
22963
|
+
font-family: inherit;
|
|
22964
|
+
resize: vertical;
|
|
22965
|
+
color: #212529;
|
|
22966
|
+
}
|
|
22967
|
+
.annotations-input-textarea:focus {
|
|
22968
|
+
outline: none;
|
|
22969
|
+
border-color: #6c5ce7;
|
|
22970
|
+
box-shadow: 0 0 0 3px rgba(108, 92, 231, 0.15);
|
|
22971
|
+
}
|
|
22972
|
+
.annotations-input-char-count {
|
|
22973
|
+
font-size: 11px;
|
|
22974
|
+
color: #6c757d;
|
|
22975
|
+
text-align: right;
|
|
22976
|
+
margin-top: 6px;
|
|
22977
|
+
}
|
|
22978
|
+
.annotations-input-actions {
|
|
22979
|
+
display: flex;
|
|
22980
|
+
gap: 12px;
|
|
22981
|
+
padding: 16px 20px;
|
|
22982
|
+
background: #f9fafb;
|
|
22983
|
+
border-top: 1px solid #e5e7eb;
|
|
22984
|
+
justify-content: flex-end;
|
|
22985
|
+
}
|
|
22986
|
+
.annotations-input-btn {
|
|
22987
|
+
padding: 10px 20px;
|
|
22988
|
+
border-radius: 8px;
|
|
22989
|
+
font-size: 14px;
|
|
22990
|
+
font-weight: 500;
|
|
22991
|
+
cursor: pointer;
|
|
22992
|
+
transition: all 0.2s ease;
|
|
22993
|
+
border: none;
|
|
22994
|
+
}
|
|
22995
|
+
.annotations-input-btn--cancel {
|
|
22996
|
+
background: #e5e7eb;
|
|
22997
|
+
color: #374151;
|
|
22998
|
+
}
|
|
22999
|
+
.annotations-input-btn--cancel:hover {
|
|
23000
|
+
background: #d1d5db;
|
|
23001
|
+
}
|
|
23002
|
+
.annotations-input-btn--confirm {
|
|
23003
|
+
background: #6c5ce7;
|
|
23004
|
+
color: white;
|
|
23005
|
+
}
|
|
23006
|
+
.annotations-input-btn--confirm:hover {
|
|
23007
|
+
background: #5b4cdb;
|
|
23008
|
+
}
|
|
23009
|
+
`;
|
|
23010
|
+
document.head.appendChild(style);
|
|
23011
|
+
}
|
|
23012
|
+
document.body.appendChild(overlay);
|
|
23013
|
+
const textarea = overlay.querySelector(".annotations-input-textarea");
|
|
23014
|
+
const charCount = overlay.querySelector("#input-char-count");
|
|
23015
|
+
textarea.focus();
|
|
23016
|
+
textarea.select();
|
|
23017
|
+
textarea.addEventListener("input", () => {
|
|
23018
|
+
charCount.textContent = String(textarea.value.length);
|
|
23019
|
+
});
|
|
23020
|
+
const cleanup = (result) => {
|
|
23021
|
+
overlay.remove();
|
|
23022
|
+
resolve(result);
|
|
23023
|
+
};
|
|
23024
|
+
overlay.querySelector('[data-action="cancel"]')?.addEventListener("click", () => cleanup(null));
|
|
23025
|
+
overlay.querySelector('[data-action="confirm"]')?.addEventListener("click", () => {
|
|
23026
|
+
const value = textarea.value.trim();
|
|
23027
|
+
cleanup(value || null);
|
|
23028
|
+
});
|
|
23029
|
+
overlay.addEventListener("click", (e) => {
|
|
23030
|
+
if (e.target === overlay) cleanup(null);
|
|
23031
|
+
});
|
|
23032
|
+
textarea.addEventListener("keydown", (e) => {
|
|
23033
|
+
if (e.key === "Enter" && e.ctrlKey) {
|
|
23034
|
+
const value = textarea.value.trim();
|
|
23035
|
+
cleanup(value || null);
|
|
23036
|
+
}
|
|
23037
|
+
if (e.key === "Escape") {
|
|
23038
|
+
cleanup(null);
|
|
23039
|
+
}
|
|
23040
|
+
});
|
|
23041
|
+
});
|
|
23042
|
+
}
|
|
23043
|
+
async showEditModal(id) {
|
|
22680
23044
|
const annotation = this.annotations.find((a) => a.id === id);
|
|
22681
23045
|
if (!annotation) return;
|
|
22682
|
-
const newText =
|
|
22683
|
-
|
|
22684
|
-
|
|
23046
|
+
const newText = await this.showInputModal(
|
|
23047
|
+
"Editar Anota\xE7\xE3o",
|
|
23048
|
+
"Digite o novo texto da anota\xE7\xE3o...",
|
|
23049
|
+
annotation.text
|
|
23050
|
+
);
|
|
23051
|
+
if (newText && newText !== annotation.text) {
|
|
23052
|
+
await this.editAnnotation(id, { text: newText });
|
|
23053
|
+
this.render();
|
|
22685
23054
|
}
|
|
22686
23055
|
}
|
|
22687
23056
|
// ============================================
|
|
@@ -22775,7 +23144,7 @@
|
|
|
22775
23144
|
overlay.querySelector(".annotation-detail__close")?.addEventListener("click", () => overlay.remove());
|
|
22776
23145
|
overlay.querySelector('[data-action="close"]')?.addEventListener("click", () => overlay.remove());
|
|
22777
23146
|
overlay.querySelector('[data-action="archive"]')?.addEventListener("click", async () => {
|
|
22778
|
-
if (
|
|
23147
|
+
if (await this.showConfirmation("Tem certeza que deseja arquivar esta anota\xE7\xE3o?", "Arquivar Anota\xE7\xE3o")) {
|
|
22779
23148
|
overlay.remove();
|
|
22780
23149
|
await this.archiveAnnotation(id);
|
|
22781
23150
|
}
|
|
@@ -27340,7 +27709,7 @@
|
|
|
27340
27709
|
const defaultDateRange = getTodaySoFar();
|
|
27341
27710
|
const startTs = params.startDate ? new Date(params.startDate).getTime() : defaultDateRange.startTs;
|
|
27342
27711
|
const endTs = params.endDate ? new Date(params.endDate).getTime() : defaultDateRange.endTs;
|
|
27343
|
-
const
|
|
27712
|
+
const state2 = {
|
|
27344
27713
|
token: params.token,
|
|
27345
27714
|
deviceId: params.deviceId,
|
|
27346
27715
|
label: params.label || "Sensor de Temperatura",
|
|
@@ -27363,58 +27732,58 @@
|
|
|
27363
27732
|
};
|
|
27364
27733
|
const savedGranularity = localStorage.getItem("myio-temp-modal-granularity");
|
|
27365
27734
|
const savedTheme = localStorage.getItem("myio-temp-modal-theme");
|
|
27366
|
-
if (savedGranularity)
|
|
27367
|
-
if (savedTheme)
|
|
27735
|
+
if (savedGranularity) state2.granularity = savedGranularity;
|
|
27736
|
+
if (savedTheme) state2.theme = savedTheme;
|
|
27368
27737
|
const modalContainer = document.createElement("div");
|
|
27369
27738
|
modalContainer.id = modalId;
|
|
27370
27739
|
document.body.appendChild(modalContainer);
|
|
27371
|
-
renderModal(modalContainer,
|
|
27740
|
+
renderModal(modalContainer, state2, modalId);
|
|
27372
27741
|
try {
|
|
27373
|
-
|
|
27374
|
-
|
|
27375
|
-
|
|
27376
|
-
renderModal(modalContainer,
|
|
27377
|
-
drawChart(modalId,
|
|
27742
|
+
state2.data = await fetchTemperatureData(state2.token, state2.deviceId, state2.startTs, state2.endTs);
|
|
27743
|
+
state2.stats = calculateStats(state2.data, state2.clampRange);
|
|
27744
|
+
state2.isLoading = false;
|
|
27745
|
+
renderModal(modalContainer, state2, modalId);
|
|
27746
|
+
drawChart(modalId, state2);
|
|
27378
27747
|
} catch (error) {
|
|
27379
27748
|
console.error("[TemperatureModal] Error fetching data:", error);
|
|
27380
|
-
|
|
27381
|
-
renderModal(modalContainer,
|
|
27749
|
+
state2.isLoading = false;
|
|
27750
|
+
renderModal(modalContainer, state2, modalId, error);
|
|
27382
27751
|
}
|
|
27383
|
-
await setupEventListeners(modalContainer,
|
|
27752
|
+
await setupEventListeners(modalContainer, state2, modalId, params.onClose);
|
|
27384
27753
|
return {
|
|
27385
27754
|
destroy: () => {
|
|
27386
27755
|
modalContainer.remove();
|
|
27387
27756
|
params.onClose?.();
|
|
27388
27757
|
},
|
|
27389
27758
|
updateData: async (startDate, endDate, granularity) => {
|
|
27390
|
-
|
|
27391
|
-
|
|
27392
|
-
if (granularity)
|
|
27393
|
-
|
|
27394
|
-
renderModal(modalContainer,
|
|
27759
|
+
state2.startTs = new Date(startDate).getTime();
|
|
27760
|
+
state2.endTs = new Date(endDate).getTime();
|
|
27761
|
+
if (granularity) state2.granularity = granularity;
|
|
27762
|
+
state2.isLoading = true;
|
|
27763
|
+
renderModal(modalContainer, state2, modalId);
|
|
27395
27764
|
try {
|
|
27396
|
-
|
|
27397
|
-
|
|
27398
|
-
|
|
27399
|
-
renderModal(modalContainer,
|
|
27400
|
-
drawChart(modalId,
|
|
27765
|
+
state2.data = await fetchTemperatureData(state2.token, state2.deviceId, state2.startTs, state2.endTs);
|
|
27766
|
+
state2.stats = calculateStats(state2.data, state2.clampRange);
|
|
27767
|
+
state2.isLoading = false;
|
|
27768
|
+
renderModal(modalContainer, state2, modalId);
|
|
27769
|
+
drawChart(modalId, state2);
|
|
27401
27770
|
} catch (error) {
|
|
27402
27771
|
console.error("[TemperatureModal] Error updating data:", error);
|
|
27403
|
-
|
|
27404
|
-
renderModal(modalContainer,
|
|
27772
|
+
state2.isLoading = false;
|
|
27773
|
+
renderModal(modalContainer, state2, modalId, error);
|
|
27405
27774
|
}
|
|
27406
27775
|
}
|
|
27407
27776
|
};
|
|
27408
27777
|
}
|
|
27409
|
-
function renderModal(container,
|
|
27410
|
-
const colors = getThemeColors(
|
|
27411
|
-
const startDateStr = new Date(
|
|
27412
|
-
const endDateStr = new Date(
|
|
27413
|
-
const statusText =
|
|
27414
|
-
const statusColor =
|
|
27415
|
-
const rangeText =
|
|
27416
|
-
new Date(
|
|
27417
|
-
new Date(
|
|
27778
|
+
function renderModal(container, state2, modalId, error) {
|
|
27779
|
+
const colors = getThemeColors(state2.theme);
|
|
27780
|
+
const startDateStr = new Date(state2.startTs).toLocaleDateString(state2.locale);
|
|
27781
|
+
const endDateStr = new Date(state2.endTs).toLocaleDateString(state2.locale);
|
|
27782
|
+
const statusText = state2.temperatureStatus === "ok" ? "Dentro da faixa" : state2.temperatureStatus === "above" ? "Acima do limite" : state2.temperatureStatus === "below" ? "Abaixo do limite" : "N/A";
|
|
27783
|
+
const statusColor = state2.temperatureStatus === "ok" ? colors.success : state2.temperatureStatus === "above" ? colors.danger : state2.temperatureStatus === "below" ? colors.primary : colors.textMuted;
|
|
27784
|
+
const rangeText = state2.temperatureMin !== null && state2.temperatureMax !== null ? `${state2.temperatureMin}\xB0C - ${state2.temperatureMax}\xB0C` : "N\xE3o definida";
|
|
27785
|
+
new Date(state2.startTs).toISOString().slice(0, 16);
|
|
27786
|
+
new Date(state2.endTs).toISOString().slice(0, 16);
|
|
27418
27787
|
const isMaximized = container.__isMaximized || false;
|
|
27419
27788
|
const contentMaxWidth = isMaximized ? "100%" : "900px";
|
|
27420
27789
|
const contentMaxHeight = isMaximized ? "100vh" : "95vh";
|
|
@@ -27441,7 +27810,7 @@
|
|
|
27441
27810
|
min-height: 20px;
|
|
27442
27811
|
">
|
|
27443
27812
|
<h2 style="margin: 6px; font-size: 18px; font-weight: 600; color: white; line-height: 2;">
|
|
27444
|
-
\u{1F321}\uFE0F ${
|
|
27813
|
+
\u{1F321}\uFE0F ${state2.label} - Hist\xF3rico de Temperatura
|
|
27445
27814
|
</h2>
|
|
27446
27815
|
<div style="display: flex; gap: 4px; align-items: center;">
|
|
27447
27816
|
<!-- Theme Toggle -->
|
|
@@ -27449,7 +27818,7 @@
|
|
|
27449
27818
|
background: none; border: none; font-size: 16px; cursor: pointer;
|
|
27450
27819
|
padding: 4px 8px; border-radius: 6px; color: rgba(255,255,255,0.8);
|
|
27451
27820
|
transition: background-color 0.2s;
|
|
27452
|
-
">${
|
|
27821
|
+
">${state2.theme === "dark" ? "\u2600\uFE0F" : "\u{1F319}"}</button>
|
|
27453
27822
|
<!-- Maximize Button -->
|
|
27454
27823
|
<button id="${modalId}-maximize" title="${isMaximized ? "Restaurar" : "Maximizar"}" style="
|
|
27455
27824
|
background: none; border: none; font-size: 16px; cursor: pointer;
|
|
@@ -27471,7 +27840,7 @@
|
|
|
27471
27840
|
<!-- Controls Row -->
|
|
27472
27841
|
<div style="
|
|
27473
27842
|
display: flex; gap: 16px; flex-wrap: wrap; align-items: flex-end;
|
|
27474
|
-
margin-bottom: 16px; padding: 16px; background: ${
|
|
27843
|
+
margin-bottom: 16px; padding: 16px; background: ${state2.theme === "dark" ? "rgba(255,255,255,0.05)" : "#f7f7f7"};
|
|
27475
27844
|
border-radius: 6px; border: 1px solid ${colors.border};
|
|
27476
27845
|
">
|
|
27477
27846
|
<!-- Granularity Select -->
|
|
@@ -27484,8 +27853,8 @@
|
|
|
27484
27853
|
font-size: 14px; color: ${colors.text}; background: ${colors.surface};
|
|
27485
27854
|
cursor: pointer; min-width: 130px;
|
|
27486
27855
|
">
|
|
27487
|
-
<option value="hour" ${
|
|
27488
|
-
<option value="day" ${
|
|
27856
|
+
<option value="hour" ${state2.granularity === "hour" ? "selected" : ""}>Hora (30 min)</option>
|
|
27857
|
+
<option value="day" ${state2.granularity === "day" ? "selected" : ""}>Dia (m\xE9dia)</option>
|
|
27489
27858
|
</select>
|
|
27490
27859
|
</div>
|
|
27491
27860
|
<!-- Day Period Filter (Multiselect) -->
|
|
@@ -27499,7 +27868,7 @@
|
|
|
27499
27868
|
cursor: pointer; min-width: 180px; text-align: left;
|
|
27500
27869
|
display: flex; align-items: center; justify-content: space-between; gap: 8px;
|
|
27501
27870
|
">
|
|
27502
|
-
<span>${getSelectedPeriodsLabel(
|
|
27871
|
+
<span>${getSelectedPeriodsLabel(state2.selectedPeriods)}</span>
|
|
27503
27872
|
<span style="font-size: 10px;">\u25BC</span>
|
|
27504
27873
|
</button>
|
|
27505
27874
|
<div id="${modalId}-period-dropdown" style="
|
|
@@ -27512,12 +27881,12 @@
|
|
|
27512
27881
|
<label style="
|
|
27513
27882
|
display: flex; align-items: center; gap: 8px; padding: 8px 12px;
|
|
27514
27883
|
cursor: pointer; font-size: 13px; color: ${colors.text};
|
|
27515
|
-
" onmouseover="this.style.background='${
|
|
27884
|
+
" onmouseover="this.style.background='${state2.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"}'"
|
|
27516
27885
|
onmouseout="this.style.background='transparent'">
|
|
27517
27886
|
<input type="checkbox"
|
|
27518
27887
|
name="${modalId}-period"
|
|
27519
27888
|
value="${period.id}"
|
|
27520
|
-
${
|
|
27889
|
+
${state2.selectedPeriods.includes(period.id) ? "checked" : ""}
|
|
27521
27890
|
style="width: 16px; height: 16px; cursor: pointer; accent-color: #3e1a7d;">
|
|
27522
27891
|
${period.label}
|
|
27523
27892
|
</label>
|
|
@@ -27525,13 +27894,13 @@
|
|
|
27525
27894
|
<div style="border-top: 1px solid ${colors.border}; margin-top: 8px; padding-top: 8px;">
|
|
27526
27895
|
<button id="${modalId}-period-select-all" type="button" style="
|
|
27527
27896
|
width: calc(100% - 16px); margin: 0 8px 4px; padding: 6px;
|
|
27528
|
-
background: ${
|
|
27897
|
+
background: ${state2.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"};
|
|
27529
27898
|
border: none; border-radius: 4px; cursor: pointer;
|
|
27530
27899
|
font-size: 12px; color: ${colors.text};
|
|
27531
27900
|
">Selecionar Todos</button>
|
|
27532
27901
|
<button id="${modalId}-period-clear" type="button" style="
|
|
27533
27902
|
width: calc(100% - 16px); margin: 0 8px; padding: 6px;
|
|
27534
|
-
background: ${
|
|
27903
|
+
background: ${state2.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"};
|
|
27535
27904
|
border: none; border-radius: 4px; cursor: pointer;
|
|
27536
27905
|
font-size: 12px; color: ${colors.text};
|
|
27537
27906
|
">Limpar Sele\xE7\xE3o</button>
|
|
@@ -27556,8 +27925,8 @@
|
|
|
27556
27925
|
font-size: 14px; font-weight: 500; height: 38px;
|
|
27557
27926
|
display: flex; align-items: center; gap: 8px;
|
|
27558
27927
|
font-family: 'Roboto', Arial, sans-serif;
|
|
27559
|
-
" ${
|
|
27560
|
-
${
|
|
27928
|
+
" ${state2.isLoading ? "disabled" : ""}>
|
|
27929
|
+
${state2.isLoading ? '<span style="animation: spin 1s linear infinite; display: inline-block;">\u21BB</span> Carregando...' : "Carregar"}
|
|
27561
27930
|
</button>
|
|
27562
27931
|
</div>
|
|
27563
27932
|
|
|
@@ -27568,40 +27937,40 @@
|
|
|
27568
27937
|
">
|
|
27569
27938
|
<!-- Current Temperature -->
|
|
27570
27939
|
<div style="
|
|
27571
|
-
padding: 16px; background: ${
|
|
27940
|
+
padding: 16px; background: ${state2.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
|
|
27572
27941
|
border-radius: 12px; border: 1px solid ${colors.border};
|
|
27573
27942
|
">
|
|
27574
27943
|
<span style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500;">Temperatura Atual</span>
|
|
27575
27944
|
<div style="font-weight: 700; font-size: 24px; color: ${statusColor}; margin-top: 4px;">
|
|
27576
|
-
${
|
|
27945
|
+
${state2.currentTemperature !== null ? formatTemperature(state2.currentTemperature) : "N/A"}
|
|
27577
27946
|
</div>
|
|
27578
27947
|
<div style="font-size: 11px; color: ${statusColor}; margin-top: 2px;">${statusText}</div>
|
|
27579
27948
|
</div>
|
|
27580
27949
|
<!-- Average -->
|
|
27581
27950
|
<div style="
|
|
27582
|
-
padding: 16px; background: ${
|
|
27951
|
+
padding: 16px; background: ${state2.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
|
|
27583
27952
|
border-radius: 12px; border: 1px solid ${colors.border};
|
|
27584
27953
|
">
|
|
27585
27954
|
<span style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500;">M\xE9dia do Per\xEDodo</span>
|
|
27586
27955
|
<div id="${modalId}-avg" style="font-weight: 600; font-size: 20px; color: ${colors.text}; margin-top: 4px;">
|
|
27587
|
-
${
|
|
27956
|
+
${state2.stats.count > 0 ? formatTemperature(state2.stats.avg) : "N/A"}
|
|
27588
27957
|
</div>
|
|
27589
27958
|
<div style="font-size: 11px; color: ${colors.textMuted}; margin-top: 2px;">${startDateStr} - ${endDateStr}</div>
|
|
27590
27959
|
</div>
|
|
27591
27960
|
<!-- Min/Max -->
|
|
27592
27961
|
<div style="
|
|
27593
|
-
padding: 16px; background: ${
|
|
27962
|
+
padding: 16px; background: ${state2.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
|
|
27594
27963
|
border-radius: 12px; border: 1px solid ${colors.border};
|
|
27595
27964
|
">
|
|
27596
27965
|
<span style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500;">Min / Max</span>
|
|
27597
27966
|
<div id="${modalId}-minmax" style="font-weight: 600; font-size: 20px; color: ${colors.text}; margin-top: 4px;">
|
|
27598
|
-
${
|
|
27967
|
+
${state2.stats.count > 0 ? `${formatTemperature(state2.stats.min)} / ${formatTemperature(state2.stats.max)}` : "N/A"}
|
|
27599
27968
|
</div>
|
|
27600
|
-
<div style="font-size: 11px; color: ${colors.textMuted}; margin-top: 2px;">${
|
|
27969
|
+
<div style="font-size: 11px; color: ${colors.textMuted}; margin-top: 2px;">${state2.stats.count} leituras</div>
|
|
27601
27970
|
</div>
|
|
27602
27971
|
<!-- Ideal Range -->
|
|
27603
27972
|
<div style="
|
|
27604
|
-
padding: 16px; background: ${
|
|
27973
|
+
padding: 16px; background: ${state2.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
|
|
27605
27974
|
border-radius: 12px; border: 1px solid ${colors.border};
|
|
27606
27975
|
">
|
|
27607
27976
|
<span style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500;">Faixa Ideal</span>
|
|
@@ -27617,18 +27986,18 @@
|
|
|
27617
27986
|
Hist\xF3rico de Temperatura
|
|
27618
27987
|
</h3>
|
|
27619
27988
|
<div id="${modalId}-chart" style="
|
|
27620
|
-
height: 320px; background: ${
|
|
27989
|
+
height: 320px; background: ${state2.theme === "dark" ? "rgba(255,255,255,0.03)" : "#fafafa"};
|
|
27621
27990
|
border-radius: 12px; display: flex; justify-content: center; align-items: center;
|
|
27622
27991
|
border: 1px solid ${colors.border}; position: relative;
|
|
27623
27992
|
">
|
|
27624
|
-
${
|
|
27993
|
+
${state2.isLoading ? `<div style="text-align: center; color: ${colors.textMuted};">
|
|
27625
27994
|
<div style="animation: spin 1s linear infinite; font-size: 32px; margin-bottom: 8px;">\u21BB</div>
|
|
27626
27995
|
<div>Carregando dados...</div>
|
|
27627
27996
|
</div>` : error ? `<div style="text-align: center; color: ${colors.danger};">
|
|
27628
27997
|
<div style="font-size: 32px; margin-bottom: 8px;">\u26A0\uFE0F</div>
|
|
27629
27998
|
<div>Erro ao carregar dados</div>
|
|
27630
27999
|
<div style="font-size: 12px; margin-top: 4px;">${error.message}</div>
|
|
27631
|
-
</div>` :
|
|
28000
|
+
</div>` : state2.data.length === 0 ? `<div style="text-align: center; color: ${colors.textMuted};">
|
|
27632
28001
|
<div style="font-size: 32px; margin-bottom: 8px;">\u{1F4ED}</div>
|
|
27633
28002
|
<div>Sem dados para o per\xEDodo selecionado</div>
|
|
27634
28003
|
</div>` : `<canvas id="${modalId}-canvas" style="width: 100%; height: 100%;"></canvas>`}
|
|
@@ -27638,12 +28007,12 @@
|
|
|
27638
28007
|
<!-- Actions -->
|
|
27639
28008
|
<div style="display: flex; justify-content: flex-end; gap: 12px;">
|
|
27640
28009
|
<button id="${modalId}-export" style="
|
|
27641
|
-
background: ${
|
|
28010
|
+
background: ${state2.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f7f7f7"};
|
|
27642
28011
|
color: ${colors.text}; border: 1px solid ${colors.border};
|
|
27643
28012
|
padding: 8px 16px; border-radius: 6px; cursor: pointer;
|
|
27644
28013
|
font-size: 14px; display: flex; align-items: center; gap: 8px;
|
|
27645
28014
|
font-family: 'Roboto', Arial, sans-serif;
|
|
27646
|
-
" ${
|
|
28015
|
+
" ${state2.data.length === 0 ? "disabled" : ""}>
|
|
27647
28016
|
\u{1F4E5} Exportar CSV
|
|
27648
28017
|
</button>
|
|
27649
28018
|
<button id="${modalId}-close-btn" style="
|
|
@@ -27694,14 +28063,14 @@
|
|
|
27694
28063
|
</style>
|
|
27695
28064
|
`;
|
|
27696
28065
|
}
|
|
27697
|
-
function drawChart(modalId,
|
|
28066
|
+
function drawChart(modalId, state2) {
|
|
27698
28067
|
const chartContainer = document.getElementById(`${modalId}-chart`);
|
|
27699
28068
|
const canvas = document.getElementById(`${modalId}-canvas`);
|
|
27700
|
-
if (!chartContainer || !canvas ||
|
|
28069
|
+
if (!chartContainer || !canvas || state2.data.length === 0) return;
|
|
27701
28070
|
const ctx = canvas.getContext("2d");
|
|
27702
28071
|
if (!ctx) return;
|
|
27703
|
-
const colors = getThemeColors(
|
|
27704
|
-
const filteredData = filterByDayPeriods(
|
|
28072
|
+
const colors = getThemeColors(state2.theme);
|
|
28073
|
+
const filteredData = filterByDayPeriods(state2.data, state2.selectedPeriods);
|
|
27705
28074
|
if (filteredData.length === 0) {
|
|
27706
28075
|
canvas.width = chartContainer.clientWidth;
|
|
27707
28076
|
canvas.height = chartContainer.clientHeight;
|
|
@@ -27712,14 +28081,14 @@
|
|
|
27712
28081
|
return;
|
|
27713
28082
|
}
|
|
27714
28083
|
let chartData;
|
|
27715
|
-
if (
|
|
28084
|
+
if (state2.granularity === "hour") {
|
|
27716
28085
|
const interpolated = interpolateTemperature(filteredData, {
|
|
27717
28086
|
intervalMinutes: 30,
|
|
27718
|
-
startTs:
|
|
27719
|
-
endTs:
|
|
27720
|
-
clampRange:
|
|
28087
|
+
startTs: state2.startTs,
|
|
28088
|
+
endTs: state2.endTs,
|
|
28089
|
+
clampRange: state2.clampRange
|
|
27721
28090
|
});
|
|
27722
|
-
const filteredInterpolated = filterByDayPeriods(interpolated,
|
|
28091
|
+
const filteredInterpolated = filterByDayPeriods(interpolated, state2.selectedPeriods);
|
|
27723
28092
|
chartData = filteredInterpolated.map((item) => ({
|
|
27724
28093
|
x: item.ts,
|
|
27725
28094
|
y: Number(item.value),
|
|
@@ -27727,7 +28096,7 @@
|
|
|
27727
28096
|
screenY: 0
|
|
27728
28097
|
}));
|
|
27729
28098
|
} else {
|
|
27730
|
-
const daily = aggregateByDay(filteredData,
|
|
28099
|
+
const daily = aggregateByDay(filteredData, state2.clampRange);
|
|
27731
28100
|
chartData = daily.map((item) => ({
|
|
27732
28101
|
x: item.dateTs,
|
|
27733
28102
|
y: item.avg,
|
|
@@ -27745,12 +28114,12 @@
|
|
|
27745
28114
|
const paddingRight = 20;
|
|
27746
28115
|
const paddingTop = 20;
|
|
27747
28116
|
const paddingBottom = 55;
|
|
27748
|
-
const isPeriodsFiltered =
|
|
28117
|
+
const isPeriodsFiltered = state2.selectedPeriods.length < 4 && state2.selectedPeriods.length > 0;
|
|
27749
28118
|
const values = chartData.map((d) => d.y);
|
|
27750
28119
|
const dataMin = Math.min(...values);
|
|
27751
28120
|
const dataMax = Math.max(...values);
|
|
27752
|
-
const thresholdMin =
|
|
27753
|
-
const thresholdMax =
|
|
28121
|
+
const thresholdMin = state2.temperatureMin !== null ? state2.temperatureMin : dataMin;
|
|
28122
|
+
const thresholdMax = state2.temperatureMax !== null ? state2.temperatureMax : dataMax;
|
|
27754
28123
|
const minY = Math.min(dataMin, thresholdMin) - 1;
|
|
27755
28124
|
const maxY = Math.max(dataMax, thresholdMax) + 1;
|
|
27756
28125
|
const chartWidth = width - paddingLeft - paddingRight;
|
|
@@ -27782,9 +28151,9 @@
|
|
|
27782
28151
|
ctx.lineTo(width - paddingRight, y);
|
|
27783
28152
|
ctx.stroke();
|
|
27784
28153
|
}
|
|
27785
|
-
if (
|
|
27786
|
-
const rangeMinY = height - paddingBottom - (
|
|
27787
|
-
const rangeMaxY = height - paddingBottom - (
|
|
28154
|
+
if (state2.temperatureMin !== null && state2.temperatureMax !== null) {
|
|
28155
|
+
const rangeMinY = height - paddingBottom - (state2.temperatureMin - minY) * scaleY;
|
|
28156
|
+
const rangeMaxY = height - paddingBottom - (state2.temperatureMax - minY) * scaleY;
|
|
27788
28157
|
ctx.fillStyle = "rgba(76, 175, 80, 0.1)";
|
|
27789
28158
|
ctx.fillRect(paddingLeft, rangeMaxY, chartWidth, rangeMinY - rangeMaxY);
|
|
27790
28159
|
ctx.strokeStyle = colors.success;
|
|
@@ -27826,10 +28195,10 @@
|
|
|
27826
28195
|
const point = chartData[i];
|
|
27827
28196
|
const date = new Date(point.x);
|
|
27828
28197
|
let label;
|
|
27829
|
-
if (
|
|
27830
|
-
label = date.toLocaleTimeString(
|
|
28198
|
+
if (state2.granularity === "hour") {
|
|
28199
|
+
label = date.toLocaleTimeString(state2.locale, { hour: "2-digit", minute: "2-digit" });
|
|
27831
28200
|
} else {
|
|
27832
|
-
label = date.toLocaleDateString(
|
|
28201
|
+
label = date.toLocaleDateString(state2.locale, { day: "2-digit", month: "2-digit" });
|
|
27833
28202
|
}
|
|
27834
28203
|
ctx.strokeStyle = colors.chartGrid;
|
|
27835
28204
|
ctx.lineWidth = 1;
|
|
@@ -27847,16 +28216,16 @@
|
|
|
27847
28216
|
ctx.lineTo(paddingLeft, height - paddingBottom);
|
|
27848
28217
|
ctx.lineTo(width - paddingRight, height - paddingBottom);
|
|
27849
28218
|
ctx.stroke();
|
|
27850
|
-
setupChartTooltip(canvas, chartContainer, chartData,
|
|
28219
|
+
setupChartTooltip(canvas, chartContainer, chartData, state2, colors);
|
|
27851
28220
|
}
|
|
27852
|
-
function setupChartTooltip(canvas, container, chartData,
|
|
28221
|
+
function setupChartTooltip(canvas, container, chartData, state2, colors) {
|
|
27853
28222
|
const existingTooltip = container.querySelector(".myio-chart-tooltip");
|
|
27854
28223
|
if (existingTooltip) existingTooltip.remove();
|
|
27855
28224
|
const tooltip = document.createElement("div");
|
|
27856
28225
|
tooltip.className = "myio-chart-tooltip";
|
|
27857
28226
|
tooltip.style.cssText = `
|
|
27858
28227
|
position: absolute;
|
|
27859
|
-
background: ${
|
|
28228
|
+
background: ${state2.theme === "dark" ? "rgba(30, 30, 40, 0.95)" : "rgba(255, 255, 255, 0.98)"};
|
|
27860
28229
|
border: 1px solid ${colors.border};
|
|
27861
28230
|
border-radius: 8px;
|
|
27862
28231
|
padding: 10px 14px;
|
|
@@ -27893,17 +28262,17 @@
|
|
|
27893
28262
|
if (point) {
|
|
27894
28263
|
const date = new Date(point.x);
|
|
27895
28264
|
let dateStr;
|
|
27896
|
-
if (
|
|
27897
|
-
dateStr = date.toLocaleDateString(
|
|
28265
|
+
if (state2.granularity === "hour") {
|
|
28266
|
+
dateStr = date.toLocaleDateString(state2.locale, {
|
|
27898
28267
|
day: "2-digit",
|
|
27899
28268
|
month: "2-digit",
|
|
27900
28269
|
year: "numeric"
|
|
27901
|
-
}) + " " + date.toLocaleTimeString(
|
|
28270
|
+
}) + " " + date.toLocaleTimeString(state2.locale, {
|
|
27902
28271
|
hour: "2-digit",
|
|
27903
28272
|
minute: "2-digit"
|
|
27904
28273
|
});
|
|
27905
28274
|
} else {
|
|
27906
|
-
dateStr = date.toLocaleDateString(
|
|
28275
|
+
dateStr = date.toLocaleDateString(state2.locale, {
|
|
27907
28276
|
day: "2-digit",
|
|
27908
28277
|
month: "2-digit",
|
|
27909
28278
|
year: "numeric"
|
|
@@ -27941,7 +28310,7 @@
|
|
|
27941
28310
|
canvas.style.cursor = "default";
|
|
27942
28311
|
});
|
|
27943
28312
|
}
|
|
27944
|
-
async function setupEventListeners(container,
|
|
28313
|
+
async function setupEventListeners(container, state2, modalId, onClose) {
|
|
27945
28314
|
const closeModal = () => {
|
|
27946
28315
|
container.remove();
|
|
27947
28316
|
onClose?.();
|
|
@@ -27952,19 +28321,19 @@
|
|
|
27952
28321
|
document.getElementById(`${modalId}-close`)?.addEventListener("click", closeModal);
|
|
27953
28322
|
document.getElementById(`${modalId}-close-btn`)?.addEventListener("click", closeModal);
|
|
27954
28323
|
const dateRangeInput = document.getElementById(`${modalId}-date-range`);
|
|
27955
|
-
if (dateRangeInput && !
|
|
28324
|
+
if (dateRangeInput && !state2.dateRangePicker) {
|
|
27956
28325
|
try {
|
|
27957
|
-
|
|
27958
|
-
presetStart: new Date(
|
|
27959
|
-
presetEnd: new Date(
|
|
28326
|
+
state2.dateRangePicker = await createDateRangePicker2(dateRangeInput, {
|
|
28327
|
+
presetStart: new Date(state2.startTs).toISOString(),
|
|
28328
|
+
presetEnd: new Date(state2.endTs).toISOString(),
|
|
27960
28329
|
includeTime: true,
|
|
27961
28330
|
timePrecision: "minute",
|
|
27962
28331
|
maxRangeDays: 90,
|
|
27963
|
-
locale:
|
|
28332
|
+
locale: state2.locale,
|
|
27964
28333
|
parentEl: container.querySelector(".myio-temp-modal-content"),
|
|
27965
28334
|
onApply: (result) => {
|
|
27966
|
-
|
|
27967
|
-
|
|
28335
|
+
state2.startTs = new Date(result.startISO).getTime();
|
|
28336
|
+
state2.endTs = new Date(result.endISO).getTime();
|
|
27968
28337
|
console.log("[TemperatureModal] Date range applied:", result);
|
|
27969
28338
|
}
|
|
27970
28339
|
});
|
|
@@ -27973,19 +28342,19 @@
|
|
|
27973
28342
|
}
|
|
27974
28343
|
}
|
|
27975
28344
|
document.getElementById(`${modalId}-theme-toggle`)?.addEventListener("click", async () => {
|
|
27976
|
-
|
|
27977
|
-
localStorage.setItem("myio-temp-modal-theme",
|
|
27978
|
-
|
|
27979
|
-
renderModal(container,
|
|
27980
|
-
if (
|
|
27981
|
-
await setupEventListeners(container,
|
|
28345
|
+
state2.theme = state2.theme === "dark" ? "light" : "dark";
|
|
28346
|
+
localStorage.setItem("myio-temp-modal-theme", state2.theme);
|
|
28347
|
+
state2.dateRangePicker = null;
|
|
28348
|
+
renderModal(container, state2, modalId);
|
|
28349
|
+
if (state2.data.length > 0) drawChart(modalId, state2);
|
|
28350
|
+
await setupEventListeners(container, state2, modalId, onClose);
|
|
27982
28351
|
});
|
|
27983
28352
|
document.getElementById(`${modalId}-maximize`)?.addEventListener("click", async () => {
|
|
27984
28353
|
container.__isMaximized = !container.__isMaximized;
|
|
27985
|
-
|
|
27986
|
-
renderModal(container,
|
|
27987
|
-
if (
|
|
27988
|
-
await setupEventListeners(container,
|
|
28354
|
+
state2.dateRangePicker = null;
|
|
28355
|
+
renderModal(container, state2, modalId);
|
|
28356
|
+
if (state2.data.length > 0) drawChart(modalId, state2);
|
|
28357
|
+
await setupEventListeners(container, state2, modalId, onClose);
|
|
27989
28358
|
});
|
|
27990
28359
|
const periodBtn = document.getElementById(`${modalId}-period-btn`);
|
|
27991
28360
|
const periodDropdown = document.getElementById(`${modalId}-period-dropdown`);
|
|
@@ -28004,71 +28373,71 @@
|
|
|
28004
28373
|
periodCheckboxes.forEach((checkbox) => {
|
|
28005
28374
|
checkbox.addEventListener("change", () => {
|
|
28006
28375
|
const checked = Array.from(periodCheckboxes).filter((cb) => cb.checked).map((cb) => cb.value);
|
|
28007
|
-
|
|
28376
|
+
state2.selectedPeriods = checked;
|
|
28008
28377
|
const btnLabel = periodBtn?.querySelector("span:first-child");
|
|
28009
28378
|
if (btnLabel) {
|
|
28010
|
-
btnLabel.textContent = getSelectedPeriodsLabel(
|
|
28379
|
+
btnLabel.textContent = getSelectedPeriodsLabel(state2.selectedPeriods);
|
|
28011
28380
|
}
|
|
28012
|
-
if (
|
|
28381
|
+
if (state2.data.length > 0) drawChart(modalId, state2);
|
|
28013
28382
|
});
|
|
28014
28383
|
});
|
|
28015
28384
|
document.getElementById(`${modalId}-period-select-all`)?.addEventListener("click", () => {
|
|
28016
28385
|
periodCheckboxes.forEach((cb) => {
|
|
28017
28386
|
cb.checked = true;
|
|
28018
28387
|
});
|
|
28019
|
-
|
|
28388
|
+
state2.selectedPeriods = ["madrugada", "manha", "tarde", "noite"];
|
|
28020
28389
|
const btnLabel = periodBtn?.querySelector("span:first-child");
|
|
28021
28390
|
if (btnLabel) {
|
|
28022
|
-
btnLabel.textContent = getSelectedPeriodsLabel(
|
|
28391
|
+
btnLabel.textContent = getSelectedPeriodsLabel(state2.selectedPeriods);
|
|
28023
28392
|
}
|
|
28024
|
-
if (
|
|
28393
|
+
if (state2.data.length > 0) drawChart(modalId, state2);
|
|
28025
28394
|
});
|
|
28026
28395
|
document.getElementById(`${modalId}-period-clear`)?.addEventListener("click", () => {
|
|
28027
28396
|
periodCheckboxes.forEach((cb) => {
|
|
28028
28397
|
cb.checked = false;
|
|
28029
28398
|
});
|
|
28030
|
-
|
|
28399
|
+
state2.selectedPeriods = [];
|
|
28031
28400
|
const btnLabel = periodBtn?.querySelector("span:first-child");
|
|
28032
28401
|
if (btnLabel) {
|
|
28033
|
-
btnLabel.textContent = getSelectedPeriodsLabel(
|
|
28402
|
+
btnLabel.textContent = getSelectedPeriodsLabel(state2.selectedPeriods);
|
|
28034
28403
|
}
|
|
28035
|
-
if (
|
|
28404
|
+
if (state2.data.length > 0) drawChart(modalId, state2);
|
|
28036
28405
|
});
|
|
28037
28406
|
document.getElementById(`${modalId}-granularity`)?.addEventListener("change", (e) => {
|
|
28038
|
-
|
|
28039
|
-
localStorage.setItem("myio-temp-modal-granularity",
|
|
28040
|
-
if (
|
|
28407
|
+
state2.granularity = e.target.value;
|
|
28408
|
+
localStorage.setItem("myio-temp-modal-granularity", state2.granularity);
|
|
28409
|
+
if (state2.data.length > 0) drawChart(modalId, state2);
|
|
28041
28410
|
});
|
|
28042
28411
|
document.getElementById(`${modalId}-query`)?.addEventListener("click", async () => {
|
|
28043
|
-
if (
|
|
28412
|
+
if (state2.startTs >= state2.endTs) {
|
|
28044
28413
|
alert("Por favor, selecione um per\xEDodo v\xE1lido");
|
|
28045
28414
|
return;
|
|
28046
28415
|
}
|
|
28047
|
-
|
|
28048
|
-
|
|
28049
|
-
renderModal(container,
|
|
28416
|
+
state2.isLoading = true;
|
|
28417
|
+
state2.dateRangePicker = null;
|
|
28418
|
+
renderModal(container, state2, modalId);
|
|
28050
28419
|
try {
|
|
28051
|
-
|
|
28052
|
-
|
|
28053
|
-
|
|
28054
|
-
renderModal(container,
|
|
28055
|
-
drawChart(modalId,
|
|
28056
|
-
await setupEventListeners(container,
|
|
28420
|
+
state2.data = await fetchTemperatureData(state2.token, state2.deviceId, state2.startTs, state2.endTs);
|
|
28421
|
+
state2.stats = calculateStats(state2.data, state2.clampRange);
|
|
28422
|
+
state2.isLoading = false;
|
|
28423
|
+
renderModal(container, state2, modalId);
|
|
28424
|
+
drawChart(modalId, state2);
|
|
28425
|
+
await setupEventListeners(container, state2, modalId, onClose);
|
|
28057
28426
|
} catch (error) {
|
|
28058
28427
|
console.error("[TemperatureModal] Error fetching data:", error);
|
|
28059
|
-
|
|
28060
|
-
renderModal(container,
|
|
28061
|
-
await setupEventListeners(container,
|
|
28428
|
+
state2.isLoading = false;
|
|
28429
|
+
renderModal(container, state2, modalId, error);
|
|
28430
|
+
await setupEventListeners(container, state2, modalId, onClose);
|
|
28062
28431
|
}
|
|
28063
28432
|
});
|
|
28064
28433
|
document.getElementById(`${modalId}-export`)?.addEventListener("click", () => {
|
|
28065
|
-
if (
|
|
28066
|
-
const startDateStr = new Date(
|
|
28067
|
-
const endDateStr = new Date(
|
|
28434
|
+
if (state2.data.length === 0) return;
|
|
28435
|
+
const startDateStr = new Date(state2.startTs).toLocaleDateString(state2.locale).replace(/\//g, "-");
|
|
28436
|
+
const endDateStr = new Date(state2.endTs).toLocaleDateString(state2.locale).replace(/\//g, "-");
|
|
28068
28437
|
exportTemperatureCSV(
|
|
28069
|
-
|
|
28070
|
-
|
|
28071
|
-
|
|
28438
|
+
state2.data,
|
|
28439
|
+
state2.label,
|
|
28440
|
+
state2.stats,
|
|
28072
28441
|
startDateStr,
|
|
28073
28442
|
endDateStr
|
|
28074
28443
|
);
|
|
@@ -28081,7 +28450,7 @@
|
|
|
28081
28450
|
const defaultDateRange = getTodaySoFar();
|
|
28082
28451
|
const startTs = params.startDate ? new Date(params.startDate).getTime() : defaultDateRange.startTs;
|
|
28083
28452
|
const endTs = params.endDate ? new Date(params.endDate).getTime() : defaultDateRange.endTs;
|
|
28084
|
-
const
|
|
28453
|
+
const state2 = {
|
|
28085
28454
|
token: params.token,
|
|
28086
28455
|
devices: params.devices,
|
|
28087
28456
|
startTs,
|
|
@@ -28100,44 +28469,44 @@
|
|
|
28100
28469
|
};
|
|
28101
28470
|
const savedGranularity = localStorage.getItem("myio-temp-comparison-granularity");
|
|
28102
28471
|
const savedTheme = localStorage.getItem("myio-temp-comparison-theme");
|
|
28103
|
-
if (savedGranularity)
|
|
28104
|
-
if (savedTheme)
|
|
28472
|
+
if (savedGranularity) state2.granularity = savedGranularity;
|
|
28473
|
+
if (savedTheme) state2.theme = savedTheme;
|
|
28105
28474
|
const modalContainer = document.createElement("div");
|
|
28106
28475
|
modalContainer.id = modalId;
|
|
28107
28476
|
document.body.appendChild(modalContainer);
|
|
28108
|
-
renderModal2(modalContainer,
|
|
28109
|
-
await fetchAllDevicesData(
|
|
28110
|
-
renderModal2(modalContainer,
|
|
28111
|
-
drawComparisonChart(modalId,
|
|
28112
|
-
await setupEventListeners2(modalContainer,
|
|
28477
|
+
renderModal2(modalContainer, state2, modalId);
|
|
28478
|
+
await fetchAllDevicesData(state2);
|
|
28479
|
+
renderModal2(modalContainer, state2, modalId);
|
|
28480
|
+
drawComparisonChart(modalId, state2);
|
|
28481
|
+
await setupEventListeners2(modalContainer, state2, modalId, params.onClose);
|
|
28113
28482
|
return {
|
|
28114
28483
|
destroy: () => {
|
|
28115
28484
|
modalContainer.remove();
|
|
28116
28485
|
params.onClose?.();
|
|
28117
28486
|
},
|
|
28118
28487
|
updateData: async (startDate, endDate, granularity) => {
|
|
28119
|
-
|
|
28120
|
-
|
|
28121
|
-
if (granularity)
|
|
28122
|
-
|
|
28123
|
-
renderModal2(modalContainer,
|
|
28124
|
-
await fetchAllDevicesData(
|
|
28125
|
-
renderModal2(modalContainer,
|
|
28126
|
-
drawComparisonChart(modalId,
|
|
28127
|
-
setupEventListeners2(modalContainer,
|
|
28488
|
+
state2.startTs = new Date(startDate).getTime();
|
|
28489
|
+
state2.endTs = new Date(endDate).getTime();
|
|
28490
|
+
if (granularity) state2.granularity = granularity;
|
|
28491
|
+
state2.isLoading = true;
|
|
28492
|
+
renderModal2(modalContainer, state2, modalId);
|
|
28493
|
+
await fetchAllDevicesData(state2);
|
|
28494
|
+
renderModal2(modalContainer, state2, modalId);
|
|
28495
|
+
drawComparisonChart(modalId, state2);
|
|
28496
|
+
setupEventListeners2(modalContainer, state2, modalId, params.onClose);
|
|
28128
28497
|
}
|
|
28129
28498
|
};
|
|
28130
28499
|
}
|
|
28131
|
-
async function fetchAllDevicesData(
|
|
28132
|
-
|
|
28133
|
-
|
|
28500
|
+
async function fetchAllDevicesData(state2) {
|
|
28501
|
+
state2.isLoading = true;
|
|
28502
|
+
state2.deviceData = [];
|
|
28134
28503
|
try {
|
|
28135
28504
|
const results = await Promise.all(
|
|
28136
|
-
|
|
28505
|
+
state2.devices.map(async (device, index) => {
|
|
28137
28506
|
const deviceId = device.tbId || device.id;
|
|
28138
28507
|
try {
|
|
28139
|
-
const data = await fetchTemperatureData(
|
|
28140
|
-
const stats = calculateStats(data,
|
|
28508
|
+
const data = await fetchTemperatureData(state2.token, deviceId, state2.startTs, state2.endTs);
|
|
28509
|
+
const stats = calculateStats(data, state2.clampRange);
|
|
28141
28510
|
return {
|
|
28142
28511
|
device,
|
|
28143
28512
|
data,
|
|
@@ -28155,21 +28524,21 @@
|
|
|
28155
28524
|
}
|
|
28156
28525
|
})
|
|
28157
28526
|
);
|
|
28158
|
-
|
|
28527
|
+
state2.deviceData = results;
|
|
28159
28528
|
} catch (error) {
|
|
28160
28529
|
console.error("[TemperatureComparisonModal] Error fetching data:", error);
|
|
28161
28530
|
}
|
|
28162
|
-
|
|
28531
|
+
state2.isLoading = false;
|
|
28163
28532
|
}
|
|
28164
|
-
function renderModal2(container,
|
|
28165
|
-
const colors = getThemeColors(
|
|
28166
|
-
new Date(
|
|
28167
|
-
new Date(
|
|
28168
|
-
new Date(
|
|
28169
|
-
new Date(
|
|
28170
|
-
const legendHTML =
|
|
28533
|
+
function renderModal2(container, state2, modalId) {
|
|
28534
|
+
const colors = getThemeColors(state2.theme);
|
|
28535
|
+
new Date(state2.startTs).toLocaleDateString(state2.locale);
|
|
28536
|
+
new Date(state2.endTs).toLocaleDateString(state2.locale);
|
|
28537
|
+
new Date(state2.startTs).toISOString().slice(0, 16);
|
|
28538
|
+
new Date(state2.endTs).toISOString().slice(0, 16);
|
|
28539
|
+
const legendHTML = state2.deviceData.map((dd) => `
|
|
28171
28540
|
<div style="display: flex; align-items: center; gap: 8px; padding: 8px 12px;
|
|
28172
|
-
background: ${
|
|
28541
|
+
background: ${state2.theme === "dark" ? "rgba(255,255,255,0.05)" : "rgba(0,0,0,0.03)"};
|
|
28173
28542
|
border-radius: 8px;">
|
|
28174
28543
|
<span style="width: 12px; height: 12px; border-radius: 50%; background: ${dd.color};"></span>
|
|
28175
28544
|
<span style="color: ${colors.text}; font-size: 13px;">${dd.device.label}</span>
|
|
@@ -28178,9 +28547,9 @@
|
|
|
28178
28547
|
</span>
|
|
28179
28548
|
</div>
|
|
28180
28549
|
`).join("");
|
|
28181
|
-
const statsHTML =
|
|
28550
|
+
const statsHTML = state2.deviceData.map((dd) => `
|
|
28182
28551
|
<div style="
|
|
28183
|
-
padding: 12px; background: ${
|
|
28552
|
+
padding: 12px; background: ${state2.theme === "dark" ? "rgba(255,255,255,0.05)" : "#fafafa"};
|
|
28184
28553
|
border-radius: 10px; border-left: 4px solid ${dd.color};
|
|
28185
28554
|
min-width: 150px;
|
|
28186
28555
|
">
|
|
@@ -28231,7 +28600,7 @@
|
|
|
28231
28600
|
min-height: 20px;
|
|
28232
28601
|
">
|
|
28233
28602
|
<h2 style="margin: 6px; font-size: 18px; font-weight: 600; color: white; line-height: 2;">
|
|
28234
|
-
\u{1F321}\uFE0F Compara\xE7\xE3o de Temperatura - ${
|
|
28603
|
+
\u{1F321}\uFE0F Compara\xE7\xE3o de Temperatura - ${state2.devices.length} sensores
|
|
28235
28604
|
</h2>
|
|
28236
28605
|
<div style="display: flex; gap: 4px; align-items: center;">
|
|
28237
28606
|
<!-- Theme Toggle -->
|
|
@@ -28239,7 +28608,7 @@
|
|
|
28239
28608
|
background: none; border: none; font-size: 16px; cursor: pointer;
|
|
28240
28609
|
padding: 4px 8px; border-radius: 6px; color: rgba(255,255,255,0.8);
|
|
28241
28610
|
transition: background-color 0.2s;
|
|
28242
|
-
">${
|
|
28611
|
+
">${state2.theme === "dark" ? "\u2600\uFE0F" : "\u{1F319}"}</button>
|
|
28243
28612
|
<!-- Maximize Button -->
|
|
28244
28613
|
<button id="${modalId}-maximize" title="${isMaximized ? "Restaurar" : "Maximizar"}" style="
|
|
28245
28614
|
background: none; border: none; font-size: 16px; cursor: pointer;
|
|
@@ -28262,7 +28631,7 @@
|
|
|
28262
28631
|
<div style="
|
|
28263
28632
|
display: flex; gap: 16px; flex-wrap: wrap; align-items: flex-end;
|
|
28264
28633
|
margin-bottom: 16px; padding: 16px;
|
|
28265
|
-
background: ${
|
|
28634
|
+
background: ${state2.theme === "dark" ? "rgba(255,255,255,0.05)" : "#f7f7f7"};
|
|
28266
28635
|
border-radius: 6px; border: 1px solid ${colors.border};
|
|
28267
28636
|
">
|
|
28268
28637
|
<!-- Granularity Select -->
|
|
@@ -28275,8 +28644,8 @@
|
|
|
28275
28644
|
font-size: 14px; color: ${colors.text}; background: ${colors.surface};
|
|
28276
28645
|
cursor: pointer; min-width: 130px;
|
|
28277
28646
|
">
|
|
28278
|
-
<option value="hour" ${
|
|
28279
|
-
<option value="day" ${
|
|
28647
|
+
<option value="hour" ${state2.granularity === "hour" ? "selected" : ""}>Hora (30 min)</option>
|
|
28648
|
+
<option value="day" ${state2.granularity === "day" ? "selected" : ""}>Dia (m\xE9dia)</option>
|
|
28280
28649
|
</select>
|
|
28281
28650
|
</div>
|
|
28282
28651
|
<!-- Day Period Filter (Multiselect) -->
|
|
@@ -28290,7 +28659,7 @@
|
|
|
28290
28659
|
cursor: pointer; min-width: 180px; text-align: left;
|
|
28291
28660
|
display: flex; align-items: center; justify-content: space-between; gap: 8px;
|
|
28292
28661
|
">
|
|
28293
|
-
<span>${getSelectedPeriodsLabel(
|
|
28662
|
+
<span>${getSelectedPeriodsLabel(state2.selectedPeriods)}</span>
|
|
28294
28663
|
<span style="font-size: 10px;">\u25BC</span>
|
|
28295
28664
|
</button>
|
|
28296
28665
|
<div id="${modalId}-period-dropdown" style="
|
|
@@ -28303,12 +28672,12 @@
|
|
|
28303
28672
|
<label style="
|
|
28304
28673
|
display: flex; align-items: center; gap: 8px; padding: 8px 12px;
|
|
28305
28674
|
cursor: pointer; font-size: 13px; color: ${colors.text};
|
|
28306
|
-
" onmouseover="this.style.background='${
|
|
28675
|
+
" onmouseover="this.style.background='${state2.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"}'"
|
|
28307
28676
|
onmouseout="this.style.background='transparent'">
|
|
28308
28677
|
<input type="checkbox"
|
|
28309
28678
|
name="${modalId}-period"
|
|
28310
28679
|
value="${period.id}"
|
|
28311
|
-
${
|
|
28680
|
+
${state2.selectedPeriods.includes(period.id) ? "checked" : ""}
|
|
28312
28681
|
style="width: 16px; height: 16px; cursor: pointer; accent-color: #3e1a7d;">
|
|
28313
28682
|
${period.label}
|
|
28314
28683
|
</label>
|
|
@@ -28316,13 +28685,13 @@
|
|
|
28316
28685
|
<div style="border-top: 1px solid ${colors.border}; margin-top: 8px; padding-top: 8px;">
|
|
28317
28686
|
<button id="${modalId}-period-select-all" type="button" style="
|
|
28318
28687
|
width: calc(100% - 16px); margin: 0 8px 4px; padding: 6px;
|
|
28319
|
-
background: ${
|
|
28688
|
+
background: ${state2.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"};
|
|
28320
28689
|
border: none; border-radius: 4px; cursor: pointer;
|
|
28321
28690
|
font-size: 12px; color: ${colors.text};
|
|
28322
28691
|
">Selecionar Todos</button>
|
|
28323
28692
|
<button id="${modalId}-period-clear" type="button" style="
|
|
28324
28693
|
width: calc(100% - 16px); margin: 0 8px; padding: 6px;
|
|
28325
|
-
background: ${
|
|
28694
|
+
background: ${state2.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f0f0f0"};
|
|
28326
28695
|
border: none; border-radius: 4px; cursor: pointer;
|
|
28327
28696
|
font-size: 12px; color: ${colors.text};
|
|
28328
28697
|
">Limpar Sele\xE7\xE3o</button>
|
|
@@ -28347,8 +28716,8 @@
|
|
|
28347
28716
|
font-size: 14px; font-weight: 500; height: 38px;
|
|
28348
28717
|
display: flex; align-items: center; gap: 8px;
|
|
28349
28718
|
font-family: 'Roboto', Arial, sans-serif;
|
|
28350
|
-
" ${
|
|
28351
|
-
${
|
|
28719
|
+
" ${state2.isLoading ? "disabled" : ""}>
|
|
28720
|
+
${state2.isLoading ? '<span style="animation: spin 1s linear infinite; display: inline-block;">\u21BB</span> Carregando...' : "Carregar"}
|
|
28352
28721
|
</button>
|
|
28353
28722
|
</div>
|
|
28354
28723
|
|
|
@@ -28364,14 +28733,14 @@
|
|
|
28364
28733
|
<div style="margin-bottom: 24px;">
|
|
28365
28734
|
<div id="${modalId}-chart" style="
|
|
28366
28735
|
height: 380px;
|
|
28367
|
-
background: ${
|
|
28736
|
+
background: ${state2.theme === "dark" ? "rgba(255,255,255,0.03)" : "#fafafa"};
|
|
28368
28737
|
border-radius: 14px; display: flex; justify-content: center; align-items: center;
|
|
28369
28738
|
border: 1px solid ${colors.border}; position: relative;
|
|
28370
28739
|
">
|
|
28371
|
-
${
|
|
28740
|
+
${state2.isLoading ? `<div style="text-align: center; color: ${colors.textMuted};">
|
|
28372
28741
|
<div style="animation: spin 1s linear infinite; font-size: 36px; margin-bottom: 12px;">\u21BB</div>
|
|
28373
|
-
<div style="font-size: 15px;">Carregando dados de ${
|
|
28374
|
-
</div>` :
|
|
28742
|
+
<div style="font-size: 15px;">Carregando dados de ${state2.devices.length} sensores...</div>
|
|
28743
|
+
</div>` : state2.deviceData.every((dd) => dd.data.length === 0) ? `<div style="text-align: center; color: ${colors.textMuted};">
|
|
28375
28744
|
<div style="font-size: 48px; margin-bottom: 12px;">\u{1F4ED}</div>
|
|
28376
28745
|
<div style="font-size: 16px;">Sem dados para o per\xEDodo selecionado</div>
|
|
28377
28746
|
</div>` : `<canvas id="${modalId}-canvas" style="width: 100%; height: 100%;"></canvas>`}
|
|
@@ -28389,12 +28758,12 @@
|
|
|
28389
28758
|
<!-- Actions -->
|
|
28390
28759
|
<div style="display: flex; justify-content: flex-end; gap: 12px;">
|
|
28391
28760
|
<button id="${modalId}-export" style="
|
|
28392
|
-
background: ${
|
|
28761
|
+
background: ${state2.theme === "dark" ? "rgba(255,255,255,0.1)" : "#f7f7f7"};
|
|
28393
28762
|
color: ${colors.text}; border: 1px solid ${colors.border};
|
|
28394
28763
|
padding: 8px 16px; border-radius: 6px; cursor: pointer;
|
|
28395
28764
|
font-size: 14px; display: flex; align-items: center; gap: 8px;
|
|
28396
28765
|
font-family: 'Roboto', Arial, sans-serif;
|
|
28397
|
-
" ${
|
|
28766
|
+
" ${state2.deviceData.every((dd) => dd.data.length === 0) ? "disabled" : ""}>
|
|
28398
28767
|
\u{1F4E5} Exportar CSV
|
|
28399
28768
|
</button>
|
|
28400
28769
|
<button id="${modalId}-close-btn" style="
|
|
@@ -28429,15 +28798,15 @@
|
|
|
28429
28798
|
</style>
|
|
28430
28799
|
`;
|
|
28431
28800
|
}
|
|
28432
|
-
function drawComparisonChart(modalId,
|
|
28801
|
+
function drawComparisonChart(modalId, state2) {
|
|
28433
28802
|
const chartContainer = document.getElementById(`${modalId}-chart`);
|
|
28434
28803
|
const canvas = document.getElementById(`${modalId}-canvas`);
|
|
28435
28804
|
if (!chartContainer || !canvas) return;
|
|
28436
|
-
const hasData =
|
|
28805
|
+
const hasData = state2.deviceData.some((dd) => dd.data.length > 0);
|
|
28437
28806
|
if (!hasData) return;
|
|
28438
28807
|
const ctx = canvas.getContext("2d");
|
|
28439
28808
|
if (!ctx) return;
|
|
28440
|
-
const colors = getThemeColors(
|
|
28809
|
+
const colors = getThemeColors(state2.theme);
|
|
28441
28810
|
const width = chartContainer.clientWidth - 2;
|
|
28442
28811
|
const height = 380;
|
|
28443
28812
|
canvas.width = width;
|
|
@@ -28448,19 +28817,19 @@
|
|
|
28448
28817
|
const paddingBottom = 55;
|
|
28449
28818
|
ctx.clearRect(0, 0, width, height);
|
|
28450
28819
|
const processedData = [];
|
|
28451
|
-
|
|
28820
|
+
state2.deviceData.forEach((dd) => {
|
|
28452
28821
|
if (dd.data.length === 0) return;
|
|
28453
|
-
const filteredData = filterByDayPeriods(dd.data,
|
|
28822
|
+
const filteredData = filterByDayPeriods(dd.data, state2.selectedPeriods);
|
|
28454
28823
|
if (filteredData.length === 0) return;
|
|
28455
28824
|
let points;
|
|
28456
|
-
if (
|
|
28825
|
+
if (state2.granularity === "hour") {
|
|
28457
28826
|
const interpolated = interpolateTemperature(filteredData, {
|
|
28458
28827
|
intervalMinutes: 30,
|
|
28459
|
-
startTs:
|
|
28460
|
-
endTs:
|
|
28461
|
-
clampRange:
|
|
28828
|
+
startTs: state2.startTs,
|
|
28829
|
+
endTs: state2.endTs,
|
|
28830
|
+
clampRange: state2.clampRange
|
|
28462
28831
|
});
|
|
28463
|
-
const filteredInterpolated = filterByDayPeriods(interpolated,
|
|
28832
|
+
const filteredInterpolated = filterByDayPeriods(interpolated, state2.selectedPeriods);
|
|
28464
28833
|
points = filteredInterpolated.map((item) => ({
|
|
28465
28834
|
x: item.ts,
|
|
28466
28835
|
y: Number(item.value),
|
|
@@ -28470,7 +28839,7 @@
|
|
|
28470
28839
|
deviceColor: dd.color
|
|
28471
28840
|
}));
|
|
28472
28841
|
} else {
|
|
28473
|
-
const daily = aggregateByDay(filteredData,
|
|
28842
|
+
const daily = aggregateByDay(filteredData, state2.clampRange);
|
|
28474
28843
|
points = daily.map((item) => ({
|
|
28475
28844
|
x: item.dateTs,
|
|
28476
28845
|
y: item.avg,
|
|
@@ -28491,7 +28860,7 @@
|
|
|
28491
28860
|
ctx.fillText("Nenhum dado para os per\xEDodos selecionados", width / 2, height / 2);
|
|
28492
28861
|
return;
|
|
28493
28862
|
}
|
|
28494
|
-
const isPeriodsFiltered =
|
|
28863
|
+
const isPeriodsFiltered = state2.selectedPeriods.length < 4 && state2.selectedPeriods.length > 0;
|
|
28495
28864
|
let dataMinY = Infinity;
|
|
28496
28865
|
let dataMaxY = -Infinity;
|
|
28497
28866
|
processedData.forEach(({ points }) => {
|
|
@@ -28501,7 +28870,7 @@
|
|
|
28501
28870
|
});
|
|
28502
28871
|
});
|
|
28503
28872
|
const rangeMap = /* @__PURE__ */ new Map();
|
|
28504
|
-
|
|
28873
|
+
state2.deviceData.forEach((dd, index) => {
|
|
28505
28874
|
const device = dd.device;
|
|
28506
28875
|
const min = device.temperatureMin;
|
|
28507
28876
|
const max = device.temperatureMax;
|
|
@@ -28520,10 +28889,10 @@
|
|
|
28520
28889
|
}
|
|
28521
28890
|
}
|
|
28522
28891
|
});
|
|
28523
|
-
if (rangeMap.size === 0 &&
|
|
28892
|
+
if (rangeMap.size === 0 && state2.temperatureMin !== null && state2.temperatureMax !== null) {
|
|
28524
28893
|
rangeMap.set("global", {
|
|
28525
|
-
min:
|
|
28526
|
-
max:
|
|
28894
|
+
min: state2.temperatureMin,
|
|
28895
|
+
max: state2.temperatureMax,
|
|
28527
28896
|
customerName: "Global",
|
|
28528
28897
|
color: colors.success,
|
|
28529
28898
|
deviceLabels: []
|
|
@@ -28644,10 +29013,10 @@
|
|
|
28644
29013
|
const point = xAxisPoints[i];
|
|
28645
29014
|
const date = new Date(point.x);
|
|
28646
29015
|
let label;
|
|
28647
|
-
if (
|
|
28648
|
-
label = date.toLocaleTimeString(
|
|
29016
|
+
if (state2.granularity === "hour") {
|
|
29017
|
+
label = date.toLocaleTimeString(state2.locale, { hour: "2-digit", minute: "2-digit" });
|
|
28649
29018
|
} else {
|
|
28650
|
-
label = date.toLocaleDateString(
|
|
29019
|
+
label = date.toLocaleDateString(state2.locale, { day: "2-digit", month: "2-digit" });
|
|
28651
29020
|
}
|
|
28652
29021
|
ctx.strokeStyle = colors.chartGrid;
|
|
28653
29022
|
ctx.lineWidth = 1;
|
|
@@ -28666,16 +29035,16 @@
|
|
|
28666
29035
|
ctx.lineTo(width - paddingRight, height - paddingBottom);
|
|
28667
29036
|
ctx.stroke();
|
|
28668
29037
|
const allChartPoints = processedData.flatMap((pd) => pd.points);
|
|
28669
|
-
setupComparisonChartTooltip(canvas, chartContainer, allChartPoints,
|
|
29038
|
+
setupComparisonChartTooltip(canvas, chartContainer, allChartPoints, state2, colors);
|
|
28670
29039
|
}
|
|
28671
|
-
function setupComparisonChartTooltip(canvas, container, chartData,
|
|
29040
|
+
function setupComparisonChartTooltip(canvas, container, chartData, state2, colors) {
|
|
28672
29041
|
const existingTooltip = container.querySelector(".myio-chart-tooltip");
|
|
28673
29042
|
if (existingTooltip) existingTooltip.remove();
|
|
28674
29043
|
const tooltip = document.createElement("div");
|
|
28675
29044
|
tooltip.className = "myio-chart-tooltip";
|
|
28676
29045
|
tooltip.style.cssText = `
|
|
28677
29046
|
position: absolute;
|
|
28678
|
-
background: ${
|
|
29047
|
+
background: ${state2.theme === "dark" ? "rgba(30, 30, 40, 0.95)" : "rgba(255, 255, 255, 0.98)"};
|
|
28679
29048
|
border: 1px solid ${colors.border};
|
|
28680
29049
|
border-radius: 8px;
|
|
28681
29050
|
padding: 10px 14px;
|
|
@@ -28712,17 +29081,17 @@
|
|
|
28712
29081
|
if (point) {
|
|
28713
29082
|
const date = new Date(point.x);
|
|
28714
29083
|
let dateStr;
|
|
28715
|
-
if (
|
|
28716
|
-
dateStr = date.toLocaleDateString(
|
|
29084
|
+
if (state2.granularity === "hour") {
|
|
29085
|
+
dateStr = date.toLocaleDateString(state2.locale, {
|
|
28717
29086
|
day: "2-digit",
|
|
28718
29087
|
month: "2-digit",
|
|
28719
29088
|
year: "numeric"
|
|
28720
|
-
}) + " " + date.toLocaleTimeString(
|
|
29089
|
+
}) + " " + date.toLocaleTimeString(state2.locale, {
|
|
28721
29090
|
hour: "2-digit",
|
|
28722
29091
|
minute: "2-digit"
|
|
28723
29092
|
});
|
|
28724
29093
|
} else {
|
|
28725
|
-
dateStr = date.toLocaleDateString(
|
|
29094
|
+
dateStr = date.toLocaleDateString(state2.locale, {
|
|
28726
29095
|
day: "2-digit",
|
|
28727
29096
|
month: "2-digit",
|
|
28728
29097
|
year: "numeric"
|
|
@@ -28764,7 +29133,7 @@
|
|
|
28764
29133
|
canvas.style.cursor = "default";
|
|
28765
29134
|
});
|
|
28766
29135
|
}
|
|
28767
|
-
async function setupEventListeners2(container,
|
|
29136
|
+
async function setupEventListeners2(container, state2, modalId, onClose) {
|
|
28768
29137
|
const closeModal = () => {
|
|
28769
29138
|
container.remove();
|
|
28770
29139
|
onClose?.();
|
|
@@ -28775,19 +29144,19 @@
|
|
|
28775
29144
|
document.getElementById(`${modalId}-close`)?.addEventListener("click", closeModal);
|
|
28776
29145
|
document.getElementById(`${modalId}-close-btn`)?.addEventListener("click", closeModal);
|
|
28777
29146
|
const dateRangeInput = document.getElementById(`${modalId}-date-range`);
|
|
28778
|
-
if (dateRangeInput && !
|
|
29147
|
+
if (dateRangeInput && !state2.dateRangePicker) {
|
|
28779
29148
|
try {
|
|
28780
|
-
|
|
28781
|
-
presetStart: new Date(
|
|
28782
|
-
presetEnd: new Date(
|
|
29149
|
+
state2.dateRangePicker = await createDateRangePicker2(dateRangeInput, {
|
|
29150
|
+
presetStart: new Date(state2.startTs).toISOString(),
|
|
29151
|
+
presetEnd: new Date(state2.endTs).toISOString(),
|
|
28783
29152
|
includeTime: true,
|
|
28784
29153
|
timePrecision: "minute",
|
|
28785
29154
|
maxRangeDays: 90,
|
|
28786
|
-
locale:
|
|
29155
|
+
locale: state2.locale,
|
|
28787
29156
|
parentEl: container.querySelector(".myio-temp-comparison-content"),
|
|
28788
29157
|
onApply: (result) => {
|
|
28789
|
-
|
|
28790
|
-
|
|
29158
|
+
state2.startTs = new Date(result.startISO).getTime();
|
|
29159
|
+
state2.endTs = new Date(result.endISO).getTime();
|
|
28791
29160
|
console.log("[TemperatureComparisonModal] Date range applied:", result);
|
|
28792
29161
|
}
|
|
28793
29162
|
});
|
|
@@ -28796,23 +29165,23 @@
|
|
|
28796
29165
|
}
|
|
28797
29166
|
}
|
|
28798
29167
|
document.getElementById(`${modalId}-theme-toggle`)?.addEventListener("click", async () => {
|
|
28799
|
-
|
|
28800
|
-
localStorage.setItem("myio-temp-comparison-theme",
|
|
28801
|
-
|
|
28802
|
-
renderModal2(container,
|
|
28803
|
-
if (
|
|
28804
|
-
drawComparisonChart(modalId,
|
|
28805
|
-
}
|
|
28806
|
-
await setupEventListeners2(container,
|
|
29168
|
+
state2.theme = state2.theme === "dark" ? "light" : "dark";
|
|
29169
|
+
localStorage.setItem("myio-temp-comparison-theme", state2.theme);
|
|
29170
|
+
state2.dateRangePicker = null;
|
|
29171
|
+
renderModal2(container, state2, modalId);
|
|
29172
|
+
if (state2.deviceData.some((dd) => dd.data.length > 0)) {
|
|
29173
|
+
drawComparisonChart(modalId, state2);
|
|
29174
|
+
}
|
|
29175
|
+
await setupEventListeners2(container, state2, modalId, onClose);
|
|
28807
29176
|
});
|
|
28808
29177
|
document.getElementById(`${modalId}-maximize`)?.addEventListener("click", async () => {
|
|
28809
29178
|
container.__isMaximized = !container.__isMaximized;
|
|
28810
|
-
|
|
28811
|
-
renderModal2(container,
|
|
28812
|
-
if (
|
|
28813
|
-
drawComparisonChart(modalId,
|
|
29179
|
+
state2.dateRangePicker = null;
|
|
29180
|
+
renderModal2(container, state2, modalId);
|
|
29181
|
+
if (state2.deviceData.some((dd) => dd.data.length > 0)) {
|
|
29182
|
+
drawComparisonChart(modalId, state2);
|
|
28814
29183
|
}
|
|
28815
|
-
await setupEventListeners2(container,
|
|
29184
|
+
await setupEventListeners2(container, state2, modalId, onClose);
|
|
28816
29185
|
});
|
|
28817
29186
|
const periodBtn = document.getElementById(`${modalId}-period-btn`);
|
|
28818
29187
|
const periodDropdown = document.getElementById(`${modalId}-period-dropdown`);
|
|
@@ -28831,13 +29200,13 @@
|
|
|
28831
29200
|
periodCheckboxes.forEach((checkbox) => {
|
|
28832
29201
|
checkbox.addEventListener("change", () => {
|
|
28833
29202
|
const checked = Array.from(periodCheckboxes).filter((cb) => cb.checked).map((cb) => cb.value);
|
|
28834
|
-
|
|
29203
|
+
state2.selectedPeriods = checked;
|
|
28835
29204
|
const btnLabel = periodBtn?.querySelector("span:first-child");
|
|
28836
29205
|
if (btnLabel) {
|
|
28837
|
-
btnLabel.textContent = getSelectedPeriodsLabel(
|
|
29206
|
+
btnLabel.textContent = getSelectedPeriodsLabel(state2.selectedPeriods);
|
|
28838
29207
|
}
|
|
28839
|
-
if (
|
|
28840
|
-
drawComparisonChart(modalId,
|
|
29208
|
+
if (state2.deviceData.some((dd) => dd.data.length > 0)) {
|
|
29209
|
+
drawComparisonChart(modalId, state2);
|
|
28841
29210
|
}
|
|
28842
29211
|
});
|
|
28843
29212
|
});
|
|
@@ -28845,77 +29214,77 @@
|
|
|
28845
29214
|
periodCheckboxes.forEach((cb) => {
|
|
28846
29215
|
cb.checked = true;
|
|
28847
29216
|
});
|
|
28848
|
-
|
|
29217
|
+
state2.selectedPeriods = ["madrugada", "manha", "tarde", "noite"];
|
|
28849
29218
|
const btnLabel = periodBtn?.querySelector("span:first-child");
|
|
28850
29219
|
if (btnLabel) {
|
|
28851
|
-
btnLabel.textContent = getSelectedPeriodsLabel(
|
|
29220
|
+
btnLabel.textContent = getSelectedPeriodsLabel(state2.selectedPeriods);
|
|
28852
29221
|
}
|
|
28853
|
-
if (
|
|
28854
|
-
drawComparisonChart(modalId,
|
|
29222
|
+
if (state2.deviceData.some((dd) => dd.data.length > 0)) {
|
|
29223
|
+
drawComparisonChart(modalId, state2);
|
|
28855
29224
|
}
|
|
28856
29225
|
});
|
|
28857
29226
|
document.getElementById(`${modalId}-period-clear`)?.addEventListener("click", () => {
|
|
28858
29227
|
periodCheckboxes.forEach((cb) => {
|
|
28859
29228
|
cb.checked = false;
|
|
28860
29229
|
});
|
|
28861
|
-
|
|
29230
|
+
state2.selectedPeriods = [];
|
|
28862
29231
|
const btnLabel = periodBtn?.querySelector("span:first-child");
|
|
28863
29232
|
if (btnLabel) {
|
|
28864
|
-
btnLabel.textContent = getSelectedPeriodsLabel(
|
|
29233
|
+
btnLabel.textContent = getSelectedPeriodsLabel(state2.selectedPeriods);
|
|
28865
29234
|
}
|
|
28866
|
-
if (
|
|
28867
|
-
drawComparisonChart(modalId,
|
|
29235
|
+
if (state2.deviceData.some((dd) => dd.data.length > 0)) {
|
|
29236
|
+
drawComparisonChart(modalId, state2);
|
|
28868
29237
|
}
|
|
28869
29238
|
});
|
|
28870
29239
|
document.getElementById(`${modalId}-granularity`)?.addEventListener("change", (e) => {
|
|
28871
|
-
|
|
28872
|
-
localStorage.setItem("myio-temp-comparison-granularity",
|
|
28873
|
-
if (
|
|
28874
|
-
drawComparisonChart(modalId,
|
|
29240
|
+
state2.granularity = e.target.value;
|
|
29241
|
+
localStorage.setItem("myio-temp-comparison-granularity", state2.granularity);
|
|
29242
|
+
if (state2.deviceData.some((dd) => dd.data.length > 0)) {
|
|
29243
|
+
drawComparisonChart(modalId, state2);
|
|
28875
29244
|
}
|
|
28876
29245
|
});
|
|
28877
29246
|
document.getElementById(`${modalId}-query`)?.addEventListener("click", async () => {
|
|
28878
|
-
if (
|
|
29247
|
+
if (state2.startTs >= state2.endTs) {
|
|
28879
29248
|
alert("Por favor, selecione um per\xEDodo v\xE1lido");
|
|
28880
29249
|
return;
|
|
28881
29250
|
}
|
|
28882
|
-
|
|
28883
|
-
|
|
28884
|
-
renderModal2(container,
|
|
28885
|
-
await fetchAllDevicesData(
|
|
28886
|
-
renderModal2(container,
|
|
28887
|
-
drawComparisonChart(modalId,
|
|
28888
|
-
await setupEventListeners2(container,
|
|
29251
|
+
state2.isLoading = true;
|
|
29252
|
+
state2.dateRangePicker = null;
|
|
29253
|
+
renderModal2(container, state2, modalId);
|
|
29254
|
+
await fetchAllDevicesData(state2);
|
|
29255
|
+
renderModal2(container, state2, modalId);
|
|
29256
|
+
drawComparisonChart(modalId, state2);
|
|
29257
|
+
await setupEventListeners2(container, state2, modalId, onClose);
|
|
28889
29258
|
});
|
|
28890
29259
|
document.getElementById(`${modalId}-export`)?.addEventListener("click", () => {
|
|
28891
|
-
if (
|
|
28892
|
-
exportComparisonCSV(
|
|
29260
|
+
if (state2.deviceData.every((dd) => dd.data.length === 0)) return;
|
|
29261
|
+
exportComparisonCSV(state2);
|
|
28893
29262
|
});
|
|
28894
29263
|
}
|
|
28895
|
-
function exportComparisonCSV(
|
|
28896
|
-
const startDateStr = new Date(
|
|
28897
|
-
const endDateStr = new Date(
|
|
29264
|
+
function exportComparisonCSV(state2) {
|
|
29265
|
+
const startDateStr = new Date(state2.startTs).toLocaleDateString(state2.locale).replace(/\//g, "-");
|
|
29266
|
+
const endDateStr = new Date(state2.endTs).toLocaleDateString(state2.locale).replace(/\//g, "-");
|
|
28898
29267
|
const BOM = "\uFEFF";
|
|
28899
29268
|
let csvContent = BOM;
|
|
28900
29269
|
csvContent += `Compara\xE7\xE3o de Temperatura
|
|
28901
29270
|
`;
|
|
28902
29271
|
csvContent += `Per\xEDodo: ${startDateStr} at\xE9 ${endDateStr}
|
|
28903
29272
|
`;
|
|
28904
|
-
csvContent += `Sensores: ${
|
|
29273
|
+
csvContent += `Sensores: ${state2.devices.map((d) => d.label).join(", ")}
|
|
28905
29274
|
`;
|
|
28906
29275
|
csvContent += "\n";
|
|
28907
29276
|
csvContent += "Estat\xEDsticas por Sensor:\n";
|
|
28908
29277
|
csvContent += "Sensor,M\xE9dia (\xB0C),Min (\xB0C),Max (\xB0C),Leituras\n";
|
|
28909
|
-
|
|
29278
|
+
state2.deviceData.forEach((dd) => {
|
|
28910
29279
|
csvContent += `"${dd.device.label}",${dd.stats.avg.toFixed(2)},${dd.stats.min.toFixed(2)},${dd.stats.max.toFixed(2)},${dd.stats.count}
|
|
28911
29280
|
`;
|
|
28912
29281
|
});
|
|
28913
29282
|
csvContent += "\n";
|
|
28914
29283
|
csvContent += "Dados Detalhados:\n";
|
|
28915
29284
|
csvContent += "Data/Hora,Sensor,Temperatura (\xB0C)\n";
|
|
28916
|
-
|
|
29285
|
+
state2.deviceData.forEach((dd) => {
|
|
28917
29286
|
dd.data.forEach((item) => {
|
|
28918
|
-
const date = new Date(item.ts).toLocaleString(
|
|
29287
|
+
const date = new Date(item.ts).toLocaleString(state2.locale);
|
|
28919
29288
|
const temp = Number(item.value).toFixed(2);
|
|
28920
29289
|
csvContent += `"${date}","${dd.device.label}",${temp}
|
|
28921
29290
|
`;
|
|
@@ -29023,10 +29392,10 @@
|
|
|
29023
29392
|
throw new Error(`Failed to save attributes: ${response.status}`);
|
|
29024
29393
|
}
|
|
29025
29394
|
}
|
|
29026
|
-
function renderModal3(container,
|
|
29027
|
-
const colors = getColors(
|
|
29028
|
-
const minValue =
|
|
29029
|
-
const maxValue =
|
|
29395
|
+
function renderModal3(container, state2, modalId, onClose, onSave) {
|
|
29396
|
+
const colors = getColors(state2.theme);
|
|
29397
|
+
const minValue = state2.minTemperature !== null ? state2.minTemperature : "";
|
|
29398
|
+
const maxValue = state2.maxTemperature !== null ? state2.maxTemperature : "";
|
|
29030
29399
|
container.innerHTML = `
|
|
29031
29400
|
<style>
|
|
29032
29401
|
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
|
|
@@ -29291,23 +29660,23 @@
|
|
|
29291
29660
|
</div>
|
|
29292
29661
|
|
|
29293
29662
|
<div class="modal-body">
|
|
29294
|
-
${
|
|
29663
|
+
${state2.isLoading ? `
|
|
29295
29664
|
<div class="loading-overlay">
|
|
29296
29665
|
<div class="loading-spinner"></div>
|
|
29297
29666
|
<div>Carregando configura\xE7\xF5es...</div>
|
|
29298
29667
|
</div>
|
|
29299
29668
|
` : `
|
|
29300
|
-
${
|
|
29301
|
-
<div class="message message-error">${
|
|
29669
|
+
${state2.error ? `
|
|
29670
|
+
<div class="message message-error">${state2.error}</div>
|
|
29302
29671
|
` : ""}
|
|
29303
29672
|
|
|
29304
|
-
${
|
|
29305
|
-
<div class="message message-success">${
|
|
29673
|
+
${state2.successMessage ? `
|
|
29674
|
+
<div class="message message-success">${state2.successMessage}</div>
|
|
29306
29675
|
` : ""}
|
|
29307
29676
|
|
|
29308
29677
|
<div class="customer-info">
|
|
29309
29678
|
<div class="customer-label">Shopping / Cliente</div>
|
|
29310
|
-
<div class="customer-name">${
|
|
29679
|
+
<div class="customer-name">${state2.customerName || "N\xE3o identificado"}</div>
|
|
29311
29680
|
</div>
|
|
29312
29681
|
|
|
29313
29682
|
<div class="form-group">
|
|
@@ -29351,11 +29720,11 @@
|
|
|
29351
29720
|
`}
|
|
29352
29721
|
</div>
|
|
29353
29722
|
|
|
29354
|
-
${!
|
|
29723
|
+
${!state2.isLoading ? `
|
|
29355
29724
|
<div class="modal-footer">
|
|
29356
29725
|
<button class="btn btn-secondary" id="${modalId}-cancel">Cancelar</button>
|
|
29357
|
-
<button class="btn btn-primary" id="${modalId}-save" ${
|
|
29358
|
-
${
|
|
29726
|
+
<button class="btn btn-primary" id="${modalId}-save" ${state2.isSaving ? "disabled" : ""}>
|
|
29727
|
+
${state2.isSaving ? '<div class="spinner"></div> Salvando...' : "Salvar"}
|
|
29359
29728
|
</button>
|
|
29360
29729
|
</div>
|
|
29361
29730
|
` : ""}
|
|
@@ -29392,18 +29761,18 @@
|
|
|
29392
29761
|
const min = parseFloat(minInput.value);
|
|
29393
29762
|
const max = parseFloat(maxInput.value);
|
|
29394
29763
|
if (isNaN(min) || isNaN(max)) {
|
|
29395
|
-
|
|
29396
|
-
renderModal3(container,
|
|
29764
|
+
state2.error = "Por favor, preencha ambos os valores.";
|
|
29765
|
+
renderModal3(container, state2, modalId, onClose, onSave);
|
|
29397
29766
|
return;
|
|
29398
29767
|
}
|
|
29399
29768
|
if (min >= max) {
|
|
29400
|
-
|
|
29401
|
-
renderModal3(container,
|
|
29769
|
+
state2.error = "A temperatura m\xEDnima deve ser menor que a m\xE1xima.";
|
|
29770
|
+
renderModal3(container, state2, modalId, onClose, onSave);
|
|
29402
29771
|
return;
|
|
29403
29772
|
}
|
|
29404
29773
|
if (min < 0 || max > 50) {
|
|
29405
|
-
|
|
29406
|
-
renderModal3(container,
|
|
29774
|
+
state2.error = "Os valores devem estar entre 0\xB0C e 50\xB0C.";
|
|
29775
|
+
renderModal3(container, state2, modalId, onClose, onSave);
|
|
29407
29776
|
return;
|
|
29408
29777
|
}
|
|
29409
29778
|
await onSave(min, max);
|
|
@@ -29411,7 +29780,7 @@
|
|
|
29411
29780
|
}
|
|
29412
29781
|
function openTemperatureSettingsModal(params) {
|
|
29413
29782
|
const modalId = `myio-temp-settings-${Date.now()}`;
|
|
29414
|
-
const
|
|
29783
|
+
const state2 = {
|
|
29415
29784
|
customerId: params.customerId,
|
|
29416
29785
|
customerName: params.customerName || "",
|
|
29417
29786
|
token: params.token,
|
|
@@ -29431,37 +29800,37 @@
|
|
|
29431
29800
|
params.onClose?.();
|
|
29432
29801
|
};
|
|
29433
29802
|
const handleSave = async (min, max) => {
|
|
29434
|
-
|
|
29435
|
-
|
|
29436
|
-
|
|
29437
|
-
renderModal3(container,
|
|
29803
|
+
state2.isSaving = true;
|
|
29804
|
+
state2.error = null;
|
|
29805
|
+
state2.successMessage = null;
|
|
29806
|
+
renderModal3(container, state2, modalId, destroy, handleSave);
|
|
29438
29807
|
try {
|
|
29439
|
-
await saveCustomerAttributes(
|
|
29440
|
-
|
|
29441
|
-
|
|
29442
|
-
|
|
29443
|
-
|
|
29444
|
-
renderModal3(container,
|
|
29808
|
+
await saveCustomerAttributes(state2.customerId, state2.token, min, max, params.onError);
|
|
29809
|
+
state2.minTemperature = min;
|
|
29810
|
+
state2.maxTemperature = max;
|
|
29811
|
+
state2.isSaving = false;
|
|
29812
|
+
state2.successMessage = "Configura\xE7\xF5es salvas com sucesso!";
|
|
29813
|
+
renderModal3(container, state2, modalId, destroy, handleSave);
|
|
29445
29814
|
params.onSave?.({ minTemperature: min, maxTemperature: max });
|
|
29446
29815
|
setTimeout(() => {
|
|
29447
29816
|
destroy();
|
|
29448
29817
|
}, 1500);
|
|
29449
29818
|
} catch (error) {
|
|
29450
|
-
|
|
29451
|
-
|
|
29452
|
-
renderModal3(container,
|
|
29819
|
+
state2.isSaving = false;
|
|
29820
|
+
state2.error = `Erro ao salvar: ${error.message}`;
|
|
29821
|
+
renderModal3(container, state2, modalId, destroy, handleSave);
|
|
29453
29822
|
}
|
|
29454
29823
|
};
|
|
29455
|
-
renderModal3(container,
|
|
29456
|
-
fetchCustomerAttributes(
|
|
29457
|
-
|
|
29458
|
-
|
|
29459
|
-
|
|
29460
|
-
renderModal3(container,
|
|
29824
|
+
renderModal3(container, state2, modalId, destroy, handleSave);
|
|
29825
|
+
fetchCustomerAttributes(state2.customerId, state2.token, params.onError).then(({ minTemperature, maxTemperature }) => {
|
|
29826
|
+
state2.minTemperature = minTemperature;
|
|
29827
|
+
state2.maxTemperature = maxTemperature;
|
|
29828
|
+
state2.isLoading = false;
|
|
29829
|
+
renderModal3(container, state2, modalId, destroy, handleSave);
|
|
29461
29830
|
}).catch((error) => {
|
|
29462
|
-
|
|
29463
|
-
|
|
29464
|
-
renderModal3(container,
|
|
29831
|
+
state2.isLoading = false;
|
|
29832
|
+
state2.error = `Erro ao carregar: ${error.message}`;
|
|
29833
|
+
renderModal3(container, state2, modalId, destroy, handleSave);
|
|
29465
29834
|
});
|
|
29466
29835
|
return { destroy };
|
|
29467
29836
|
}
|
|
@@ -29497,11 +29866,13 @@
|
|
|
29497
29866
|
border: 1px solid #e2e8f0;
|
|
29498
29867
|
border-radius: 12px;
|
|
29499
29868
|
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.15), 0 2px 10px rgba(0, 0, 0, 0.08);
|
|
29869
|
+
min-width: 380px;
|
|
29500
29870
|
width: max-content;
|
|
29501
29871
|
max-width: 90vw;
|
|
29502
29872
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
29503
29873
|
font-size: 12px;
|
|
29504
29874
|
color: #1e293b;
|
|
29875
|
+
overflow: hidden;
|
|
29505
29876
|
}
|
|
29506
29877
|
|
|
29507
29878
|
.energy-summary-tooltip__header {
|
|
@@ -29807,8 +30178,7 @@
|
|
|
29807
30178
|
align-items: center;
|
|
29808
30179
|
padding: 10px 14px;
|
|
29809
30180
|
background: linear-gradient(135deg, #047857 0%, #059669 100%);
|
|
29810
|
-
|
|
29811
|
-
border-radius: 0 0 12px 12px;
|
|
30181
|
+
border-radius: 0 0 11px 11px;
|
|
29812
30182
|
}
|
|
29813
30183
|
|
|
29814
30184
|
.energy-summary-tooltip__total-label {
|
|
@@ -29951,7 +30321,7 @@
|
|
|
29951
30321
|
{ key: "failure", label: "Falha", count: status.failure },
|
|
29952
30322
|
{ key: "standby", label: "Standby", count: status.standby },
|
|
29953
30323
|
{ key: "offline", label: "Offline", count: status.offline },
|
|
29954
|
-
{ key: "no-consumption", label: "Sem
|
|
30324
|
+
{ key: "no-consumption", label: "Sem Consumo", count: status.noConsumption }
|
|
29955
30325
|
];
|
|
29956
30326
|
return items.map((item) => `
|
|
29957
30327
|
<div class="energy-summary-tooltip__status-item ${item.key}">
|
|
@@ -30396,7 +30766,7 @@
|
|
|
30396
30766
|
* Build summary data from TELEMETRY_INFO STATE
|
|
30397
30767
|
* This is called by the widget controller to get data for the tooltip
|
|
30398
30768
|
*/
|
|
30399
|
-
buildSummaryFromState(
|
|
30769
|
+
buildSummaryFromState(state2, receivedData) {
|
|
30400
30770
|
const summary = {
|
|
30401
30771
|
totalDevices: 0,
|
|
30402
30772
|
totalConsumption: 0,
|
|
@@ -30412,22 +30782,22 @@
|
|
|
30412
30782
|
},
|
|
30413
30783
|
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
30414
30784
|
};
|
|
30415
|
-
if (!
|
|
30785
|
+
if (!state2) return summary;
|
|
30416
30786
|
const entrada = {
|
|
30417
30787
|
id: "entrada",
|
|
30418
30788
|
name: "Entrada",
|
|
30419
30789
|
icon: CATEGORY_ICONS.entrada,
|
|
30420
|
-
deviceCount:
|
|
30421
|
-
consumption:
|
|
30790
|
+
deviceCount: state2.entrada?.devices?.length || (receivedData?.entrada_total?.device_count || 0),
|
|
30791
|
+
consumption: state2.entrada?.total || 0,
|
|
30422
30792
|
percentage: 100
|
|
30423
30793
|
};
|
|
30424
30794
|
const lojas = {
|
|
30425
30795
|
id: "lojas",
|
|
30426
30796
|
name: "Lojas",
|
|
30427
30797
|
icon: CATEGORY_ICONS.lojas,
|
|
30428
|
-
deviceCount:
|
|
30429
|
-
consumption:
|
|
30430
|
-
percentage:
|
|
30798
|
+
deviceCount: state2.consumidores?.lojas?.devices?.length || (receivedData?.lojas_total?.device_count || 0),
|
|
30799
|
+
consumption: state2.consumidores?.lojas?.total || 0,
|
|
30800
|
+
percentage: state2.consumidores?.lojas?.perc || 0
|
|
30431
30801
|
};
|
|
30432
30802
|
const climatizacaoData = receivedData?.climatizacao || {};
|
|
30433
30803
|
const elevadoresData = receivedData?.elevadores || {};
|
|
@@ -30438,9 +30808,9 @@
|
|
|
30438
30808
|
id: "climatizacao",
|
|
30439
30809
|
name: "Climatizacao",
|
|
30440
30810
|
icon: CATEGORY_ICONS.climatizacao,
|
|
30441
|
-
deviceCount: climatizacaoData.count ||
|
|
30442
|
-
consumption:
|
|
30443
|
-
percentage:
|
|
30811
|
+
deviceCount: climatizacaoData.count || state2.consumidores?.climatizacao?.devices?.length || 0,
|
|
30812
|
+
consumption: state2.consumidores?.climatizacao?.total || 0,
|
|
30813
|
+
percentage: state2.consumidores?.climatizacao?.perc || 0
|
|
30444
30814
|
};
|
|
30445
30815
|
if (climatizacaoData.subcategories) {
|
|
30446
30816
|
climatizacao.children = [];
|
|
@@ -30491,54 +30861,67 @@
|
|
|
30491
30861
|
id: "elevadores",
|
|
30492
30862
|
name: "Elevadores",
|
|
30493
30863
|
icon: CATEGORY_ICONS.elevadores,
|
|
30494
|
-
deviceCount: elevadoresData.count ||
|
|
30495
|
-
consumption:
|
|
30496
|
-
percentage:
|
|
30864
|
+
deviceCount: elevadoresData.count || state2.consumidores?.elevadores?.devices?.length || 0,
|
|
30865
|
+
consumption: state2.consumidores?.elevadores?.total || 0,
|
|
30866
|
+
percentage: state2.consumidores?.elevadores?.perc || 0
|
|
30497
30867
|
});
|
|
30498
30868
|
areaComumChildren.push({
|
|
30499
30869
|
id: "escadasRolantes",
|
|
30500
30870
|
name: "Esc. Rolantes",
|
|
30501
30871
|
icon: CATEGORY_ICONS.escadas,
|
|
30502
|
-
deviceCount: escadasData.count ||
|
|
30503
|
-
consumption:
|
|
30504
|
-
percentage:
|
|
30872
|
+
deviceCount: escadasData.count || state2.consumidores?.escadasRolantes?.devices?.length || 0,
|
|
30873
|
+
consumption: state2.consumidores?.escadasRolantes?.total || 0,
|
|
30874
|
+
percentage: state2.consumidores?.escadasRolantes?.perc || 0
|
|
30505
30875
|
});
|
|
30506
30876
|
areaComumChildren.push({
|
|
30507
30877
|
id: "outros",
|
|
30508
30878
|
name: "Outros",
|
|
30509
30879
|
icon: CATEGORY_ICONS.outros,
|
|
30510
|
-
deviceCount: outrosData.count ||
|
|
30511
|
-
consumption:
|
|
30512
|
-
percentage:
|
|
30880
|
+
deviceCount: outrosData.count || state2.consumidores?.outros?.devices?.length || 0,
|
|
30881
|
+
consumption: state2.consumidores?.outros?.total || 0,
|
|
30882
|
+
percentage: state2.consumidores?.outros?.perc || 0
|
|
30513
30883
|
});
|
|
30514
30884
|
const areaComumDeviceCount = areaComumChildren.reduce((sum, c) => sum + c.deviceCount, 0);
|
|
30515
|
-
const areaComumConsumption =
|
|
30885
|
+
const areaComumConsumption = state2.consumidores?.areaComum?.total || areaComumChildren.reduce((sum, c) => sum + c.consumption, 0);
|
|
30516
30886
|
const areaComum = {
|
|
30517
30887
|
id: "areaComum",
|
|
30518
30888
|
name: "Area Comum",
|
|
30519
30889
|
icon: CATEGORY_ICONS.areaComum,
|
|
30520
30890
|
deviceCount: areaComumDeviceCount,
|
|
30521
30891
|
consumption: areaComumConsumption,
|
|
30522
|
-
percentage:
|
|
30892
|
+
percentage: state2.consumidores?.areaComum?.perc || 0,
|
|
30523
30893
|
children: areaComumChildren
|
|
30524
30894
|
};
|
|
30525
30895
|
summary.byCategory = [entrada, lojas, areaComum];
|
|
30526
30896
|
summary.totalDevices = entrada.deviceCount + lojas.deviceCount + areaComumDeviceCount;
|
|
30527
|
-
summary.totalConsumption =
|
|
30897
|
+
summary.totalConsumption = state2.grandTotal || entrada.consumption;
|
|
30528
30898
|
const totalDevices = summary.totalDevices;
|
|
30529
|
-
|
|
30530
|
-
|
|
30531
|
-
|
|
30532
|
-
|
|
30533
|
-
|
|
30534
|
-
|
|
30535
|
-
|
|
30536
|
-
|
|
30537
|
-
|
|
30538
|
-
|
|
30539
|
-
|
|
30540
|
-
|
|
30541
|
-
|
|
30899
|
+
const statusData = receivedData?.statusCounts || receivedData?.deviceStatus || null;
|
|
30900
|
+
if (statusData && typeof statusData === "object") {
|
|
30901
|
+
summary.byStatus = {
|
|
30902
|
+
normal: statusData.normal || 0,
|
|
30903
|
+
alert: statusData.alert || 0,
|
|
30904
|
+
failure: statusData.failure || 0,
|
|
30905
|
+
standby: statusData.standby || 0,
|
|
30906
|
+
offline: statusData.offline || 0,
|
|
30907
|
+
noConsumption: statusData.noConsumption || statusData.zeroConsumption || 0
|
|
30908
|
+
};
|
|
30909
|
+
} else {
|
|
30910
|
+
summary.byStatus = {
|
|
30911
|
+
normal: Math.floor(totalDevices * 0.75),
|
|
30912
|
+
// Estimate 75% normal (with consumption)
|
|
30913
|
+
alert: Math.floor(totalDevices * 0.06),
|
|
30914
|
+
// Estimate 6% alert
|
|
30915
|
+
failure: Math.floor(totalDevices * 0.02),
|
|
30916
|
+
// Estimate 2% failure
|
|
30917
|
+
standby: Math.floor(totalDevices * 0.02),
|
|
30918
|
+
// Estimate 2% standby
|
|
30919
|
+
offline: Math.floor(totalDevices * 0.03),
|
|
30920
|
+
// Estimate 3% offline
|
|
30921
|
+
noConsumption: Math.floor(totalDevices * 0.12)
|
|
30922
|
+
// Estimate 12% sem consumo
|
|
30923
|
+
};
|
|
30924
|
+
}
|
|
30542
30925
|
const statusSum = Object.values(summary.byStatus).reduce((a, b) => a + b, 0);
|
|
30543
30926
|
if (statusSum !== totalDevices && totalDevices > 0) {
|
|
30544
30927
|
summary.byStatus.normal += totalDevices - statusSum;
|
|
@@ -30547,6 +30930,654 @@
|
|
|
30547
30930
|
}
|
|
30548
30931
|
};
|
|
30549
30932
|
|
|
30933
|
+
// src/utils/InfoTooltip.ts
|
|
30934
|
+
var INFO_TOOLTIP_CSS = `
|
|
30935
|
+
/* ============================================
|
|
30936
|
+
Info Tooltip (RFC-0105)
|
|
30937
|
+
Premium draggable tooltip with actions
|
|
30938
|
+
============================================ */
|
|
30939
|
+
|
|
30940
|
+
.myio-info-tooltip {
|
|
30941
|
+
position: fixed;
|
|
30942
|
+
z-index: 99999;
|
|
30943
|
+
pointer-events: none;
|
|
30944
|
+
opacity: 0;
|
|
30945
|
+
transition: opacity 0.25s ease, transform 0.25s ease;
|
|
30946
|
+
transform: translateY(5px);
|
|
30947
|
+
}
|
|
30948
|
+
|
|
30949
|
+
.myio-info-tooltip.visible {
|
|
30950
|
+
opacity: 1;
|
|
30951
|
+
pointer-events: auto;
|
|
30952
|
+
transform: translateY(0);
|
|
30953
|
+
}
|
|
30954
|
+
|
|
30955
|
+
.myio-info-tooltip.closing {
|
|
30956
|
+
opacity: 0;
|
|
30957
|
+
transform: translateY(8px);
|
|
30958
|
+
transition: opacity 0.4s ease, transform 0.4s ease;
|
|
30959
|
+
}
|
|
30960
|
+
|
|
30961
|
+
.myio-info-tooltip.pinned {
|
|
30962
|
+
box-shadow: 0 0 0 2px #047857, 0 10px 40px rgba(0, 0, 0, 0.2);
|
|
30963
|
+
border-radius: 12px;
|
|
30964
|
+
}
|
|
30965
|
+
|
|
30966
|
+
.myio-info-tooltip.dragging {
|
|
30967
|
+
transition: none !important;
|
|
30968
|
+
cursor: move;
|
|
30969
|
+
}
|
|
30970
|
+
|
|
30971
|
+
.myio-info-tooltip.maximized {
|
|
30972
|
+
top: 20px !important;
|
|
30973
|
+
left: 20px !important;
|
|
30974
|
+
right: 20px !important;
|
|
30975
|
+
bottom: 20px !important;
|
|
30976
|
+
width: auto !important;
|
|
30977
|
+
max-width: none !important;
|
|
30978
|
+
}
|
|
30979
|
+
|
|
30980
|
+
.myio-info-tooltip.maximized .myio-info-tooltip__panel {
|
|
30981
|
+
width: 100%;
|
|
30982
|
+
height: 100%;
|
|
30983
|
+
max-width: none;
|
|
30984
|
+
display: flex;
|
|
30985
|
+
flex-direction: column;
|
|
30986
|
+
}
|
|
30987
|
+
|
|
30988
|
+
.myio-info-tooltip.maximized .myio-info-tooltip__content {
|
|
30989
|
+
flex: 1;
|
|
30990
|
+
overflow-y: auto;
|
|
30991
|
+
}
|
|
30992
|
+
|
|
30993
|
+
.myio-info-tooltip__panel {
|
|
30994
|
+
background: #ffffff;
|
|
30995
|
+
border: 1px solid #e2e8f0;
|
|
30996
|
+
border-radius: 12px;
|
|
30997
|
+
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.12), 0 2px 10px rgba(0, 0, 0, 0.08);
|
|
30998
|
+
min-width: 320px;
|
|
30999
|
+
max-width: 400px;
|
|
31000
|
+
font-size: 12px;
|
|
31001
|
+
color: #1e293b;
|
|
31002
|
+
overflow: hidden;
|
|
31003
|
+
font-family: Inter, system-ui, -apple-system, sans-serif;
|
|
31004
|
+
}
|
|
31005
|
+
|
|
31006
|
+
.myio-info-tooltip__header {
|
|
31007
|
+
display: flex;
|
|
31008
|
+
align-items: center;
|
|
31009
|
+
gap: 8px;
|
|
31010
|
+
padding: 12px 16px;
|
|
31011
|
+
background: linear-gradient(90deg, #f1f5f9 0%, #e2e8f0 100%);
|
|
31012
|
+
border-bottom: 1px solid #cbd5e1;
|
|
31013
|
+
cursor: move;
|
|
31014
|
+
user-select: none;
|
|
31015
|
+
}
|
|
31016
|
+
|
|
31017
|
+
.myio-info-tooltip__icon {
|
|
31018
|
+
font-size: 18px;
|
|
31019
|
+
}
|
|
31020
|
+
|
|
31021
|
+
.myio-info-tooltip__title {
|
|
31022
|
+
font-weight: 700;
|
|
31023
|
+
font-size: 14px;
|
|
31024
|
+
color: #475569;
|
|
31025
|
+
letter-spacing: 0.3px;
|
|
31026
|
+
flex: 1;
|
|
31027
|
+
}
|
|
31028
|
+
|
|
31029
|
+
.myio-info-tooltip__header-actions {
|
|
31030
|
+
display: flex;
|
|
31031
|
+
align-items: center;
|
|
31032
|
+
gap: 4px;
|
|
31033
|
+
}
|
|
31034
|
+
|
|
31035
|
+
.myio-info-tooltip__header-btn {
|
|
31036
|
+
width: 24px;
|
|
31037
|
+
height: 24px;
|
|
31038
|
+
border: none;
|
|
31039
|
+
background: rgba(255, 255, 255, 0.6);
|
|
31040
|
+
border-radius: 4px;
|
|
31041
|
+
cursor: pointer;
|
|
31042
|
+
display: flex;
|
|
31043
|
+
align-items: center;
|
|
31044
|
+
justify-content: center;
|
|
31045
|
+
transition: all 0.15s ease;
|
|
31046
|
+
color: #64748b;
|
|
31047
|
+
}
|
|
31048
|
+
|
|
31049
|
+
.myio-info-tooltip__header-btn:hover {
|
|
31050
|
+
background: rgba(255, 255, 255, 0.9);
|
|
31051
|
+
color: #1e293b;
|
|
31052
|
+
}
|
|
31053
|
+
|
|
31054
|
+
.myio-info-tooltip__header-btn.pinned {
|
|
31055
|
+
background: #047857;
|
|
31056
|
+
color: white;
|
|
31057
|
+
}
|
|
31058
|
+
|
|
31059
|
+
.myio-info-tooltip__header-btn.pinned:hover {
|
|
31060
|
+
background: #065f46;
|
|
31061
|
+
color: white;
|
|
31062
|
+
}
|
|
31063
|
+
|
|
31064
|
+
.myio-info-tooltip__header-btn svg {
|
|
31065
|
+
width: 14px;
|
|
31066
|
+
height: 14px;
|
|
31067
|
+
}
|
|
31068
|
+
|
|
31069
|
+
.myio-info-tooltip__content {
|
|
31070
|
+
padding: 16px;
|
|
31071
|
+
max-height: 500px;
|
|
31072
|
+
overflow-y: auto;
|
|
31073
|
+
}
|
|
31074
|
+
|
|
31075
|
+
/* Content styles */
|
|
31076
|
+
.myio-info-tooltip__section {
|
|
31077
|
+
margin-bottom: 14px;
|
|
31078
|
+
padding-bottom: 12px;
|
|
31079
|
+
border-bottom: 1px solid #f1f5f9;
|
|
31080
|
+
}
|
|
31081
|
+
|
|
31082
|
+
.myio-info-tooltip__section:last-child {
|
|
31083
|
+
margin-bottom: 0;
|
|
31084
|
+
padding-bottom: 0;
|
|
31085
|
+
border-bottom: none;
|
|
31086
|
+
}
|
|
31087
|
+
|
|
31088
|
+
.myio-info-tooltip__section-title {
|
|
31089
|
+
font-size: 11px;
|
|
31090
|
+
font-weight: 600;
|
|
31091
|
+
color: #64748b;
|
|
31092
|
+
text-transform: uppercase;
|
|
31093
|
+
letter-spacing: 0.8px;
|
|
31094
|
+
margin-bottom: 10px;
|
|
31095
|
+
display: flex;
|
|
31096
|
+
align-items: center;
|
|
31097
|
+
gap: 6px;
|
|
31098
|
+
}
|
|
31099
|
+
|
|
31100
|
+
.myio-info-tooltip__row {
|
|
31101
|
+
display: flex;
|
|
31102
|
+
justify-content: space-between;
|
|
31103
|
+
align-items: center;
|
|
31104
|
+
padding: 5px 0;
|
|
31105
|
+
gap: 12px;
|
|
31106
|
+
}
|
|
31107
|
+
|
|
31108
|
+
.myio-info-tooltip__label {
|
|
31109
|
+
color: #64748b;
|
|
31110
|
+
font-size: 12px;
|
|
31111
|
+
flex-shrink: 0;
|
|
31112
|
+
}
|
|
31113
|
+
|
|
31114
|
+
.myio-info-tooltip__value {
|
|
31115
|
+
color: #1e293b;
|
|
31116
|
+
font-weight: 600;
|
|
31117
|
+
text-align: right;
|
|
31118
|
+
}
|
|
31119
|
+
|
|
31120
|
+
.myio-info-tooltip__value--highlight {
|
|
31121
|
+
color: #10b981;
|
|
31122
|
+
font-weight: 700;
|
|
31123
|
+
font-size: 14px;
|
|
31124
|
+
}
|
|
31125
|
+
|
|
31126
|
+
.myio-info-tooltip__notice {
|
|
31127
|
+
display: flex;
|
|
31128
|
+
align-items: flex-start;
|
|
31129
|
+
gap: 10px;
|
|
31130
|
+
padding: 12px 14px;
|
|
31131
|
+
background: #f0fdf4;
|
|
31132
|
+
border: 1px solid #bbf7d0;
|
|
31133
|
+
border-radius: 8px;
|
|
31134
|
+
margin-top: 12px;
|
|
31135
|
+
}
|
|
31136
|
+
|
|
31137
|
+
.myio-info-tooltip__notice-icon {
|
|
31138
|
+
font-size: 14px;
|
|
31139
|
+
flex-shrink: 0;
|
|
31140
|
+
margin-top: 1px;
|
|
31141
|
+
}
|
|
31142
|
+
|
|
31143
|
+
.myio-info-tooltip__notice-text {
|
|
31144
|
+
font-size: 11px;
|
|
31145
|
+
color: #475569;
|
|
31146
|
+
line-height: 1.5;
|
|
31147
|
+
}
|
|
31148
|
+
|
|
31149
|
+
.myio-info-tooltip__notice-text strong {
|
|
31150
|
+
font-weight: 700;
|
|
31151
|
+
color: #334155;
|
|
31152
|
+
}
|
|
31153
|
+
|
|
31154
|
+
.myio-info-tooltip__category {
|
|
31155
|
+
display: flex;
|
|
31156
|
+
align-items: center;
|
|
31157
|
+
gap: 10px;
|
|
31158
|
+
padding: 8px 12px;
|
|
31159
|
+
background: #f8fafc;
|
|
31160
|
+
border-radius: 8px;
|
|
31161
|
+
margin-bottom: 6px;
|
|
31162
|
+
border-left: 3px solid #94a3b8;
|
|
31163
|
+
}
|
|
31164
|
+
|
|
31165
|
+
.myio-info-tooltip__category:last-child {
|
|
31166
|
+
margin-bottom: 0;
|
|
31167
|
+
}
|
|
31168
|
+
|
|
31169
|
+
.myio-info-tooltip__category--climatizacao {
|
|
31170
|
+
border-left-color: #00C896;
|
|
31171
|
+
background: #ecfdf5;
|
|
31172
|
+
}
|
|
31173
|
+
|
|
31174
|
+
.myio-info-tooltip__category--outros {
|
|
31175
|
+
border-left-color: #9C27B0;
|
|
31176
|
+
background: #fdf4ff;
|
|
31177
|
+
}
|
|
31178
|
+
|
|
31179
|
+
.myio-info-tooltip__category-icon {
|
|
31180
|
+
font-size: 14px;
|
|
31181
|
+
flex-shrink: 0;
|
|
31182
|
+
}
|
|
31183
|
+
|
|
31184
|
+
.myio-info-tooltip__category-info {
|
|
31185
|
+
flex: 1;
|
|
31186
|
+
}
|
|
31187
|
+
|
|
31188
|
+
.myio-info-tooltip__category-name {
|
|
31189
|
+
font-weight: 600;
|
|
31190
|
+
color: #334155;
|
|
31191
|
+
font-size: 12px;
|
|
31192
|
+
}
|
|
31193
|
+
|
|
31194
|
+
.myio-info-tooltip__category-desc {
|
|
31195
|
+
font-size: 10px;
|
|
31196
|
+
color: #64748b;
|
|
31197
|
+
margin-top: 2px;
|
|
31198
|
+
}
|
|
31199
|
+
|
|
31200
|
+
.myio-info-tooltip__category-value {
|
|
31201
|
+
font-weight: 700;
|
|
31202
|
+
color: #334155;
|
|
31203
|
+
font-size: 13px;
|
|
31204
|
+
}
|
|
31205
|
+
`;
|
|
31206
|
+
var cssInjected4 = false;
|
|
31207
|
+
function injectCSS4() {
|
|
31208
|
+
if (cssInjected4) return;
|
|
31209
|
+
if (typeof document === "undefined") return;
|
|
31210
|
+
const styleId = "myio-info-tooltip-styles";
|
|
31211
|
+
if (document.getElementById(styleId)) {
|
|
31212
|
+
cssInjected4 = true;
|
|
31213
|
+
return;
|
|
31214
|
+
}
|
|
31215
|
+
const style = document.createElement("style");
|
|
31216
|
+
style.id = styleId;
|
|
31217
|
+
style.textContent = INFO_TOOLTIP_CSS;
|
|
31218
|
+
document.head.appendChild(style);
|
|
31219
|
+
cssInjected4 = true;
|
|
31220
|
+
}
|
|
31221
|
+
var state = {
|
|
31222
|
+
hideTimer: null,
|
|
31223
|
+
isMouseOverTooltip: false,
|
|
31224
|
+
isMaximized: false,
|
|
31225
|
+
isDragging: false,
|
|
31226
|
+
dragOffset: { x: 0, y: 0 },
|
|
31227
|
+
savedPosition: null,
|
|
31228
|
+
pinnedCounter: 0
|
|
31229
|
+
};
|
|
31230
|
+
function generateHeaderHTML(icon, title) {
|
|
31231
|
+
return `
|
|
31232
|
+
<div class="myio-info-tooltip__header" data-drag-handle>
|
|
31233
|
+
<span class="myio-info-tooltip__icon">${icon}</span>
|
|
31234
|
+
<span class="myio-info-tooltip__title">${title}</span>
|
|
31235
|
+
<div class="myio-info-tooltip__header-actions">
|
|
31236
|
+
<button class="myio-info-tooltip__header-btn" data-action="pin" title="Fixar na tela">
|
|
31237
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
31238
|
+
<path d="M9 4v6l-2 4v2h10v-2l-2-4V4"/>
|
|
31239
|
+
<line x1="12" y1="16" x2="12" y2="21"/>
|
|
31240
|
+
<line x1="8" y1="4" x2="16" y2="4"/>
|
|
31241
|
+
</svg>
|
|
31242
|
+
</button>
|
|
31243
|
+
<button class="myio-info-tooltip__header-btn" data-action="maximize" title="Maximizar">
|
|
31244
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
31245
|
+
<rect x="3" y="3" width="18" height="18" rx="2"/>
|
|
31246
|
+
</svg>
|
|
31247
|
+
</button>
|
|
31248
|
+
<button class="myio-info-tooltip__header-btn" data-action="close" title="Fechar">
|
|
31249
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
31250
|
+
<path d="M18 6L6 18M6 6l12 12"/>
|
|
31251
|
+
</svg>
|
|
31252
|
+
</button>
|
|
31253
|
+
</div>
|
|
31254
|
+
</div>
|
|
31255
|
+
`;
|
|
31256
|
+
}
|
|
31257
|
+
function setupHoverListeners(container) {
|
|
31258
|
+
container.onmouseenter = () => {
|
|
31259
|
+
state.isMouseOverTooltip = true;
|
|
31260
|
+
if (state.hideTimer) {
|
|
31261
|
+
clearTimeout(state.hideTimer);
|
|
31262
|
+
state.hideTimer = null;
|
|
31263
|
+
}
|
|
31264
|
+
};
|
|
31265
|
+
container.onmouseleave = () => {
|
|
31266
|
+
state.isMouseOverTooltip = false;
|
|
31267
|
+
startDelayedHide();
|
|
31268
|
+
};
|
|
31269
|
+
}
|
|
31270
|
+
function setupButtonListeners(container) {
|
|
31271
|
+
const buttons = container.querySelectorAll("[data-action]");
|
|
31272
|
+
buttons.forEach((btn) => {
|
|
31273
|
+
btn.onclick = (e) => {
|
|
31274
|
+
e.stopPropagation();
|
|
31275
|
+
const action = btn.dataset.action;
|
|
31276
|
+
switch (action) {
|
|
31277
|
+
case "pin":
|
|
31278
|
+
createPinnedClone(container);
|
|
31279
|
+
break;
|
|
31280
|
+
case "maximize":
|
|
31281
|
+
toggleMaximize(container);
|
|
31282
|
+
break;
|
|
31283
|
+
case "close":
|
|
31284
|
+
InfoTooltip.close();
|
|
31285
|
+
break;
|
|
31286
|
+
}
|
|
31287
|
+
};
|
|
31288
|
+
});
|
|
31289
|
+
}
|
|
31290
|
+
function setupDragListeners(container) {
|
|
31291
|
+
const header = container.querySelector("[data-drag-handle]");
|
|
31292
|
+
if (!header) return;
|
|
31293
|
+
header.onmousedown = (e) => {
|
|
31294
|
+
if (e.target.closest("[data-action]")) return;
|
|
31295
|
+
if (state.isMaximized) return;
|
|
31296
|
+
state.isDragging = true;
|
|
31297
|
+
container.classList.add("dragging");
|
|
31298
|
+
const rect = container.getBoundingClientRect();
|
|
31299
|
+
state.dragOffset = {
|
|
31300
|
+
x: e.clientX - rect.left,
|
|
31301
|
+
y: e.clientY - rect.top
|
|
31302
|
+
};
|
|
31303
|
+
const onMouseMove = (e2) => {
|
|
31304
|
+
if (!state.isDragging) return;
|
|
31305
|
+
const newLeft = e2.clientX - state.dragOffset.x;
|
|
31306
|
+
const newTop = e2.clientY - state.dragOffset.y;
|
|
31307
|
+
const maxLeft = window.innerWidth - container.offsetWidth;
|
|
31308
|
+
const maxTop = window.innerHeight - container.offsetHeight;
|
|
31309
|
+
container.style.left = Math.max(0, Math.min(newLeft, maxLeft)) + "px";
|
|
31310
|
+
container.style.top = Math.max(0, Math.min(newTop, maxTop)) + "px";
|
|
31311
|
+
};
|
|
31312
|
+
const onMouseUp = () => {
|
|
31313
|
+
state.isDragging = false;
|
|
31314
|
+
container.classList.remove("dragging");
|
|
31315
|
+
document.removeEventListener("mousemove", onMouseMove);
|
|
31316
|
+
document.removeEventListener("mouseup", onMouseUp);
|
|
31317
|
+
};
|
|
31318
|
+
document.addEventListener("mousemove", onMouseMove);
|
|
31319
|
+
document.addEventListener("mouseup", onMouseUp);
|
|
31320
|
+
};
|
|
31321
|
+
}
|
|
31322
|
+
function createPinnedClone(container) {
|
|
31323
|
+
state.pinnedCounter++;
|
|
31324
|
+
const pinnedId = `myio-info-tooltip-pinned-${state.pinnedCounter}`;
|
|
31325
|
+
const clone = container.cloneNode(true);
|
|
31326
|
+
clone.id = pinnedId;
|
|
31327
|
+
clone.classList.add("pinned");
|
|
31328
|
+
clone.classList.remove("closing");
|
|
31329
|
+
const pinBtn = clone.querySelector('[data-action="pin"]');
|
|
31330
|
+
if (pinBtn) {
|
|
31331
|
+
pinBtn.classList.add("pinned");
|
|
31332
|
+
pinBtn.setAttribute("title", "Desafixar");
|
|
31333
|
+
pinBtn.innerHTML = `
|
|
31334
|
+
<svg viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="1">
|
|
31335
|
+
<path d="M9 4v6l-2 4v2h10v-2l-2-4V4"/>
|
|
31336
|
+
<line x1="12" y1="16" x2="12" y2="21"/>
|
|
31337
|
+
<line x1="8" y1="4" x2="16" y2="4"/>
|
|
31338
|
+
</svg>
|
|
31339
|
+
`;
|
|
31340
|
+
}
|
|
31341
|
+
document.body.appendChild(clone);
|
|
31342
|
+
setupPinnedCloneListeners(clone, pinnedId);
|
|
31343
|
+
InfoTooltip.hide();
|
|
31344
|
+
}
|
|
31345
|
+
function setupPinnedCloneListeners(clone, cloneId) {
|
|
31346
|
+
let isMaximized = false;
|
|
31347
|
+
let savedPosition = null;
|
|
31348
|
+
const pinBtn = clone.querySelector('[data-action="pin"]');
|
|
31349
|
+
if (pinBtn) {
|
|
31350
|
+
pinBtn.onclick = (e) => {
|
|
31351
|
+
e.stopPropagation();
|
|
31352
|
+
closePinnedClone(cloneId);
|
|
31353
|
+
};
|
|
31354
|
+
}
|
|
31355
|
+
const closeBtn = clone.querySelector('[data-action="close"]');
|
|
31356
|
+
if (closeBtn) {
|
|
31357
|
+
closeBtn.onclick = (e) => {
|
|
31358
|
+
e.stopPropagation();
|
|
31359
|
+
closePinnedClone(cloneId);
|
|
31360
|
+
};
|
|
31361
|
+
}
|
|
31362
|
+
const maxBtn = clone.querySelector('[data-action="maximize"]');
|
|
31363
|
+
if (maxBtn) {
|
|
31364
|
+
maxBtn.onclick = (e) => {
|
|
31365
|
+
e.stopPropagation();
|
|
31366
|
+
isMaximized = !isMaximized;
|
|
31367
|
+
if (isMaximized) {
|
|
31368
|
+
savedPosition = { left: clone.style.left, top: clone.style.top };
|
|
31369
|
+
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>`;
|
|
31370
|
+
maxBtn.setAttribute("title", "Restaurar");
|
|
31371
|
+
} else {
|
|
31372
|
+
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>`;
|
|
31373
|
+
maxBtn.setAttribute("title", "Maximizar");
|
|
31374
|
+
if (savedPosition) {
|
|
31375
|
+
clone.style.left = savedPosition.left;
|
|
31376
|
+
clone.style.top = savedPosition.top;
|
|
31377
|
+
}
|
|
31378
|
+
}
|
|
31379
|
+
clone.classList.toggle("maximized", isMaximized);
|
|
31380
|
+
};
|
|
31381
|
+
}
|
|
31382
|
+
const header = clone.querySelector("[data-drag-handle]");
|
|
31383
|
+
if (header) {
|
|
31384
|
+
let isDragging = false;
|
|
31385
|
+
let dragOffset = { x: 0, y: 0 };
|
|
31386
|
+
header.onmousedown = (e) => {
|
|
31387
|
+
if (e.target.closest("[data-action]")) return;
|
|
31388
|
+
if (isMaximized) return;
|
|
31389
|
+
isDragging = true;
|
|
31390
|
+
clone.classList.add("dragging");
|
|
31391
|
+
const rect = clone.getBoundingClientRect();
|
|
31392
|
+
dragOffset = { x: e.clientX - rect.left, y: e.clientY - rect.top };
|
|
31393
|
+
const onMouseMove = (e2) => {
|
|
31394
|
+
if (!isDragging) return;
|
|
31395
|
+
const newLeft = e2.clientX - dragOffset.x;
|
|
31396
|
+
const newTop = e2.clientY - dragOffset.y;
|
|
31397
|
+
const maxLeft = window.innerWidth - clone.offsetWidth;
|
|
31398
|
+
const maxTop = window.innerHeight - clone.offsetHeight;
|
|
31399
|
+
clone.style.left = Math.max(0, Math.min(newLeft, maxLeft)) + "px";
|
|
31400
|
+
clone.style.top = Math.max(0, Math.min(newTop, maxTop)) + "px";
|
|
31401
|
+
};
|
|
31402
|
+
const onMouseUp = () => {
|
|
31403
|
+
isDragging = false;
|
|
31404
|
+
clone.classList.remove("dragging");
|
|
31405
|
+
document.removeEventListener("mousemove", onMouseMove);
|
|
31406
|
+
document.removeEventListener("mouseup", onMouseUp);
|
|
31407
|
+
};
|
|
31408
|
+
document.addEventListener("mousemove", onMouseMove);
|
|
31409
|
+
document.addEventListener("mouseup", onMouseUp);
|
|
31410
|
+
};
|
|
31411
|
+
}
|
|
31412
|
+
}
|
|
31413
|
+
function closePinnedClone(cloneId) {
|
|
31414
|
+
const clone = document.getElementById(cloneId);
|
|
31415
|
+
if (clone) {
|
|
31416
|
+
clone.classList.add("closing");
|
|
31417
|
+
setTimeout(() => clone.remove(), 400);
|
|
31418
|
+
}
|
|
31419
|
+
}
|
|
31420
|
+
function toggleMaximize(container) {
|
|
31421
|
+
state.isMaximized = !state.isMaximized;
|
|
31422
|
+
if (state.isMaximized) {
|
|
31423
|
+
state.savedPosition = {
|
|
31424
|
+
left: container.style.left,
|
|
31425
|
+
top: container.style.top
|
|
31426
|
+
};
|
|
31427
|
+
}
|
|
31428
|
+
container.classList.toggle("maximized", state.isMaximized);
|
|
31429
|
+
const maxBtn = container.querySelector('[data-action="maximize"]');
|
|
31430
|
+
if (maxBtn) {
|
|
31431
|
+
if (state.isMaximized) {
|
|
31432
|
+
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>`;
|
|
31433
|
+
maxBtn.setAttribute("title", "Restaurar");
|
|
31434
|
+
} else {
|
|
31435
|
+
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>`;
|
|
31436
|
+
maxBtn.setAttribute("title", "Maximizar");
|
|
31437
|
+
if (state.savedPosition) {
|
|
31438
|
+
container.style.left = state.savedPosition.left;
|
|
31439
|
+
container.style.top = state.savedPosition.top;
|
|
31440
|
+
}
|
|
31441
|
+
}
|
|
31442
|
+
}
|
|
31443
|
+
}
|
|
31444
|
+
function startDelayedHide() {
|
|
31445
|
+
if (state.isMouseOverTooltip) return;
|
|
31446
|
+
if (state.hideTimer) {
|
|
31447
|
+
clearTimeout(state.hideTimer);
|
|
31448
|
+
}
|
|
31449
|
+
state.hideTimer = setTimeout(() => {
|
|
31450
|
+
hideWithAnimation();
|
|
31451
|
+
}, 1500);
|
|
31452
|
+
}
|
|
31453
|
+
function hideWithAnimation() {
|
|
31454
|
+
const container = document.getElementById("myio-info-tooltip");
|
|
31455
|
+
if (container && container.classList.contains("visible")) {
|
|
31456
|
+
container.classList.add("closing");
|
|
31457
|
+
setTimeout(() => {
|
|
31458
|
+
container.classList.remove("visible", "closing");
|
|
31459
|
+
}, 400);
|
|
31460
|
+
}
|
|
31461
|
+
}
|
|
31462
|
+
function positionTooltip(container, triggerElement) {
|
|
31463
|
+
const rect = triggerElement.getBoundingClientRect();
|
|
31464
|
+
let left = rect.left;
|
|
31465
|
+
let top = rect.bottom + 8;
|
|
31466
|
+
const tooltipWidth = 380;
|
|
31467
|
+
if (left + tooltipWidth > window.innerWidth - 20) {
|
|
31468
|
+
left = window.innerWidth - tooltipWidth - 20;
|
|
31469
|
+
}
|
|
31470
|
+
if (left < 10) left = 10;
|
|
31471
|
+
if (top + 400 > window.innerHeight) {
|
|
31472
|
+
top = rect.top - 8 - 400;
|
|
31473
|
+
if (top < 10) top = 10;
|
|
31474
|
+
}
|
|
31475
|
+
container.style.left = left + "px";
|
|
31476
|
+
container.style.top = top + "px";
|
|
31477
|
+
}
|
|
31478
|
+
var InfoTooltip = {
|
|
31479
|
+
containerId: "myio-info-tooltip",
|
|
31480
|
+
/**
|
|
31481
|
+
* Get or create container
|
|
31482
|
+
*/
|
|
31483
|
+
getContainer() {
|
|
31484
|
+
injectCSS4();
|
|
31485
|
+
let container = document.getElementById(this.containerId);
|
|
31486
|
+
if (!container) {
|
|
31487
|
+
container = document.createElement("div");
|
|
31488
|
+
container.id = this.containerId;
|
|
31489
|
+
container.className = "myio-info-tooltip";
|
|
31490
|
+
document.body.appendChild(container);
|
|
31491
|
+
}
|
|
31492
|
+
return container;
|
|
31493
|
+
},
|
|
31494
|
+
/**
|
|
31495
|
+
* Show tooltip
|
|
31496
|
+
*/
|
|
31497
|
+
show(triggerElement, options) {
|
|
31498
|
+
if (state.hideTimer) {
|
|
31499
|
+
clearTimeout(state.hideTimer);
|
|
31500
|
+
state.hideTimer = null;
|
|
31501
|
+
}
|
|
31502
|
+
const container = this.getContainer();
|
|
31503
|
+
container.classList.remove("closing");
|
|
31504
|
+
container.innerHTML = `
|
|
31505
|
+
<div class="myio-info-tooltip__panel">
|
|
31506
|
+
${generateHeaderHTML(options.icon, options.title)}
|
|
31507
|
+
<div class="myio-info-tooltip__content">
|
|
31508
|
+
${options.content}
|
|
31509
|
+
</div>
|
|
31510
|
+
</div>
|
|
31511
|
+
`;
|
|
31512
|
+
positionTooltip(container, triggerElement);
|
|
31513
|
+
container.classList.add("visible");
|
|
31514
|
+
setupHoverListeners(container);
|
|
31515
|
+
setupButtonListeners(container);
|
|
31516
|
+
setupDragListeners(container);
|
|
31517
|
+
},
|
|
31518
|
+
/**
|
|
31519
|
+
* Start delayed hide
|
|
31520
|
+
*/
|
|
31521
|
+
startDelayedHide() {
|
|
31522
|
+
startDelayedHide();
|
|
31523
|
+
},
|
|
31524
|
+
/**
|
|
31525
|
+
* Hide immediately
|
|
31526
|
+
*/
|
|
31527
|
+
hide() {
|
|
31528
|
+
if (state.hideTimer) {
|
|
31529
|
+
clearTimeout(state.hideTimer);
|
|
31530
|
+
state.hideTimer = null;
|
|
31531
|
+
}
|
|
31532
|
+
state.isMouseOverTooltip = false;
|
|
31533
|
+
const container = document.getElementById(this.containerId);
|
|
31534
|
+
if (container) {
|
|
31535
|
+
container.classList.remove("visible", "closing");
|
|
31536
|
+
}
|
|
31537
|
+
},
|
|
31538
|
+
/**
|
|
31539
|
+
* Close and reset all states
|
|
31540
|
+
*/
|
|
31541
|
+
close() {
|
|
31542
|
+
state.isMaximized = false;
|
|
31543
|
+
state.isDragging = false;
|
|
31544
|
+
state.savedPosition = null;
|
|
31545
|
+
if (state.hideTimer) {
|
|
31546
|
+
clearTimeout(state.hideTimer);
|
|
31547
|
+
state.hideTimer = null;
|
|
31548
|
+
}
|
|
31549
|
+
state.isMouseOverTooltip = false;
|
|
31550
|
+
const container = document.getElementById(this.containerId);
|
|
31551
|
+
if (container) {
|
|
31552
|
+
container.classList.remove("visible", "pinned", "maximized", "dragging", "closing");
|
|
31553
|
+
}
|
|
31554
|
+
},
|
|
31555
|
+
/**
|
|
31556
|
+
* Attach tooltip to trigger element with hover behavior
|
|
31557
|
+
*/
|
|
31558
|
+
attach(triggerElement, getOptions) {
|
|
31559
|
+
const self = this;
|
|
31560
|
+
const handleMouseEnter = () => {
|
|
31561
|
+
if (state.hideTimer) {
|
|
31562
|
+
clearTimeout(state.hideTimer);
|
|
31563
|
+
state.hideTimer = null;
|
|
31564
|
+
}
|
|
31565
|
+
const options = getOptions();
|
|
31566
|
+
self.show(triggerElement, options);
|
|
31567
|
+
};
|
|
31568
|
+
const handleMouseLeave = () => {
|
|
31569
|
+
startDelayedHide();
|
|
31570
|
+
};
|
|
31571
|
+
triggerElement.addEventListener("mouseenter", handleMouseEnter);
|
|
31572
|
+
triggerElement.addEventListener("mouseleave", handleMouseLeave);
|
|
31573
|
+
return () => {
|
|
31574
|
+
triggerElement.removeEventListener("mouseenter", handleMouseEnter);
|
|
31575
|
+
triggerElement.removeEventListener("mouseleave", handleMouseLeave);
|
|
31576
|
+
self.hide();
|
|
31577
|
+
};
|
|
31578
|
+
}
|
|
31579
|
+
};
|
|
31580
|
+
|
|
30550
31581
|
// src/components/ModalHeader/index.ts
|
|
30551
31582
|
var DEFAULT_BG_COLOR = "#3e1a7d";
|
|
30552
31583
|
var DEFAULT_TEXT_COLOR = "white";
|
|
@@ -34320,6 +35351,7 @@
|
|
|
34320
35351
|
exports.IMPORTANCE_COLORS = IMPORTANCE_COLORS;
|
|
34321
35352
|
exports.IMPORTANCE_LABELS = IMPORTANCE_LABELS;
|
|
34322
35353
|
exports.IMPORTANCE_LABELS_EN = IMPORTANCE_LABELS_EN;
|
|
35354
|
+
exports.InfoTooltip = InfoTooltip;
|
|
34323
35355
|
exports.MyIOChartModal = MyIOChartModal;
|
|
34324
35356
|
exports.MyIODraggableCard = MyIODraggableCard;
|
|
34325
35357
|
exports.MyIOSelectionStoreClass = MyIOSelectionStoreClass;
|