myio-js-library 0.1.28 → 0.1.30
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 +472 -88
- package/dist/index.d.ts +12 -24
- package/dist/index.js +472 -88
- package/dist/myio-js-library.umd.js +472 -88
- package/dist/myio-js-library.umd.min.js +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1297,39 +1297,77 @@ function findValue(data, keyOrPath, legacyDataKey) {
|
|
|
1297
1297
|
}
|
|
1298
1298
|
|
|
1299
1299
|
// src/utils/deviceType.js
|
|
1300
|
+
function normalize(str) {
|
|
1301
|
+
if (typeof str !== "string") return "";
|
|
1302
|
+
let normalized = str.toUpperCase().trim();
|
|
1303
|
+
normalized = normalized.replace(/[ÀÁÂÃÄÅ]/g, "A").replace(/[ÈÉÊË]/g, "E").replace(/[ÌÍÎÏ]/g, "I").replace(/[ÒÓÔÕÖ]/g, "O").replace(/[ÙÚÛÜ]/g, "U").replace(/[Ç]/g, "C").replace(/[Ñ]/g, "N");
|
|
1304
|
+
normalized = normalized.replace(/\s+/g, " ");
|
|
1305
|
+
return normalized;
|
|
1306
|
+
}
|
|
1307
|
+
function matchCaixaDAgua(normalizedStr) {
|
|
1308
|
+
if (normalizedStr.includes("SCD")) return true;
|
|
1309
|
+
const caixaVariants = [
|
|
1310
|
+
"CAIXA D'AGUA",
|
|
1311
|
+
"CAIXA D AGUA",
|
|
1312
|
+
"CAIXA_D_AGUA",
|
|
1313
|
+
"CAIXA DAGUA"
|
|
1314
|
+
];
|
|
1315
|
+
return caixaVariants.some((variant) => normalizedStr.includes(variant));
|
|
1316
|
+
}
|
|
1300
1317
|
var contexts = {
|
|
1301
1318
|
building: (name) => {
|
|
1302
|
-
const
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1319
|
+
const normalized = normalize(name);
|
|
1320
|
+
const rules = [
|
|
1321
|
+
{ test: (s) => s.includes("COMPRESSOR"), type: "COMPRESSOR" },
|
|
1322
|
+
{ test: (s) => s.includes("VENT"), type: "VENTILADOR" },
|
|
1323
|
+
{ test: (s) => s.includes("AUTOMATICO"), type: "SELETOR_AUTO_MANUAL" },
|
|
1324
|
+
{ test: (s) => s.includes("ESRL"), type: "ESCADA_ROLANTE" },
|
|
1325
|
+
{ test: (s) => s.includes("ESCADA"), type: "ESCADA_ROLANTE" },
|
|
1326
|
+
{ test: (s) => s.includes("ELEV"), type: "ELEVADOR" },
|
|
1327
|
+
{ test: (s) => s.includes("MOTR") || s.includes("RECALQUE"), type: "MOTOR" },
|
|
1328
|
+
{ test: (s) => s.includes("TERMOSTATO"), type: "TERMOSTATO" },
|
|
1329
|
+
{ test: (s) => s.includes("TERMO") || s.includes("TEMP"), type: "TERMOSTATO" },
|
|
1330
|
+
{ test: (s) => s.includes("3F"), type: "3F_MEDIDOR" },
|
|
1331
|
+
{ test: (s) => s.includes("HIDR"), type: "HIDROMETRO" },
|
|
1332
|
+
{ test: (s) => s.includes("ABRE"), type: "SOLENOIDE" },
|
|
1333
|
+
{ test: (s) => matchCaixaDAgua(s), type: "CAIXA_D_AGUA" },
|
|
1334
|
+
{ test: (s) => s.includes("AUTOMACAO") || s.includes("GW_AUTO"), type: "GLOBAL_AUTOMACAO" },
|
|
1335
|
+
{ test: (s) => s.includes("AC"), type: "CONTROLE REMOTO" }
|
|
1336
|
+
];
|
|
1337
|
+
for (const rule of rules) {
|
|
1338
|
+
if (rule.test(normalized)) {
|
|
1339
|
+
return rule.type;
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1315
1342
|
return "default";
|
|
1316
1343
|
},
|
|
1317
1344
|
mall: (name) => {
|
|
1318
|
-
const
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1345
|
+
const normalized = normalize(name);
|
|
1346
|
+
const rules = [
|
|
1347
|
+
{ test: (s) => s.includes("CHILLER"), type: "CHILLER" },
|
|
1348
|
+
{ test: (s) => s.includes("ESCADA"), type: "ESCADA_ROLANTE" },
|
|
1349
|
+
{ test: (s) => s.includes("LOJA"), type: "LOJA_SENSOR" },
|
|
1350
|
+
{ test: (s) => s.includes("ILUMINACAO"), type: "ILUMINACAO" }
|
|
1351
|
+
];
|
|
1352
|
+
for (const rule of rules) {
|
|
1353
|
+
if (rule.test(normalized)) {
|
|
1354
|
+
return rule.type;
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1323
1357
|
return "default";
|
|
1324
1358
|
}
|
|
1325
1359
|
};
|
|
1360
|
+
var warnedContexts = /* @__PURE__ */ new Set();
|
|
1326
1361
|
function detectDeviceType(name, context = "building") {
|
|
1327
1362
|
if (typeof name !== "string") {
|
|
1328
1363
|
throw new Error("Device name must be a string.");
|
|
1329
1364
|
}
|
|
1330
1365
|
const detectFunction = contexts[context];
|
|
1331
1366
|
if (!detectFunction) {
|
|
1332
|
-
|
|
1367
|
+
if (!warnedContexts.has(context)) {
|
|
1368
|
+
console.warn(`[myio-js-library] Context "${context}" not found. Using default fallback.`);
|
|
1369
|
+
warnedContexts.add(context);
|
|
1370
|
+
}
|
|
1333
1371
|
return contexts.building(name);
|
|
1334
1372
|
}
|
|
1335
1373
|
return detectFunction(name);
|
|
@@ -2112,6 +2150,85 @@ function renderCardComponentV2({
|
|
|
2112
2150
|
timeVal,
|
|
2113
2151
|
valType
|
|
2114
2152
|
} = entityObject;
|
|
2153
|
+
const MyIOToast = (function() {
|
|
2154
|
+
let toastContainer = null;
|
|
2155
|
+
let toastTimeout = null;
|
|
2156
|
+
const TOAST_CSS = `
|
|
2157
|
+
#myio-global-toast-container {
|
|
2158
|
+
position: fixed;
|
|
2159
|
+
top: 25px;
|
|
2160
|
+
right: 25px;
|
|
2161
|
+
z-index: 99999;
|
|
2162
|
+
width: 320px;
|
|
2163
|
+
padding: 16px;
|
|
2164
|
+
border-radius: 8px;
|
|
2165
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
|
2166
|
+
font-size: 15px;
|
|
2167
|
+
color: #fff;
|
|
2168
|
+
transform: translateX(120%);
|
|
2169
|
+
transition: transform 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);
|
|
2170
|
+
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
|
|
2171
|
+
border-left: 5px solid transparent;
|
|
2172
|
+
display: flex;
|
|
2173
|
+
align-items: center;
|
|
2174
|
+
}
|
|
2175
|
+
#myio-global-toast-container.show {
|
|
2176
|
+
transform: translateX(0);
|
|
2177
|
+
}
|
|
2178
|
+
#myio-global-toast-container.warning {
|
|
2179
|
+
background-color: #ff9800; /* Laranja para alerta */
|
|
2180
|
+
border-color: #f57c00;
|
|
2181
|
+
}
|
|
2182
|
+
#myio-global-toast-container.error {
|
|
2183
|
+
background-color: #d32f2f; /* Vermelho para erro */
|
|
2184
|
+
border-color: #b71c1c;
|
|
2185
|
+
}
|
|
2186
|
+
#myio-global-toast-container::before {
|
|
2187
|
+
content: '\u26A0\uFE0F'; /* \xCDcone de alerta */
|
|
2188
|
+
margin-right: 12px;
|
|
2189
|
+
font-size: 20px;
|
|
2190
|
+
}
|
|
2191
|
+
#myio-global-toast-container.error::before {
|
|
2192
|
+
content: '\u{1F6AB}'; /* \xCDcone de erro */
|
|
2193
|
+
}
|
|
2194
|
+
`;
|
|
2195
|
+
function createToastElement() {
|
|
2196
|
+
if (document.getElementById("myio-global-toast-container")) {
|
|
2197
|
+
toastContainer = document.getElementById("myio-global-toast-container");
|
|
2198
|
+
return;
|
|
2199
|
+
}
|
|
2200
|
+
const style = document.createElement("style");
|
|
2201
|
+
style.id = "myio-global-toast-styles";
|
|
2202
|
+
style.textContent = TOAST_CSS;
|
|
2203
|
+
document.head.appendChild(style);
|
|
2204
|
+
toastContainer = document.createElement("div");
|
|
2205
|
+
toastContainer.id = "myio-global-toast-container";
|
|
2206
|
+
document.body.appendChild(toastContainer);
|
|
2207
|
+
}
|
|
2208
|
+
function show(message, type = "warning", duration = 3500) {
|
|
2209
|
+
if (!toastContainer) {
|
|
2210
|
+
createToastElement();
|
|
2211
|
+
}
|
|
2212
|
+
clearTimeout(toastTimeout);
|
|
2213
|
+
toastContainer.textContent = message;
|
|
2214
|
+
toastContainer.className = "";
|
|
2215
|
+
toastContainer.classList.add(type);
|
|
2216
|
+
setTimeout(() => {
|
|
2217
|
+
toastContainer.classList.add("show");
|
|
2218
|
+
}, 10);
|
|
2219
|
+
toastTimeout = setTimeout(() => {
|
|
2220
|
+
toastContainer.classList.remove("show");
|
|
2221
|
+
}, duration);
|
|
2222
|
+
}
|
|
2223
|
+
if (document.readyState === "loading") {
|
|
2224
|
+
document.addEventListener("DOMContentLoaded", createToastElement);
|
|
2225
|
+
} else {
|
|
2226
|
+
createToastElement();
|
|
2227
|
+
}
|
|
2228
|
+
return {
|
|
2229
|
+
show
|
|
2230
|
+
};
|
|
2231
|
+
})();
|
|
2115
2232
|
if (!useNewComponents) {
|
|
2116
2233
|
return renderCardComponentLegacy({
|
|
2117
2234
|
entityObject,
|
|
@@ -2184,10 +2301,31 @@ function renderCardComponentV2({
|
|
|
2184
2301
|
const normalizedType = deviceType2?.toUpperCase() || "";
|
|
2185
2302
|
return typeMap[normalizedType] || "generic";
|
|
2186
2303
|
};
|
|
2187
|
-
const
|
|
2304
|
+
const isEnergyDevice = (valType2, deviceType2) => {
|
|
2305
|
+
if (valType2 === "ENERGY") {
|
|
2306
|
+
return true;
|
|
2307
|
+
}
|
|
2308
|
+
const energyDeviceTypes = ["MOTOR", "3F_MEDIDOR", "RELOGIO", "ENTRADA"];
|
|
2309
|
+
const normalizedType = deviceType2?.toUpperCase() || "";
|
|
2310
|
+
return energyDeviceTypes.includes(normalizedType);
|
|
2311
|
+
};
|
|
2312
|
+
const formatCardValue = (value, valType2, deviceType2) => {
|
|
2313
|
+
const numValue = Number(value) || 0;
|
|
2314
|
+
if (isEnergyDevice(valType2, deviceType2)) {
|
|
2315
|
+
return formatEnergy(numValue);
|
|
2316
|
+
} else {
|
|
2317
|
+
const unit = determineUnit(valType2, numValue, deviceType2);
|
|
2318
|
+
const formattedValue = numValue.toLocaleString("pt-BR", {
|
|
2319
|
+
minimumFractionDigits: 0,
|
|
2320
|
+
maximumFractionDigits: 2
|
|
2321
|
+
});
|
|
2322
|
+
return `${formattedValue} ${unit}`;
|
|
2323
|
+
}
|
|
2324
|
+
};
|
|
2325
|
+
const determineUnit = (valType2, val2, deviceType2) => {
|
|
2188
2326
|
switch (valType2) {
|
|
2189
2327
|
case "ENERGY":
|
|
2190
|
-
return "
|
|
2328
|
+
return "";
|
|
2191
2329
|
case "WATER":
|
|
2192
2330
|
return "m\xB3";
|
|
2193
2331
|
case "TANK":
|
|
@@ -2485,7 +2623,7 @@ function renderCardComponentV2({
|
|
|
2485
2623
|
<span class="flash-icon ${shouldFlashIcon ? "flash" : ""}">
|
|
2486
2624
|
${icon}
|
|
2487
2625
|
</span>
|
|
2488
|
-
<span class="consumption-value">${cardEntity.lastValue
|
|
2626
|
+
<span class="consumption-value">${formatCardValue(cardEntity.lastValue, valType, deviceType)}</span>
|
|
2489
2627
|
<span class="device-title-percent">(${perc.toFixed(1)}%)</span>
|
|
2490
2628
|
</div>
|
|
2491
2629
|
</div>
|
|
@@ -2938,6 +3076,14 @@ function renderCardComponentV2({
|
|
|
2938
3076
|
checkbox.addEventListener("change", (e) => {
|
|
2939
3077
|
e.stopPropagation();
|
|
2940
3078
|
if (e.target.checked) {
|
|
3079
|
+
const currentCount = MyIOSelectionStore.getSelectedEntities().length;
|
|
3080
|
+
const isTryingToAdd = e.target.checked;
|
|
3081
|
+
if (isTryingToAdd && currentCount >= 6) {
|
|
3082
|
+
e.preventDefault();
|
|
3083
|
+
e.target.checked = false;
|
|
3084
|
+
MyIOToast.show("N\xE3o \xE9 poss\xEDvel selecionar mais de 6 itens.", "warning");
|
|
3085
|
+
return;
|
|
3086
|
+
}
|
|
2941
3087
|
MyIOSelectionStore.add(entityId);
|
|
2942
3088
|
} else {
|
|
2943
3089
|
MyIOSelectionStore.remove(entityId);
|
|
@@ -4123,7 +4269,10 @@ var MyIOChartModalClass = class {
|
|
|
4123
4269
|
const store = this._getSelectionStore();
|
|
4124
4270
|
if (store) {
|
|
4125
4271
|
store.on("comparison:open", (data) => this.open(data));
|
|
4126
|
-
store.on(
|
|
4272
|
+
store.on(
|
|
4273
|
+
"comparison:too_many",
|
|
4274
|
+
(data) => this._showTooManyEntitiesWarning(data)
|
|
4275
|
+
);
|
|
4127
4276
|
}
|
|
4128
4277
|
}
|
|
4129
4278
|
async _createModal() {
|
|
@@ -4148,74 +4297,166 @@ var MyIOChartModalClass = class {
|
|
|
4148
4297
|
_generateModalHTML() {
|
|
4149
4298
|
const { entities, totals, count } = this.currentData;
|
|
4150
4299
|
return `
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4300
|
+
<div class="chart-modal-container"
|
|
4301
|
+
style="
|
|
4302
|
+
background: rgba(20, 20, 20, 0.95);
|
|
4303
|
+
backdrop-filter: blur(10px);
|
|
4304
|
+
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
4305
|
+
border-radius: 16px;
|
|
4306
|
+
padding: 20px;
|
|
4307
|
+
max-width: 900px;
|
|
4308
|
+
margin: auto;
|
|
4309
|
+
color: #fff;
|
|
4310
|
+
font-family: Arial, sans-serif;
|
|
4311
|
+
box-shadow: 0 8px 30px rgba(0,0,0,0.5);
|
|
4312
|
+
">
|
|
4313
|
+
|
|
4314
|
+
<div class="chart-modal-header"
|
|
4315
|
+
style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
|
|
4316
|
+
<h2 id="chart-modal-title"
|
|
4317
|
+
style="font-size: 20px; font-weight: bold; margin: 0; color: #fff;">
|
|
4318
|
+
Comparativo de Dispositivos (${count} selecionados)
|
|
4319
|
+
</h2>
|
|
4320
|
+
<button class="chart-modal-close" aria-label="Fechar modal"
|
|
4321
|
+
style="background: none; border: none; color: #fff; font-size: 24px; cursor: pointer; padding: 5px; transition: 0.3s;">
|
|
4322
|
+
\xD7
|
|
4323
|
+
</button>
|
|
4324
|
+
</div>
|
|
4325
|
+
|
|
4326
|
+
<div class="chart-modal-controls"
|
|
4327
|
+
style="display: flex; gap: 20px; flex-wrap: wrap; margin-bottom: 20px;">
|
|
4328
|
+
|
|
4329
|
+
<div class="chart-type-controls" style="flex: 1; min-width: 180px;">
|
|
4330
|
+
<label style="display: block; margin-bottom: 6px; color: rgba(255,255,255,0.8); font-size: 14px;">
|
|
4331
|
+
Tipo de Gr\xE1fico:
|
|
4332
|
+
</label>
|
|
4333
|
+
<select class="chart-type-select" aria-label="Selecionar tipo de gr\xE1fico"
|
|
4334
|
+
style="width: 100%; padding: 8px 12px; border-radius: 8px; border: 1px solid rgba(255,255,255,0.08);
|
|
4335
|
+
background: rgba(255,255,255,0.08); color: #fff; outline: none; cursor: pointer;">
|
|
4336
|
+
<option value="line" ${this.chartConfig.type === "line" ? "selected" : ""}>Linha</option>
|
|
4337
|
+
<option value="bar" ${this.chartConfig.type === "bar" ? "selected" : ""}>Barras</option>
|
|
4338
|
+
</select>
|
|
4155
4339
|
</div>
|
|
4156
4340
|
|
|
4157
|
-
<div class="chart-
|
|
4158
|
-
<
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
<select class="chart-range-select" aria-label="Selecionar per\xEDodo">
|
|
4169
|
-
<option value="7" ${this.chartConfig.timeRange === 7 ? "selected" : ""}>7 dias</option>
|
|
4170
|
-
<option value="14" ${this.chartConfig.timeRange === 14 ? "selected" : ""}>14 dias</option>
|
|
4171
|
-
<option value="30" ${this.chartConfig.timeRange === 30 ? "selected" : ""}>30 dias</option>
|
|
4172
|
-
</select>
|
|
4173
|
-
</div>
|
|
4174
|
-
|
|
4175
|
-
<div class="chart-export-controls">
|
|
4176
|
-
<button class="export-csv-btn">Exportar CSV</button>
|
|
4177
|
-
<button class="export-png-btn">Exportar PNG</button>
|
|
4178
|
-
<button class="export-pdf-btn">Exportar PDF</button>
|
|
4179
|
-
</div>
|
|
4341
|
+
<div class="chart-range-controls" style="flex: 1; min-width: 180px;">
|
|
4342
|
+
<label style="display: block; margin-bottom: 6px; color: rgba(255,255,255,0.8); font-size: 14px;">
|
|
4343
|
+
Per\xEDodo:
|
|
4344
|
+
</label>
|
|
4345
|
+
<select class="chart-range-select" aria-label="Selecionar per\xEDodo"
|
|
4346
|
+
style="width: 100%; padding: 8px 12px; border-radius: 8px; border: 1px solid rgba(255,255,255,0.08);
|
|
4347
|
+
background: rgba(255,255,255,0.08); color: #fff; outline: none; cursor: pointer;">
|
|
4348
|
+
<option value="7" ${this.chartConfig.timeRange === 7 ? "selected" : ""}>7 dias</option>
|
|
4349
|
+
<option value="14" ${this.chartConfig.timeRange === 14 ? "selected" : ""}>14 dias</option>
|
|
4350
|
+
<option value="30" ${this.chartConfig.timeRange === 30 ? "selected" : ""}>30 dias</option>
|
|
4351
|
+
</select>
|
|
4180
4352
|
</div>
|
|
4181
4353
|
|
|
4182
|
-
<div class="chart-
|
|
4183
|
-
<
|
|
4184
|
-
|
|
4185
|
-
|
|
4186
|
-
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
</
|
|
4354
|
+
<div class="chart-export-controls" style="display: flex; gap: 10px; align-items: end;">
|
|
4355
|
+
<button class="export-csv-btn"
|
|
4356
|
+
style="padding: 8px 14px; border-radius: 8px; border: none; cursor: pointer;
|
|
4357
|
+
background: rgba(255,255,255,0.08); color: #fff; transition: 0.3s;">
|
|
4358
|
+
Exportar CSV
|
|
4359
|
+
</button>
|
|
4360
|
+
<button class="export-png-btn"
|
|
4361
|
+
style="padding: 8px 14px; border-radius: 8px; border: none; cursor: pointer;
|
|
4362
|
+
background: rgba(255,255,255,0.08); color: #fff; transition: 0.3s;">
|
|
4363
|
+
Exportar PNG
|
|
4364
|
+
</button>
|
|
4365
|
+
<button class="export-pdf-btn"
|
|
4366
|
+
style="padding: 8px 14px; border-radius: 8px; border: none; cursor: pointer;
|
|
4367
|
+
background: rgba(255,255,255,0.08); color: #fff; transition: 0.3s;">
|
|
4368
|
+
Exportar PDF
|
|
4369
|
+
</button>
|
|
4370
|
+
</div>
|
|
4371
|
+
</div>
|
|
4372
|
+
|
|
4373
|
+
<div class="chart-modal-body" style="display: flex; flex-direction: column; gap: 20px;">
|
|
4374
|
+
<div class="chart-container"
|
|
4375
|
+
style="background: rgba(255,255,255,0.05); border-radius: 12px; padding: 15px;">
|
|
4376
|
+
<canvas id="comparison-chart" aria-label="Gr\xE1fico comparativo de dispositivos"></canvas>
|
|
4193
4377
|
</div>
|
|
4194
4378
|
|
|
4195
|
-
<div class="chart-
|
|
4196
|
-
|
|
4379
|
+
<div class="chart-summary"
|
|
4380
|
+
style="background: rgba(255,255,255,0.05); border-radius: 12px; padding: 15px;">
|
|
4381
|
+
<h3 style="margin: 0 0 12px; font-size: 16px; font-weight: bold;">Resumo da Sele\xE7\xE3o</h3>
|
|
4382
|
+
<div class="summary-grid"
|
|
4383
|
+
style="display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px;">
|
|
4384
|
+
${this._generateSummaryHTML(totals)}
|
|
4385
|
+
</div>
|
|
4197
4386
|
</div>
|
|
4198
4387
|
</div>
|
|
4199
|
-
|
|
4388
|
+
|
|
4389
|
+
<div class="chart-modal-footer"
|
|
4390
|
+
style="display: flex; justify-content: flex-end; margin-top: 20px;">
|
|
4391
|
+
<button class="chart-modal-close-btn"
|
|
4392
|
+
style="padding: 10px 20px; border-radius: 8px; border: none; cursor: pointer;
|
|
4393
|
+
background: rgba(255,255,255,0.08); color: #fff; font-weight: bold; transition: 0.3s;">
|
|
4394
|
+
Fechar
|
|
4395
|
+
</button>
|
|
4396
|
+
</div>
|
|
4397
|
+
</div>
|
|
4398
|
+
`;
|
|
4399
|
+
}
|
|
4400
|
+
_generateSummaryHTML(totals) {
|
|
4401
|
+
const items = [];
|
|
4402
|
+
if (totals.energyKwh > 0) {
|
|
4403
|
+
items.push(`<div class="summary-item"
|
|
4404
|
+
style="background: rgba(255,255,255,0.08); padding: 10px; border-radius: 8px;">
|
|
4405
|
+
<span class="summary-label" style="display: block; font-size: 13px; color: rgba(255,255,255,0.7);">Energia Total:</span>
|
|
4406
|
+
<span class="summary-value" style="font-weight: bold; font-size: 14px;">${this._formatNumber(
|
|
4407
|
+
totals.energyKwh
|
|
4408
|
+
)} kWh</span>
|
|
4409
|
+
</div>`);
|
|
4410
|
+
}
|
|
4411
|
+
if (totals.waterM3 > 0) {
|
|
4412
|
+
items.push(`<div class="summary-item"
|
|
4413
|
+
style="background: rgba(255,255,255,0.08); padding: 10px; border-radius: 8px;">
|
|
4414
|
+
<span class="summary-label" style="display: block; font-size: 13px; color: rgba(255,255,255,0.7);">\xC1gua Total:</span>
|
|
4415
|
+
<span class="summary-value" style="font-weight: bold; font-size: 14px;">${this._formatNumber(
|
|
4416
|
+
totals.waterM3
|
|
4417
|
+
)} m\xB3</span>
|
|
4418
|
+
</div>`);
|
|
4419
|
+
}
|
|
4420
|
+
if (totals.tempC > 0) {
|
|
4421
|
+
items.push(`<div class="summary-item"
|
|
4422
|
+
style="background: rgba(255,255,255,0.08); padding: 10px; border-radius: 8px;">
|
|
4423
|
+
<span class="summary-label" style="display: block; font-size: 13px; color: rgba(255,255,255,0.7);">Temperatura M\xE9dia:</span>
|
|
4424
|
+
<span class="summary-value" style="font-weight: bold; font-size: 14px;">${this._formatNumber(
|
|
4425
|
+
totals.tempC / totals.count
|
|
4426
|
+
)} \xB0C</span>
|
|
4427
|
+
</div>`);
|
|
4428
|
+
}
|
|
4429
|
+
items.push(`<div class="summary-item"
|
|
4430
|
+
style="background: rgba(255,255,255,0.08); padding: 10px; border-radius: 8px;">
|
|
4431
|
+
<span class="summary-label" style="display: block; font-size: 13px; color: rgba(255,255,255,0.7);">Dispositivos:</span>
|
|
4432
|
+
<span class="summary-value" style="font-weight: bold; font-size: 14px;">${totals.count}</span>
|
|
4433
|
+
</div>`);
|
|
4434
|
+
return items.join("");
|
|
4200
4435
|
}
|
|
4201
4436
|
_generateSummaryHTML(totals) {
|
|
4202
4437
|
const items = [];
|
|
4203
4438
|
if (totals.energyKwh > 0) {
|
|
4204
4439
|
items.push(`<div class="summary-item">
|
|
4205
4440
|
<span class="summary-label">Energia Total:</span>
|
|
4206
|
-
<span class="summary-value">${this._formatNumber(
|
|
4441
|
+
<span class="summary-value">${this._formatNumber(
|
|
4442
|
+
totals.energyKwh
|
|
4443
|
+
)} kWh</span>
|
|
4207
4444
|
</div>`);
|
|
4208
4445
|
}
|
|
4209
4446
|
if (totals.waterM3 > 0) {
|
|
4210
4447
|
items.push(`<div class="summary-item">
|
|
4211
4448
|
<span class="summary-label">\xC1gua Total:</span>
|
|
4212
|
-
<span class="summary-value">${this._formatNumber(
|
|
4449
|
+
<span class="summary-value">${this._formatNumber(
|
|
4450
|
+
totals.waterM3
|
|
4451
|
+
)} m\xB3</span>
|
|
4213
4452
|
</div>`);
|
|
4214
4453
|
}
|
|
4215
4454
|
if (totals.tempC > 0) {
|
|
4216
4455
|
items.push(`<div class="summary-item">
|
|
4217
4456
|
<span class="summary-label">Temperatura M\xE9dia:</span>
|
|
4218
|
-
<span class="summary-value">${this._formatNumber(
|
|
4457
|
+
<span class="summary-value">${this._formatNumber(
|
|
4458
|
+
totals.tempC / totals.count
|
|
4459
|
+
)} \xB0C</span>
|
|
4219
4460
|
</div>`);
|
|
4220
4461
|
}
|
|
4221
4462
|
items.push(`<div class="summary-item">
|
|
@@ -4226,7 +4467,9 @@ var MyIOChartModalClass = class {
|
|
|
4226
4467
|
}
|
|
4227
4468
|
_attachModalEventListeners() {
|
|
4228
4469
|
if (!this.modalElement) return;
|
|
4229
|
-
const closeButtons = this.modalElement.querySelectorAll(
|
|
4470
|
+
const closeButtons = this.modalElement.querySelectorAll(
|
|
4471
|
+
".chart-modal-close, .chart-modal-close-btn"
|
|
4472
|
+
);
|
|
4230
4473
|
closeButtons.forEach((btn) => {
|
|
4231
4474
|
btn.addEventListener("click", () => this.close());
|
|
4232
4475
|
});
|
|
@@ -4302,7 +4545,11 @@ var MyIOChartModalClass = class {
|
|
|
4302
4545
|
startDate.setDate(startDate.getDate() - this.chartConfig.timeRange);
|
|
4303
4546
|
let timeSeriesData = {};
|
|
4304
4547
|
if (store && store.getTimeSeriesData) {
|
|
4305
|
-
timeSeriesData = await store.getTimeSeriesData(
|
|
4548
|
+
timeSeriesData = await store.getTimeSeriesData(
|
|
4549
|
+
entityIds,
|
|
4550
|
+
startDate,
|
|
4551
|
+
endDate
|
|
4552
|
+
);
|
|
4306
4553
|
}
|
|
4307
4554
|
const chartData = this._prepareChartData(timeSeriesData);
|
|
4308
4555
|
this.chartInstance = new globalThis.Chart(canvas, {
|
|
@@ -7545,8 +7792,12 @@ var DEFAULT_STYLES = {
|
|
|
7545
7792
|
};
|
|
7546
7793
|
var CHART_JS_CDN = "https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.js";
|
|
7547
7794
|
var ZOOM_PLUGIN_CDN = "https://cdn.jsdelivr.net/npm/chartjs-plugin-zoom@2.0.1/dist/chartjs-plugin-zoom.min.js";
|
|
7795
|
+
var JSPDF_VERSION = "2.5.1";
|
|
7796
|
+
var JSPDF_CDN = `https://cdnjs.cloudflare.com/ajax/libs/jspdf/${JSPDF_VERSION}/jspdf.umd.min.js`;
|
|
7548
7797
|
var chartJsLoaded = false;
|
|
7549
7798
|
var zoomPluginLoaded = false;
|
|
7799
|
+
var jsPdfLoaded = false;
|
|
7800
|
+
var _jspdfPromise = null;
|
|
7550
7801
|
var cssInjected = false;
|
|
7551
7802
|
var STRINGS = {
|
|
7552
7803
|
"pt-BR": {
|
|
@@ -7596,25 +7847,24 @@ async function loadScript(url, checkGlobal) {
|
|
|
7596
7847
|
}
|
|
7597
7848
|
const existingScript = document.querySelector(`script[src="${url}"]`);
|
|
7598
7849
|
if (existingScript) {
|
|
7599
|
-
|
|
7850
|
+
existingScript.addEventListener("load", () => {
|
|
7600
7851
|
if (window[checkGlobal]) {
|
|
7601
7852
|
resolve();
|
|
7602
7853
|
} else {
|
|
7603
7854
|
reject(new Error(`Library ${checkGlobal} not available after loading ${url}`));
|
|
7604
7855
|
}
|
|
7605
|
-
}
|
|
7856
|
+
});
|
|
7857
|
+
existingScript.addEventListener("error", () => reject(new Error(`Failed to load ${url}`)));
|
|
7606
7858
|
return;
|
|
7607
7859
|
}
|
|
7608
7860
|
const script = document.createElement("script");
|
|
7609
7861
|
script.src = url;
|
|
7610
7862
|
script.onload = () => {
|
|
7611
|
-
|
|
7612
|
-
|
|
7613
|
-
|
|
7614
|
-
}
|
|
7615
|
-
|
|
7616
|
-
}
|
|
7617
|
-
}, 200);
|
|
7863
|
+
if (window[checkGlobal]) {
|
|
7864
|
+
resolve();
|
|
7865
|
+
} else {
|
|
7866
|
+
reject(new Error(`Library ${checkGlobal} not available after loading ${url}`));
|
|
7867
|
+
}
|
|
7618
7868
|
};
|
|
7619
7869
|
script.onerror = () => reject(new Error(`Failed to load ${url}`));
|
|
7620
7870
|
document.head.appendChild(script);
|
|
@@ -7630,10 +7880,90 @@ async function loadExternalLibraries() {
|
|
|
7630
7880
|
await loadScript(ZOOM_PLUGIN_CDN, "ChartZoom");
|
|
7631
7881
|
zoomPluginLoaded = true;
|
|
7632
7882
|
}
|
|
7883
|
+
if (!jsPdfLoaded) {
|
|
7884
|
+
await ensureJsPDF();
|
|
7885
|
+
jsPdfLoaded = true;
|
|
7886
|
+
}
|
|
7633
7887
|
} catch (error) {
|
|
7634
7888
|
throw new Error(`Failed to load external libraries: ${error}`);
|
|
7635
7889
|
}
|
|
7636
7890
|
}
|
|
7891
|
+
function ensureJsPDF() {
|
|
7892
|
+
if (window.jspdf?.jsPDF) {
|
|
7893
|
+
console.info("jsPDF already loaded.");
|
|
7894
|
+
return Promise.resolve();
|
|
7895
|
+
}
|
|
7896
|
+
if (_jspdfPromise) {
|
|
7897
|
+
console.info("jsPDF loading already in progress.");
|
|
7898
|
+
return _jspdfPromise;
|
|
7899
|
+
}
|
|
7900
|
+
_jspdfPromise = new Promise((resolve, reject) => {
|
|
7901
|
+
const existing = document.querySelector('script[data-lib="jspdf"]');
|
|
7902
|
+
if (existing) {
|
|
7903
|
+
existing.addEventListener("load", () => {
|
|
7904
|
+
if (window.jspdf?.jsPDF) {
|
|
7905
|
+
console.info("jsPDF loaded via existing script.");
|
|
7906
|
+
resolve();
|
|
7907
|
+
} else {
|
|
7908
|
+
reject(new Error("jsPDF loaded but window.jspdf.jsPDF missing"));
|
|
7909
|
+
}
|
|
7910
|
+
});
|
|
7911
|
+
existing.addEventListener("error", () => reject(new Error("Failed to load jsPDF via existing script")));
|
|
7912
|
+
return;
|
|
7913
|
+
}
|
|
7914
|
+
const s = document.createElement("script");
|
|
7915
|
+
s.src = JSPDF_CDN;
|
|
7916
|
+
s.async = true;
|
|
7917
|
+
s.defer = true;
|
|
7918
|
+
s.dataset.lib = "jspdf";
|
|
7919
|
+
s.onload = () => {
|
|
7920
|
+
if (window.jspdf?.jsPDF) {
|
|
7921
|
+
console.info("jsPDF loaded successfully.");
|
|
7922
|
+
resolve();
|
|
7923
|
+
} else {
|
|
7924
|
+
reject(new Error("jsPDF loaded but window.jspdf.jsPDF missing"));
|
|
7925
|
+
}
|
|
7926
|
+
};
|
|
7927
|
+
s.onerror = () => reject(new Error("Failed to load jsPDF from CDN"));
|
|
7928
|
+
document.head.appendChild(s);
|
|
7929
|
+
}).finally(() => {
|
|
7930
|
+
_jspdfPromise = null;
|
|
7931
|
+
});
|
|
7932
|
+
return _jspdfPromise;
|
|
7933
|
+
}
|
|
7934
|
+
function getJsPDFCtor() {
|
|
7935
|
+
if (window.jspdf?.jsPDF) return window.jspdf.jsPDF;
|
|
7936
|
+
if (window.jsPDF?.jsPDF) return window.jsPDF.jsPDF;
|
|
7937
|
+
if (window.jsPDF) return window.jsPDF;
|
|
7938
|
+
throw new Error("jsPDF constructor not found on window");
|
|
7939
|
+
}
|
|
7940
|
+
function savePdfSafe(doc, filename) {
|
|
7941
|
+
try {
|
|
7942
|
+
doc.save(filename);
|
|
7943
|
+
} catch (e) {
|
|
7944
|
+
console.warn("doc.save() failed, attempting Blob URL fallback:", e);
|
|
7945
|
+
const blob = doc.output("blob");
|
|
7946
|
+
const url = URL.createObjectURL(blob);
|
|
7947
|
+
window.open(url, "_blank") || alert("Pop-up blocked. Allow pop-ups to download the PDF.");
|
|
7948
|
+
setTimeout(() => URL.revokeObjectURL(url), 3e4);
|
|
7949
|
+
}
|
|
7950
|
+
}
|
|
7951
|
+
function addCanvasToPdf(doc, canvas, x = 10, y = 20, maxWmm = 190) {
|
|
7952
|
+
const img = canvas.toDataURL("image/png", 1);
|
|
7953
|
+
const pageWmm = doc.internal.pageSize.getWidth();
|
|
7954
|
+
const mmW = Math.min(maxWmm, pageWmm - x * 2);
|
|
7955
|
+
const mmH = canvas.height / canvas.width * mmW;
|
|
7956
|
+
doc.addImage(img, "PNG", x, y, mmW, mmH, void 0, "FAST");
|
|
7957
|
+
return y + mmH;
|
|
7958
|
+
}
|
|
7959
|
+
function ensureRoom(doc, nextY, minRoom = 40) {
|
|
7960
|
+
const h = doc.internal.pageSize.getHeight();
|
|
7961
|
+
if (nextY + minRoom > h) {
|
|
7962
|
+
doc.addPage();
|
|
7963
|
+
return 20;
|
|
7964
|
+
}
|
|
7965
|
+
return nextY;
|
|
7966
|
+
}
|
|
7637
7967
|
function injectCSS(styles) {
|
|
7638
7968
|
if (cssInjected) return;
|
|
7639
7969
|
const css = `
|
|
@@ -8120,18 +8450,72 @@ async function openDemandModal(params) {
|
|
|
8120
8450
|
alert("Nenhum dado dispon\xEDvel para exportar");
|
|
8121
8451
|
return;
|
|
8122
8452
|
}
|
|
8453
|
+
const btn = pdfBtn;
|
|
8454
|
+
const originalHtml = btn.innerHTML;
|
|
8455
|
+
btn.disabled = true;
|
|
8456
|
+
btn.innerHTML = "<span>\u23F3</span> Gerando PDF...";
|
|
8123
8457
|
try {
|
|
8124
|
-
|
|
8125
|
-
|
|
8126
|
-
|
|
8127
|
-
|
|
8128
|
-
|
|
8129
|
-
|
|
8130
|
-
|
|
8131
|
-
|
|
8458
|
+
await ensureJsPDF();
|
|
8459
|
+
await document.fonts?.ready?.catch((e) => console.warn("Font loading interrupted or failed:", e));
|
|
8460
|
+
const JsPDF = getJsPDFCtor();
|
|
8461
|
+
const doc = new JsPDF("p", "mm", "a4");
|
|
8462
|
+
doc.setFontSize(20);
|
|
8463
|
+
doc.setTextColor(74, 20, 140);
|
|
8464
|
+
doc.text(strings.reportTitle, 20, 20);
|
|
8465
|
+
doc.setFontSize(14);
|
|
8466
|
+
doc.setTextColor(0, 0, 0);
|
|
8467
|
+
const label2 = params.label || "Dispositivo";
|
|
8468
|
+
doc.text(`${strings.title} - ${label2}`, 20, 35);
|
|
8469
|
+
const startDate = formatDate(new Date(params.startDate), params.locale || "pt-BR");
|
|
8470
|
+
const endDate = formatDate(new Date(params.endDate), params.locale || "pt-BR");
|
|
8471
|
+
doc.text(`${strings.period}: ${startDate} - ${endDate}`, 20, 45);
|
|
8472
|
+
let currentY = 55;
|
|
8473
|
+
if (chartData.globalPeak) {
|
|
8474
|
+
const peak = chartData.globalPeak;
|
|
8475
|
+
doc.setFontSize(12);
|
|
8476
|
+
doc.setTextColor(255, 152, 0);
|
|
8477
|
+
doc.text(
|
|
8478
|
+
`${strings.maximum}: ${peak.formattedValue} kW ${peak.key ? `(${peak.key}) ` : ""}${strings.at} ${peak.formattedTime}`,
|
|
8479
|
+
20,
|
|
8480
|
+
currentY
|
|
8481
|
+
);
|
|
8482
|
+
currentY += 10;
|
|
8483
|
+
}
|
|
8484
|
+
currentY = ensureRoom(doc, currentY, 120);
|
|
8485
|
+
currentY = addCanvasToPdf(doc, chartCanvas, 20, currentY);
|
|
8486
|
+
currentY += 10;
|
|
8487
|
+
if (chartData.series.length > 0 && chartData.series[0].points.length > 0) {
|
|
8488
|
+
currentY = ensureRoom(doc, currentY, 60);
|
|
8489
|
+
doc.setFontSize(12);
|
|
8490
|
+
doc.setTextColor(0, 0, 0);
|
|
8491
|
+
doc.text("Amostra de Dados:", 20, currentY);
|
|
8492
|
+
currentY += 10;
|
|
8493
|
+
const samplePoints = chartData.series[0].points.slice(0, 10);
|
|
8494
|
+
doc.setFontSize(10);
|
|
8495
|
+
doc.text("Data/Hora", 20, currentY);
|
|
8496
|
+
doc.text(params.yAxisLabel || strings.demand, 100, currentY);
|
|
8497
|
+
currentY += 7;
|
|
8498
|
+
samplePoints.forEach((point) => {
|
|
8499
|
+
currentY = ensureRoom(doc, currentY, 10);
|
|
8500
|
+
const dateStr = formatDateTime(new Date(point.x), params.locale || "pt-BR");
|
|
8501
|
+
doc.text(dateStr, 20, currentY);
|
|
8502
|
+
doc.text(point.y.toFixed(2), 100, currentY);
|
|
8503
|
+
currentY += 7;
|
|
8504
|
+
});
|
|
8505
|
+
}
|
|
8506
|
+
currentY = ensureRoom(doc, currentY, 20);
|
|
8507
|
+
doc.setFontSize(8);
|
|
8508
|
+
doc.setTextColor(128, 128, 128);
|
|
8509
|
+
doc.text(`${strings.reportFooter}`, 20, doc.internal.pageSize.getHeight() - 15);
|
|
8510
|
+
doc.text(`Gerado em: ${(/* @__PURE__ */ new Date()).toLocaleString(params.locale || "pt-BR")}`, 20, doc.internal.pageSize.getHeight() - 10);
|
|
8511
|
+
const fileName = params.pdf?.fileName || `demanda_${label2.replace(/\s+/g, "_")}_${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}.pdf`;
|
|
8512
|
+
savePdfSafe(doc, fileName);
|
|
8132
8513
|
} catch (error) {
|
|
8133
|
-
console.error("
|
|
8134
|
-
alert("Erro ao
|
|
8514
|
+
console.error("[PDF Export] Error:", error);
|
|
8515
|
+
alert("Erro ao gerar PDF. Por favor, tente novamente. Verifique o console para mais detalhes.");
|
|
8516
|
+
} finally {
|
|
8517
|
+
btn.disabled = false;
|
|
8518
|
+
btn.innerHTML = originalHtml;
|
|
8135
8519
|
}
|
|
8136
8520
|
}
|
|
8137
8521
|
closeBtn.addEventListener("click", closeModal);
|