myio-js-library 0.1.166 → 0.1.167
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 +5 -1
- package/dist/index.d.cts +2 -0
- package/dist/index.js +5 -1
- package/dist/myio-js-library.umd.js +5 -1
- package/dist/myio-js-library.umd.min.js +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(global,factory){typeof exports==="object"&&typeof module!=="undefined"?factory(exports):typeof define==="function"&&define.amd?define(["exports"],factory):(global=typeof globalThis!=="undefined"?globalThis:global||self,factory(global.MyIOLibrary={}))})(this,function(exports){"use strict";var __defProp=Object.defineProperty;var __getOwnPropDesc=Object.getOwnPropertyDescriptor;var __getOwnPropNames=Object.getOwnPropertyNames;var __hasOwnProp=Object.prototype.hasOwnProperty;var __esm=(fn,res)=>function __init(){return fn&&(res=(0,fn[__getOwnPropNames(fn)[0]])(fn=0)),res};var __export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:true})};var __copyProps=(to,from,except,desc)=>{if(from&&typeof from==="object"||typeof from==="function"){for(let key of __getOwnPropNames(from))if(!__hasOwnProp.call(to,key)&&key!==except)__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable})}return to};var __toCommonJS=mod=>__copyProps(__defProp({},"__esModule",{value:true}),mod);var template_card_exports={};__export(template_card_exports,{renderCardComponent:()=>renderCardComponent});function renderCardComponent({entityObject:entityObject,handleActionDashboard:handleActionDashboard,handleActionReport:handleActionReport,handleActionSettings:handleActionSettings,handleSelect:handleSelect,handInfo:handInfo,handleClickCard:handleClickCard}){const{entityId:entityId,labelOrName:labelOrName,deviceIdentifier:deviceIdentifier,entityType:entityType,deviceType:deviceType,slaveId:slaveId,ingestionId:ingestionId,val:val,centralId:centralId,updatedIdentifiers:updatedIdentifiers={},perc:perc=0,connectionStatus:connectionStatus,centralName:centralName,connectionStatusTime:connectionStatusTime,timaVal:timaVal,valType:valType}=entityObject;const DeviceStatus={CONNECTED:"connected",OFFLINE:"offline",POWER_ON:"power_on",STANDBY:"standby",POWER_OFF:"power_off",WARNING:"warning",DANGER:"danger",MAINTENANCE:"maintenance"};const statusIcons={[DeviceStatus.CONNECTED]:"✅",[DeviceStatus.OFFLINE]:"🔌",[DeviceStatus.POWER_ON]:"⚡",[DeviceStatus.STANDBY]:"⏸️",[DeviceStatus.POWER_OFF]:"⏹️",[DeviceStatus.WARNING]:"⚠️",[DeviceStatus.DANGER]:"🚨",[DeviceStatus.MAINTENANCE]:"🛠️"};const isOfflineOrDanger=connectionStatus===DeviceStatus.OFFLINE||connectionStatus===DeviceStatus.DANGER;const shouldFlashIcon2=connectionStatus===DeviceStatus.OFFLINE||connectionStatus===DeviceStatus.WARNING||connectionStatus===DeviceStatus.DANGER||connectionStatus===DeviceStatus.MAINTENANCE;const icon=statusIcons[connectionStatus]||statusIcons[DeviceStatus.POWER_ON];const MyIO=typeof MyIOLibrary!=="undefined"&&MyIOLibrary||typeof window!=="undefined"&&window.MyIOLibrary||{formatEnergy:(v,g)=>`${v} kWh`,formatNumberReadable:n=>Number(n??0).toFixed(1)};let valFormatted=MyIO.formatEnergy(val);if(valType==="ENERGY"){valFormatted=MyIO.formatEnergy(val)}else if(valType==="WATER"){valFormatted=`${val} m³`}else if(valType==="TANK"){valFormatted=`${val} m.c.a`}else{valFormatted=val}const percFormatted=MyIO.formatNumberReadable(perc);if(!document.getElementById("myio-card-styles")){const style=document.createElement("style");style.id="myio-card-styles";style.textContent=` \n.device-card-centered,\n.clickable {\n width: 100%;\n border-radius: 10px;\n padding: 8px 12px;\n background: #fff;\n box-shadow: 0 4px 10px rgba(0, 0, 0, .05);\n /* display: flex; REMOVIDO */\n /* align-items: center; REMOVIDO */\n /* justify-content: center; REMOVIDO */\n cursor: pointer;\n transition: transform .2s;\n /* min-height: 140px; REMOVIDO (movido para .device-card-inner) */\n box-sizing: border-box;\n overflow: hidden;\n}\n\n.device-card-centered:hover,\n.clickable:hover {\n transform: scale(1.05);\n}\n\n.device-title-row {\n width: 100%;\n display: flex;\n justify-content: center;\n align-items: center;\n margin-bottom: 4px;\n padding: 0 4px;\n min-height: 22px;\n}\n\n.device-title {\n font-weight: 700;\n font-size: .85rem;\n text-align: center;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n max-width: 90%;\n line-height: 1.1;\n}\n\n.device-subtitle {\n font-size: 0.7rem; \n font-weight: 500;\n color: #888; /* Cinza */\n text-align: center;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n max-width: 90%;\n line-height: 1.1;\n margin-top: 2px;\n}\n\n.device-image {\n max-height: 44px;\n width: auto;\n margin: 4px 0;\n display: block;\n}\n\n.device-data-row {\n display: flex;\n justify-content: center;\n align-items: center;\n margin-top: auto;\n margin-bottom: 6px;\n gap: 6px;\n width: 100%;\n}\n\n.consumption-main {\n font-size: .9rem;\n font-weight: 700;\n color: #28a745;\n display: flex;\n align-items: center;\n gap: 6px;\n justify-content: center;\n white-space: nowrap;\n}\n\n.device-title-percent {\n font-size: .75rem;\n color: rgba(0, 0, 0, .45);\n font-weight: 500;\n}\n\n.flash {\n animation: flash 1s infinite;\n color: #ff9800;\n}\n\n@keyframes flash {\n 0% { opacity: 1; }\n 50% { opacity: .2; }\n 100% { opacity: 1; }\n}\n\n.card-actions {\n flex-shrink: 0;\n height: 100%;\n box-shadow: 1px 0 2px rgba(0, 0, 0, .1);\n display: flex;\n flex-direction: column;\n padding: 0 4px;\n justify-content: space-around;\n align-items: center;\n}\n\n.card-action img {\n width: 24px;\n height: 24px;\n transition: transform .2s ease;\n cursor: pointer;\n}\n\n.card-action img:hover {\n transform: scale(1.15);\n}\n\n.device-card-centered.offline {\n border: 2px solid #ff4d4f;\n animation: border-blink 1s infinite;\n}\n\n.device-card-centered.flipped .device-card-inner {\n transform: rotateY(180deg);\n}\n\n@keyframes border-blink {\n 0%, 100% { box-shadow: 0 0 8px rgba(255, 77, 79, 0.9); }\n 50% { box-shadow: 0 0 16px rgba(255, 0, 0, 0.6); }\n}\n\n.device-card-centered.offline .flash-icon {\n color: #ff4d4f !important;\n font-size: 1.2rem;\n} \n.device-card-centered {\n perspective: 1000px;\n}\n\n.device-card-inner {\n position: relative;\n width: 100%;\n height: 100%;\n transform-style: preserve-3d;\n min-height: 140px; /* ATUALIZADO: A altura mínima agora é controlada aqui */\n}\n\n.device-card-front {\n display: flex;\n flex-direction: row;\n justify-content: flex-start;\n align-items: flex-start; /* Agora isso vai funcionar */\n position: absolute;\n width: 100%;\n height: 100%;\n top: 0;\n left: 0;\n backface-visibility: hidden;\n gap: 4px;\n}\n\n.device-card-back {\n transform: rotateY(180deg);\n}\n\n.device-card-front .device-info,\n.device-card-back .device-info {\n position: absolute;\n top: 8px;\n right: 12px;\n margin: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 4px;\n border-radius: 50%;\n cursor: pointer;\n z-index: 10;\n background: none;\n border: none;\n transition: background 0.2s;\n}\n.device-card-front .device-info:hover,\n.device-card-back .device-info:hover {\n background: rgba(0, 0, 0, 0.05);\n}\n\n\n.device-card-back {\n display: flex;\n flex-direction: column;\n justify-content: space-around; /* Mantido para preencher o verso */\n align-items: stretch;\n padding: 10px;\n gap: 5px;\n height: 100%;\n width: 100%;\n box-sizing: border-box;\n \n /* Posições para o flip */\n position: absolute;\n top: 0;\n left: 0;\n backface-visibility: hidden;\n transform: rotateY(180deg);\n}\n\n.device-card-back #status-bar {\n position: relative;\n width: 100%;\n padding-top: 10px;\n}\n\n.device-card-back .value-container {\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: 5px;\n color: #c19efc;\n align-self: center;\n}\n\n.device-card-back #lastconsumptionTime {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 2px;\n font-size: 11px;\n font-weight: bold;\n color: black;\n width: 100%;\n padding-bottom: 10px;\n}\n\n\n.device-card-centered.flipped .device-card-inner {\n transform: rotateY(180deg);\n}\n\n.flash-icon.flash {\n animation: icon-blink 1s infinite;\n}\n\n@keyframes icon-blink {\n 0%, 100% { opacity: 1; transform: scale(1); }\n 50% { opacity: 0.2; transform: scale(1.2); }\n}\n\n.device-card-centered.offline .flash-icon {\n color: #ff4d4f !important;\n}\n\n.device-card-centered.online .flash-icon {\n color: #28a745 !important;\n}\n `;document.head.appendChild(style)}let formattedDateVal;let formattedDate;let Infcolor="#5cb85c";if(connectionStatusTime){const date=new Date(connectionStatusTime);const day=String(date.getDate()).padStart(2,"0");const month=String(date.getMonth()+1).padStart(2,"0");const year=date.getFullYear();const hours=String(date.getHours()).padStart(2,"0");const minutes=String(date.getMinutes()).padStart(2,"0");formattedDate=`Online: (${day}/${month}/${year} - ${hours}:${minutes})`;const datVal=new Date(timaVal);const dayVal=String(datVal.getDate()).padStart(2,"0");const monthVal=String(datVal.getMonth()+1).padStart(2,"0");const yearVal=date.getFullYear();const hoursVal=String(date.getHours()).padStart(2,"0");const minutesVal=String(date.getMinutes()).padStart(2,"0");const now=new Date;const diffMs=now-datVal;const diffMinutes=Math.floor(diffMs/(1e3*60));const diffHours=Math.floor(diffMs/(1e3*60*60));const diffDays=Math.floor(diffMs/(1e3*60*60*24));let diffText="";if(diffMinutes<60){diffText=`${diffMinutes} minuto${diffMinutes!==1?"s":""}`}else if(diffHours<24){diffText=`${diffHours} hora${diffHours!==1?"s":""}`}else{diffText=`${diffDays} dia${diffDays!==1?"s":""} `}formattedDateVal=`${dayVal}/${monthVal}/${yearVal} - ${hoursVal}:${minutesVal} (${diffText})`;if(diffHours>=24){Infcolor="#cc2900"}else if(diffMinutes>=30){Infcolor="#e89105"}}else{Infcolor="#d6dcdd"}function normalizeString(str){if(typeof str!=="string"){str=""}return str.normalize("NFD").replace(/[\u0300-\u036f]/g,"").toUpperCase()}const deviceImages={MOTOR:"https://dashboard.myio-bas.com/api/images/public/8Ezn8qVBJ3jXD0iDfnEAZ0MZhAP1b5Ts","3F_MEDIDOR":"https://dashboard.myio-bas.com/api/images/public/f9Ce4meybsdaAhAkUlAfy5ei3I4kcN4k",RELOGIO:"https://dashboard.myio-bas.com/api/images/public/ljHZostWg0G5AfKiyM8oZixWRIIGRASB",HIDROMETRO:"https://dashboard.myio-bas.com/api/images/public/aMQYFJbGHs9gQbQkMn6XseAlUZHanBR4",ENTRADA:"https://dashboard.myio-bas.com/api/images/public/TQHPFqiejMW6lOSVsb8Pi85WtC0QKOLU",CAIXA_DAGUA:"https://dashboard.myio-bas.com/api/images/public/3t6WVhMQJFsrKA8bSZmrngDsNPkZV7fq"};const defaultImage="https://cdn-icons-png.flaticon.com/512/1178/1178428.png";const nameType=normalizeString(deviceType);const img=deviceImages[nameType]||defaultImage;const html=`\n <div class="device-card-centered clickable ${isOfflineOrDanger?"offline":""}"\n data-entity-id="${entityId}"\n data-entity-label="${labelOrName}"\n data-entity-type="${entityType}"\n data-entity-slaveid="${slaveId}"\n data-entity-ingestionid="${ingestionId}"\n data-entity-consumption="${val}"\n data-entity-centralid="${centralId}"\n data-entity-updated-identifiers='${JSON.stringify(updatedIdentifiers)}'>\n <div class="device-card-inner" style="width:100%; height:100%; transform-style: preserve-3d; transition: transform 0.6s;">\n <div class="device-card-front">\n\n <div class="card-actions" >\n ${typeof handleActionDashboard==="function"?`\n <div class="card-action action-dashboard" data-action="dashboard" title="Dashboard">\n <img src="https://dashboard.myio-bas.com/api/images/public/TAVXE0sTbCZylwGsMF9lIWdllBB3iFtS"/>\n </div>`:``}\n ${typeof handleActionReport==="function"?`\n <div class="card-action action-report" data-action="report" title="Relatório">\n <img src="https://dashboard.myio-bas.com/api/images/public/d9XuQwMYQCG2otvtNSlqUHGavGaSSpz4"/>\n </div>`:``}\n ${typeof handleActionSettings==="function"?`\n <div class="card-action action-settings" data-action="settings" title="Configurações">\n <img src="https://dashboard.myio-bas.com/api/images/public/5n9tze6vED2uwIs5VvJxGzNNZ9eV4yoz"/>\n </div>`:``}\n ${typeof handleSelect==="function"?`\n <input class="card-action action-checker" data-action="checker" title="Selecionar" type="checkbox">`:``}\n </div>\n\n <div style="display:flex;flex-direction:column;justify-content:center;align-items:center;height:100%; flex-grow: 1; min-width: 0; padding: 0 12px;">\n \n <div class="device-title-row" style="flex-direction: column; min-height: 38px;">\n <span class="device-title" title="${labelOrName}">\n ${String(labelOrName??"").length>15?String(labelOrName).slice(0,15)+"…":String(labelOrName??"")}\n </span>\n ${deviceIdentifier?`\n <span class="device-subtitle" title="${deviceIdentifier}">\n ${deviceIdentifier}\n </span>\n `:""}\n </div>\n\n <img class="device-image" src="${img}" />\n <div class="device-data-row">\n <div class="consumption-main">\n \n <span class="flash-icon ${shouldFlashIcon2?"flash":""}">\n ${icon}\n </span>\n\n <span class="consumption-value" data-entity-consumption="${val}">${valFormatted}</span>\n <span class="device-title-percent">(${percFormatted}%)</span>\n </div>\n </div>\n </div>\n \n ${handInfo?`\n <button id="infoButtom-front" class="device-info info-button">\n <svg xmlns="http://www.w3.org/2000/svg" height="17px" viewBox="0 -960 960 960" width="17px" fill=${Infcolor}>\n <path d="M440-280h80v-240h-80v240Zm40-320q17 0 28.5-11.5T520-640q0-17-11.5-28.5T480-680q-17 0-28.5 11.5T440-640q0 17 11.5 28.5T480-600Zm0 520q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/>\n </svg>\n </button>`:``}\n </div>\n <div class="device-card-back" >\n <div id="status-bar"> \n <div id="status-information">\n <div style="font-size: 0.85rem; font-weight: bold; line-height: 1;"><span>Central: ${centralName}</span></div>\n <div style="display: flex; flex-direction: row; gap: 4px; font-weight: bold; align-items: center;">\n <div style="font-size: 12px; font-weight: bold; line-height: 1;"><span> ${formattedDate}</span></div>\n <div style="font-size: 10px; line-height: 1;"><span></span></div> \n </div>\n </div>\n <button id="infoButtom-back" class="device-info" style="background:none; border:none; cursor:pointer;">\n <svg xmlns="http://www.w3.org/2000/svg" height="17px" viewBox="0 -960 960 960" width="17px" fill=${Infcolor}>\n <path d="M440-280h80v-240h-80v240Zm40-320q17 0 28.5-11.5T520-640q0-17-11.5-28.5T480-680q-17 0-28.5 11.5T440-640q0 17 11.5 28.5T480-600Zm0 520q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/>\n </svg>\n </button>\n </div>\n\n <div class="value-container">\n <svg xmlns="http://www.w3.org/2000/svg" height="30px" viewBox="0 -960 960 960" width="30px" fill="#dea404">\n <path d="m456-200 174-340H510v-220L330-420h126v220Zm24 120q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/>\n </svg>\n <div><span>${MyIO.formatEnergy(val/1e3)||"-"}</span></div>\n </div>\n\n <div id="lastconsumptionTime">\n <div style="font-size: 12px; font-weight: bold; color: black; line-height: 1;">Ultima Telemetria:</div>\n <div style="font-size: 11px; font-weight: bold; color: black; line-height: 1;">${formattedDateVal}</div>\n </div>\n </div>\n </div>\n </div> \n </div>\n \n `;const $card=$(html);if(typeof handleActionDashboard==="function"){$card.find(".action-dashboard").on("click",e=>{e.stopPropagation();handleActionDashboard(entityObject)})}if(typeof handleActionReport==="function"){$card.find(".action-report").on("click",e=>{e.stopPropagation();handleActionReport(entityObject)})}if(typeof handleActionSettings==="function"){$card.find(".action-settings").on("click",e=>{e.stopPropagation();handleActionSettings(entityObject)})}if(typeof handleSelect==="function"){$card.find(".action-checker").on("click",e=>{e.stopPropagation();handleSelect(entityObject)})}if(typeof handleClickCard==="function"){$card.find(".action-checker").on("click",e=>{e.stopPropagation();handleClickCard(entityObject)})}$card.on("click",e=>{if(!$(e.target).closest(".card-action").length){if(typeof handleClickCard==="function"){handleClickCard(entityObject)}else if(typeof handleActionDashboard==="function"){handleActionDashboard(entityObject)}}});$card.find("#infoButtom-front").on("click",function(e){e.stopPropagation();$(this).closest(".device-card-centered").addClass("flipped")});$card.find("#infoButtom-back").on("click",function(e){e.stopPropagation();$(this).closest(".device-card-centered").removeClass("flipped")});return $card}var init_template_card=__esm({"src/thingsboard/main-dashboard-shopping/v-4.0.0/card/template-card.js"(){}});function formatEnergy(value,unit){if(value===null||value===void 0||isNaN(value)){return"-"}let adjustedValue=value;let adjustedUnit=unit;if(!adjustedUnit){if(value>=1e6){adjustedValue=value/1e6;adjustedUnit="GWh"}else if(value>=1e3){adjustedValue=value/1e3;adjustedUnit="MWh"}else{adjustedUnit="kWh"}}const formattedValue=adjustedValue.toLocaleString("pt-BR",{minimumFractionDigits:2,maximumFractionDigits:2});return`${formattedValue} ${adjustedUnit}`}function formatAllInSameUnit(values,targetUnit,sourceUnit="kWh"){const unitMultipliers={kWh:1,MWh:1e3,GWh:1e6};const targetMultiplier=unitMultipliers[targetUnit]||1;if(typeof values[0]==="number"){const numberValues=values;const sourceMultiplier=unitMultipliers[sourceUnit]||1;return numberValues.map(value=>{if(value===null||value===void 0||isNaN(value)){return"-"}const convertedValue=value*sourceMultiplier/targetMultiplier;return formatEnergy(convertedValue,targetUnit)})}const objectValues=values;return objectValues.map(item=>{if(item.value===null||item.value===void 0||isNaN(item.value)){return"-"}const sourceMultiplier=unitMultipliers[item.unit]||1;const convertedValue=item.value*sourceMultiplier/targetMultiplier;return formatEnergy(convertedValue,targetUnit)})}function fmtPerc(value){if(value===null||value===void 0||isNaN(value)||!isFinite(value)){return"-"}const percentage=value*100;return percentage.toLocaleString("pt-BR",{minimumFractionDigits:2,maximumFractionDigits:2})+"%"}function formatNumberReadable(value,locale="pt-BR",minimumFractionDigits=2,maximumFractionDigits=2){const n=typeof value==="string"?Number(value.replace(",",".")):Number(value);if(!Number.isFinite(n)){return"-"}const safe=Object.is(n,-0)?0:n;return safe.toLocaleString(locale,{minimumFractionDigits:minimumFractionDigits,maximumFractionDigits:maximumFractionDigits})}function formatWater(value){const num=Number(value)||0;return`${num.toFixed(2)} m³`}function formatWaterVolumeM3(value,locale="pt-BR"){if(value===null||value===void 0||isNaN(value)){return"-"}const formattedValue=value.toLocaleString(locale,{minimumFractionDigits:2,maximumFractionDigits:2});return`${formattedValue} M³`}function formatTankHeadFromCm(valueCm,locale="pt-BR"){if(valueCm===null||valueCm===void 0||isNaN(valueCm)){return"-"}const valueMeters=valueCm/100;const formattedValue=valueMeters.toLocaleString(locale,{minimumFractionDigits:2,maximumFractionDigits:2});return`${formattedValue} m.c.a.`}function calcDeltaPercent(prev,current){if(prev===null||prev===void 0||isNaN(prev)||current===null||current===void 0||isNaN(current)){return{value:0,type:"neutral"}}if(prev===0&¤t===0){return{value:0,type:"neutral"}}if(prev===0&¤t>0){return{value:100,type:"increase"}}if(prev===0&¤t<0){return{value:100,type:"decrease"}}const percentChange=(current-prev)/prev*100;if(percentChange>0){return{value:percentChange,type:"increase"}}else if(percentChange<0){return{value:Math.abs(percentChange),type:"decrease"}}else{return{value:0,type:"neutral"}}}function formatWaterByGroup(value,group){if(value===null||value===void 0||isNaN(value)){return"-"}if(group==="Caixas D'Água"){return formatTankHeadFromCm(value)}if(value>=1e3){return formatWaterVolumeM3(value/1e3)+" x 10³ "}return formatWaterVolumeM3(value)}function formatAllInSameWaterUnit(values){const max=Math.max(...values.filter(v=>!isNaN(v)&&v!==null&&v!==void 0));let divisor=1;let unit="M³";if(max>=1e6){divisor=1e6;unit="M³"}else if(max>=1e3){divisor=1e3;unit="M³"}return{format:val=>{if(val===null||val===void 0||isNaN(val)){return"-"}return(val/divisor).toFixed(2)+" "+unit},unit:unit}}function formatRelativeTime(timestamp){if(!timestamp||timestamp<=0){return"—"}const now=Date.now();const diffSeconds=Math.round((now-timestamp)/1e3);if(diffSeconds<10){return"agora"}if(diffSeconds<60){return`há ${diffSeconds}s`}const diffMinutes=Math.round(diffSeconds/60);if(diffMinutes===1){return"há 1 min"}if(diffMinutes<60){return`há ${diffMinutes} mins`}const diffHours=Math.round(diffMinutes/60);if(diffHours===1){return"há 1 hora"}if(diffHours<24){return`há ${diffHours} horas`}const diffDays=Math.round(diffHours/24);if(diffDays===1){return"ontem"}if(diffDays<=30){return`há ${diffDays} dias`}return new Date(timestamp).toLocaleDateString("pt-BR")}function formatarDuracao(ms){if(typeof ms!=="number"||ms<0||!isFinite(ms)){return"0s"}if(ms===0){return"0s"}const segundos=Math.floor(ms/1e3%60);const minutos=Math.floor(ms/(1e3*60)%60);const horas=Math.floor(ms/(1e3*60*60)%24);const dias=Math.floor(ms/(1e3*60*60*24));const parts=[];if(dias>0){parts.push(`${dias}d`);if(horas>0){parts.push(`${horas}h`)}}else if(horas>0){parts.push(`${horas}h`);if(minutos>0){parts.push(`${minutos}m`)}}else if(minutos>0){parts.push(`${minutos}m`);if(segundos>0){parts.push(`${segundos}s`)}}else{parts.push(`${segundos}s`)}return parts.length>0?parts.join(" "):"0s"}var formatDuration=formatarDuracao;function formatDateToYMD(date){if(!date){return""}const dateObj=new Date(date);if(isNaN(dateObj.getTime())){return""}const year=dateObj.getFullYear();const month=String(dateObj.getMonth()+1).padStart(2,"0");const day=String(dateObj.getDate()).padStart(2,"0");return`${year}-${month}-${day}`}function determineInterval(startDate,endDate){const start=new Date(startDate);const end=new Date(endDate);if(isNaN(start.getTime())||isNaN(end.getTime())){return"day"}const diffMs=end.getTime()-start.getTime();const diffDays=diffMs/(1e3*60*60*24);if(diffDays<=1){return"hour"}else if(diffDays<=7){return"day"}else if(diffDays<=31){return"week"}else if(diffDays<=365){return"month"}else{return"year"}}function getSaoPauloISOString(date,edge="start"){const dateObj=new Date(date);if(isNaN(dateObj.getTime())){return""}const saoPauloOffset=-3;const saoPauloDate=new Date(dateObj.getTime()+saoPauloOffset*60*60*1e3);if(edge==="start"){saoPauloDate.setHours(0,0,0,0)}else{saoPauloDate.setHours(23,59,59,999)}const utcDate=new Date(saoPauloDate.getTime()-saoPauloOffset*60*60*1e3);return utcDate.toISOString()}function getDateRangeArray(startDate,endDate,interval="day"){const start=new Date(startDate);const end=new Date(endDate);const dates=[];if(isNaN(start.getTime())||isNaN(end.getTime())||start>end){return dates}const current=new Date(start);while(current<=end){dates.push(new Date(current));switch(interval){case"day":current.setDate(current.getDate()+1);break;case"week":current.setDate(current.getDate()+7);break;case"month":current.setMonth(current.getMonth()+1);break;case"year":current.setFullYear(current.getFullYear()+1);break;default:current.setDate(current.getDate()+1)}}return dates}function formatDateForInput(date){if(!date||isNaN(date.getTime())){return""}const year=date.getFullYear();const month=String(date.getMonth()+1).padStart(2,"0");const day=String(date.getDate()).padStart(2,"0");return`${year}-${month}-${day}`}function parseInputDateToDate(inputDateStr){if(!inputDateStr){return null}const parts=inputDateStr.split("-");if(parts.length!==3){return null}const year=parseInt(parts[0],10);const month=parseInt(parts[1],10)-1;const day=parseInt(parts[2],10);if(isNaN(year)||isNaN(month)||isNaN(day)){return null}return new Date(year,month,day,0,0,0,0)}function timeWindowFromInputYMD(startYmd,endYmd,tzOffset="-03:00"){if(!startYmd||!endYmd){return{startTs:0,endTs:0}}const startParts=startYmd.split("-");const endParts=endYmd.split("-");if(startParts.length!==3||endParts.length!==3){return{startTs:0,endTs:0}}const startDate=new Date(parseInt(startParts[0],10),parseInt(startParts[1],10)-1,parseInt(startParts[2],10),0,0,0,0);const endDate=new Date(parseInt(endParts[0],10),parseInt(endParts[1],10)-1,parseInt(endParts[2],10),23,59,59,999);return{startTs:startDate.getTime(),endTs:endDate.getTime()}}function formatDateWithTimezoneOffset(date,endOfDay=false,tzOffset="-03:00"){if(!date||isNaN(date.getTime())){return""}const year=date.getFullYear();const month=String(date.getMonth()+1).padStart(2,"0");const day=String(date.getDate()).padStart(2,"0");let hours,minutes,seconds,milliseconds;if(endOfDay){hours="23";minutes="59";seconds="59";milliseconds="999"}else{hours=String(date.getHours()).padStart(2,"0");minutes=String(date.getMinutes()).padStart(2,"0");seconds=String(date.getSeconds()).padStart(2,"0");milliseconds=String(date.getMilliseconds()).padStart(3,"0")}return`${year}-${month}-${day}T${hours}:${minutes}:${seconds}.${milliseconds}${tzOffset}`}function getSaoPauloISOStringFixed(dateStr,endOfDay=false){if(!dateStr)return"";if(endOfDay){return`${dateStr}T23:59:59.999-03:00`}else{return`${dateStr}T00:00:00.000-03:00`}}function averageByDay(data){if(!data||data.length===0){return[]}const grouped={};data.forEach(item=>{if(!item||item.value===null||item.value===void 0||isNaN(item.value)){return}const date=new Date(item.ts);if(isNaN(date.getTime())){return}const day=date.toISOString().split("T")[0];if(!grouped[day]){grouped[day]=[]}grouped[day].push(Number(item.value))});const result=Object.entries(grouped).map(([day,values])=>{const sum=values.reduce((acc,val)=>acc+val,0);const average=sum/values.length;return{day:day,average:average}});result.sort((a,b)=>a.day.localeCompare(b.day));return result}function groupByDay(data){if(!data||data.length===0){return{}}const grouped={};data.forEach(item=>{if(!item||item.value===null||item.value===void 0||isNaN(item.value)){return}const date=new Date(item.ts);if(isNaN(date.getTime())){return}const day=date.toISOString().split("T")[0];if(!grouped[day]){grouped[day]=[]}grouped[day].push(Number(item.value))});return grouped}function exportToCSV(data,headers,filename){if(!data||data.length===0){return""}const csvHeaders=headers.join(",");const csvRows=data.map(row=>headers.map(header=>{const value=row[header];if(value===null||value===void 0){return""}const stringValue=String(value);if(stringValue.includes(",")||stringValue.includes('"')||stringValue.includes("\n")){return`"${stringValue.replace(/"/g,'""')}"`}return stringValue}).join(","));const csvContent=[csvHeaders,...csvRows].join("\n");return csvContent}function exportToCSVAll(storesData,headers,filename){if(!storesData||Object.keys(storesData).length===0){return""}const csvRows=[];const csvHeaders=["Store",...headers].join(",");csvRows.push(csvHeaders);Object.entries(storesData).forEach(([storeName,storeData])=>{if(!storeData||storeData.length===0){return}storeData.forEach(row=>{let formattedStoreName=storeName;if(storeName.includes(",")||storeName.includes('"')||storeName.includes("\n")){formattedStoreName=`"${storeName.replace(/"/g,'""')}"`}const csvRow=[formattedStoreName,...headers.map(header=>{const value=row[header];if(value===null||value===void 0){return""}const stringValue=String(value);if(stringValue.includes(",")||stringValue.includes('"')||stringValue.includes("\n")){return`"${stringValue.replace(/"/g,'""')}"`}return stringValue})].join(",");csvRows.push(csvRow)})});return csvRows.join("\n")}function buildWaterReportCSV(rows,meta){if(!rows||rows.length===0){return""}const csvRows=[];let totalConsumption=0;rows.forEach(row=>{const consumptionStr=String(row.totalConsumption).replace(",",".");const consumption=Number(consumptionStr)||0;totalConsumption+=consumption});const finalTotal=meta.total!==void 0?meta.total:totalConsumption;csvRows.push(["DATA EMISSÃO",meta.issueDate]);csvRows.push(["Total",finalTotal.toFixed(2)]);if(meta.name&&meta.identifier){csvRows.push(["Loja:",meta.name,meta.identifier])}csvRows.push(["Data","Dia da Semana","Consumo Médio (m³)","Consumo Mínimo (m³)","Consumo Máximo (m³)","Consumo (m³)"]);rows.forEach(row=>{csvRows.push([row.formattedDate,row.day,String(row.avgConsumption),String(row.minDemand),String(row.maxDemand),String(row.totalConsumption)])});return csvRows.map(row=>row.join(";")).join("\n")}function buildWaterStoresCSV(rows,meta){if(!rows||rows.length===0){return""}const csvRows=[];let totalConsumption=0;rows.forEach(row=>{const consumption=row.consumptionM3!==void 0?row.consumptionM3:row.consumptionKwh||0;totalConsumption+=consumption});const finalTotal=meta.total!==void 0?meta.total:totalConsumption;csvRows.push(["DATA EMISSÃO",meta.issueDate]);csvRows.push(["Total",finalTotal.toFixed(2)]);csvRows.push(["Loja","Identificador","Consumo"]);rows.forEach(row=>{const label=row.entityLabel||row.deviceName||"-";const deviceId=row.deviceId||"-";const consumption=row.consumptionM3!==void 0?row.consumptionM3:row.consumptionKwh||0;const formattedConsumption=consumption!==null&&consumption!==void 0?formatNumberReadable(consumption):"0,00";csvRows.push([label,deviceId,formattedConsumption])});return csvRows.map(row=>row.join(";")).join("\n")}function toCSV(rows,delimiter=";"){if(!rows||rows.length===0){return""}return rows.map(row=>row.map(cell=>{const value=String(cell);if(value.includes(delimiter)||value.includes('"')||value.includes("\n")){return`"${value.replace(/"/g,'""')}"`}return value}).join(delimiter)).join("\n")}function classify(entity,criteria){if(!entity||!criteria){return{category:"unknown",confidence:0}}let category="unknown";let subcategory;let confidence=0;if(entity.type){switch(entity.type.toLowerCase()){case"consumption":category="energy_consumption";confidence=.9;break;case"generation":category="energy_generation";confidence=.9;break;case"storage":category="energy_storage";confidence=.9;break;case"distribution":category="energy_distribution";confidence=.8;break;default:category="energy_other";confidence=.5}}if(entity.powerRating){const power=parseFloat(entity.powerRating);if(!isNaN(power)){if(power<1e3){subcategory="small_scale"}else if(power<1e4){subcategory="medium_scale"}else{subcategory="large_scale"}confidence=Math.min(confidence+.1,1)}}return{category:category,subcategory:subcategory,confidence:confidence}}function classifyWaterLabel(label){if(!label){console.warn('classifyWaterLabel: empty label, defaulting to "Lojas"');return"Lojas"}const normalizedLabel=label.toLowerCase().trim();if(/rel[óo]gio|caixa|superior|inferior|nível_terraço/.test(normalizedLabel)){return"Caixas D'Água"}if(/administra|bomba|chiller|adm/.test(normalizedLabel)){return"Área Comum"}return"Lojas"}function classifyWaterLabels(labels){const counts={"Caixas D'Água":0,Lojas:0,"Área Comum":0,total:0};labels.forEach(label=>{const category=classifyWaterLabel(label);counts[category]++;counts.total++});return counts}function getWaterCategories(){return["Caixas D'Água","Lojas","Área Comum"]}function isWaterCategory(label,category){return classifyWaterLabel(label)===category}function getValueByDatakey(data,datakey){if(!data||!datakey){return void 0}if(Array.isArray(data)){for(const item of data){const value=getValueByDatakey(item,datakey);if(value!==void 0){return value}}return void 0}if(typeof data==="object"&&data!==null){const keys=datakey.split(".");let current=data;for(const key of keys){if(current===null||current===void 0){return void 0}if(key.includes("[")&&key.includes("]")){const arrayKey=key.substring(0,key.indexOf("["));const indexMatch=key.match(/\[(\d+)\]/);if(indexMatch){const index=parseInt(indexMatch[1],10);current=current[arrayKey];if(Array.isArray(current)&&index>=0&&index<current.length){current=current[index]}else{return void 0}}else{return void 0}}else{current=current[key]}}return current}return void 0}function getValueByDatakeyLegacy(dataList,dataSourceNameTarget,dataKeyTarget){if(!Array.isArray(dataList)||!dataSourceNameTarget||!dataKeyTarget){return void 0}for(const item of dataList){if(item&&item.dataSourceName===dataSourceNameTarget&&item.dataKey===dataKeyTarget){return item.value}}return void 0}function findValue(data,keyOrPath,legacyDataKey){if(legacyDataKey!==void 0){return getValueByDatakeyLegacy(data,keyOrPath,legacyDataKey)}return getValueByDatakey(data,keyOrPath)}function findValueWithDefault(values,key,defaultValue=null){if(!Array.isArray(values))return defaultValue;const found=values.find(v=>v.key===key||v.dataType===key);return found?found.value:defaultValue}var DeviceStatusType={POWER_ON:"power_on",STANDBY:"standby",POWER_OFF:"power_off",WARNING:"warning",FAILURE:"failure",MAINTENANCE:"maintenance",NO_INFO:"no_info",NOT_INSTALLED:"not_installed"};var ConnectionStatusType={CONNECTED:"connected",OFFLINE:"offline"};var deviceStatusIcons={[DeviceStatusType.POWER_ON]:"⚡",[DeviceStatusType.STANDBY]:"🔌",[DeviceStatusType.POWER_OFF]:"🔴",[DeviceStatusType.WARNING]:"⚠️",[DeviceStatusType.FAILURE]:"🚨",[DeviceStatusType.MAINTENANCE]:"🛠️",[DeviceStatusType.NO_INFO]:"❓️",[DeviceStatusType.NOT_INSTALLED]:"📦"};var waterDeviceStatusIcons={[DeviceStatusType.POWER_ON]:"💧",[DeviceStatusType.STANDBY]:"🚰",[DeviceStatusType.POWER_OFF]:"🔴",[DeviceStatusType.WARNING]:"⚠️",[DeviceStatusType.FAILURE]:"🚨",[DeviceStatusType.MAINTENANCE]:"🛠️",[DeviceStatusType.NO_INFO]:"❓️",[DeviceStatusType.NOT_INSTALLED]:"📦"};var temperatureDeviceStatusIcons={[DeviceStatusType.POWER_ON]:"🌡️",[DeviceStatusType.STANDBY]:"🌡️",[DeviceStatusType.POWER_OFF]:"🔴",[DeviceStatusType.WARNING]:"⚠️",[DeviceStatusType.FAILURE]:"🚨",[DeviceStatusType.MAINTENANCE]:"🛠️",[DeviceStatusType.NO_INFO]:"❓️",[DeviceStatusType.NOT_INSTALLED]:"📦"};var connectionStatusIcons={[ConnectionStatusType.CONNECTED]:"🟢",[ConnectionStatusType.OFFLINE]:"🚫"};function mapDeviceToConnectionStatus(deviceStatus){if(deviceStatus===DeviceStatusType.NO_INFO){return ConnectionStatusType.OFFLINE}return ConnectionStatusType.CONNECTED}function mapConnectionStatus(rawStatus){const statusLower=String(rawStatus||"").toLowerCase().trim();if(statusLower==="online"||statusLower==="ok"||statusLower==="running"){return"online"}if(statusLower==="waiting"||statusLower==="connecting"||statusLower==="pending"){return"waiting"}return"offline"}function mapDeviceStatusToCardStatus(deviceStatus){const statusMap={[DeviceStatusType.POWER_ON]:"ok",[DeviceStatusType.STANDBY]:"alert",[DeviceStatusType.POWER_OFF]:"fail",[DeviceStatusType.WARNING]:"alert",[DeviceStatusType.FAILURE]:"fail",[DeviceStatusType.MAINTENANCE]:"alert",[DeviceStatusType.NO_INFO]:"unknown",[DeviceStatusType.NOT_INSTALLED]:"not_installed"};return statusMap[deviceStatus]||"unknown"}function shouldFlashIcon(deviceStatus){return deviceStatus===DeviceStatusType.POWER_OFF||deviceStatus===DeviceStatusType.WARNING||deviceStatus===DeviceStatusType.FAILURE||deviceStatus===DeviceStatusType.MAINTENANCE}function isDeviceOffline(deviceStatus){return deviceStatus===DeviceStatusType.NO_INFO}function getDeviceStatusIcon(deviceStatus,deviceType=null){const normalizedType=deviceType?.toUpperCase()||"";const isWaterDevice=normalizedType==="TANK"||normalizedType==="CAIXA_DAGUA";const isTemperatureDevice=normalizedType==="TERMOSTATO";let iconMap;if(isWaterDevice){iconMap=waterDeviceStatusIcons}else if(isTemperatureDevice){iconMap=temperatureDeviceStatusIcons}else{iconMap=deviceStatusIcons}return iconMap[deviceStatus]||iconMap[DeviceStatusType.POWER_ON]}function getConnectionStatusIcon(connectionStatus){return connectionStatusIcons[connectionStatus]||connectionStatusIcons[ConnectionStatusType.OFFLINE]}function isValidDeviceStatus(status){return Object.values(DeviceStatusType).includes(status)}function isValidConnectionStatus(status){return Object.values(ConnectionStatusType).includes(status)}function getDeviceStatusInfo(deviceStatus){const connectionStatus=mapDeviceToConnectionStatus(deviceStatus);return{deviceStatus:deviceStatus,connectionStatus:connectionStatus,cardStatus:mapDeviceStatusToCardStatus(deviceStatus),deviceIcon:getDeviceStatusIcon(deviceStatus),connectionIcon:getConnectionStatusIcon(connectionStatus),shouldFlash:shouldFlashIcon(deviceStatus),isOffline:isDeviceOffline(deviceStatus),isValid:isValidDeviceStatus(deviceStatus)}}function calculateDeviceStatus({connectionStatus:connectionStatus,lastConsumptionValue:lastConsumptionValue,limitOfPowerOnStandByWatts:limitOfPowerOnStandByWatts,limitOfPowerOnAlertWatts:limitOfPowerOnAlertWatts,limitOfPowerOnFailureWatts:limitOfPowerOnFailureWatts}){const validConnectionStatuses=["waiting","offline","online"];if(!validConnectionStatuses.includes(connectionStatus)){return DeviceStatusType.MAINTENANCE}if(connectionStatus==="waiting"){return DeviceStatusType.NOT_INSTALLED}if(connectionStatus==="offline"){return DeviceStatusType.NO_INFO}if(connectionStatus==="online"&&(lastConsumptionValue===null||lastConsumptionValue===void 0)){return DeviceStatusType.POWER_ON}if(connectionStatus==="online"&&lastConsumptionValue!==null&&lastConsumptionValue!==void 0){const consumption=Number(lastConsumptionValue);if(isNaN(consumption)){return DeviceStatusType.MAINTENANCE}if(consumption>=0&&consumption<=limitOfPowerOnStandByWatts){return DeviceStatusType.STANDBY}if(consumption>limitOfPowerOnStandByWatts&&consumption<=limitOfPowerOnAlertWatts){return DeviceStatusType.POWER_ON}if(consumption>limitOfPowerOnAlertWatts&&consumption<=limitOfPowerOnFailureWatts){return DeviceStatusType.WARNING}if(consumption>limitOfPowerOnFailureWatts){return DeviceStatusType.FAILURE}}return DeviceStatusType.MAINTENANCE}function calculateDeviceStatusWithRanges({connectionStatus:connectionStatus,lastConsumptionValue:lastConsumptionValue,ranges:ranges}){const validConnectionStatuses=["waiting","offline","online"];if(!validConnectionStatuses.includes(connectionStatus)){return DeviceStatusType.MAINTENANCE}if(connectionStatus==="waiting"){return DeviceStatusType.NOT_INSTALLED}if(connectionStatus==="offline"){return DeviceStatusType.NO_INFO}if(connectionStatus==="online"&&(lastConsumptionValue===null||lastConsumptionValue===void 0)){return DeviceStatusType.POWER_ON}if(!ranges||!ranges.standbyRange||!ranges.normalRange||!ranges.alertRange||!ranges.failureRange){console.error("[RFC-0077] Invalid ranges object:",ranges);return DeviceStatusType.MAINTENANCE}if(connectionStatus==="online"&&lastConsumptionValue!==null&&lastConsumptionValue!==void 0){const consumption=Number(lastConsumptionValue);if(isNaN(consumption)){return DeviceStatusType.MAINTENANCE}const{standbyRange:standbyRange,normalRange:normalRange,alertRange:alertRange,failureRange:failureRange}=ranges;if(consumption>=standbyRange.down&&consumption<=standbyRange.up){return DeviceStatusType.STANDBY}if(consumption>=normalRange.down&&consumption<=normalRange.up){return DeviceStatusType.POWER_ON}if(consumption>=alertRange.down&&consumption<=alertRange.up){return DeviceStatusType.WARNING}if(consumption>=failureRange.down&&consumption<=failureRange.up){return DeviceStatusType.FAILURE}if(consumption>failureRange.up){return DeviceStatusType.FAILURE}if(consumption<standbyRange.down){return DeviceStatusType.MAINTENANCE}}return DeviceStatusType.MAINTENANCE}function normalizeAttrKey(raw){const k=String(raw||"").trim();const lower=k.toLowerCase();if(lower==="ingestionid")return"ingestionId";if(lower==="identifier")return"identifier";if(lower==="label")return"label";if(lower==="id")return"id";return k}function buildEntityMapFromDatasource(datasources){const dsArray=Array.isArray(datasources)?datasources:[];const map=new Map;dsArray.forEach(ds=>{const entityId=ds?.entityId;if(!entityId)return;if(!map.has(entityId)){const entity=ds?.entity;const draftLabel=entity?.label||entity?.name||ds?.name||null;map.set(entityId,{id:entityId,identifier:null,label:draftLabel,expectedKeys:new Set,attrs:{}})}const keys=Array.isArray(ds?.dataKeys)?ds.dataKeys:[];const rec=map.get(entityId);keys.forEach(k=>{if(k?.name){rec.expectedKeys.add(String(k.name).toLowerCase())}})});return map}function hydrateEntityMapWithCtxData(data,map){const rows=Array.isArray(data)?data:[];rows.forEach(row=>{const entityId=row?.datasource?.entityId||null;if(!entityId||!map.has(entityId))return;const rawKey=row?.dataKey?.name||"";if(!rawKey)return;const val=Array.isArray(row?.data)&&Array.isArray(row.data[0])?row.data[0][1]:null;if(val==null)return;const rec=map.get(entityId);const attrKey=normalizeAttrKey(rawKey);if(attrKey==="identifier"){rec.identifier=val}else if(attrKey==="label"){rec.label=val}else if(attrKey==="ingestionId"){rec.id=val}else{rec.attrs[attrKey]=val}})}function buildListItemsThingsboardByUniqueDatasource(datasources,data){const map=buildEntityMapFromDatasource(datasources);hydrateEntityMapWithCtxData(data,map);const items=Array.from(map.values()).map(rec=>({id:rec.id,identifier:rec.identifier??rec.label??"",label:rec.label??rec.identifier??""}));items.sort((a,b)=>String(a.label).localeCompare(String(b.label),"pt-BR"));return items}var globalCache=new Map;function generateCacheKey(config){return`${config.dataApiHost}:${config.clientId}:${config.clientSecret}`}function buildMyioIngestionAuth(config){const{dataApiHost:dataApiHost,clientId:clientId,clientSecret:clientSecret,renewSkewSeconds:renewSkewSeconds=60,retryBaseMs:retryBaseMs=500,retryMaxAttempts:retryMaxAttempts=3}=config;if(!dataApiHost||!clientId||!clientSecret){throw new Error("dataApiHost, clientId, and clientSecret are required")}const authUrl=new URL(`${dataApiHost}/api/v1/auth`);const cacheKey=generateCacheKey(config);if(!globalCache.has(cacheKey)){globalCache.set(cacheKey,{token:null,expiresAt:0,inFlight:null})}const cache=globalCache.get(cacheKey);function now(){return Date.now()}function aboutToExpire(){if(!cache.token)return true;const skewMs=renewSkewSeconds*1e3;return now()>=cache.expiresAt-skewMs}async function sleep(ms){return new Promise(resolve=>setTimeout(resolve,ms))}async function requestNewToken(){const body={client_id:clientId,client_secret:clientSecret};let attempt=0;while(true){try{const response=await fetch(authUrl,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(body)});if(!response.ok){const text=await response.text().catch(()=>"");throw new Error(`Auth failed: HTTP ${response.status} ${response.statusText} ${text}`)}const json=await response.json();if(!json||!json.access_token||!json.expires_in){throw new Error("Auth response missing required fields (access_token, expires_in)")}cache.token=json.access_token;cache.expiresAt=now()+Number(json.expires_in)*1e3;console.log(`[MyIOAuth] New token obtained for ${clientId}. Expires in ~${Math.round(Number(json.expires_in)/60)} min`);return cache.token}catch(error){attempt++;console.warn(`[MyIOAuth] Error obtaining token (attempt ${attempt}/${retryMaxAttempts}):`,error instanceof Error?error.message:error);if(attempt>=retryMaxAttempts){throw error}const backoff=retryBaseMs*Math.pow(2,attempt-1);await sleep(backoff)}}}async function getToken(){if(cache.inFlight){return cache.inFlight}if(aboutToExpire()){cache.inFlight=requestNewToken().finally(()=>{cache.inFlight=null});return cache.inFlight}return cache.token}function getExpiryInfo(){return{expiresAt:cache.expiresAt,expiresInSeconds:Math.max(0,Math.floor((cache.expiresAt-now())/1e3))}}function clearCache(){cache.token=null;cache.expiresAt=0;cache.inFlight=null}function isTokenValid(){if(!globalCache.has(cacheKey)){return false}return!aboutToExpire()}return{getToken:getToken,getExpiryInfo:getExpiryInfo,clearCache:clearCache,isTokenValid:isTokenValid}}function clearAllAuthCaches(){globalCache.clear()}function getAuthCacheStats(){return{totalCaches:globalCache.size,cacheKeys:Array.from(globalCache.keys())}}async function fetchThingsboardCustomerServerScopeAttrs(config){const{customerId:customerId,tbToken:tbToken,baseUrl:baseUrl=""}=config;if(!customerId||!tbToken){throw new Error("customerId and tbToken are required")}const url=`${baseUrl}/api/plugins/telemetry/CUSTOMER/${customerId}/values/attributes/SERVER_SCOPE`;try{const response=await fetch(url,{method:"GET",headers:{"Content-Type":"application/json","X-Authorization":`Bearer ${tbToken}`}});if(!response.ok){console.warn(`[ThingsBoard Customer Attrs] HTTP ${response.status} ${response.statusText}`);if(response.status===404||response.status===403){return{}}throw new Error(`ThingsBoard API error: HTTP ${response.status} ${response.statusText}`)}const payload=await response.json();return processAttributesResponse(payload)}catch(error){console.error("[ThingsBoard Customer Attrs] Error fetching attributes:",error);if(error instanceof Error){throw new Error(`Failed to fetch customer attributes: ${error.message}`)}throw new Error("Failed to fetch customer attributes: Unknown error")}}function processAttributesResponse(payload){const attributesMap={};if(!payload){return attributesMap}if(Array.isArray(payload)){for(const item of payload){if(item&&typeof item==="object"&&item.key!==void 0){attributesMap[item.key]=item.value}}return attributesMap}if(typeof payload==="object"){for(const key of Object.keys(payload)){const value=payload[key];if(Array.isArray(value)&&value.length>0){const firstItem=value[0];attributesMap[key]=firstItem?.value??firstItem}else{attributesMap[key]=value}}return attributesMap}console.warn("[ThingsBoard Customer Attrs] Unexpected payload format:",typeof payload);return attributesMap}async function fetchThingsboardCustomerAttrsFromStorage(customerId,tbToken,baseUrl){return fetchThingsboardCustomerServerScopeAttrs({customerId:customerId,tbToken:tbToken,baseUrl:baseUrl})}function extractMyIOCredentials(attributes){if(!attributes||typeof attributes!=="object"){return{clientId:"",clientSecret:"",ingestionId:""}}return{clientId:attributes.client_id||attributes.clientId||"",clientSecret:attributes.client_secret||attributes.clientSecret||"",ingestionId:attributes.ingestionId||attributes.ingestion_id||""}}function normalize(str){if(typeof str!=="string")return"";let normalized=str.toUpperCase().trim();normalized=normalized.replace(/[ÀÁÂÃÄÅ]/g,"A").replace(/[ÈÉÊË]/g,"E").replace(/[ÌÍÎÏ]/g,"I").replace(/[ÒÓÔÕÖ]/g,"O").replace(/[ÙÚÛÜ]/g,"U").replace(/[Ç]/g,"C").replace(/[Ñ]/g,"N");normalized=normalized.replace(/\s+/g," ");return normalized}function matchCaixaDAgua(normalizedStr){if(normalizedStr.includes("SCD"))return true;const caixaVariants=["CAIXA D'AGUA","CAIXA D AGUA","CAIXA_D_AGUA","CAIXA DAGUA"];return caixaVariants.some(variant=>normalizedStr.includes(variant))}var contexts={building:name=>{const normalized=normalize(name);const rules=[{test:s=>s.includes("COMPRESSOR"),type:"COMPRESSOR"},{test:s=>s.includes("VENT"),type:"VENTILADOR"},{test:s=>s.includes("AUTOMATICO"),type:"SELETOR_AUTO_MANUAL"},{test:s=>s.includes("ESRL"),type:"ESCADA_ROLANTE"},{test:s=>s.includes("ESCADA"),type:"ESCADA_ROLANTE"},{test:s=>s.includes("ELEV"),type:"ELEVADOR"},{test:s=>s.includes("MOTR")||s.includes("RECALQUE"),type:"MOTOR"},{test:s=>s.includes("TERMOSTATO"),type:"TERMOSTATO"},{test:s=>s.includes("TERMO")||s.includes("TEMP"),type:"TERMOSTATO"},{test:s=>s.includes("3F"),type:"3F_MEDIDOR"},{test:s=>s.includes("HIDR"),type:"HIDROMETRO"},{test:s=>s.includes("ABRE"),type:"SOLENOIDE"},{test:s=>matchCaixaDAgua(s),type:"CAIXA_D_AGUA"},{test:s=>s.includes("AUTOMACAO")||s.includes("GW_AUTO"),type:"GLOBAL_AUTOMACAO"},{test:s=>s.includes("AC"),type:"CONTROLE REMOTO"}];for(const rule of rules){if(rule.test(normalized)){return rule.type}}return"default"},mall:name=>{const normalized=normalize(name);const rules=[{test:s=>s.includes("CHILLER"),type:"CHILLER"},{test:s=>s.includes("ESCADA"),type:"ESCADA_ROLANTE"},{test:s=>s.includes("LOJA"),type:"LOJA_SENSOR"},{test:s=>s.includes("ILUMINACAO"),type:"ILUMINACAO"}];for(const rule of rules){if(rule.test(normalized)){return rule.type}}return"default"}};var warnedContexts=new Set;function detectDeviceType(name,context="building"){if(typeof name!=="string"){throw new Error("Device name must be a string.")}const detectFunction=contexts[context];if(!detectFunction){if(!warnedContexts.has(context)){console.warn(`[myio-js-library] Context "${context}" not found. Using default fallback.`);warnedContexts.add(context)}return contexts.building(name)}return detectFunction(name)}function getAvailableContexts(){return Object.keys(contexts)}function addDetectionContext(contextName,detectFunction){if(typeof contextName!=="string"){throw new Error("Context name must be a string.")}if(typeof detectFunction!=="function"){throw new Error("Detection function must be a function.")}contexts[contextName]=detectFunction}function addNamespace(payload,namespace=""){if(!payload||typeof payload!=="object"||Array.isArray(payload)){throw new Error("Payload must be an object.")}const keys=Object.keys(payload);const suffix=namespace.trim()?` (${namespace.trim()})`:"";return keys.reduce((acc,key)=>{acc[`${key}${suffix}`]=payload[key];return acc},{})}var numbers_exports={};__export(numbers_exports,{fmtPerc:()=>fmtPerc2,toFixedSafe:()=>toFixedSafe});function fmtPerc2(x,digits=2){if(!Number.isFinite(x))return"—";return(x*100).toFixed(digits)+"%"}function toFixedSafe(x,digits=2){if(!Number.isFinite(x))return"—";return x.toFixed(digits)}var strings_exports={};__export(strings_exports,{normalizeRecipients:()=>normalizeRecipients});function normalizeRecipients(val){if(val===null||val===void 0||val==="")return"";if(Object.prototype.toString.call(val)==="[object Array]"){return val.filter(Boolean).join(",")}let s=String(val).trim();if(/^\s*\[/.test(s)){try{const arr=JSON.parse(s);if(Object.prototype.toString.call(arr)==="[object Array]"){return arr.filter(Boolean).join(",")}}catch{}}s=s.replace(/[;\s]+/g,",");s=s.replace(/,+/g,",").replace(/^,|,$/g,"");return s}function decodePayload(encoded,key){const bytes=base64ToBytesStrict(encoded);if(bytes.length===0)return"";if(key===""||key===void 0||key===null){return(new TextDecoder).decode(bytes)}if(typeof key==="number"&&Number.isFinite(key)){const k=key&255;for(let i=0;i<bytes.length;i++)bytes[i]^=k;return(new TextDecoder).decode(bytes)}const keyStr=String(key);const keyBytes=(new TextEncoder).encode(keyStr);if(keyBytes.length===0){return(new TextDecoder).decode(bytes)}for(let i=0;i<bytes.length;i++){bytes[i]^=keyBytes[i%keyBytes.length]}return(new TextDecoder).decode(bytes)}function decodePayloadBase64Xor(encoded,xorKey=73){const bytes=base64ToBytesStrict(encoded);for(let i=0;i<bytes.length;i++)bytes[i]^=xorKey&255;return(new TextDecoder).decode(bytes)}function base64ToBytesStrict(b64){if(b64===""||b64===void 0||b64===null)return new Uint8Array;const s=String(b64).replace(/\s+/g,"");const re=/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;if(!re.test(s))throw new Error("Invalid base64");if(typeof Buffer!=="undefined"&&Buffer.from){return Uint8Array.from(Buffer.from(s,"base64"))}const bin=atob(s);const out=new Uint8Array(bin.length);for(let i=0;i<bin.length;i++)out[i]=bin.charCodeAt(i);return out}init_template_card();var MyIOSelectionStoreClass=class _MyIOSelectionStoreClass{static GlobalDebug=false;static setGlobalDebug(enabled){_MyIOSelectionStoreClass.GlobalDebug=!!enabled;console.log(`[SelectionStore] 🔧 Global debug ${enabled?"ENABLED":"DISABLED"}`)}_log(level,...args){if(!_MyIOSelectionStoreClass.GlobalDebug)return;const logMethod=console[level]||console.log;logMethod.apply(console,args)}constructor(){this._log("log","[SelectionStore] 🔍 Constructor called - checking for existing instance...");this._log("log","[SelectionStore] typeof document:",typeof document);if(typeof window!=="undefined"){this._log("log","[SelectionStore] window.top === window:",window.top===window);this._log("log","[SelectionStore] document location:",window.location.href);this._log("log","[SelectionStore] Is in iframe:",window!==window.top)}this._log("log","[SelectionStore] document.__MyIOSelectionStore_INSTANCE__:",!!document?.__MyIOSelectionStore_INSTANCE__);if(typeof document!=="undefined"){const myioProps=Object.getOwnPropertyNames(document).filter(key=>key.startsWith("__MyIO"));this._log("log","[SelectionStore] All __MyIO* properties on document:",myioProps)}let existingInstance=null;try{const targetWindow=typeof window!=="undefined"&&window.top?window.top:window;existingInstance=targetWindow?.__MyIOSelectionStore_INSTANCE__;this._log("log","[SelectionStore] Checking window.top.__MyIOSelectionStore_INSTANCE__:",!!existingInstance)}catch(e){this._log("warn","[SelectionStore] Cannot access window.top:",e.message)}if(!existingInstance){existingInstance=typeof document!=="undefined"&&document.__MyIOSelectionStore_INSTANCE__||typeof window!=="undefined"&&window.__MyIOSelectionStore_INSTANCE__}if(existingInstance){this._log("warn","[SelectionStore] ⚠️ Constructor called but instance already exists! Returning existing instance.");this._log("log","[SelectionStore] Existing instance has listeners:",existingInstance.eventListeners.get("selection:change")?.length||0);return existingInstance}this._log("log","[SelectionStore] 🏗️ NEW INSTANCE CREATED at:",(new Date).toISOString());if(_MyIOSelectionStoreClass.GlobalDebug){console.trace("[SelectionStore] Constructor called from:")}this.MAX_SELECTION=6;this.state={selectedDevice:null};this.selectedIds=new Set;this.entities=new Map;this.eventListeners=new Map;this.analytics=null;this.timeSeriesCache=new Map;this.cacheExpiry=5*60*1e3;this.eventListeners.set("selection:change",[]);this.eventListeners.set("selection:totals",[]);this.eventListeners.set("selection:limit-reached",[]);this.eventListeners.set("comparison:open",[]);this.eventListeners.set("comparison:too_many",[]);try{const targetWindow=typeof window!=="undefined"&&window.top?window.top:window;if(targetWindow){this._log("log","[SelectionStore] 💾 Storing instance in window.top.__MyIOSelectionStore_INSTANCE__");targetWindow.__MyIOSelectionStore_INSTANCE__=this;this._log("log","[SelectionStore] ✅ Stored in top window! Verify:",!!targetWindow.__MyIOSelectionStore_INSTANCE__)}}catch(e){this._log("warn","[SelectionStore] ⚠️ Cannot access window.top (cross-origin iframe):",e.message);if(typeof document!=="undefined"){this._log("log","[SelectionStore] 💾 Storing instance in document.__MyIOSelectionStore_INSTANCE__ (fallback)");document.__MyIOSelectionStore_INSTANCE__=this;this._log("log","[SelectionStore] ✅ Stored! Verify:",!!document.__MyIOSelectionStore_INSTANCE__)}}}add(id){this._log("log","[MyIOSelectionStoreClass] Entrou na LIB",id);const wasSelected=this.selectedIds.has(id);if(wasSelected){this._log("log","[MyIOSelectionStoreClass] Item já está selecionado:",id);return}if(this.selectedIds.size>=this.MAX_SELECTION){this._log("warn",`[MyIOSelectionStoreClass] Limite de seleção atingido (${this.MAX_SELECTION})`);this._emit("selection:limit-reached",{maxAllowed:this.MAX_SELECTION,currentCount:this.selectedIds.size,attemptedId:id});this._trackEvent("selection.limit_reached",{entityId:id,limit:this.MAX_SELECTION});return}this.selectedIds.add(id);this._emitSelectionChange("add",id);this._trackEvent("footer_dock.drop_add",{entityId:id})}remove(id){this._log("log","[MyIOSelectionStoreClass] ITEM PARA REMOÇÃO ID",id);if(!this.selectedIds.has(id))return;this._log("log","[MyIOSelectionStoreClass] DELETE ID",id);this.selectedIds.delete(id);this._emitSelectionChange("remove",id);this._trackEvent("footer_dock.remove_chip",{entityId:id})}toggle(id){if(this.isSelected(id)){this.remove(id)}else{this.add(id)}}clear(){if(this.selectedIds.size===0)return;this.selectedIds.clear();this._emitSelectionChange("clear");this._trackEvent("footer_dock.clear_all",{count:0})}syncFromCheckbox(id,checked){if(typeof id!=="string"||typeof checked!=="boolean")return;if(checked&&!this.isSelected(id)){this.add(id)}else if(!checked&&this.isSelected(id)){this.remove(id)}this._trackEvent("card.checkbox_toggle",{entityId:id,checked:checked})}registerEntity(entity){if(!entity||typeof entity!=="object"||!entity.id){throw new Error("Entity must be an object with an id property")}const normalizedEntity={id:entity.id,name:entity.name||"",icon:entity.icon||"generic",group:entity.group||"",lastValue:Number(entity.lastValue)||0,unit:entity.unit||"",status:entity.status||"unknown",ingestionId:entity.ingestionId||entity.id,customerName:entity.customerName||""};this.entities.set(entity.id,normalizedEntity)}unregisterEntity(id){if(typeof id!=="string")return;this.entities.delete(id);this.remove(id)}getSelectedIds(){return Array.from(this.selectedIds)}getSelectedEntities(){this._log("log","[MyIOSelectionStoreClass] biblioteca:",this.getSelectedIds());return this.getSelectedIds().map(id=>this.entities.get(id)).filter(entity=>entity!==void 0)}getTotals(){const selectedEntities=this.getSelectedEntities();const totals={energyKwh:0,waterM3:0,tempC:0,percentage:0,count:selectedEntities.length,unitBreakdown:{}};selectedEntities.forEach(entity=>{const value=entity.lastValue||0;const unit=entity.unit||"";if(!totals.unitBreakdown[unit]){totals.unitBreakdown[unit]=0}totals.unitBreakdown[unit]+=value;switch(unit.toLowerCase()){case"kwh":case"mwh":case"gwh":totals.energyKwh+=this._convertToKwh(value,unit);break;case"m³":case"m3":totals.waterM3+=value;break;case"°c":case"celsius":totals.tempC+=value;break;case"%":case"percent":totals.percentage+=value;break}});return totals}isSelected(id){return this.selectedIds.has(id)}getSelectionCount(){return this.selectedIds.size}getMultiUnitTotalDisplay(){const totals=this.getTotals();const parts=[];if(totals.energyKwh>0){parts.push(`Energy: ${this._formatNumber(totals.energyKwh)} kWh`)}if(totals.waterM3>0){parts.push(`Water: ${this._formatNumber(totals.waterM3)} m³`)}if(totals.tempC>0){parts.push(`Temp: ${this._formatNumber(totals.tempC)} °C`)}if(totals.percentage>0){parts.push(`${this._formatNumber(totals.percentage)}%`)}return parts.join(" | ")||"No selection"}on(event,callback){this._log("log",`[SelectionStore] 📝 Registering listener for event: ${event}`);if(typeof event!=="string"||typeof callback!=="function"){this._log("error",`[SelectionStore] ❌ Invalid registration: event=${typeof event}, callback=${typeof callback}`);return}if(!this.eventListeners.has(event)){this._log("log",`[SelectionStore] 🆕 Creating new listener array for: ${event}`);this.eventListeners.set(event,[])}this.eventListeners.get(event).push(callback);this._log("log",`[SelectionStore] ✅ Listener registered! Total for ${event}: ${this.eventListeners.get(event).length}`)}off(event,callback){if(!this.eventListeners.has(event))return;const listeners=this.eventListeners.get(event);const index=listeners.indexOf(callback);if(index>-1){listeners.splice(index,1)}}async getTimeSeriesData(entityIds,startDate,endDate){if(!Array.isArray(entityIds)||entityIds.length===0){return{}}const cacheKey=`${entityIds.join(",")}_${startDate.getTime()}_${endDate.getTime()}`;const cached=this.timeSeriesCache.get(cacheKey);if(cached&&Date.now()-cached.timestamp<this.cacheExpiry){return cached.data}const mockData={};entityIds.forEach(id=>{mockData[id]=this._generateMockTimeSeriesData(startDate,endDate)});this.timeSeriesCache.set(cacheKey,{data:mockData,timestamp:Date.now()});return mockData}invalidateCache(reason="manual"){this.timeSeriesCache.clear();this._trackEvent("cache.invalidated",{reason:reason})}setAnalytics(analyticsInstance){if(!analyticsInstance||typeof analyticsInstance.track!=="function"){throw new Error("Analytics instance must have a track method")}this.analytics=analyticsInstance}trackEvent(eventName,payload={}){this._trackEvent(eventName,payload)}openComparison(){const count=this.getSelectionCount();if(count===0){this.announceToScreenReader("No items selected for comparison");return false}if(count>20){this._emit("comparison:too_many",{count:count,maxAllowed:20,selectedIds:this.getSelectedIds()});this._trackEvent("chart_modal.too_many_entities",{count:count});return false}const data={entities:this.getSelectedEntities(),totals:this.getTotals(),count:count};this._emit("comparison:open",data);this._trackEvent("chart_modal.open",{entityCount:count});return true}startDrag(id){if(typeof id!=="string")return;this._trackEvent("drag.start",{entityId:id})}announceToScreenReader(message){if(typeof message!=="string"||typeof document==="undefined")return;let announcer=document.getElementById("myio-sr-announcer");if(!announcer){announcer=document.createElement("div");announcer.id="myio-sr-announcer";announcer.setAttribute("aria-live","polite");announcer.setAttribute("aria-atomic","true");announcer.style.position="absolute";announcer.style.left="-10000px";announcer.style.width="1px";announcer.style.height="1px";announcer.style.overflow="hidden";document.body.appendChild(announcer)}announcer.textContent=message}_emitSelectionChange(action,id=null){const data={action:action,id:id,selectedIds:this.getSelectedIds(),totals:this.getTotals()};this._emit("selection:change",data);this._emit("selection:totals",data.totals);this._trackEvent("footer_dock.total_update",{action:action,count:data.selectedIds.length,totals:data.totals})}_emit(event,data){this._log("log",`[SelectionStore] 🔔 Emitting event: ${event}, listeners: ${this.eventListeners.get(event)?.length||0}`);this._log("log",`[SelectionStore] 📦 Event data:`,data);if(!this.eventListeners.has(event)){this._log("warn",`[SelectionStore] ⚠️ No listener map for event: ${event}`);return}const listeners=this.eventListeners.get(event);if(listeners.length===0){this._log("warn",`[SelectionStore] ⚠️ No listeners registered for event: ${event}`)}listeners.forEach((callback,index)=>{this._log("log",`[SelectionStore] 🎯 Calling listener #${index} for ${event}`);try{callback(data)}catch(error){this._log("error",`[SelectionStore] ❌ Error in ${event} listener #${index}:`,error)}})}_trackEvent(eventName,payload={}){if(!this.analytics)return;try{this.analytics.track(eventName,{timestamp:Date.now(),...payload})}catch(error){this._log("error","Analytics tracking error:",error)}}_convertToKwh(value,unit){switch(unit.toLowerCase()){case"mwh":return value*1e3;case"gwh":return value*1e6;case"kwh":default:return value}}_formatNumber(value){if(typeof value!=="number"||isNaN(value))return"0";return new Intl.NumberFormat("pt-BR",{minimumFractionDigits:0,maximumFractionDigits:2}).format(value)}_generateMockTimeSeriesData(startDate,endDate){const data=[];const current=new Date(startDate);const end=new Date(endDate);while(current<=end){data.push({timestamp:current.getTime(),value:Math.random()*100+50,unit:"kWh"});current.setHours(current.getHours()+1)}return data}};function _moduleLog(level,...args){if(!MyIOSelectionStoreClass.GlobalDebug)return;const logMethod=console[level]||console.log;logMethod.apply(console,args)}exports.MyIOSelectionStore=void 0;var _singletonInstance=null;if(typeof globalThis!=="undefined"&&typeof globalThis.window!=="undefined"){_moduleLog("log","[SelectionStore] 🔧 Module initialization - checking for existing instance...");if(!globalThis.window.__MyIOLibrary_PROTECTED__){_moduleLog("log","[SelectionStore] 🛡️ Protecting window.MyIOLibrary object from UMD overwrites...");const existingLib=globalThis.window.MyIOLibrary;Object.defineProperty(globalThis.window,"MyIOLibrary",{get:function(){if(!globalThis.window.__MyIOLibrary_INSTANCE__){_moduleLog("log","[SelectionStore] 📦 Creating protected MyIOLibrary container object");globalThis.window.__MyIOLibrary_INSTANCE__=existingLib||{}}return globalThis.window.__MyIOLibrary_INSTANCE__},set:function(value){_moduleLog("log","[SelectionStore] 🔄 UMD tried to overwrite MyIOLibrary - merging properties instead");if(value&&typeof value==="object"){const currentLib=globalThis.window.__MyIOLibrary_INSTANCE__||{};Object.keys(value).forEach(key=>{if(key==="MyIOSelectionStore"&¤tLib.MyIOSelectionStore){_moduleLog("log","[SelectionStore] ⏭️ Skipping MyIOSelectionStore - already set");return}currentLib[key]=value[key]});globalThis.window.__MyIOLibrary_INSTANCE__=currentLib}},configurable:false,enumerable:true});globalThis.window.__MyIOLibrary_PROTECTED__=true;_moduleLog("log","[SelectionStore] ✅ window.MyIOLibrary protected!")}let existingInstance=null;try{const targetWindow=globalThis.window.top?globalThis.window.top:globalThis.window;existingInstance=targetWindow.__MyIOSelectionStore_INSTANCE__;_moduleLog("log","[SelectionStore] window.top.__MyIOSelectionStore_INSTANCE__:",!!existingInstance)}catch(e){_moduleLog("warn","[SelectionStore] Cannot access window.top during module init:",e.message)}if(!existingInstance){existingInstance=typeof document!=="undefined"&&document.__MyIOSelectionStore_INSTANCE__||globalThis.window.__MyIOSelectionStore_INSTANCE__;_moduleLog("log","[SelectionStore] document.__MyIOSelectionStore_INSTANCE__:",!!(typeof document!=="undefined"&&document.__MyIOSelectionStore_INSTANCE__));_moduleLog("log","[SelectionStore] window.__MyIOSelectionStore_INSTANCE__:",!!globalThis.window.__MyIOSelectionStore_INSTANCE__)}if(existingInstance){_moduleLog("log","[SelectionStore] 🔄 REUSING constructor-created instance from __MyIOSelectionStore_INSTANCE__");_singletonInstance=existingInstance;exports.MyIOSelectionStore=_singletonInstance;if(!Object.getOwnPropertyDescriptor(globalThis.window,"MyIOSelectionStore")?.get){_moduleLog("log","[SelectionStore] 🔗 Defining window.MyIOSelectionStore getter to point to singleton");Object.defineProperty(globalThis.window,"MyIOSelectionStore",{get:function(){return _singletonInstance},set:function(value){_moduleLog("warn","[SelectionStore] ⚠️ Attempted to overwrite singleton - ignoring")},configurable:false,enumerable:true})}if(globalThis.window.MyIOLibrary&&typeof globalThis.window.MyIOLibrary==="object"){_moduleLog("log","[SelectionStore] 🔗 Updating window.MyIOLibrary.MyIOSelectionStore to point to singleton");globalThis.window.MyIOLibrary.MyIOSelectionStore=_singletonInstance}}else if(Object.getOwnPropertyDescriptor(globalThis.window,"MyIOSelectionStore")?.get){_moduleLog("log","[SelectionStore] 🔄 REUSING protected global instance via getter");exports.MyIOSelectionStore=globalThis.window.MyIOSelectionStore;_singletonInstance=exports.MyIOSelectionStore;if(globalThis.window.MyIOLibrary&&typeof globalThis.window.MyIOLibrary==="object"){_moduleLog("log","[SelectionStore] 🔗 Updating window.MyIOLibrary.MyIOSelectionStore to point to singleton");globalThis.window.MyIOLibrary.MyIOSelectionStore=_singletonInstance}}else if(globalThis.window.MyIOSelectionStore&&typeof globalThis.window.MyIOSelectionStore==="object"){_moduleLog("log","[SelectionStore] 🔒 UPGRADING existing instance to protected");_singletonInstance=globalThis.window.MyIOSelectionStore;exports.MyIOSelectionStore=_singletonInstance;Object.defineProperty(globalThis.window,"MyIOSelectionStore",{get:function(){return _singletonInstance},set:function(value){_moduleLog("warn","[SelectionStore] ⚠️ Attempted to overwrite singleton - ignoring")},configurable:false,enumerable:true});if(globalThis.window.MyIOLibrary&&typeof globalThis.window.MyIOLibrary==="object"){_moduleLog("log","[SelectionStore] 🔗 Updating window.MyIOLibrary.MyIOSelectionStore to point to singleton");globalThis.window.MyIOLibrary.MyIOSelectionStore=_singletonInstance}}else{_moduleLog("log","[SelectionStore] 🆕 Creating new protected singleton instance");_singletonInstance=new MyIOSelectionStoreClass;exports.MyIOSelectionStore=_singletonInstance;Object.defineProperty(globalThis.window,"MyIOSelectionStore",{get:function(){return _singletonInstance},set:function(value){_moduleLog("warn","[SelectionStore] ⚠️ Attempted to overwrite singleton - ignoring")},configurable:false,enumerable:true});if(globalThis.window.MyIOLibrary&&typeof globalThis.window.MyIOLibrary==="object"){_moduleLog("log","[SelectionStore] 🔗 Setting window.MyIOLibrary.MyIOSelectionStore to singleton");globalThis.window.MyIOLibrary.MyIOSelectionStore=_singletonInstance}_moduleLog("log","[SelectionStore] 🔒 Instance protected and ready")}if(!globalThis.window.MyIOSelectionStoreClass){globalThis.window.MyIOSelectionStoreClass=MyIOSelectionStoreClass}}else{exports.MyIOSelectionStore=new MyIOSelectionStoreClass}if(typeof module!=="undefined"&&module.exports){module.exports={MyIOSelectionStore:exports.MyIOSelectionStore,MyIOSelectionStoreClass:MyIOSelectionStoreClass}}var MyIODraggableCard=class{constructor(container,entity,options={}){if(!container||!entity||!entity.id){throw new Error("Container and entity with id are required")}this.container=container;this.entity=entity;this.options={showCheckbox:true,draggable:true,className:"",...options};this.cardElement=null;this.checkbox=null;this.isDragging=false;this.touchStartTime=0;this.touchTimer=null;this.destroyed=false;this._init()}destroy(){if(this.destroyed)return;this._removeEventListeners();if(this.cardElement&&this.cardElement.parentNode){this.cardElement.parentNode.removeChild(this.cardElement)}this.destroyed=true}updateEntity(newEntity){if(this.destroyed)return;this.entity={...this.entity,...newEntity};this._updateCardContent()}setSelected(selected){if(this.destroyed)return;if(this.checkbox){this.checkbox.checked=selected}this._updateSelectionState(selected)}_init(){this._createCardElement();this._attachEventListeners();this._updateSelectionState(this._getSelectionStore()?.isSelected(this.entity.id)||false)}_createCardElement(){if(typeof document==="undefined")return;const card=document.createElement("div");card.className=`myio-draggable-card ${this.options.className}`.trim();card.setAttribute("data-entity-id",this.entity.id);card.setAttribute("role","article");card.setAttribute("tabindex","0");if(this.options.draggable){card.draggable=true;card.setAttribute("aria-grabbed","false")}card.innerHTML=this._generateCardHTML();this.cardElement=card;this.checkbox=card.querySelector(".card-checkbox");this.container.appendChild(card)}_generateCardHTML(){const{id:id,name:name,icon:icon,group:group,lastValue:lastValue,unit:unit,status:status}=this.entity;const checkboxHtml=this.options.showCheckbox?`<input type="checkbox" class="card-checkbox" aria-label="Select ${name}">`:"";return`\n <div class="card-header">\n ${checkboxHtml}\n <div class="card-icon card-icon-${icon}">\n ${this._getIconSvg(icon)}\n </div>\n <div class="card-status card-status-${status}"></div>\n </div>\n <div class="card-body">\n <h3 class="card-title">${this._escapeHtml(name)}</h3>\n <p class="card-group">${this._escapeHtml(group)}</p>\n <div class="card-value">\n <span class="value">${this._formatValue(lastValue)}</span>\n <span class="unit">${this._escapeHtml(unit)}</span>\n </div>\n </div>\n <div class="card-footer">\n <span class="card-id">${this._escapeHtml(id)}</span>\n </div>\n `}_attachEventListeners(){if(!this.cardElement)return;this.cardElement.addEventListener("dragstart",this._handleDragStart.bind(this));this.cardElement.addEventListener("dragend",this._handleDragEnd.bind(this));this.cardElement.addEventListener("click",this._handleClick.bind(this));this.cardElement.addEventListener("touchstart",this._handleTouchStart.bind(this),{passive:false});this.cardElement.addEventListener("touchmove",this._handleTouchMove.bind(this),{passive:false});this.cardElement.addEventListener("touchend",this._handleTouchEnd.bind(this));this.cardElement.addEventListener("keydown",this._handleKeyDown.bind(this));if(this.checkbox){this.checkbox.addEventListener("change",this._handleCheckboxChange.bind(this));this.checkbox.addEventListener("click",this._handleCheckboxClick.bind(this))}const store=this._getSelectionStore();if(store){this._selectionChangeHandler=data=>{const isSelected=data.selectedIds.includes(this.entity.id);this._updateSelectionState(isSelected)};store.on("selection:change",this._selectionChangeHandler)}}_removeEventListeners(){if(!this.cardElement)return;const store=this._getSelectionStore();if(store&&this._selectionChangeHandler){store.off("selection:change",this._selectionChangeHandler)}this._selectionChangeHandler=null}_handleDragStart(event){if(!this.options.draggable){event.preventDefault();return}this.isDragging=true;this.cardElement.setAttribute("aria-grabbed","true");this.cardElement.classList.add("dragging");event.dataTransfer.setData("text/myio-id",this.entity.id);event.dataTransfer.setData("text/plain",this.entity.id);event.dataTransfer.effectAllowed="copy";const store=this._getSelectionStore();if(store){store.startDrag(this.entity.id)}this._announceToScreenReader(`Started dragging ${this.entity.name}`)}_handleDragEnd(event){this.isDragging=false;this.cardElement.setAttribute("aria-grabbed","false");this.cardElement.classList.remove("dragging");this._announceToScreenReader(`Finished dragging ${this.entity.name}`)}_handleTouchStart(event){if(!this.options.draggable)return;this.touchStartTime=Date.now();this.touchTimer=setTimeout(()=>{this._startTouchDrag(event)},500)}_handleTouchMove(event){if(this.touchTimer){clearTimeout(this.touchTimer);this.touchTimer=null}if(this.isDragging){event.preventDefault()}}_handleTouchEnd(event){if(this.touchTimer){clearTimeout(this.touchTimer);this.touchTimer=null}if(this.isDragging){this._endTouchDrag(event)}}_startTouchDrag(event){if(!this.options.draggable)return;this.isDragging=true;this.cardElement.classList.add("touch-dragging");if(navigator.vibrate){navigator.vibrate(50)}this._announceToScreenReader(`Touch drag started for ${this.entity.name}`)}_endTouchDrag(event){this.isDragging=false;this.cardElement.classList.remove("touch-dragging");this._announceToScreenReader(`Touch drag ended for ${this.entity.name}`)}_handleClick(event){if(event.target===this.checkbox)return;if(this.isDragging)return;const store=this._getSelectionStore();if(store){store.toggle(this.entity.id)}}_handleKeyDown(event){switch(event.key){case"Enter":case" ":event.preventDefault();if(event.shiftKey){const store=this._getSelectionStore();if(store){store.openComparison()}}else{const store=this._getSelectionStore();if(store){store.toggle(this.entity.id)}}break;case"Delete":case"Backspace":{event.preventDefault();const store=this._getSelectionStore();if(store){store.remove(this.entity.id)}break}}}_handleCheckboxChange(event){event.stopPropagation();const store=this._getSelectionStore();if(store){store.syncFromCheckbox(this.entity.id,event.target.checked)}}_handleCheckboxClick(event){event.stopPropagation()}_updateSelectionState(isSelected){if(this.destroyed)return;if(this.checkbox){this.checkbox.checked=isSelected}this.cardElement.classList.toggle("selected",isSelected);this.cardElement.setAttribute("aria-selected",isSelected.toString());const action=isSelected?"selected":"not selected";this.cardElement.setAttribute("aria-label",`${this.entity.name}, ${this.entity.group}, ${action}`)}_updateCardContent(){if(this.destroyed)return;const titleElement=this.cardElement.querySelector(".card-title");const groupElement=this.cardElement.querySelector(".card-group");const valueElement=this.cardElement.querySelector(".value");const unitElement=this.cardElement.querySelector(".unit");const idElement=this.cardElement.querySelector(".card-id");if(titleElement)titleElement.textContent=this.entity.name||"";if(groupElement)groupElement.textContent=this.entity.group||"";if(valueElement)valueElement.textContent=this._formatValue(this.entity.lastValue);if(unitElement)unitElement.textContent=this.entity.unit||"";if(idElement)idElement.textContent=this.entity.id||"";const iconElement=this.cardElement.querySelector(".card-icon");const statusElement=this.cardElement.querySelector(".card-status");if(iconElement){iconElement.className=`card-icon card-icon-${this.entity.icon||"generic"}`;iconElement.innerHTML=this._getIconSvg(this.entity.icon||"generic")}if(statusElement){statusElement.className=`card-status card-status-${this.entity.status||"unknown"}`}}_getSelectionStore(){if(typeof globalThis!=="undefined"&&globalThis.window?.MyIOSelectionStore){return globalThis.window.MyIOSelectionStore}if(typeof globalThis!=="undefined"&&globalThis.window&&globalThis.window.MyIOSelectionStore){return globalThis.window.MyIOSelectionStore}return null}_formatValue(value){if(value===null||value===void 0||isNaN(value)){return"-"}return new Intl.NumberFormat("pt-BR",{minimumFractionDigits:0,maximumFractionDigits:2}).format(Number(value))}_escapeHtml(text){if(typeof text!=="string")return"";if(typeof document==="undefined")return text;const div=document.createElement("div");div.textContent=text;return div.innerHTML}_getIconSvg(iconType){const icons={energy:'<svg viewBox="0 0 24 24"><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/></svg>',water:'<svg viewBox="0 0 24 24"><path d="M12 2c-5.33 4.55-8 8.48-8 11.8 0 4.98 3.8 8.2 8 8.2s8-3.22 8-8.2c0-3.32-2.67-7.25-8-11.8z"/></svg>',temp:'<svg viewBox="0 0 24 24"><path d="M15 13V5c0-1.66-1.34-3-3-3S9 3.34 9 5v8c-1.21.91-2 2.37-2 4 0 2.76 2.24 5 5 5s5-2.24 5-5c0-1.63-.79-3.09-2-4z"/></svg>',net:'<svg viewBox="0 0 24 24"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM9 17H7v-7h2v7zm4 0h-2V7h2v10zm4 0h-2v-4h2v4z"/></svg>',alert:'<svg viewBox="0 0 24 24"><path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z"/></svg>',generic:'<svg viewBox="0 0 24 24"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/></svg>'};return icons[iconType]||icons.generic}_announceToScreenReader(message){const store=this._getSelectionStore();if(store&&store.announceToScreenReader){store.announceToScreenReader(message)}}};if(typeof globalThis!=="undefined"&&typeof globalThis.window!=="undefined"){globalThis.window.MyIODraggableCard=MyIODraggableCard}if(typeof module!=="undefined"&&module.exports){module.exports={MyIODraggableCard:MyIODraggableCard}}function renderCardComponentV2({entityObject:entityObject,handleActionDashboard:handleActionDashboard,handleActionReport:handleActionReport,handleActionSettings:handleActionSettings,handleSelect:handleSelect,handInfo:handInfo,handleClickCard:handleClickCard,useNewComponents:useNewComponents=true,enableSelection:enableSelection=true,enableDragDrop:enableDragDrop=true}){const{entityId:entityId,labelOrName:labelOrName,deviceIdentifier:deviceIdentifier,entityType:entityType,deviceType:deviceType,slaveId:slaveId,ingestionId:ingestionId,val:val,centralId:centralId,updatedIdentifiers:updatedIdentifiers={},perc:perc=0,deviceStatus:deviceStatus,centralName:centralName,connectionStatusTime:connectionStatusTime,timeVal:timeVal}=entityObject;const MyIOToast2=function(){let toastContainer=null;let toastTimeout=null;const TOAST_CSS=`\n #myio-global-toast-container {\n position: fixed;\n top: 25px;\n right: 25px;\n z-index: 99999;\n width: 320px;\n padding: 16px;\n border-radius: 8px;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;\n font-size: 15px;\n color: #fff;\n transform: translateX(120%);\n transition: transform 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);\n box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);\n border-left: 5px solid transparent;\n display: flex;\n align-items: center;\n }\n #myio-global-toast-container.show {\n transform: translateX(0);\n }\n #myio-global-toast-container.warning {\n background-color: #ff9800; /* Laranja para alerta */\n border-color: #f57c00;\n }\n #myio-global-toast-container.error {\n background-color: #d32f2f; /* Vermelho para erro */\n border-color: #b71c1c;\n }\n #myio-global-toast-container::before {\n content: '⚠️'; /* Ícone de alerta */\n margin-right: 12px;\n font-size: 20px;\n }\n #myio-global-toast-container.error::before {\n content: '🚫'; /* Ícone de erro */\n }\n `;function createToastElement(){if(document.getElementById("myio-global-toast-container")){toastContainer=document.getElementById("myio-global-toast-container");return}const style=document.createElement("style");style.id="myio-global-toast-styles";style.textContent=TOAST_CSS;document.head.appendChild(style);toastContainer=document.createElement("div");toastContainer.id="myio-global-toast-container";document.body.appendChild(toastContainer)}function show(message,type="warning",duration=3500){if(!toastContainer){createToastElement()}clearTimeout(toastTimeout);toastContainer.textContent=message;toastContainer.className="";toastContainer.classList.add(type);setTimeout(()=>{toastContainer.classList.add("show")},10);toastTimeout=setTimeout(()=>{toastContainer.classList.remove("show")},duration)}if(document.readyState==="loading"){document.addEventListener("DOMContentLoaded",createToastElement)}else{createToastElement()}return{show:show}}();if(!useNewComponents){return renderCardComponentLegacy({entityObject:entityObject,handleActionDashboard:handleActionDashboard,handleActionReport:handleActionReport,handleActionSettings:handleActionSettings,handleSelect:handleSelect,handInfo:handInfo,handleClickCard:handleClickCard})}const connectionStatus=mapDeviceToConnectionStatus(deviceStatus);isDeviceOffline(deviceStatus);const shouldFlashIcon2=shouldFlashIcon(deviceStatus);const icon=getDeviceStatusIcon(deviceStatus);getConnectionStatusIcon(connectionStatus);const mapDeviceTypeToIcon=deviceType2=>{const typeMap={COMPRESSOR:"energy",VENTILADOR:"energy",ESCADA_ROLANTE:"energy",ELEVADOR:"energy",MOTOR:"energy","3F_MEDIDOR":"energy",RELOGIO:"energy",ENTRADA:"energy",SUBESTACAO:"energy",HIDROMETRO:"water",CAIXA_DAGUA:"water",TANK:"water"};const normalizedType=deviceType2?.toUpperCase()||"";return typeMap[normalizedType]||"generic"};const getValueTypeFromDeviceType=deviceType2=>{const typeMap={COMPRESSOR:"ENERGY",VENTILADOR:"ENERGY",ESCADA_ROLANTE:"ENERGY",ELEVADOR:"ENERGY",MOTOR:"ENERGY","3F_MEDIDOR":"ENERGY",RELOGIO:"ENERGY",ENTRADA:"ENERGY",SUBESTACAO:"ENERGY",HIDROMETRO:"WATER",CAIXA_DAGUA:"WATER",TANK:"TANK"};const normalizedType=deviceType2?.toUpperCase()||"";return typeMap[normalizedType]||"ENERGY"};const isEnergyDevice=deviceType2=>{const energyDeviceTypes=["COMPRESSOR","VENTILADOR","ESCADA_ROLANTE","ELEVADOR","MOTOR","3F_MEDIDOR","RELOGIO","ENTRADA","SUBESTACAO"];const normalizedType=deviceType2?.toUpperCase()||"";return energyDeviceTypes.includes(normalizedType)};const formatCardValue=(value,deviceType2)=>{const numValue=Number(value)||0;if(isEnergyDevice(deviceType2)){return formatEnergy(numValue)}else{const unit=determineUnit(deviceType2);const formattedValue=numValue.toLocaleString("pt-BR",{minimumFractionDigits:0,maximumFractionDigits:2});return`${formattedValue} ${unit}`}};const determineUnit=deviceType2=>{const valueType=getValueTypeFromDeviceType(deviceType2);switch(valueType){case"ENERGY":return"";case"WATER":return"m³";case"TANK":return"m.c.a";default:return""}};const cardEntity={id:entityId,name:labelOrName||"Dispositivo",icon:mapDeviceTypeToIcon(deviceType),group:deviceIdentifier||entityType||"Dispositivo",lastValue:Number(val)||0,unit:determineUnit(deviceType),status:mapDeviceStatusToCardStatus(deviceStatus),ingestionId:ingestionId||entityId};if(enableSelection&&exports.MyIOSelectionStore){exports.MyIOSelectionStore.registerEntity(cardEntity)}const container=document.createElement("div");container.className="myio-enhanced-card-container";if(!document.getElementById("myio-enhanced-card-styles")){const style=document.createElement("style");style.id="myio-enhanced-card-styles";style.textContent=`\n .myio-enhanced-card-container {\n position: relative;\n width: 100%;\n height: 100%;\n }\n\n .myio-enhanced-card-container .myio-draggable-card {\n width: 100%;\n border-radius: 10px;\n padding: 8px 12px; \n background: #fff;\n box-shadow: 0 4px 10px rgba(0, 0, 0, .05);\n cursor: pointer;\n \n transition: transform .2s;\n box-sizing: border-box;\n overflow: hidden;\n min-height: 140px;\n display: flex;\n flex-direction: row;\n align-items: stretch;\n }\n \n .myio-enhanced-card-container .myio-draggable-card:hover {\n transform: scale(1.05);\n }\n \n .myio-enhanced-card-container .myio-draggable-card.selected {\n border: 2px solid #00e09e;\n box-shadow: 0 4px 12px rgba(0,224,158,0.2);\n background: linear-gradient(135deg, #f0fdf9, #ecfdf5);\n }\n \n .myio-enhanced-card-container .myio-draggable-card.offline {\n border: 2px solid #ff4d4f;\n animation: border-blink 1s infinite;\n }\n \n @keyframes border-blink {\n 0%, 100% { box-shadow: 0 0 8px rgba(255, 77, 79, 0.9); }\n 50% { box-shadow: 0 0 16px rgba(255, 0, 0, 0.6); }\n }\n \n .myio-enhanced-card-container .card-actions {\n flex-shrink: 0;\n height: 100%;\n box-shadow: 1px 0 2px rgba(0, 0, 0, .1);\n display: flex;\n flex-direction: column;\n padding: 0 4px;\n justify-content: space-around;\n align-items: center;\n gap: 8px;\n }\n \n .myio-enhanced-card-container .card-action {\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 6px;\n transition: all 0.2s ease;\n cursor: pointer;\n border: none;\n background: rgba(0, 0, 0, 0.05);\n }\n \n .myio-enhanced-card-container .card-action:hover {\n background: rgba(0, 224, 158, 0.1);\n transform: scale(1.1);\n }\n \n .myio-enhanced-card-container .card-action img {\n width: 20px;\n height: 20px;\n }\n \n .myio-enhanced-card-container .card-checkbox {\n width: 16px;\n height: 16px;\n cursor: pointer;\n }\n \n .myio-enhanced-card-container .card-body {\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: center;\n align-items: center;\n padding: 0 12px;\n text-align: center;\n }\n \n .myio-enhanced-card-container .card-title {\n font-weight: 700;\n font-size: 0.85rem;\n margin-bottom: 4px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n max-width: 100%;\n }\n \n .myio-enhanced-card-container .card-group {\n font-size: 0.7rem;\n color: #888;\n margin-bottom: 8px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n max-width: 100%;\n }\n \n .myio-enhanced-card-container .card-value {\n display: flex;\n align-items: center;\n gap: 4px;\n font-size: 0.9rem;\n font-weight: 700;\n color: #28a745;\n }\n \n .myio-enhanced-card-container .card-unit {\n font-size: 0.75rem;\n color: rgba(0, 0, 0, 0.6);\n }\n \n .myio-enhanced-card-container .card-percentage {\n font-size: 0.75rem;\n color: rgba(0, 0, 0, 0.45);\n margin-left: 4px;\n }\n \n .myio-enhanced-card-container .card-icon {\n width: 24px;\n height: 24px;\n margin-bottom: 8px;\n }\n \n .myio-enhanced-card-container .card-icon svg {\n width: 100%;\n height: 100%;\n fill: currentColor;\n }\n \n .myio-enhanced-card-container .card-status-indicator {\n position: absolute;\n top: 8px;\n right: 8px;\n width: 12px;\n height: 12px;\n border-radius: 50%;\n z-index: 10;\n }\n \n .myio-enhanced-card-container .card-status-ok {\n background: #28a745;\n }\n \n .myio-enhanced-card-container .card-status-alert {\n background: #ffc107;\n }\n \n .myio-enhanced-card-container .card-status-fail,\n .myio-enhanced-card-container .card-status-offline {\n background: #dc3545;\n }\n \n .myio-enhanced-card-container .card-status-unknown {\n background: #6c757d;\n }\n \n .myio-enhanced-card-container .info-panel {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(255, 255, 255, 0.95);\n backdrop-filter: blur(4px);\n display: none;\n flex-direction: column;\n justify-content: center;\n align-items: center;\n padding: 16px;\n border-radius: 10px;\n z-index: 20;\n }\n \n .myio-enhanced-card-container .info-panel.active {\n display: flex;\n }\n \n .myio-enhanced-card-container .info-close {\n position: absolute;\n top: 8px;\n right: 8px;\n background: none;\n border: none;\n font-size: 18px;\n cursor: pointer;\n color: #666;\n }\n \n .myio-enhanced-card-container .info-content {\n text-align: center;\n font-size: 0.8rem;\n line-height: 1.4;\n }\n \n .myio-enhanced-card-container .info-content strong {\n display: block;\n margin-bottom: 8px;\n color: #333;\n }\n `;document.head.appendChild(style)}const getDeviceImageUrl=deviceType2=>{function normalizeString(str){if(typeof str!=="string"){str=""}return str.normalize("NFD").replace(/[\u0300-\u036f]/g,"").toUpperCase()}const deviceImages={MOTOR:"https://dashboard.myio-bas.com/api/images/public/Rge8Q3t0CP5PW8XyTn9bBK9aVP6uzSTT","3F_MEDIDOR":"https://dashboard.myio-bas.com/api/images/public/f9Ce4meybsdaAhAkUlAfy5ei3I4kcN4k",RELOGIO:"https://dashboard.myio-bas.com/api/images/public/ljHZostWg0G5AfKiyM8oZixWRIIGRASB",HIDROMETRO:"https://dashboard.myio-bas.com/api/images/public/aMQYFJbGHs9gQbQkMn6XseAlUZHanBR4",ENTRADA:"https://dashboard.myio-bas.com/api/images/public/TQHPFqiejMW6lOSVsb8Pi85WtC0QKOLU",CAIXA_DAGUA:"https://dashboard.myio-bas.com/api/images/public/3t6WVhMQJFsrKA8bSZmrngDsNPkZV7fq",ELEVADOR:"https://dashboard.myio-bas.com/api/images/public/rAjOvdsYJLGah6w6BABPJSD9znIyrkJX",ESCADA_ROLANTE:"https://dashboard.myio-bas.com/api/images/public/EJ997iB2HD1AYYUHwIloyQOOszeqb2jp"};const defaultImage="https://cdn-icons-png.flaticon.com/512/1178/1178428.png";const nameType=normalizeString(deviceType2);return deviceImages[nameType]||defaultImage};const deviceImageUrl=getDeviceImageUrl(deviceType);const cardHTML=`\n <div class="device-card-centered clickable ${cardEntity.status==="offline"?"offline":""}" \n data-entity-id="${entityId}"\n draggable="${enableDragDrop}"\n tabindex="0"\n role="article"\n aria-label="${cardEntity.name}, ${cardEntity.group}">\n \n <div class="device-card-inner">\n <div class="device-card-front">\n ${enableSelection&&typeof handleSelect==="function"?`<input type="checkbox" class="card-checkbox action-checker" aria-label="Select ${cardEntity.name}" style="position: absolute; top: 8px; right: 8px; z-index: 10;">`:""}\n \n <div style="display:flex;flex-direction:column;justify-content:center;align-items:center;height:100%; flex-grow: 1; min-width: 0; padding: 0 12px 0 20px; margin-left: 16px;">\n \n <div class="device-title-row" style="flex-direction: column; min-height: 38px; text-align: center; width: 100%;">\n <span class="device-title" title="${cardEntity.name}">\n ${cardEntity.name.length>15?cardEntity.name.slice(0,15)+"…":cardEntity.name}\n </span>\n ${deviceIdentifier?`\n <span class="device-subtitle" title="${deviceIdentifier}">\n ${deviceIdentifier}\n </span>\n `:""}\n </div>\n\n <img class="device-image" src="${deviceImageUrl}" alt="${deviceType}" />\n \n <div class="device-data-row">\n <div class="consumption-main">\n <span class="flash-icon ${shouldFlashIcon2?"flash":""}">\n ${icon}\n </span>\n <span class="consumption-value">${formatCardValue(cardEntity.lastValue,deviceType)}</span>\n <span class="device-title-percent">(${perc.toFixed(1)}%)</span>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <div class="connection-status-icon" data-conn="${connectionStatus}" data-state="${deviceStatus}" aria-label="${connectionStatus}"></div>\n \n </div>\n `;container.innerHTML=cardHTML;const enhancedCardElement=container.querySelector(".device-card-centered");if(!document.getElementById("myio-enhanced-card-layout-styles")){const layoutStyle=document.createElement("style");layoutStyle.id="myio-enhanced-card-layout-styles";layoutStyle.textContent=`\n /* ===== MYIO Card v2 — Clean Piano Keys & Flat Status ===== */\n\n /* Card shell (kept) */\n .device-card-centered.clickable {\n width: 90% !important;\n max-width: 280px !important;\n border-radius: 14px !important;\n padding: 18px !important;\n background: linear-gradient(145deg, #ffffff 0%, #f8fafc 100%) !important;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.08), 0 2px 8px rgba(0, 0, 0, 0.04) !important;\n cursor: pointer;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;\n border: 1px solid rgba(226, 232, 240, 0.8) !important;\n position: relative;\n overflow: hidden;\n backdrop-filter: blur(10px);\n min-height: 126px !important;\n margin: 0 auto;\n }\n \n .device-card-centered.clickable::before {\n content: '';\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n height: 3px;\n background: linear-gradient(90deg, #00e09e 0%, #00b4d8 50%, #7209b7 100%);\n opacity: 0;\n transition: opacity 0.3s ease;\n }\n \n .device-card-centered.clickable:hover {\n transform: translateY(-4px) scale(1.02) !important;\n box-shadow: 0 16px 48px rgba(0, 0, 0, 0.12), 0 8px 16px rgba(0, 0, 0, 0.08) !important;\n border-color: rgba(0, 224, 158, 0.3) !important;\n }\n \n .device-card-centered.clickable:hover::before {\n opacity: 1;\n }\n\n /* Selected / Offline (kept) */\n .device-card-centered.selected {\n border: 2px solid #00e09e !important;\n box-shadow: 0 12px 40px rgba(0, 224, 158, 0.25), 0 4px 12px rgba(0, 224, 158, 0.15) !important;\n background: linear-gradient(145deg, #f0fdf9 0%, #ecfdf5 50%, #f0fdf9 100%) !important;\n transform: translateY(-2px) !important;\n }\n \n .device-card-centered.selected::before {\n opacity: 1;\n background: linear-gradient(90deg, #00e09e 0%, #00d4aa 100%);\n }\n \n .device-card-centered.offline {\n border: 2px solid #ef4444 !important;\n background: linear-gradient(145deg, #fef2f2 0%, #fee2e2 50%, #fef2f2 100%) !important;\n animation: premium-offline-pulse 2s infinite !important;\n }\n \n .device-card-centered.offline::before {\n opacity: 1;\n background: linear-gradient(90deg, #ef4444 0%, #dc2626 100%);\n }\n \n @keyframes premium-offline-pulse {\n 0%, 100% { \n box-shadow: 0 8px 32px rgba(239, 68, 68, 0.15), 0 2px 8px rgba(239, 68, 68, 0.1);\n }\n 50% { \n box-shadow: 0 12px 40px rgba(239, 68, 68, 0.25), 0 4px 12px rgba(239, 68, 68, 0.2);\n }\n }\n\n /* Device image & titles (kept) */\n .device-card-centered .device-image {\n max-height: 47px !important;\n width: auto;\n margin: 10px 0 !important;\n display: block;\n filter: drop-shadow(0 3px 6px rgba(0, 0, 0, 0.1));\n transition: all 0.3s ease;\n border-radius: 7px;\n }\n \n .device-card-centered:hover .device-image {\n filter: drop-shadow(0 5px 10px rgba(0, 0, 0, 0.15));\n transform: scale(1.05);\n }\n \n .device-card-centered .device-title-row {\n display: flex !important;\n flex-direction: column !important;\n align-items: center !important;\n justify-content: center !important;\n text-align: center !important;\n width: 100% !important;\n min-height: 38px !important;\n margin-bottom: 8px !important;\n }\n \n .device-card-centered .device-title {\n font-weight: 700 !important;\n font-size: 0.85rem !important;\n color: #1e293b !important;\n margin: 0 0 4px 0 !important;\n text-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n letter-spacing: -0.025em;\n display: block !important;\n width: 100% !important;\n text-align: center !important;\n }\n \n .device-card-centered .device-subtitle {\n font-size: 0.67rem !important;\n color: #64748b !important;\n font-weight: 500 !important;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n opacity: 0.8;\n display: block !important;\n width: 100% !important;\n text-align: center !important;\n margin: 0 !important;\n padding: 0 !important;\n }\n\n /* Value pill (kept) */\n .device-card-centered .consumption-main {\n background: linear-gradient(135deg, rgba(0, 224, 158, 0.1) 0%, rgba(0, 180, 216, 0.1) 100%);\n border-radius: 10px;\n padding: 7px 10px;\n margin-top: 7px;\n border: 1px solid rgba(0, 224, 158, 0.2);\n backdrop-filter: blur(10px);\n }\n \n .device-card-centered .consumption-value {\n font-weight: 700 !important;\n font-size: 0.9rem !important;\n color: #059669 !important;\n text-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n }\n \n .device-card-centered .device-title-percent {\n font-size: 0.72rem !important;\n color: #6b7280 !important;\n font-weight: 600 !important;\n margin-left: 5px;\n }\n \n .device-card-centered .flash-icon {\n font-size: 1rem !important;\n margin-right: 7px;\n transition: all 0.3s ease;\n }\n \n .device-card-centered:hover .flash-icon {\n transform: scale(1.1);\n }\n \n .device-card-centered .flash-icon.flash {\n animation: premium-flash 1.5s infinite;\n }\n \n @keyframes premium-flash {\n 0%, 100% { \n opacity: 1; \n transform: scale(1);\n filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.1));\n }\n 50% { \n opacity: 0.3; \n transform: scale(1.15);\n filter: drop-shadow(0 4px 8px rgba(239, 68, 68, 0.3));\n }\n }\n\n /* Checkbox (kept) */\n .device-card-centered .card-checkbox {\n width: 16px !important;\n height: 16px !important;\n cursor: pointer;\n background: rgba(255, 255, 255, 0.9) !important;\n border: 2px solid #e2e8f0 !important;\n border-radius: 5px !important;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n backdrop-filter: blur(10px);\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);\n position: relative;\n }\n \n .device-card-centered .card-checkbox:hover {\n border-color: #00e09e !important;\n box-shadow: 0 3px 6px rgba(0, 224, 158, 0.15);\n transform: scale(1.05);\n }\n \n .device-card-centered .card-checkbox:checked {\n background: linear-gradient(135deg, #00e09e 0%, #00d4aa 100%) !important;\n border-color: #00e09e !important;\n box-shadow: 0 3px 10px rgba(0, 224, 158, 0.3);\n }\n \n .device-card-centered .card-checkbox:checked::after {\n content: '✓';\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n color: white;\n font-size: 10px;\n font-weight: bold;\n text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);\n }\n\n /* ——— NEW: Piano-Key Actions (flat by default, full height) ——— */\n .device-card-centered .card-actions {\n position: absolute;\n left: 12px;\n top: 12px;\n bottom: 12px;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 0;\n border: 1px solid rgba(226, 232, 240, 0.9);\n border-radius: 8px;\n background: #fff;\n overflow: visible;\n box-shadow: none !important;\n z-index: 10;\n }\n \n .device-card-centered .card-action {\n width: 36px !important;\n height: 36px !important;\n border: 0;\n border-bottom: 1px solid rgba(226, 232, 240, 0.9);\n background: #fff !important;\n box-shadow: none !important;\n backdrop-filter: none !important;\n transform: translateZ(0);\n transition: transform 0.18s ease, box-shadow 0.18s ease;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n margin: 0;\n border-radius: 0;\n }\n \n .device-card-centered .card-action:first-child {\n border-top-left-radius: 7px;\n border-top-right-radius: 7px;\n }\n \n .device-card-centered .card-action:last-child {\n border-bottom: 0;\n border-bottom-left-radius: 7px;\n border-bottom-right-radius: 7px;\n }\n \n .device-card-centered .card-action img {\n width: 16px !important;\n height: 16px !important;\n filter: grayscale(0.2) brightness(0.85);\n transition: transform 0.18s ease, filter 0.18s ease;\n }\n\n /* Lift on interaction only */\n .device-card-centered .card-action:hover,\n .device-card-centered .card-action:focus-visible {\n transform: translateY(-2px) scale(1.05);\n box-shadow: 0 6px 14px rgba(16, 24, 40, 0.12), 0 2px 6px rgba(16, 24, 40, 0.08);\n outline: none;\n }\n \n .device-card-centered .card-action:hover img,\n .device-card-centered .card-action:focus-visible img {\n filter: grayscale(0) brightness(1);\n transform: scale(1.08);\n }\n\n /* ——— NEW: Flat Status (CSS dot, never clipped) ——— */\n .device-card-centered .connection-status-icon {\n position: absolute;\n bottom: 18px;\n right: 18px;\n width: 14px;\n height: 14px;\n border-radius: 50%;\n background: #22c55e;\n border: 1px solid rgba(0, 0, 0, 0.06);\n box-shadow: 0 3px 6px rgba(0, 0, 0, 0.15),\n 0 1px 3px rgba(0, 0, 0, 0.1),\n inset 0 -2px 4px rgba(0, 0, 0, 0.1),\n inset 0 2px 3px rgba(255, 255, 255, 0.4) !important;\n backdrop-filter: none !important;\n z-index: 5;\n transform: translateZ(0);\n transition: transform 0.2s ease, box-shadow 0.2s ease;\n }\n\n .device-card-centered .connection-status-icon:hover {\n transform: translateZ(2px) scale(1.05);\n box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2),\n 0 2px 4px rgba(0, 0, 0, 0.12),\n inset 0 -2px 5px rgba(0, 0, 0, 0.15),\n inset 0 2px 4px rgba(255, 255, 255, 0.5) !important;\n }\n\n /* Map colors by connection or device state */\n .device-card-centered .connection-status-icon[data-conn="offline"] {\n background: #94a3b8;\n }\n .device-card-centered .connection-status-icon[data-state="warning"] {\n background: #f59e0b;\n }\n .device-card-centered .connection-status-icon[data-state="danger"] {\n background: #ef4444;\n }\n .device-card-centered .connection-status-icon[data-state="no_info"] {\n background: #94a3b8;\n }\n .device-card-centered .connection-status-icon[data-state="maintenance"] {\n background: #0ea5e9;\n }\n \n .myio-enhanced-card-container .info-close:hover {\n background: rgba(239, 68, 68, 0.2) !important;\n transform: scale(1.1);\n }\n\n /* Responsive / Dark mode (kept) */\n @media (max-width: 768px) {\n .device-card-centered.clickable {\n padding: 16px !important;\n border-radius: 12px !important;\n }\n \n .device-card-centered .device-image {\n max-height: 44px !important;\n }\n \n .device-card-centered .card-action {\n width: 36px !important;\n height: 36px !important;\n }\n }\n \n @media (prefers-color-scheme: dark) {\n .device-card-centered.clickable {\n background: linear-gradient(145deg, #1e293b 0%, #334155 100%) !important;\n border-color: rgba(71, 85, 105, 0.8) !important;\n color: #f1f5f9 !important;\n }\n \n .device-card-centered .device-title {\n color: #f1f5f9 !important;\n }\n \n .device-card-centered .device-subtitle {\n color: #94a3b8 !important;\n }\n \n .device-card-centered .card-actions {\n border-color: rgba(71, 85, 105, 0.8);\n background: #1e293b;\n }\n \n .device-card-centered .card-action {\n background: #1e293b !important;\n border-bottom: 1px solid rgba(71, 85, 105, 0.8);\n }\n }\n `;document.head.appendChild(layoutStyle)}const actionsContainer=document.createElement("div");actionsContainer.className="card-actions";if(typeof handleActionDashboard==="function"){const dashboardBtn=document.createElement("button");dashboardBtn.className="card-action action-dashboard";dashboardBtn.title="Dashboard";dashboardBtn.innerHTML='<img src="https://dashboard.myio-bas.com/api/images/public/TAVXE0sTbCZylwGsMF9lIWdllBB3iFtS"/>';dashboardBtn.addEventListener("click",e=>{e.stopPropagation();handleActionDashboard(entityObject)});actionsContainer.appendChild(dashboardBtn)}if(typeof handleActionReport==="function"){const reportBtn=document.createElement("button");reportBtn.className="card-action action-report";reportBtn.title="Relatório";reportBtn.innerHTML='<img src="https://dashboard.myio-bas.com/api/images/public/d9XuQwMYQCG2otvtNSlqUHGavGaSSpz4"/>';reportBtn.addEventListener("click",e=>{e.stopPropagation();handleActionReport(entityObject)});actionsContainer.appendChild(reportBtn)}if(typeof handleActionSettings==="function"){const settingsBtn=document.createElement("button");settingsBtn.className="card-action action-settings";settingsBtn.title="Configurações";settingsBtn.innerHTML='<img src="https://dashboard.myio-bas.com/api/images/public/5n9tze6vED2uwIs5VvJxGzNNZ9eV4yoz"/>';settingsBtn.addEventListener("click",e=>{e.stopPropagation();handleActionSettings(entityObject)});actionsContainer.appendChild(settingsBtn)}if(handInfo){const infoPanel=document.createElement("div");infoPanel.className="info-panel";const closeBtn=document.createElement("button");closeBtn.className="info-close";closeBtn.innerHTML="×";closeBtn.addEventListener("click",()=>{infoPanel.classList.remove("active")});const infoContent=document.createElement("div");infoContent.className="info-content";let connectionInfo="";if(connectionStatusTime){const date=new Date(connectionStatusTime);connectionInfo=`<strong>Central:</strong> ${centralName||"N/A"}<br>`;connectionInfo+=`<strong>Última Conexão:</strong> ${date.toLocaleString("pt-BR")}<br>`}if(timeVal){const telemetryDate=new Date(timeVal);const now=new Date;const diffMs=now-telemetryDate;const diffHours=Math.floor(diffMs/(1e3*60*60));connectionInfo+=`<strong>Última Telemetria:</strong> ${telemetryDate.toLocaleString("pt-BR")}`;if(diffHours>0){connectionInfo+=` (${diffHours}h atrás)`}}infoContent.innerHTML=connectionInfo||"<strong>Informações não disponíveis</strong>";infoPanel.appendChild(closeBtn);infoPanel.appendChild(infoContent);container.appendChild(infoPanel);const infoBtn=document.createElement("button");infoBtn.className="card-action action-info";infoBtn.title="Informações";infoBtn.innerHTML="ℹ️";infoBtn.addEventListener("click",e=>{e.stopPropagation();infoPanel.classList.toggle("active")});actionsContainer.appendChild(infoBtn)}if(enhancedCardElement&&actionsContainer.children.length>0){enhancedCardElement.insertBefore(actionsContainer,enhancedCardElement.firstChild)}if(enableSelection&&exports.MyIOSelectionStore){const checkbox=enhancedCardElement.querySelector(".card-checkbox");if(checkbox){checkbox.addEventListener("change",e=>{e.stopPropagation();if(e.target.checked){const currentCount=exports.MyIOSelectionStore.getSelectedEntities().length;const selectedEntityes=exports.MyIOSelectionStore.getSelectedEntities();console.log("selectedEntityes",selectedEntityes);const isTryingToAdd=e.target.checked;if(isTryingToAdd&¤tCount>=6){e.preventDefault();e.target.checked=false;MyIOToast2.show("Não é possível selecionar mais de 6 itens.","warning");return}exports.MyIOSelectionStore.add(entityId)}else{exports.MyIOSelectionStore.remove(entityId)}})}const handleSelectionChange=data=>{const isSelected=data.selectedIds.includes(entityId);if(checkbox){checkbox.checked=isSelected}enhancedCardElement.classList.toggle("selected",isSelected)};exports.MyIOSelectionStore.on("selection:change",handleSelectionChange);const isInitiallySelected=exports.MyIOSelectionStore.isSelected(entityId);if(checkbox){checkbox.checked=isInitiallySelected}enhancedCardElement.classList.toggle("selected",isInitiallySelected);container._cleanup=()=>{exports.MyIOSelectionStore.off("selection:change",handleSelectionChange)}}if(enableDragDrop){enhancedCardElement.addEventListener("dragstart",e=>{e.dataTransfer.setData("text/myio-id",entityId);e.dataTransfer.setData("application/json",JSON.stringify(entityObject));e.dataTransfer.setData("text/myio-name",entityObject.labelOrName);e.dataTransfer.effectAllowed="copy";if(exports.MyIOSelectionStore){exports.MyIOSelectionStore.startDrag(entityId)}});enhancedCardElement.addEventListener("dragend",()=>{})}if(typeof handleClickCard==="function"){enhancedCardElement.addEventListener("click",e=>{if(!e.target.closest(".card-action")&&!e.target.closest(".card-checkbox")){handleClickCard(entityObject)}})}const jQueryLikeObject={get:index=>index===0?container:void 0,0:container,length:1,find:selector=>{const found=container.querySelector(selector);return{get:index=>index===0?found:void 0,0:found,length:found?1:0,on:(event,handler)=>{if(found)found.addEventListener(event,handler);return this}}},on:(event,handler)=>{container.addEventListener(event,handler);return this},addClass:className=>{container.classList.add(className);return this},removeClass:className=>{container.classList.remove(className);return this},destroy:()=>{if(container._cleanup){container._cleanup()}}};return jQueryLikeObject}function renderCardComponentLegacy(options){const{renderCardComponent:renderCardComponent4}=(init_template_card(),__toCommonJS(template_card_exports));return renderCardComponent4(options)}function renderCardComponent2(options){const useNewComponents=options.useNewComponents!==false&&typeof exports.MyIOSelectionStore!=="undefined"&&typeof MyIODraggableCard!=="undefined";if(useNewComponents){return renderCardComponentV2(options)}else{return renderCardComponentLegacy(options)}}var CSS_STRING=`\n/* CSS Variables for theming */\n:root {\n --myio-card-radius: 16px;\n --myio-card-shadow: 0 2px 8px rgba(10, 31, 68, .06);\n --myio-card-bg: #fff;\n --myio-card-border: #e9eef5;\n\n /* Status colors - Normal/Power On (blue) */\n --myio-chip-ok-bg: #dbeafe;\n --myio-chip-ok-fg: #1d4ed8;\n --myio-border-ok: rgba(59, 130, 246, 0.4);\n\n /* Status colors - Standby (green) */\n --myio-chip-standby-bg: #dcfce7;\n --myio-chip-standby-fg: #15803d;\n --myio-border-standby: rgba(34, 197, 94, 0.4);\n\n /* Status colors - Alert/Warning (yellow) */\n --myio-chip-alert-bg: #fef3c7;\n --myio-chip-alert-fg: #b45309;\n --myio-border-alert: rgba(245, 158, 11, 0.5);\n\n /* Status colors - Failure (red) */\n --myio-chip-failure-bg: #fee2e2;\n --myio-chip-failure-fg: #b91c1c;\n --myio-border-failure: rgba(239, 68, 68, 0.5);\n\n /* Status colors - Offline (gray) */\n --myio-chip-offline-bg: #f1f5f9;\n --myio-chip-offline-fg: #64748b;\n --myio-border-offline: rgba(100, 116, 139, 0.4);\n\n --myio-text-1: #0f172a;\n --myio-text-2: #4b5563;\n --myio-muted: #94a3b8;\n\n --myio-eff-bar-bg: #e6edf5;\n --myio-eff-bar-a: #1e90ff;\n --myio-eff-bar-b: #a3d1ff;\n\n --myio-badge-border: rgba(255, 153, 0, .35);\n --myio-badge-border-failure: rgba(244, 67, 54, .45);\n}\n\n/* Main card container */\n.myio-ho-card {\n background: var(--myio-card-bg);\n border: 1px solid var(--myio-card-border);\n border-radius: var(--myio-card-radius);\n box-shadow: var(--myio-card-shadow);\n padding: 14px;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n line-height: 1.4;\n color: var(--myio-text-1);\n position: relative;\n cursor: pointer;\n transition: all 0.2s ease;\n min-width: 252px;\n max-width: 288px;\n overflow: visible;\n}\n\n.myio-ho-card:hover {\n box-shadow: 0 4px 12px rgba(10, 31, 68, .12);\n transform: translateY(-1px);\n}\n\n.myio-ho-card:focus-within {\n outline: 2px solid #007ecc;\n outline-offset: 2px;\n}\n\n/* Selected state with light green background */\n.myio-ho-card.is-selected {\n border: 2px solid #00e09e;\n box-shadow: 0 12px 40px rgba(0, 224, 158, 0.25), 0 4px 12px rgba(0, 224, 158, 0.15);\n background: linear-gradient(145deg, #f0fdf9 0%, #ecfdf5 50%, #f0fdf9 100%);\n transform: translateY(-2px);\n}\n\n.myio-ho-card.is-selected:hover {\n box-shadow: 0 16px 48px rgba(0, 224, 158, 0.3), 0 8px 16px rgba(0, 224, 158, 0.2);\n}\n\n/* Card border states based on device status */\n.myio-ho-card.is-ok {\n border-color: var(--myio-border-ok);\n box-shadow: 0 0 0 2px var(--myio-border-ok), var(--myio-card-shadow);\n}\n\n.myio-ho-card.is-standby {\n border-color: var(--myio-border-standby);\n box-shadow: 0 0 0 2px var(--myio-border-standby), var(--myio-card-shadow);\n}\n\n.myio-ho-card.is-alert {\n border-color: var(--myio-border-alert);\n box-shadow: 0 0 0 2px var(--myio-border-alert), var(--myio-card-shadow);\n}\n\n.myio-ho-card.is-failure {\n border-color: var(--myio-border-failure);\n box-shadow: 0 0 0 2px var(--myio-border-failure), var(--myio-card-shadow);\n}\n\n/* Header section */\n.myio-ho-card__header {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n margin-bottom: 10px;\n overflow: visible;\n}\n\n.myio-ho-card__icon {\n width: 28px;\n height: 28px;\n color: #4b6bfb;\n flex-shrink: 0;\n margin-top: 2px;\n}\n\n.myio-ho-card.is-alert .myio-ho-card__icon {\n color: var(--myio-chip-alert-fg);\n}\n\n.myio-ho-card.is-failure .myio-ho-card__icon {\n color: var(--myio-chip-failure-fg);\n}\n\n.myio-ho-card__title {\n flex: 1;\n min-width: 0;\n}\n\n/* Adicione estas duas novas regras ao seu CSS_STRING */\n\n/* Estado Offline - borda cinza */\n.myio-ho-card.is-offline {\n border-color: var(--myio-border-offline);\n box-shadow: 0 0 0 2px var(--myio-border-offline), var(--myio-card-shadow);\n}\n\n.myio-ho-card__name {\n font-weight: 600;\n font-size: 13px;\n color: var(--myio-text-1);\n margin-bottom: 2px;\n word-wrap: break-word;\n line-height: 1.3;\n}\n\n.myio-ho-card__code {\n font-size: 12px;\n color: var(--myio-muted);\n font-weight: 500;\n}\n\n.myio-ho-card__actions {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-shrink: 0;\n position: relative;\n z-index: 10;\n}\n\n.myio-ho-card__kebab {\n background: none;\n border: none;\n padding: 4px;\n cursor: pointer;\n border-radius: 4px;\n color: var(--myio-muted);\n transition: all 0.15s ease;\n position: relative;\n overflow: visible;\n}\n\n.myio-ho-card__kebab:hover {\n background: #f1f5f9;\n color: var(--myio-text-2);\n}\n\n.myio-ho-card__kebab:focus {\n outline: 2px solid #007ecc;\n outline-offset: 1px;\n}\n\n.myio-ho-card__menu {\n position: absolute;\n top: 100%;\n right: 0;\n background: white;\n border: 1px solid var(--myio-card-border);\n border-radius: 8px;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n z-index: 10000;\n min-width: 160px;\n padding: 4px 0;\n margin-top: 4px;\n}\n\n/* Estilos para o Modal */\n.myio-modal-overlay {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background-color: rgba(0, 0, 0, 0.6);\n display: flex;\n justify-content: center;\n align-items: center;\n z-index: 1000; /* Garante que o modal fique na frente de tudo */\n opacity: 0;\n visibility: hidden;\n transition: opacity 0.3s ease, visibility 0.3s ease;\n}\n\n.myio-modal-overlay.visible {\n opacity: 1;\n visibility: visible;\n}\n\n.myio-modal-content {\n background-color: #fff;\n padding: 25px;\n border-radius: 8px;\n box-shadow: 0 4px 15px rgba(0,0,0,0.2);\n position: relative;\n min-width: 300px;\n text-align: left;\n font-family: sans-serif; /* Use a fonte que preferir */\n line-height: 1.6;\n}\n\n.myio-modal-close {\n position: absolute;\n top: 10px;\n right: 15px;\n border: none;\n background: none;\n font-size: 24px;\n cursor: pointer;\n color: #888;\n}\n\n.myio-modal-close:hover {\n color: #000;\n}\n\n.myio-modal-content p {\n margin: 0;\n color: #333;\n}\n\n.myio-modal-content strong {\n color: #000;\n}\n\n.myio-modal-title {\n margin-top: 0;\n margin-bottom: 15px;\n padding-bottom: 10px;\n border-bottom: 1px solid #eee;\n font-size: 1.25rem; /* 20px */\n color: #333;\n font-weight: 600;\n}\n\n.myio-ho-card__menu[hidden] {\n display: none;\n}\n\n.myio-ho-card__menu button {\n width: 100%;\n background: none;\n border: none;\n padding: 8px 12px;\n text-align: left;\n cursor: pointer;\n font-size: 13px;\n color: var(--myio-text-1);\n transition: background-color 0.15s ease;\n}\n\n.myio-ho-card__menu button:hover {\n background: #f8fafc;\n}\n\n.myio-ho-card__menu button:focus {\n background: #e2e8f0;\n outline: none;\n}\n\n.myio-ho-card__select {\n display: flex;\n align-items: center;\n cursor: pointer;\n}\n\n.myio-ho-card__select[hidden] {\n display: none;\n}\n\n.myio-ho-card__select input[type="checkbox"] {\n width: 16px;\n height: 16px;\n margin: 0;\n cursor: pointer;\n}\n\n/* ======================================================== */\n/* === BLOCO AJUSTADO PARA ALINHAMENTO DE CHIPS === */\n/* ======================================================== */\n\n/* ====== CONTAINER DOS CHIPS DE SHOPPING E STATUS ====== */\n.myio-ho-card__chips-row {\n display: flex; /* Usa Flexbox para alinhamento horizontal */\n justify-content: space-between; /* Empurra os itens para as extremidades (esquerda e direita) */\n align-items: center; /* Centraliza verticalmente os chips */\n \n /* Usa o mesmo padding lateral do card (14px) */\n padding: 0 0px; \n \n /* Espaçamento acima (para separar do header) e abaixo (para separar do valor) */\n margin-top: 10px; \n margin-bottom: 12px;\n}\n\n/* ====== ESTILO DO CHIP DE SHOPPING (estilo "Mont Serrat") ====== */\n.myio-ho-card__shopping-chip {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n\n /* Cores baseadas na imagem "Mont Serrat" */\n background-color: #EBF4FF; /* Fundo azul bem claro */\n border: 1px solid #BEE3F8; /* Borda azul clara */\n color: #2C5282; /* Texto azul escuro */\n\n border-radius: 8px;\n padding: 4px 10px;\n font-size: 11px;\n font-weight: 500;\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n white-space: nowrap;\n}\n\n.myio-ho-card__shopping-chip .chip-icon {\n /* Ajusta o ícone SVG */\n width: 14px;\n height: 14px;\n opacity: 0.7;\n stroke: currentColor; /* Faz o SVG usar a cor do texto */\n}\n\n/* ====== CONTAINER ADICIONAL PARA O CHIP DE STATUS ====== */\n.myio-ho-card__status-chip-container {\n /* Este seletor é um container para o chip de status, \n permitindo que o flexbox o alinhe à direita. */\n /* O alinhamento é feito pelo justify-content: space-between no pai */\n}\n\n/* =============================================== */\n/* === ESTILOS PARA O MODAL DE INFORMAÇÕES === */\n/* =============================================== */\n\n/* Fundo escurecido que cobre a tela */\n.myio-modal-overlay {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background: rgba(0, 0, 0, 0.6);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1000;\n opacity: 0;\n visibility: hidden;\n transition: opacity 0.3s ease, visibility 0.3s ease;\n}\n\n/* Torna o modal visível */\n.myio-modal-overlay.visible {\n opacity: 1;\n visibility: visible;\n}\n\n/* Caixa de conteúdo do modal */\n.myio-modal-content {\n background: #fff;\n padding: 24px;\n border-radius: 12px;\n box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);\n width: 90%;\n max-width: 450px;\n position: relative;\n transform: translateY(-20px);\n transition: transform 0.3s ease;\n}\n\n.myio-modal-overlay.visible .myio-modal-content {\n transform: translateY(0);\n}\n\n.myio-ho-card__menu button {\n display: flex;\n align-items: center;\n gap: 8px; /* Espaço entre o ícone e o texto */\n text-align: left;\n width: 100%;\n}\n\n.myio-ho-card__menu button img {\n flex-shrink: 0; /* Impede que o ícone seja espremido */\n}\n\n/* Título do modal */\n.myio-modal-title {\n margin-top: 0;\n margin-bottom: 16px;\n font-size: 1.25rem;\n font-weight: 600;\n color: #333;\n border-bottom: 1px solid #eee;\n padding-bottom: 12px;\n}\n\n/* Botão de fechar (X) */\n.myio-modal-close {\n position: absolute;\n top: 10px;\n right: 14px;\n background: none;\n border: none;\n font-size: 2rem;\n cursor: pointer;\n color: #aaa;\n line-height: 1;\n padding: 0;\n}\n.myio-modal-close:hover {\n color: #333;\n}\n\n/* Linha de informação (ícone + label + valor) */\n.info-row {\n display: flex;\n align-items: center;\n margin-bottom: 12px;\n font-size: 0.95rem;\n}\n\n/* Estilo do ícone */\n.info-icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n margin-right: 12px;\n color: #555;\n}\n.info-icon svg {\n width: 20px;\n height: 20px;\n}\n\n/* Rótulo (Ex: "Central:") */\n.info-label {\n color: #666;\n font-weight: 500;\n}\n\n/* Valor da informação */\n.info-value {\n margin-left: auto;\n font-weight: 600;\n color: #333;\n text-align: right;\n}\n\n/* Divisor entre seções */\n.info-divider {\n border: none;\n border-top: 1px solid #eee;\n margin: 16px 0;\n}\n\n/* Status chip */\n.myio-ho-card__status {\n /* margin-bottom: 7px; <-- Esta linha foi removida */\n /* O espaçamento agora é controlado por .myio-ho-card__chips-row */\n}\n\n.chip {\n display: inline-flex;\n align-items: center;\n padding: 4px 8px;\n border-radius: 12px;\n font-size: 12px;\n font-weight: 500;\n gap: 4px;\n}\n\n.chip--ok {\n background: var(--myio-chip-ok-bg);\n color: var(--myio-chip-ok-fg);\n}\n\n.chip--standby {\n background: var(--myio-chip-standby-bg);\n color: var(--myio-chip-standby-fg);\n}\n\n.chip--alert {\n background: var(--myio-chip-alert-bg);\n color: var(--myio-chip-alert-fg);\n}\n\n.chip--failure {\n background: var(--myio-chip-failure-bg);\n color: var(--myio-chip-failure-fg);\n}\n\n.chip--offline {\n background: var(--myio-chip-offline-bg);\n color: var(--myio-chip-offline-fg);\n}\n\n/* Status indicator dot for power metric */\n.status-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n flex-shrink: 0;\n background: var(--myio-chip-offline-fg);\n}\n\n.status-dot.dot--ok {\n background: var(--myio-chip-ok-fg);\n}\n\n.status-dot.dot--standby {\n background: var(--myio-chip-standby-fg);\n}\n\n.status-dot.dot--alert {\n background: var(--myio-chip-alert-fg);\n}\n\n.status-dot.dot--failure {\n background: var(--myio-chip-failure-fg);\n}\n\n.status-dot.dot--offline {\n background: var(--myio-chip-offline-fg);\n}\n\n.status-dot.dot--neutral {\n background: var(--myio-muted);\n}\n\n/* Primary metric */\n.myio-ho-card__primary {\n margin-bottom: 14px;\n padding: 7px 0;\n border-radius: 8px;\n transition: background-color 0.15s ease;\n}\n\n.myio-ho-card__primary:hover {\n background: #f8fafc;\n}\n\n.myio-ho-card__primary:focus {\n outline: 2px solid #007ecc;\n outline-offset: 2px;\n}\n\n.myio-ho-card__value {\n display: flex;\n align-items: baseline;\n gap: 4px;\n flex-wrap: wrap;\n justify-content: center;\n}\n\n.myio-ho-card__value .num {\n font-size: 24px;\n font-weight: 700;\n color: var(--myio-text-1);\n line-height: 1;\n}\n\n.myio-ho-card__value .unit {\n font-size: 16px;\n font-weight: 600;\n color: var(--myio-text-2);\n line-height: 1;\n}\n\n.myio-ho-card__value .suffix {\n font-size: 12px;\n color: var(--myio-muted);\n margin-left: 4px;\n}\n\n/* Efficiency bar */\n.myio-ho-card__eff {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 14px;\n}\n\n.myio-ho-card__eff .label {\n font-size: 12px;\n color: var(--myio-text-2);\n font-weight: 500;\n min-width: 60px;\n}\n\n.myio-ho-card__eff .bar {\n flex: 1;\n height: 6px;\n background: var(--myio-eff-bar-bg);\n border-radius: 3px;\n position: relative;\n overflow: hidden;\n}\n\n.myio-ho-card__eff .bar__fill {\n height: 100%;\n background: linear-gradient(90deg, var(--myio-eff-bar-a) 0%, var(--myio-eff-bar-b) 100%);\n border-radius: 3px;\n transition: width 0.3s ease;\n min-width: 2px;\n}\n\n.myio-ho-card__eff .perc {\n font-size: 12px;\n font-weight: 600;\n color: var(--myio-text-1);\n min-width: 32px;\n text-align: right;\n}\n\n/* Footer metrics - Now with 2 columns (removed temperature) */\n.myio-ho-card__footer {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 16px;\n padding: 0 8px;\n}\n\n.myio-ho-card__footer .metric {\n display: flex;\n flex-direction: column;\n align-items: center;\n text-align: center;\n gap: 2px;\n}\n\n.myio-ho-card__footer .metric .status-dot {\n margin-bottom: 2px;\n}\n\n.myio-ho-card__footer .metric .ico {\n width: 18px;\n height: 18px;\n color: var(--myio-muted);\n flex-shrink: 0;\n}\n\n.myio-ho-card__footer .metric .label {\n font-size: 10.5px;\n color: var(--myio-muted);\n font-weight: 500;\n line-height: 1.2;\n}\n\n.myio-ho-card__footer .metric .val {\n font-size: 11px;\n font-weight: 600;\n color: var(--myio-text-1);\n line-height: 1.3;\n word-break: break-word;\n max-width: 100%;\n}\n\n/* Drag and drop states */\n.myio-ho-card[draggable="true"] {\n cursor: grab;\n}\n\n.myio-ho-card[draggable="true"]:active {\n cursor: grabbing;\n}\n\n.myio-ho-card.is-dragging {\n opacity: 0.5;\n transform: rotate(2deg);\n}\n\n/* Responsive adjustments */\n@media (max-width: 320px) {\n .myio-ho-card {\n min-width: 234px;\n padding: 12px;\n }\n\n .myio-ho-card__value .num {\n font-size: 20px;\n }\n\n .myio-ho-card__footer {\n gap: 7px;\n }\n}\n\n/* High contrast mode support */\n@media (prefers-contrast: high) {\n .myio-ho-card {\n border-width: 2px;\n }\n \n .chip {\n border: 1px solid currentColor;\n }\n \n .myio-ho-card__eff .bar {\n border: 1px solid var(--myio-text-2);\n }\n}\n\n/* Reduced motion support */\n@media (prefers-reduced-motion: reduce) {\n .myio-ho-card,\n .myio-ho-card__primary,\n .myio-ho-card__kebab,\n .myio-ho-card__eff .bar__fill {\n transition: none;\n }\n}\n`;var Icons={gear:`\n<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"\n viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" focusable="false">\n <path d="M19.14 12.94c.04-.31.06-.63.06-.94s-.02-.63-.06-.94l2.03-1.58a.5.5 0 0 0 .12-.66l-1.92-3.32a.5.5 0 0 0-.62-.22l-2.39.96a7.36 7.36 0 0 0-1.63-.94l-.36-2.55A.5.5 0 0 0 13.89 1h-3.78a.5.5 0 0 0-.49.41l-.36 2.55c-.58.23-1.12.53-1.63.94l-2.39-.96a.5.5 0 0 0-.62.22L2.7 7.48a.5.5 0 0 0 .12.66l2.03 1.58c-.04.31-.06.63-.06.94s.02.63.06.94L2.82 13.18a.5.5 0 0 0-.12.66l1.92 3.32c.13.23.4.32.62.22l2.39-.96c.5.4 1.05.71 1.63.94l.36 2.55c.05.23.26.41.49.41h3.78c.23 0 .44-.18.49-.41l.36-2.55c.58-.23 1.12-.53 1.63-.94l2.39.96c.22.1.49 0 .62-.22l1.92-3.32a.5.5 0 0 0-.12-.66l-2.03-1.58ZM12 15.5A3.5 3.5 0 1 1 12 8.5a3.5 3.5 0 0 1 0 7Z"/>\n</svg>`,elevator:`\n<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"\n viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" focusable="false">\n <path d="M6 3h12a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2Zm0 2v14h12V5H6Z"/>\n <path d="M11 17h2v-5h-2v5Zm-2.5-7.5 2-2-2-2v4Zm7 0v-4l-2 2 2 2Z"/>\n</svg>`,escalator:`\n<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"\n viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" focusable="false">\n <path d="M3 16a3 3 0 0 1 3-3h3.6l4.4-6H20a2 2 0 1 1 0 4h-3.6l-4.4 6H6a3 3 0 0 1-3-3Z"/>\n <circle cx="8.5" cy="6" r="1.5"/>\n</svg>`,chiller:`\n<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"\n viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6"\n stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false">\n <path d="M12 2v20M4 7l16 10M4 17L20 7"/>\n <path d="M8 4l4 3 4-3M8 20l4-3 4 3M3 12h18"/>\n</svg>`,pump:`\n<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"\n viewBox="0 0 24 24" aria-hidden="true" focusable="false">\n <path fill="currentColor" d="M12 3s-5 5.6-5 9a5 5 0 0 0 10 0c0-3.4-5-9-5-9Zm0 12a3 3 0 1 1 0-6 3 3 0 0 1 0 6Z"/>\n <path fill="currentColor" d="M12 9.3c.9 0 1.7.8 1.7 1.7S12.9 12.7 12 12.7 10.3 12 10.3 11 11.1 9.3 12 9.3Z"/>\n</svg>`,fan:`\n<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"\n viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" focusable="false">\n <circle cx="12" cy="12" r="2.2"/>\n <path d="M12 4c2.5 0 4 1.5 4 3.3 0 1.3-.7 2.4-1.7 3.2-1.3 1-3 .7-3-.9V4Z"/>\n <path d="M20 12c0 2.5-1.5 4-3.3 4-1.3 0-2.4-.7-3.2-1.7-1-1.3-.7-3 .9-3H20Z"/>\n <path d="M12 20c-2.5 0-4-1.5-4-3.3 0-1.3.7-2.4 1.7-3.2 1.3-1 3-.7 3 .9V20Z"/>\n <path d="M4 12c0-2.5 1.5-4 3.3-4 1.3 0 2.4.7 3.2 1.7 1 1.3.7 3-.9 3H4Z"/>\n</svg>`,motor:`\n<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"\n viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" focusable="false">\n <rect x="3" y="7" width="14" height="10" rx="3"/>\n <rect x="17" y="10" width="4" height="4" rx="1"/>\n <circle cx="10" cy="12" r="2.5" fill="none" stroke="currentColor" stroke-width="1.6"/>\n</svg>`,thermometer:`\n<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"\n viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" focusable="false">\n <path d="M11 5a3 3 0 0 1 6 0v6.1a5 5 0 1 1-6 0V5Z"/>\n <rect x="13" y="4" width="2" height="9" rx="1"/>\n</svg>`,switch:`\n<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"\n viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" focusable="false">\n <rect x="2" y="7" width="20" height="10" rx="5"/>\n <circle cx="9" cy="12" r="3.5" fill="#fff"/>\n</svg>`,energyMeter:`\n<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"\n viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" focusable="false">\n <rect x="3" y="4" width="18" height="14" rx="2"/>\n <path d="M6 16h12v2H6z"/>\n <path d="M12 8a5 5 0 0 0-5 5h2a3 3 0 0 1 6 0h2a5 5 0 0 0-5-5Z"/>\n</svg>`,waterTank:`\n<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"\n viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" focusable="false">\n <rect x="5" y="4" width="14" height="16" rx="2"/>\n <path d="M7 12c1.5 1 3 .9 5-.3s3.5-1.3 5 .3v4H7v-4Z" fill="#fff" fill-opacity=".25"/>\n</svg>`,waterDrop:`\n<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">\n <g id="water" transform="translate(-4 -2)">\n <path id="secondary" fill="#2ca9bc" d="M19,14A7,7,0,1,1,5,14C5,8,12,3,12,3S19,8,19,14Z"/>\n <path id="primary" d="M19,14A7,7,0,1,1,5,14C5,8,12,3,12,3S19,8,19,14Z" fill="none" stroke="#000000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/>\n </g>\n</svg>`,kebab:`\n<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"\n viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" focusable="false">\n <circle cx="12" cy="5" r="2"/><circle cx="12" cy="12" r="2"/><circle cx="12" cy="19" r="2"/>\n</svg>`};var ICON_MAP={ESCADA_ROLANTE:Icons.escalator,ELEVADOR:Icons.elevator,ELEVADOR_SERVICO:Icons.elevator,CHILLER:Icons.chiller,PUMP:Icons.pump,COMPRESSOR:Icons.motor,VENTILADOR:Icons.fan,MOTOR:Icons.motor,TERMOSTATO:Icons.thermometer,SELETOR_AUTO_MANUAL:Icons.switch,"3F_MEDIDOR":Icons.energyMeter,CAIXA_D_AGUA:Icons.waterTank,DEFAULT:Icons.gear};var DEFAULT_I18N={in_operation:"⚡ Normal",in_operation_water:"💧 Normal",standby:"💤 Em standby",alert:"⚠️ Alerta",failure:"🚨 Falha",maintenance:"🔧 Manutenção",not_installed:"📦 Não instalado",offline:"📡 Offline",efficiency:"Eficiência",temperature:"Temperatura",operation_time:"Tempo em operação",last_telemetry:"Últ. Telemetria",instantaneous_power:"Potência",current_suffix:"Atual",menu_dashboard:"Dashboard",menu_report:"Relatório",menu_settings:"Configurações"};var CSS_TAG="head-office-card-v1";function ensureCss(){if(!document.querySelector(`style[data-myio-css="${CSS_TAG}"]`)){const style=document.createElement("style");style.setAttribute("data-myio-css",CSS_TAG);style.textContent=CSS_STRING;document.head.appendChild(style)}}function normalizeParams(params){if(!params||!params.entityObject){throw new Error("renderCardCompenteHeadOffice: entityObject is required")}const entityObject=params.entityObject;if(!entityObject.entityId){console.warn("renderCardCompenteHeadOffice: entityId is missing, generating temporary ID");entityObject.entityId=`temp-${Date.now()}-${Math.random().toString(36).substr(2,9)}`}return{entityObject:entityObject,i18n:{...DEFAULT_I18N,...params.i18n||{}},enableSelection:Boolean(params.enableSelection),enableDragDrop:Boolean(params.enableDragDrop),useNewComponents:Boolean(params.useNewComponents),delayTimeConnectionInMins:params.delayTimeConnectionInMins??15,callbacks:{handleActionDashboard:params.handleActionDashboard,handleActionReport:params.handleActionReport,handleActionSettings:params.handleActionSettings,handleSelect:params.handleSelect,handInfo:params.handInfo,handleClickCard:params.handleClickCard}}}function getIconSvg(deviceType,domain){if(domain==="water"){return Icons.waterDrop}if(domain==="temperature"){return Icons.thermometer}return ICON_MAP[deviceType]||ICON_MAP.DEFAULT}function formatPower(valueInWatts){if(valueInWatts===null||valueInWatts===void 0||isNaN(valueInWatts)){return{num:"-",unit:""}}const val=Number(valueInWatts);if(val>=1e3){const kw=Math.ceil(val/1e3*100)/100;return{num:kw.toFixed(2),unit:"kW"}}else{const w=Math.ceil(val);return{num:w.toString(),unit:"W"}}}function formatValueByDomain(value,domain){if(domain==="water"){return formatWaterVolumeM3(value)}if(domain==="temperature"){return formatTemperature(value)}return formatEnergy(value)}function formatTemperature(temp){if(temp===null||temp===void 0||isNaN(temp)){return"—"}return`${temp.toFixed(0)}°C`}function calculateConsumptionPercentage(target,consumption){const numericTarget=Number(target);const numericConsumption=Number(consumption);if(isNaN(numericTarget)||isNaN(numericConsumption)||numericTarget<=0){return 0}const percentage=numericConsumption/numericTarget*100;return percentage}function getStatusInfo(deviceStatus,i18n,domain){switch(deviceStatus){case"normal":return{chipClass:"chip--ok",label:"Normal"};case"cold":return{chipClass:"chip--standby",label:"Frio"};case"hot":return{chipClass:"chip--alert",label:"Quente"};case DeviceStatusType.POWER_ON:if(domain==="water"){return{chipClass:"chip--ok",label:i18n.in_operation_water}}return{chipClass:"chip--ok",label:i18n.in_operation};case DeviceStatusType.STANDBY:return{chipClass:"chip--standby",label:i18n.standby};case DeviceStatusType.WARNING:return{chipClass:"chip--alert",label:i18n.alert};case DeviceStatusType.FAILURE:case DeviceStatusType.POWER_OFF:return{chipClass:"chip--failure",label:i18n.failure};case DeviceStatusType.MAINTENANCE:return{chipClass:"chip--alert",label:i18n.maintenance};case DeviceStatusType.NOT_INSTALLED:return{chipClass:"chip--offline",label:i18n.not_installed};case DeviceStatusType.NO_INFO:default:return{chipClass:"chip--offline",label:i18n.offline}}}function getCardStateClass(deviceStatus){switch(deviceStatus){case DeviceStatusType.POWER_ON:return"is-ok";case DeviceStatusType.STANDBY:return"is-standby";case DeviceStatusType.WARNING:case DeviceStatusType.MAINTENANCE:return"is-alert";case DeviceStatusType.FAILURE:case DeviceStatusType.POWER_OFF:return"is-failure";case DeviceStatusType.NO_INFO:case DeviceStatusType.NOT_INSTALLED:return"is-offline";default:return""}}function getStatusDotClass(deviceStatus){switch(deviceStatus){case"normal":return"dot--ok";case"cold":return"dot--standby";case"hot":return"dot--alert";case DeviceStatusType.POWER_ON:return"dot--ok";case DeviceStatusType.STANDBY:return"dot--standby";case DeviceStatusType.WARNING:case DeviceStatusType.MAINTENANCE:return"dot--alert";case DeviceStatusType.FAILURE:case DeviceStatusType.POWER_OFF:return"dot--failure";default:return"dot--offline"}}function buildDOM(state){const{entityObject:entityObject,i18n:i18n,enableSelection:enableSelection,enableDragDrop:enableDragDrop}=state;const root=document.createElement("div");root.className="myio-ho-card";root.setAttribute("role","group");root.setAttribute("data-entity-id",entityObject.entityId);if(enableDragDrop){root.setAttribute("draggable","true")}const header=document.createElement("div");header.className="myio-ho-card__header";const iconContainer=document.createElement("div");iconContainer.className="myio-ho-card__icon";iconContainer.innerHTML=getIconSvg(entityObject.deviceType,entityObject.domain);header.appendChild(iconContainer);const titleSection=document.createElement("div");titleSection.className="myio-ho-card__title";const nameEl=document.createElement("div");nameEl.className="myio-ho-card__name";nameEl.textContent=entityObject.labelOrName||"Unknown Device";titleSection.appendChild(nameEl);if(entityObject.deviceIdentifier){const codeEl=document.createElement("div");codeEl.className="myio-ho-card__code";codeEl.textContent=entityObject.deviceIdentifier;titleSection.appendChild(codeEl)}header.appendChild(titleSection);const actionsSection=document.createElement("div");actionsSection.className="myio-ho-card__actions";const kebabBtn=document.createElement("button");kebabBtn.className="myio-ho-card__kebab";kebabBtn.setAttribute("aria-label","Open actions");kebabBtn.setAttribute("aria-haspopup","menu");kebabBtn.innerHTML=Icons.kebab;actionsSection.appendChild(kebabBtn);const menu=document.createElement("div");menu.className="myio-ho-card__menu";menu.setAttribute("role","menu");menu.setAttribute("hidden","");const dashboardBtn=document.createElement("button");dashboardBtn.setAttribute("role","menuitem");dashboardBtn.setAttribute("data-action","dashboard");dashboardBtn.innerHTML=`<img src="https://dashboard.myio-bas.com/api/images/public/TAVXE0sTbCZylwGsMF9lIWdllBB3iFtS" width="16" height="16"/> <span>Dashboard</span>`;menu.appendChild(dashboardBtn);const reportBtn=document.createElement("button");reportBtn.setAttribute("role","menuitem");reportBtn.setAttribute("data-action","report");reportBtn.innerHTML=`<img src="https://dashboard.myio-bas.com/api/images/public/d9XuQwMYQCG2otvtNSlqUHGavGaSSpz4" width="16" height="16"/> <span>${i18n.menu_report}</span>`;menu.appendChild(reportBtn);const settingsBtn=document.createElement("button");settingsBtn.setAttribute("role","menuitem");settingsBtn.setAttribute("data-action","settings");settingsBtn.innerHTML=`<img src="https://dashboard.myio-bas.com/api/images/public/5n9tze6vED2uwIs5VvJxGzNNZ9eV4yoz" width="16" height="16"/> <span>${i18n.menu_settings}</span>`;menu.appendChild(settingsBtn);actionsSection.appendChild(menu);if(enableSelection){const selectLabel=document.createElement("label");selectLabel.className="myio-ho-card__select";const checkbox=document.createElement("input");checkbox.type="checkbox";selectLabel.appendChild(checkbox);actionsSection.appendChild(selectLabel)}header.appendChild(actionsSection);root.appendChild(header);const chipsRow=document.createElement("div");chipsRow.className="myio-ho-card__chips-row";const statusChipContainer=document.createElement("div");statusChipContainer.className="myio-ho-card__status-chip-container";const chip=document.createElement("span");chip.className="chip";statusChipContainer.appendChild(chip);chipsRow.appendChild(statusChipContainer);const chipShopping=document.createElement("span");chipShopping.className="myio-ho-card__shopping-chip";const chipIcon=`<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="chip-icon"><path d="M4 22h16"/><path d="M7 22V4a2 2 0 0 1 2-2h6a2 2 0 0 1 2 2v18"/><path d="M9 18h6"/><path d="M9 14h6"/><path d="M9 10h6"/><path d="M9 6h6"/></svg>`;chipShopping.innerHTML=`${chipIcon}<span>${entityObject.customerName}</span>`;chipsRow.appendChild(chipShopping);root.appendChild(chipsRow);const primarySection=document.createElement("div");primarySection.className="myio-ho-card__primary";primarySection.setAttribute("role","button");primarySection.setAttribute("tabindex","0");const valueContainer=document.createElement("div");valueContainer.className="myio-ho-card__value";const numSpan=document.createElement("span");numSpan.className="num";valueContainer.appendChild(numSpan);const unitSpan=document.createElement("span");unitSpan.className="unit";valueContainer.appendChild(unitSpan);primarySection.appendChild(valueContainer);root.appendChild(primarySection);const effSection=document.createElement("div");effSection.className="myio-ho-card__eff";const effLabel=document.createElement("div");effLabel.className="label";effLabel.textContent=i18n.efficiency;effSection.appendChild(effLabel);const barContainer=document.createElement("div");barContainer.className="bar";barContainer.setAttribute("role","progressbar");barContainer.setAttribute("aria-valuemin","0");barContainer.setAttribute("aria-valuemax","100");const barFill=document.createElement("div");barFill.className="bar__fill";barContainer.appendChild(barFill);effSection.appendChild(barContainer);const percSpan=document.createElement("div");percSpan.className="perc";effSection.appendChild(percSpan);root.appendChild(effSection);const footer=document.createElement("div");footer.className="myio-ho-card__footer";const opTimeMetric=document.createElement("div");opTimeMetric.className="metric";const opTimeDot=document.createElement("span");opTimeDot.className="status-dot dot--neutral";opTimeMetric.appendChild(opTimeDot);const opTimeLabel=document.createElement("div");opTimeLabel.className="label";opTimeLabel.textContent=i18n.operation_time;opTimeMetric.appendChild(opTimeLabel);const opTimeVal=document.createElement("div");opTimeVal.className="val";opTimeMetric.appendChild(opTimeVal);footer.appendChild(opTimeMetric);const powerMetric=document.createElement("div");powerMetric.className="metric";const statusDot=document.createElement("span");statusDot.className="status-dot";powerMetric.appendChild(statusDot);const powerLabel=document.createElement("div");powerLabel.className="label";if(entityObject.domain==="water"){powerLabel.textContent="Leitura"}else{powerLabel.textContent=i18n.instantaneous_power||"Potência"}powerMetric.appendChild(powerLabel);const powerVal=document.createElement("div");powerVal.className="val";powerMetric.appendChild(powerVal);footer.appendChild(powerMetric);root.appendChild(footer);return root}function verifyOfflineStatus(entityObject,delayTimeInMins=15){const lastConnectionTime=new Date(entityObject.lastConnectTime||0);const lastDisconnectTime=new Date(entityObject.lastDisconnectTime||0);const now=new Date;const delayTimeInMs=delayTimeInMins*60*1e3;const timeSinceConnection=now.getTime()-lastConnectionTime.getTime();if(lastDisconnectTime.getTime()>lastConnectionTime.getTime()){return false}if(timeSinceConnection<delayTimeInMs){return false}return true}function paint(root,state){const{entityObject:entityObject,i18n:i18n,delayTimeConnectionInMins:delayTimeConnectionInMins,isSelected:isSelected}=state;if(entityObject.connectionStatus){if(entityObject.connectionStatus==="offline"){entityObject.deviceStatus=DeviceStatusType.NO_INFO}}else{if(verifyOfflineStatus(entityObject,delayTimeConnectionInMins)===false){entityObject.deviceStatus=DeviceStatusType.NO_INFO}}const stateClass=getCardStateClass(entityObject.deviceStatus);root.className=`myio-ho-card ${stateClass}`;const statusInfo=getStatusInfo(entityObject.deviceStatus,i18n,entityObject.domain);const chip=root.querySelector(".chip");chip.className=`chip ${statusInfo.chipClass}`;chip.innerHTML=statusInfo.label;const primaryValue=formatValueByDomain(entityObject.val,entityObject.domain);const numSpan=root.querySelector(".myio-ho-card__value .num");root.querySelector(".myio-ho-card__value .unit");numSpan.textContent=primaryValue;const barContainer=root.querySelector(".bar");const effContainer=root.querySelector(".myio-ho-card__eff");if(state.enableSelection){const checkbox=root.querySelector('.myio-ho-card__select input[type="checkbox"]');if(checkbox){checkbox.checked=!!isSelected}root.classList.toggle("is-selected",!!isSelected)}const targetValue=entityObject.consumptionTargetValue;if(targetValue){barContainer.style.display="";effContainer.style.display="";const barFill=root.querySelector(".bar__fill");const percSpan=root.querySelector(".myio-ho-card__eff .perc");const perc=calculateConsumptionPercentage(targetValue,entityObject.val);barFill.style.width=`${Math.max(0,Math.min(100,perc))}%`;percSpan.textContent=`${Math.round(perc)}%`;barContainer.setAttribute("aria-valuenow",Math.round(perc).toString());barContainer.setAttribute("aria-label",`${i18n.efficiency} ${Math.round(perc)}%`)}else{barContainer.style.display="none";effContainer.style.display="none"}const opTimeVal=root.querySelector(".myio-ho-card__footer .metric:nth-child(1) .val");if(opTimeVal){opTimeVal.textContent=entityObject.operationHours??"-"}const powerVal=root.querySelector(".myio-ho-card__footer .metric:nth-child(2) .val");if(powerVal){if(entityObject.domain==="water"){const pulses=entityObject.pulses??0;powerVal.textContent=`${pulses} L`}else{const instantPower=entityObject.instantaneousPower??entityObject.consumption_power??null;const powerFormatted=formatPower(instantPower);powerVal.textContent=instantPower!==null?`${powerFormatted.num} ${powerFormatted.unit}`:"-"}}const statusDot=root.querySelector(".myio-ho-card__footer .metric:nth-child(2) .status-dot");if(statusDot){const dotClass=getStatusDotClass(entityObject.deviceStatus);statusDot.className=`status-dot ${dotClass}`}}function bindEvents(root,state,callbacks){const{entityObject:entityObject}=state;const kebabBtn=root.querySelector(".myio-ho-card__kebab");const menu=root.querySelector(".myio-ho-card__menu");function toggleMenu(){const isHidden=menu.hasAttribute("hidden");if(isHidden){menu.removeAttribute("hidden");kebabBtn.setAttribute("aria-expanded","true")}else{menu.setAttribute("hidden","");kebabBtn.setAttribute("aria-expanded","false")}}function closeMenu(){menu.setAttribute("hidden","");kebabBtn.setAttribute("aria-expanded","false")}kebabBtn.addEventListener("click",e=>{e.stopPropagation();toggleMenu()});document.addEventListener("click",closeMenu);document.addEventListener("keydown",e=>{if(e.key==="Escape"){closeMenu()}});const dashboardBtn=menu.querySelector('[data-action="dashboard"]');const reportBtn=menu.querySelector('[data-action="report"]');const settingsBtn=menu.querySelector('[data-action="settings"]');if(callbacks.handleActionDashboard){dashboardBtn.addEventListener("click",e=>{e.stopPropagation();closeMenu();callbacks.handleActionDashboard(e,entityObject)})}if(callbacks.handleActionReport){reportBtn.addEventListener("click",e=>{e.stopPropagation();closeMenu();callbacks.handleActionReport(e,entityObject)})}if(callbacks.handleActionSettings){settingsBtn.addEventListener("click",e=>{e.stopPropagation();closeMenu();callbacks.handleActionSettings(e,entityObject)})}const MyIOSelectionStore2=window.MyIOLibrary?.MyIOSelectionStore||window.MyIOSelectionStore;if(MyIOSelectionStore2){const onSelectionChange=()=>{const selectedIds=MyIOSelectionStore2.getSelectedIds();const isSelected=selectedIds.includes(entityObject.entityId);if(state.isSelected!==isSelected){state.isSelected=isSelected;paint(root,state)}};MyIOSelectionStore2.on("selection:change",onSelectionChange);root._selectionListener=onSelectionChange}root._cleanup=()=>{document.removeEventListener("click",closeMenu);document.removeEventListener("keydown",closeMenu);if(MyIOSelectionStore2&&root._selectionListener){MyIOSelectionStore2.off("selection:change",root._selectionListener)}};const checkbox=root.querySelector('.myio-ho-card__select input[type="checkbox"]');if(checkbox&&callbacks.handleSelect){checkbox.addEventListener("change",e=>{e.stopPropagation();const isSelected=checkbox.checked;root.classList.toggle("is-selected",isSelected);callbacks.handleSelect(isSelected,entityObject)})}const primarySection=root.querySelector(".myio-ho-card__primary");if(callbacks.handleClickCard){let handleCardClick=function(e){if(e.target.closest(".myio-ho-card__actions"))return;callbacks.handleClickCard(e,entityObject)};root.addEventListener("click",handleCardClick);primarySection.addEventListener("keydown",e=>{if(e.key==="Enter"||e.key===" "){e.preventDefault();callbacks.handleClickCard(e,entityObject)}})}if(state.enableDragDrop){root.addEventListener("dragstart",e=>{root.classList.add("is-dragging");e.dataTransfer.setData("text/plain",entityObject.entityId);const customEvent=new CustomEvent("myio:dragstart",{detail:{entityObject:entityObject},bubbles:true});root.dispatchEvent(customEvent)});root.addEventListener("dragend",()=>{root.classList.remove("is-dragging")});root.addEventListener("drop",e=>{e.preventDefault();const draggedId=e.dataTransfer.getData("text/plain");const customEvent=new CustomEvent("myio:drop",{detail:{draggedId:draggedId,targetEntity:entityObject},bubbles:true});root.dispatchEvent(customEvent)});root.addEventListener("dragover",e=>{e.preventDefault()})}root._cleanup=()=>{document.removeEventListener("click",closeMenu);document.removeEventListener("keydown",closeMenu)}}function unbindEvents(root){if(root._cleanup){root._cleanup();delete root._cleanup}}function renderCardComponentHeadOffice(containerEl,params){if(!containerEl){throw new Error("renderCardComponentHeadOffice: containerEl is required")}ensureCss();const state=normalizeParams(params);const root=buildDOM(state);state.isSelected=params.isSelected||false;containerEl.appendChild(root);bindEvents(root,state,state.callbacks);paint(root,state);return{update(next){if(next){Object.assign(state.entityObject,next);paint(root,state)}},destroy(){unbindEvents(root);if(root.parentNode){root.parentNode.removeChild(root)}},getRoot(){return root}}}function renderCardComponentV5({entityObject:entityObject,handleActionDashboard:handleActionDashboard,handleActionReport:handleActionReport,handleActionSettings:handleActionSettings,handleSelect:handleSelect,handInfo:handInfo,handleClickCard:handleClickCard,useNewComponents:useNewComponents=true,enableSelection:enableSelection=true,enableDragDrop:enableDragDrop=true}){const{entityId:entityId,labelOrName:labelOrName,deviceIdentifier:deviceIdentifier,entityType:entityType,deviceType:deviceType,slaveId:slaveId,ingestionId:ingestionId,val:val,centralId:centralId,updatedIdentifiers:updatedIdentifiers={},perc:perc=0,deviceStatus:deviceStatus,centralName:centralName,connectionStatusTime:connectionStatusTime,timeVal:timeVal,customerName:customerName,waterLevel:waterLevel,waterPercentage:waterPercentage,temperature:temperature,temperatureMin:temperatureMin,temperatureMax:temperatureMax,temperatureStatus:temperatureStatus}=entityObject;const MyIOToast2=function(){let toastContainer=null;let toastTimeout=null;const TOAST_CSS=`\n #myio-global-toast-container {\n position: fixed;\n top: 25px;\n right: 25px;\n z-index: 99999;\n width: 320px;\n padding: 16px;\n border-radius: 8px;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;\n font-size: 15px;\n color: #fff;\n transform: translateX(120%);\n transition: transform 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);\n box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);\n border-left: 5px solid transparent;\n display: flex;\n align-items: center;\n }\n #myio-global-toast-container.show {\n transform: translateX(0);\n }\n #myio-global-toast-container.warning {\n background-color: #ff9800; /* Laranja para alerta */\n border-color: #f57c00;\n }\n #myio-global-toast-container.error {\n background-color: #d32f2f; /* Vermelho para erro */\n border-color: #b71c1c;\n }\n #myio-global-toast-container::before {\n content: '⚠️'; /* Ícone de alerta */\n margin-right: 12px;\n font-size: 20px;\n }\n #myio-global-toast-container.error::before {\n content: '🚫'; /* Ícone de erro */\n }\n `;function createToastElement(){if(document.getElementById("myio-global-toast-container")){toastContainer=document.getElementById("myio-global-toast-container");return}const style=document.createElement("style");style.id="myio-global-toast-styles";style.textContent=TOAST_CSS;document.head.appendChild(style);toastContainer=document.createElement("div");toastContainer.id="myio-global-toast-container";document.body.appendChild(toastContainer)}function show(message,type="warning",duration=3500){if(!toastContainer){createToastElement()}clearTimeout(toastTimeout);toastContainer.textContent=message;toastContainer.className="";toastContainer.classList.add(type);setTimeout(()=>{toastContainer.classList.add("show")},10);toastTimeout=setTimeout(()=>{toastContainer.classList.remove("show")},duration)}if(document.readyState==="loading"){document.addEventListener("DOMContentLoaded",createToastElement)}else{createToastElement()}return{show:show}}();if(handInfo){console.warn("[template-card-v5] handInfo parameter is deprecated. Info functionality has been moved to settings modal.")}if(!useNewComponents){console.warn("[template-card-v5] useNewComponents=false is not recommended. Consider using template-card-v2 directly.")}const connectionStatus=mapDeviceToConnectionStatus(deviceStatus);const isOffline=isDeviceOffline(deviceStatus);const shouldFlashIcon2=shouldFlashIcon(deviceStatus);const icon=getDeviceStatusIcon(deviceStatus,deviceType);getConnectionStatusIcon(connectionStatus);const mapDeviceTypeToIcon=deviceType2=>{const typeMap={COMPRESSOR:"energy",VENTILADOR:"energy",ESCADA_ROLANTE:"energy",ELEVADOR:"energy",MOTOR:"energy","3F_MEDIDOR":"energy",RELOGIO:"energy",ENTRADA:"energy",SUBESTACAO:"energy",HIDROMETRO:"water",CAIXA_DAGUA:"water",TANK:"water",TERMOSTATO:"temperature"};const normalizedType=deviceType2?.toUpperCase()||"";return typeMap[normalizedType]||"generic"};const getValueTypeFromDeviceType=deviceType2=>{const typeMap={COMPRESSOR:"ENERGY",VENTILADOR:"ENERGY",ESCADA_ROLANTE:"ENERGY",ELEVADOR:"ENERGY",MOTOR:"ENERGY","3F_MEDIDOR":"ENERGY",RELOGIO:"ENERGY",ENTRADA:"ENERGY",SUBESTACAO:"ENERGY",HIDROMETRO:"WATER",CAIXA_DAGUA:"WATER",TANK:"TANK",TERMOSTATO:"TEMPERATURE"};const normalizedType=deviceType2?.toUpperCase()||"";return typeMap[normalizedType]||"ENERGY"};const isEnergyDevice=deviceType2=>{const energyDeviceTypes=["COMPRESSOR","VENTILADOR","ESCADA_ROLANTE","ELEVADOR","MOTOR","3F_MEDIDOR","RELOGIO","ENTRADA","SUBESTACAO"];const normalizedType=deviceType2?.toUpperCase()||"";return energyDeviceTypes.includes(normalizedType)};const isTemperatureDevice=deviceType2=>{const temperatureDeviceTypes=["TERMOSTATO"];const normalizedType=deviceType2?.toUpperCase()||"";return temperatureDeviceTypes.includes(normalizedType)};const formatCardValue=(value,deviceType2)=>{const numValue=Number(value)||0;if(isEnergyDevice(deviceType2)){return formatEnergy(numValue)}else if(isTemperatureDevice(deviceType2)){const formattedTemp=numValue.toLocaleString("pt-BR",{minimumFractionDigits:1,maximumFractionDigits:1});return`${formattedTemp} °C`}else{const unit=determineUnit(deviceType2);const formattedValue=numValue.toLocaleString("pt-BR",{minimumFractionDigits:0,maximumFractionDigits:2});return`${formattedValue} ${unit}`}};const determineUnit=deviceType2=>{const valueType=getValueTypeFromDeviceType(deviceType2);switch(valueType){case"ENERGY":return"";case"WATER":return"m³";case"TANK":return"m.c.a";case"TEMPERATURE":return"°C";default:return""}};const cardEntity={id:entityId,name:labelOrName||"Dispositivo",icon:mapDeviceTypeToIcon(deviceType),group:deviceIdentifier||entityType||"Dispositivo",lastValue:Number(val)||0,unit:determineUnit(deviceType),status:mapDeviceStatusToCardStatus(deviceStatus),ingestionId:ingestionId||entityId};if(enableSelection&&exports.MyIOSelectionStore){exports.MyIOSelectionStore.registerEntity(cardEntity)}const container=document.createElement("div");container.className="myio-enhanced-card-container-v5";if(!document.getElementById("myio-enhanced-card-styles-v5")){const style=document.createElement("style");style.id="myio-enhanced-card-styles-v5";style.textContent=`\n .myio-enhanced-card-container-v5 {\n position: relative;\n width: 100%;\n height: 100%;\n }\n\n .myio-enhanced-card-container-v5 .myio-draggable-card {\n width: 100%;\n border-radius: 10px;\n padding: 8px 12px;\n background: #fff;\n box-shadow: 0 4px 10px rgba(0, 0, 0, .05);\n cursor: pointer;\n\n transition: transform .2s;\n box-sizing: border-box;\n overflow: hidden;\n min-height: 140px;\n display: flex;\n flex-direction: row;\n align-items: stretch;\n }\n\n .myio-enhanced-card-container-v5 .myio-draggable-card:hover {\n transform: scale(1.05);\n }\n\n .myio-enhanced-card-container-v5 .myio-draggable-card.selected {\n border: 2px solid #00e09e;\n box-shadow: 0 4px 12px rgba(0,224,158,0.2);\n background: linear-gradient(135deg, #f0fdf9, #ecfdf5);\n }\n\n .myio-enhanced-card-container-v5 .myio-draggable-card.offline {\n border: 2px solid #ff4d4f;\n animation: border-blink 1s infinite;\n }\n\n @keyframes border-blink {\n 0%, 100% { box-shadow: 0 0 8px rgba(255, 77, 79, 0.9); }\n 50% { box-shadow: 0 0 16px rgba(255, 0, 0, 0.6); }\n }\n\n .myio-enhanced-card-container-v5 .card-actions {\n flex-shrink: 0;\n height: 100%;\n box-shadow: 1px 0 2px rgba(0, 0, 0, .1);\n display: flex;\n flex-direction: column;\n padding: 0 4px;\n justify-content: space-evenly;\n align-items: center;\n gap: 8px;\n }\n\n .myio-enhanced-card-container-v5 .card-action {\n width: 32px;\n flex: 1;\n min-height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 6px;\n transition: all 0.2s ease;\n cursor: pointer;\n border: none;\n background: rgba(0, 0, 0, 0.05);\n }\n\n .myio-enhanced-card-container-v5 .card-action:hover {\n background: rgba(0, 224, 158, 0.1);\n transform: scale(1.1);\n }\n\n .myio-enhanced-card-container-v5 .card-action img {\n width: 20px;\n height: 20px;\n }\n\n .myio-enhanced-card-container-v5 .card-checkbox {\n width: 16px;\n height: 16px;\n cursor: pointer;\n }\n\n .myio-enhanced-card-container-v5 .card-body {\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: center;\n align-items: center;\n padding: 0 12px;\n text-align: center;\n }\n\n .myio-enhanced-card-container-v5 .card-title {\n font-weight: 700;\n font-size: 0.85rem;\n margin-bottom: 4px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n max-width: 100%;\n }\n\n .myio-enhanced-card-container-v5 .card-group {\n font-size: 0.7rem;\n color: #888;\n margin-bottom: 8px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n max-width: 100%;\n }\n\n .myio-enhanced-card-container-v5 .card-value {\n display: flex;\n align-items: center;\n gap: 4px;\n font-size: 0.9rem;\n font-weight: 700;\n color: #28a745;\n }\n\n .myio-enhanced-card-container-v5 .card-unit {\n font-size: 0.75rem;\n color: rgba(0, 0, 0, 0.6);\n }\n\n .myio-enhanced-card-container-v5 .card-percentage {\n font-size: 0.75rem;\n color: rgba(0, 0, 0, 0.45);\n margin-left: 4px;\n }\n\n .myio-enhanced-card-container-v5 .card-icon {\n width: 24px;\n height: 24px;\n margin-bottom: 8px;\n }\n\n .myio-enhanced-card-container-v5 .card-icon svg {\n width: 100%;\n height: 100%;\n fill: currentColor;\n }\n\n .myio-enhanced-card-container-v5 .card-status-indicator {\n position: absolute;\n top: 8px;\n right: 8px;\n width: 12px;\n height: 12px;\n border-radius: 50%;\n z-index: 10;\n }\n\n .myio-enhanced-card-container-v5 .card-status-ok {\n background: #28a745;\n }\n\n .myio-enhanced-card-container-v5 .card-status-alert {\n background: #ffc107;\n }\n\n .myio-enhanced-card-container-v5 .card-status-fail,\n .myio-enhanced-card-container-v5 .card-status-offline {\n background: #dc3545;\n }\n\n .myio-enhanced-card-container-v5 .card-status-unknown {\n background: #6c757d;\n }\n `;document.head.appendChild(style)}const getDeviceImageUrl=(deviceType2,percentage=0,options={})=>{const{tempStatus:tempStatus2,isOffline:isOffline2}=options;function normalizeString(str){if(typeof str!=="string"){str=""}return str.normalize("NFD").replace(/[\u0300-\u036f]/g,"").toUpperCase()}const nameType=normalizeString(deviceType2);if(nameType==="TERMOSTATO"){if(isOffline2){return"https://dashboard.myio-bas.com/api/images/public/Q4bE6zWz4pL3u5M3rjmMt2uSis6Xe52F"}if(tempStatus2==="above"){return"https://dashboard.myio-bas.com/api/images/public/S3IvpZRJvskqFrhoypKBCKKsLaKiqzJI"}else if(tempStatus2==="below"){return"https://dashboard.myio-bas.com/api/images/public/ctfORoxVGP2bB7VKeprJfIvNgmNjpaO4"}else{return"https://dashboard.myio-bas.com/api/images/public/rtCcq6kZZVCD7wgJywxEurRZwR8LA7Q7"}}if(nameType==="TANK"){if(percentage>=70){return"https://dashboard.myio-bas.com/api/images/public/3t6WVhMQJFsrKA8bSZmrngDsNPkZV7fq"}else if(percentage>=40){return"https://dashboard.myio-bas.com/api/images/public/4UBbShfXCVWR9wcw6IzVMNran4x1EW5n"}else if(percentage>=20){return"https://dashboard.myio-bas.com/api/images/public/aB9nX28F54fBBQs1Ht8jKUdYAMcq9QSm"}else{return"https://dashboard.myio-bas.com/api/images/public/qLdwhV4qw295poSCa7HinpnmXoN7dAPO"}}const deviceImages={MOTOR:"https://dashboard.myio-bas.com/api/images/public/Rge8Q3t0CP5PW8XyTn9bBK9aVP6uzSTT","3F_MEDIDOR":"https://dashboard.myio-bas.com/api/images/public/f9Ce4meybsdaAhAkUlAfy5ei3I4kcN4k",RELOGIO:"https://dashboard.myio-bas.com/api/images/public/ljHZostWg0G5AfKiyM8oZixWRIIGRASB",HIDROMETRO:"https://dashboard.myio-bas.com/api/images/public/aMQYFJbGHs9gQbQkMn6XseAlUZHanBR4",ENTRADA:"https://dashboard.myio-bas.com/api/images/public/TQHPFqiejMW6lOSVsb8Pi85WtC0QKOLU",CAIXA_DAGUA:"https://dashboard.myio-bas.com/api/images/public/3t6WVhMQJFsrKA8bSZmrngDsNPkZV7fq",ELEVADOR:"https://dashboard.myio-bas.com/api/images/public/rAjOvdsYJLGah6w6BABPJSD9znIyrkJX",ESCADA_ROLANTE:"https://dashboard.myio-bas.com/api/images/public/EJ997iB2HD1AYYUHwIloyQOOszeqb2jp"};const defaultImage="https://cdn-icons-png.flaticon.com/512/1178/1178428.png";return deviceImages[nameType]||defaultImage};const isTankDevice=deviceType==="TANK"||deviceType==="CAIXA_DAGUA";const isTermostatoDevice=deviceType?.toUpperCase()==="TERMOSTATO";const percentageForDisplay=isTankDevice?(waterPercentage||0)*100:perc;const calculateTempStatus=()=>{if(temperatureStatus)return temperatureStatus;const currentTemp=Number(val)||0;if(temperatureMin!==void 0&&temperatureMax!==void 0){if(currentTemp>temperatureMax)return"above";if(currentTemp<temperatureMin)return"below";return"ok"}return"ok"};const tempStatus=isTermostatoDevice?calculateTempStatus():null;const deviceImageUrl=getDeviceImageUrl(deviceType,percentageForDisplay,{tempStatus:tempStatus,isOffline:isOffline});const getTemperatureTooltip=()=>{if(!isTermostatoDevice)return"";const currentTemp=Number(val)||0;const hasRange=temperatureMin!==void 0&&temperatureMax!==void 0&&temperatureMin!==null&&temperatureMax!==null;if(isOffline){return"Dispositivo offline"}if(!hasRange){return`Temperatura atual: ${currentTemp.toFixed(1)}°C\nFaixa não configurada`}const rangeText=`Faixa ideal: ${temperatureMin}°C a ${temperatureMax}°C`;if(tempStatus==="above"){return`ACIMA da faixa ideal\nTemperatura atual: ${currentTemp.toFixed(1)}°C\n${rangeText}`}else if(tempStatus==="below"){return`ABAIXO da faixa ideal\nTemperatura atual: ${currentTemp.toFixed(1)}°C\n${rangeText}`}else{return`DENTRO da faixa ideal\nTemperatura atual: ${currentTemp.toFixed(1)}°C\n${rangeText}`}};const temperatureTooltip=getTemperatureTooltip();const cardHTML=`\n <div class="device-card-centered clickable ${cardEntity.status==="offline"?"offline":""}"\n data-entity-id="${entityId}"\n draggable="${enableDragDrop}"\n tabindex="0"\n role="article"\n aria-label="${cardEntity.name}, ${cardEntity.group}">\n\n <div class="device-card-inner">\n <div class="device-card-front">\n ${enableSelection&&typeof handleSelect==="function"?`<input type="checkbox" class="card-checkbox action-checker" aria-label="Select ${cardEntity.name}" style="position: absolute; top: 8px; right: 8px; z-index: 10;">`:""}\n\n <div style="display:flex;flex-direction:column;justify-content:center;align-items:center;height:100%; flex-grow: 1; min-width: 0; padding: 0 12px 0 20px; margin-left: 16px;">\n\n <div class="device-title-row" style="flex-direction: column; min-height: 38px; text-align: center; width: 100%;">\n <span class="device-title" title="${cardEntity.name}">\n ${cardEntity.name.length>18?cardEntity.name.slice(0,18)+"…":cardEntity.name}\n </span>\n ${deviceIdentifier?`\n <span class="device-subtitle" title="${deviceIdentifier}">\n ${deviceIdentifier}\n </span>\n `:""}\n </div>\n\n <img class="device-image" src="${deviceImageUrl}" alt="${deviceType}" ${isTermostatoDevice&&temperatureTooltip?`title="${temperatureTooltip}"`:""} style="${isTermostatoDevice?"cursor: help;":""}" />\n\n\n ${customerName&&String(customerName).trim()!==""?`\n <div class="myio-v5-shopping-badge-row">\n <span class="myio-v5-shopping-badge" title="${customerName}">\n <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="chip-icon"><path d="M4 22h16"/><path d="M7 22V4a2 2 0 0 1 2-2h6a2 2 0 0 1 2 2v18"/></svg>\n <span style="font-size: 0.65rem;">${customerName}</span>\n </span>\n </div>\n `:""}\n\n <div class="device-data-row">\n <div class="consumption-main">\n <span class="flash-icon ${shouldFlashIcon2?"flash":""}">\n ${icon}\n </span>\n <span class="consumption-value">${formatCardValue(cardEntity.lastValue,deviceType)}</span>\n ${!isTermostatoDevice?`<span class="device-title-percent">(${percentageForDisplay.toFixed(1)}%)</span>`:""}\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <div style="display: none;" class="connection-status-icon" data-conn="${connectionStatus}" data-state="${deviceStatus}" aria-label="${connectionStatus}"></div>\n\n </div>\n `;container.innerHTML=cardHTML;const enhancedCardElement=container.querySelector(".device-card-centered");if(!document.getElementById("myio-enhanced-card-layout-styles-v5")){const layoutStyle=document.createElement("style");layoutStyle.id="myio-enhanced-card-layout-styles-v5";layoutStyle.textContent=`\n /* ===== MYIO Card v5 — Optimized Spacing & Clean Piano Keys ===== */\n\n /* Card shell - UPDATED min-height: 126px → 114px */\n .device-card-centered.clickable {\n width: 90% !important;\n max-width: 280px !important;\n border-radius: 14px !important;\n padding: 18px !important;\n background: linear-gradient(145deg, #ffffff 0%, #f8fafc 100%) !important;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.08), 0 2px 8px rgba(0, 0, 0, 0.04) !important;\n cursor: pointer;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;\n border: 1px solid rgba(226, 232, 240, 0.8) !important;\n position: relative;\n overflow: hidden;\n backdrop-filter: blur(10px);\n min-height: 114px !important;\n margin: 0 auto;\n }\n\n .device-card-centered.clickable::before {\n content: '';\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n height: 3px;\n background: linear-gradient(90deg, #00e09e 0%, #00b4d8 50%, #7209b7 100%);\n opacity: 0;\n transition: opacity 0.3s ease;\n }\n\n .device-card-centered.clickable:hover {\n transform: translateY(-4px) scale(1.02) !important;\n box-shadow: 0 16px 48px rgba(0, 0, 0, 0.12), 0 8px 16px rgba(0, 0, 0, 0.08) !important;\n border-color: rgba(0, 224, 158, 0.3) !important;\n }\n\n .device-card-centered.clickable:hover::before {\n opacity: 1;\n }\n\n /* Selected / Offline */\n .device-card-centered.selected {\n border: 2px solid #00e09e !important;\n box-shadow: 0 12px 40px rgba(0, 224, 158, 0.25), 0 4px 12px rgba(0, 224, 158, 0.15) !important;\n background: linear-gradient(145deg, #f0fdf9 0%, #ecfdf5 50%, #f0fdf9 100%) !important;\n transform: translateY(-2px) !important;\n }\n\n .device-card-centered.selected::before {\n opacity: 1;\n background: linear-gradient(90deg, #00e09e 0%, #00d4aa 100%);\n }\n\n .device-card-centered.offline {\n border: 2px solid #ef4444 !important;\n background: linear-gradient(145deg, #fef2f2 0%, #fee2e2 50%, #fef2f2 100%) !important;\n animation: premium-offline-pulse 2s infinite !important;\n }\n\n .device-card-centered.offline::before {\n opacity: 1;\n background: linear-gradient(90deg, #ef4444 0%, #dc2626 100%);\n }\n\n @keyframes premium-offline-pulse {\n 0%, 100% {\n box-shadow: 0 8px 32px rgba(239, 68, 68, 0.15), 0 2px 8px rgba(239, 68, 68, 0.1);\n }\n 50% {\n box-shadow: 0 12px 40px rgba(239, 68, 68, 0.25), 0 4px 12px rgba(239, 68, 68, 0.2);\n }\n }\n\n /* Device image - UPDATED margin: 10px → 4px */\n .device-card-centered .device-image {\n max-height: 47px !important;\n width: auto;\n margin: 4px 0 !important;\n display: block;\n filter: drop-shadow(0 3px 6px rgba(0, 0, 0, 0.1));\n transition: all 0.3s ease;\n border-radius: 7px;\n }\n\n .device-card-centered:hover .device-image {\n filter: drop-shadow(0 5px 10px rgba(0, 0, 0, 0.15));\n transform: scale(1.05);\n }\n\n .device-card-centered .device-title-row {\n display: flex !important;\n flex-direction: column !important;\n align-items: center !important;\n justify-content: center !important;\n text-align: center !important;\n width: 100% !important;\n min-height: 38px !important;\n margin-bottom: 8px !important;\n }\n\n .device-card-centered .device-title {\n font-weight: 700 !important;\n font-size: 0.80rem !important;\n color: #1e293b !important;\n margin: 0 0 4px 0 !important;\n text-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n letter-spacing: -0.025em;\n display: block !important;\n width: 100% !important;\n text-align: center !important;\n }\n\n .device-card-centered .device-subtitle {\n font-size: 0.67rem !important;\n color: #64748b !important;\n font-weight: 500 !important;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n opacity: 0.8;\n display: block !important;\n width: 100% !important;\n text-align: center !important;\n margin: 0 !important;\n padding: 0 !important;\n }\n\n /* Value pill - COMPACT */\n .device-card-centered .consumption-main {\n background: linear-gradient(135deg, rgba(0, 224, 158, 0.1) 0%, rgba(0, 180, 216, 0.1) 100%);\n border-radius: 8px;\n padding: 4px 8px;\n margin-top: 5px;\n border: 1px solid rgba(0, 224, 158, 0.2);\n backdrop-filter: blur(10px);\n }\n\n .device-card-centered .consumption-value {\n font-weight: 700 !important;\n font-size: 0.75rem !important;\n color: #059669 !important;\n text-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n }\n\n .device-card-centered .device-title-percent {\n font-size: 0.65rem !important;\n color: #6b7280 !important;\n font-weight: 600 !important;\n margin-left: 4px;\n }\n\n .device-card-centered .flash-icon {\n font-size: 0.85rem !important;\n margin-right: 5px;\n transition: all 0.3s ease;\n }\n\n .device-card-centered:hover .flash-icon {\n transform: scale(1.1);\n }\n\n .device-card-centered .flash-icon.flash {\n animation: premium-flash 1.5s infinite;\n }\n\n @keyframes premium-flash {\n 0%, 100% {\n opacity: 1;\n transform: scale(1);\n filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.1));\n }\n 50% {\n opacity: 0.3;\n transform: scale(1.15);\n filter: drop-shadow(0 4px 8px rgba(239, 68, 68, 0.3));\n }\n }\n\n /* Checkbox */\n .device-card-centered .card-checkbox {\n width: 16px !important;\n height: 16px !important;\n cursor: pointer;\n background: rgba(255, 255, 255, 0.9) !important;\n border: 2px solid #e2e8f0 !important;\n border-radius: 5px !important;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n backdrop-filter: blur(10px);\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);\n position: relative;\n }\n\n .device-card-centered .card-checkbox:hover {\n border-color: #00e09e !important;\n box-shadow: 0 3px 6px rgba(0, 224, 158, 0.15);\n transform: scale(1.05);\n }\n\n .device-card-centered .card-checkbox:checked {\n background: linear-gradient(135deg, #00e09e 0%, #00d4aa 100%) !important;\n border-color: #00e09e !important;\n box-shadow: 0 3px 10px rgba(0, 224, 158, 0.3);\n }\n\n .device-card-centered .card-checkbox:checked::after {\n content: '✓';\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n color: white;\n font-size: 10px;\n font-weight: bold;\n text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);\n }\n\n /* Piano-Key Actions (3 BUTTONS - EVENLY DISTRIBUTED) */\n .device-card-centered .card-actions {\n position: absolute;\n left: 12px;\n top: 12px;\n bottom: 12px;\n padding: 0;\n display: flex;\n flex-direction: column;\n justify-content: space-evenly;\n gap: 0;\n border: 1px solid rgba(226, 232, 240, 0.9);\n border-radius: 8px;\n background: #fff;\n overflow: visible;\n box-shadow: none !important;\n z-index: 10;\n }\n\n .device-card-centered .card-action {\n width: 36px !important;\n flex: 1;\n min-height: 36px !important;\n border: 0;\n border-bottom: 1px solid rgba(226, 232, 240, 0.9);\n background: #fff !important;\n box-shadow: none !important;\n backdrop-filter: none !important;\n transform: translateZ(0);\n transition: transform 0.18s ease, box-shadow 0.18s ease;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n margin: 0;\n border-radius: 0;\n }\n\n .device-card-centered .card-action:first-child {\n border-top-left-radius: 7px;\n border-top-right-radius: 7px;\n }\n\n .device-card-centered .card-action:last-child {\n border-bottom: 0;\n border-bottom-left-radius: 7px;\n border-bottom-right-radius: 7px;\n }\n\n .device-card-centered .card-action img {\n width: 16px !important;\n height: 16px !important;\n filter: grayscale(0.2) brightness(0.85);\n transition: transform 0.18s ease, filter 0.18s ease;\n }\n\n /* Lift on interaction only */\n .device-card-centered .card-action:hover,\n .device-card-centered .card-action:focus-visible {\n transform: translateY(-2px) scale(1.05);\n box-shadow: 0 6px 14px rgba(16, 24, 40, 0.12), 0 2px 6px rgba(16, 24, 40, 0.08);\n outline: none;\n }\n\n .device-card-centered .card-action:hover img,\n .device-card-centered .card-action:focus-visible img {\n filter: grayscale(0) brightness(1);\n transform: scale(1.08);\n }\n\n /* Flat Status Indicator */\n .device-card-centered .connection-status-icon {\n position: absolute;\n bottom: 18px;\n right: 18px;\n width: 14px;\n height: 14px;\n border-radius: 50%;\n background: #22c55e;\n border: 1px solid rgba(0, 0, 0, 0.06);\n box-shadow: 0 3px 6px rgba(0, 0, 0, 0.15),\n 0 1px 3px rgba(0, 0, 0, 0.1),\n inset 0 -2px 4px rgba(0, 0, 0, 0.1),\n inset 0 2px 3px rgba(255, 255, 255, 0.4) !important;\n backdrop-filter: none !important;\n z-index: 5;\n transform: translateZ(0);\n transition: transform 0.2s ease, box-shadow 0.2s ease;\n }\n\n .device-card-centered .connection-status-icon:hover {\n transform: translateZ(2px) scale(1.05);\n box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2),\n 0 2px 4px rgba(0, 0, 0, 0.12),\n inset 0 -2px 5px rgba(0, 0, 0, 0.15),\n inset 0 2px 4px rgba(255, 255, 255, 0.5) !important;\n }\n\n /* Map colors by connection or device state */\n .device-card-centered .connection-status-icon[data-conn="offline"] {\n background: #94a3b8;\n }\n .device-card-centered .connection-status-icon[data-state="warning"] {\n background: #f59e0b;\n }\n .device-card-centered .connection-status-icon[data-state="danger"] {\n background: #ef4444;\n }\n .device-card-centered .connection-status-icon[data-state="no_info"] {\n background: #94a3b8;\n }\n .device-card-centered .connection-status-icon[data-state="maintenance"] {\n background: #0ea5e9;\n }\n\n .myio-v5-shopping-badge-row {\n width: 100%;\n text-align: center;\n margin-bottom: 8px; /* Espaço ANTES da imagem */\n }\n \n /* Este é o estilo da badge, copiado do v1 */\n .myio-v5-shopping-badge {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n \n /* Estilo do v1 (head-office) para replicar o visual */\n background-color: #EBF4FF; /* Fundo azul bem claro */\n border: 1px solid #BEE3F8; /* Borda azul clara */\n color: #2C5282; /* Texto azul escuro */\n \n border-radius: 8px;\n padding: 4px 10px;\n font-size: 11px; /* Um pouco menor para caber no v5 */\n font-weight: 500;\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n\n /* Garantir que não quebre o layout */\n max-width: 90%;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n \n .myio-v5-shopping-badge .chip-icon {\n width: 12px;\n height: 12px;\n stroke: currentColor;\n opacity: 0.7;\n flex-shrink: 0;\n }\n\n /* Responsive / Dark mode */\n @media (max-width: 768px) {\n .device-card-centered.clickable {\n padding: 16px !important;\n border-radius: 12px !important;\n min-height: 110px !important;\n }\n\n .device-card-centered .device-image {\n max-height: 44px !important;\n margin: 3px 0 !important;\n }\n\n .device-card-centered .card-action {\n width: 36px !important;\n height: 36px !important;\n }\n }\n\n @media (prefers-color-scheme: dark) {\n .device-card-centered.clickable {\n background: linear-gradient(145deg, #1e293b 0%, #334155 100%) !important;\n border-color: rgba(71, 85, 105, 0.8) !important;\n color: #f1f5f9 !important;\n }\n\n .myio-v5-shopping-badge {\n background-color: #334155; /* Fundo mais escuro */\n border-color: #475569;\n color: #cbd5e1; /* Texto mais claro */\n }\n\n .device-card-centered .device-title {\n color: #f1f5f9 !important;\n }\n\n .device-card-centered .device-subtitle {\n color: #94a3b8 !important;\n }\n\n .device-card-centered .card-actions {\n border-color: rgba(71, 85, 105, 0.8);\n background: #1e293b;\n }\n\n .device-card-centered .card-action {\n background: #1e293b !important;\n border-bottom: 1px solid rgba(71, 85, 105, 0.8);\n }\n }\n `;document.head.appendChild(layoutStyle)}const actionsContainer=document.createElement("div");actionsContainer.className="card-actions";if(typeof handleActionDashboard==="function"){const dashboardBtn=document.createElement("button");dashboardBtn.className="card-action action-dashboard";dashboardBtn.title="Dashboard";dashboardBtn.innerHTML='<img src="https://dashboard.myio-bas.com/api/images/public/TAVXE0sTbCZylwGsMF9lIWdllBB3iFtS"/>';dashboardBtn.addEventListener("click",e=>{e.stopPropagation();handleActionDashboard(entityObject)});actionsContainer.appendChild(dashboardBtn)}if(typeof handleActionReport==="function"){const reportBtn=document.createElement("button");reportBtn.className="card-action action-report";reportBtn.title="Relatório";reportBtn.innerHTML='<img src="https://dashboard.myio-bas.com/api/images/public/d9XuQwMYQCG2otvtNSlqUHGavGaSSpz4"/>';reportBtn.addEventListener("click",e=>{e.stopPropagation();handleActionReport(entityObject)});actionsContainer.appendChild(reportBtn)}if(typeof handleActionSettings==="function"){const settingsBtn=document.createElement("button");settingsBtn.className="card-action action-settings";settingsBtn.title="Configurações";settingsBtn.innerHTML='<img src="https://dashboard.myio-bas.com/api/images/public/5n9tze6vED2uwIs5VvJxGzNNZ9eV4yoz"/>';settingsBtn.addEventListener("click",e=>{e.stopPropagation();handleActionSettings(entityObject,{includeInfo:true,connectionData:{centralName:centralName,connectionStatusTime:connectionStatusTime,timeVal:timeVal,deviceStatus:deviceStatus}})});actionsContainer.appendChild(settingsBtn)}if(enhancedCardElement&&actionsContainer.children.length>0){enhancedCardElement.insertBefore(actionsContainer,enhancedCardElement.firstChild)}if(enableSelection&&exports.MyIOSelectionStore){const checkbox=enhancedCardElement.querySelector(".card-checkbox");if(checkbox){checkbox.addEventListener("change",e=>{e.stopPropagation();if(e.target.checked){const currentCount=exports.MyIOSelectionStore.getSelectedEntities().length;const selectedEntities=exports.MyIOSelectionStore.getSelectedEntities();console.log("selectedEntities",selectedEntities);const isTryingToAdd=e.target.checked;if(isTryingToAdd&¤tCount>=6){e.preventDefault();e.target.checked=false;MyIOToast2.show("Não é possível selecionar mais de 6 itens.","warning");return}exports.MyIOSelectionStore.add(entityId)}else{exports.MyIOSelectionStore.remove(entityId)}})}const handleSelectionChange=data=>{const isSelected=data.selectedIds.includes(entityId);if(checkbox){checkbox.checked=isSelected}enhancedCardElement.classList.toggle("selected",isSelected)};exports.MyIOSelectionStore.on("selection:change",handleSelectionChange);const isInitiallySelected=exports.MyIOSelectionStore.isSelected(entityId);if(checkbox){checkbox.checked=isInitiallySelected}enhancedCardElement.classList.toggle("selected",isInitiallySelected);container._cleanup=()=>{exports.MyIOSelectionStore.off("selection:change",handleSelectionChange)}}if(enableDragDrop){enhancedCardElement.addEventListener("dragstart",e=>{e.dataTransfer.setData("text/myio-id",entityId);e.dataTransfer.setData("application/json",JSON.stringify(entityObject));e.dataTransfer.setData("text/myio-name",entityObject.labelOrName);e.dataTransfer.effectAllowed="copy";if(exports.MyIOSelectionStore){exports.MyIOSelectionStore.startDrag(entityId)}});enhancedCardElement.addEventListener("dragend",()=>{})}if(typeof handleClickCard==="function"){enhancedCardElement.addEventListener("click",e=>{if(!e.target.closest(".card-action")&&!e.target.closest(".card-checkbox")){handleClickCard(entityObject)}})}const jQueryLikeObject={get:index=>index===0?container:void 0,0:container,length:1,find:selector=>{const found=container.querySelector(selector);return{get:index=>index===0?found:void 0,0:found,length:found?1:0,on:(event,handler)=>{if(found)found.addEventListener(event,handler);return this}}},on:(event,handler)=>{container.addEventListener(event,handler);return this},addClass:className=>{container.classList.add(className);return this},removeClass:className=>{container.classList.remove(className);return this},destroy:()=>{if(container._cleanup){container._cleanup()}}};return jQueryLikeObject}function renderCardComponent3(options){return renderCardComponentV5(options)}var MyIOChartModalClass=class{constructor(){this.isOpen=false;this.modalElement=null;this.chartInstance=null;this.currentData=null;this.chartConfig={type:"line",timeRange:7,maxEntities:20};this._init()}async open(data){if(!data||!data.entities||data.entities.length===0){console.warn("ChartModal: No data provided for comparison");return}if(data.count>this.chartConfig.maxEntities){this._showTooManyEntitiesWarning(data);return}this.currentData=data;this.isOpen=true;await this._createModal();await this._loadChartJS();await this._renderChart();this._trackEvent("chart_modal.open",{entityCount:data.count,chartType:this.chartConfig.type,timeRange:this.chartConfig.timeRange})}close(){if(!this.isOpen)return;this.isOpen=false;if(this.chartInstance){this.chartInstance.destroy();this.chartInstance=null}if(this.modalElement){this.modalElement.remove();this.modalElement=null}if(typeof document!=="undefined"){document.body.focus()}this._trackEvent("chart_modal.close",{entityCount:this.currentData?.count||0})}exportCsv(){if(!this.currentData)return;const timestamp=(new Date).toISOString().slice(0,19).replace(/:/g,"-");const filename=`comparativo_${timestamp}.csv`;const csvData=this._generateCsvData();this._downloadFile(csvData,filename,"text/csv");this._trackEvent("chart_modal.export",{format:"csv",entityCount:this.currentData.count})}exportPng(){if(!this.chartInstance)return;const timestamp=(new Date).toISOString().slice(0,19).replace(/:/g,"-");const filename=`grafico_comparativo_${timestamp}.png`;const canvas=this.chartInstance.canvas;const url=canvas.toDataURL("image/png");this._downloadFile(url,filename,"image/png",true);this._trackEvent("chart_modal.export",{format:"png",entityCount:this.currentData.count})}exportPdf(){this._showNotImplementedNotice("PDF export");this._trackEvent("chart_modal.export",{format:"pdf",entityCount:this.currentData?.count||0,status:"not_implemented"})}_init(){const store=this._getSelectionStore();if(store){store.on("comparison:open",data=>this.open(data));store.on("comparison:too_many",data=>this._showTooManyEntitiesWarning(data))}}async _createModal(){if(typeof document==="undefined")return;const existing=document.getElementById("myio-chart-modal");if(existing){existing.remove()}const modal=document.createElement("div");modal.id="myio-chart-modal";modal.className="chart-modal-overlay";modal.setAttribute("role","dialog");modal.setAttribute("aria-modal","true");modal.setAttribute("aria-labelledby","chart-modal-title");modal.innerHTML=this._generateModalHTML();document.body.appendChild(modal);this.modalElement=modal;this._attachModalEventListeners();this._trapFocus();this._announceToScreenReader("Chart comparison modal opened")}_generateModalHTML(){const{entities:entities,totals:totals,count:count}=this.currentData;return`\n <div class="chart-modal-container" \n style="\n background: rgba(20, 20, 20, 0.95);\n backdrop-filter: blur(10px);\n border: 1px solid rgba(255, 255, 255, 0.08);\n border-radius: 16px;\n padding: 20px;\n max-width: 900px;\n margin: auto;\n color: #fff;\n font-family: Arial, sans-serif;\n box-shadow: 0 8px 30px rgba(0,0,0,0.5);\n ">\n \n <div class="chart-modal-header" \n style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">\n <h2 id="chart-modal-title" \n style="font-size: 20px; font-weight: bold; margin: 0; color: #fff;">\n Comparativo de Dispositivos (${count} selecionados)\n </h2>\n <button class="chart-modal-close" aria-label="Fechar modal"\n style="background: none; border: none; color: #fff; font-size: 24px; cursor: pointer; padding: 5px; transition: 0.3s;">\n ×\n </button>\n </div>\n \n <div class="chart-modal-controls" \n style="display: flex; gap: 20px; flex-wrap: wrap; margin-bottom: 20px;">\n \n <div class="chart-type-controls" style="flex: 1; min-width: 180px;">\n <label style="display: block; margin-bottom: 6px; color: rgba(255,255,255,0.8); font-size: 14px;">\n Tipo de Gráfico:\n </label>\n <select class="chart-type-select" aria-label="Selecionar tipo de gráfico"\n style="width: 100%; padding: 8px 12px; border-radius: 8px; border: 1px solid rgba(255,255,255,0.08); \n background: rgba(255,255,255,0.08); color: #fff; outline: none; cursor: pointer;">\n <option value="line" ${this.chartConfig.type==="line"?"selected":""}>Linha</option>\n <option value="bar" ${this.chartConfig.type==="bar"?"selected":""}>Barras</option>\n </select>\n </div>\n \n <div class="chart-range-controls" style="flex: 1; min-width: 180px;">\n <label style="display: block; margin-bottom: 6px; color: rgba(255,255,255,0.8); font-size: 14px;">\n Período:\n </label>\n <select class="chart-range-select" aria-label="Selecionar período"\n style="width: 100%; padding: 8px 12px; border-radius: 8px; border: 1px solid rgba(255,255,255,0.08); \n background: rgba(255,255,255,0.08); color: #fff; outline: none; cursor: pointer;">\n <option value="7" ${this.chartConfig.timeRange===7?"selected":""}>7 dias</option>\n <option value="14" ${this.chartConfig.timeRange===14?"selected":""}>14 dias</option>\n <option value="30" ${this.chartConfig.timeRange===30?"selected":""}>30 dias</option>\n </select>\n </div>\n \n <div class="chart-export-controls" style="display: flex; gap: 10px; align-items: end;">\n <button class="export-csv-btn"\n style="padding: 8px 14px; border-radius: 8px; border: none; cursor: pointer; \n background: rgba(255,255,255,0.08); color: #fff; transition: 0.3s;">\n Exportar CSV\n </button>\n <button class="export-png-btn"\n style="padding: 8px 14px; border-radius: 8px; border: none; cursor: pointer; \n background: rgba(255,255,255,0.08); color: #fff; transition: 0.3s;">\n Exportar PNG\n </button>\n <button class="export-pdf-btn"\n style="padding: 8px 14px; border-radius: 8px; border: none; cursor: pointer; \n background: rgba(255,255,255,0.08); color: #fff; transition: 0.3s;">\n Exportar PDF\n </button>\n </div>\n </div>\n \n <div class="chart-modal-body" style="display: flex; flex-direction: column; gap: 20px;">\n <div class="chart-container" \n style="background: rgba(255,255,255,0.05); border-radius: 12px; padding: 15px;">\n <canvas id="comparison-chart" aria-label="Gráfico comparativo de dispositivos"></canvas>\n </div>\n \n <div class="chart-summary" \n style="background: rgba(255,255,255,0.05); border-radius: 12px; padding: 15px;">\n <h3 style="margin: 0 0 12px; font-size: 16px; font-weight: bold;">Resumo da Seleção</h3>\n <div class="summary-grid" \n style="display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px;">\n ${this._generateSummaryHTML(totals)}\n </div>\n </div>\n </div>\n \n <div class="chart-modal-footer" \n style="display: flex; justify-content: flex-end; margin-top: 20px;">\n <button class="chart-modal-close-btn"\n style="padding: 10px 20px; border-radius: 8px; border: none; cursor: pointer; \n background: rgba(255,255,255,0.08); color: #fff; font-weight: bold; transition: 0.3s;">\n Fechar\n </button>\n </div>\n </div>\n `}_generateSummaryHTML(totals){const items=[];if(totals.energyKwh>0){items.push(`<div class="summary-item" \n style="background: rgba(255,255,255,0.08); padding: 10px; border-radius: 8px;">\n <span class="summary-label" style="display: block; font-size: 13px; color: rgba(255,255,255,0.7);">Energia Total:</span>\n <span class="summary-value" style="font-weight: bold; font-size: 14px;">${this._formatNumber(totals.energyKwh)} kWh</span>\n </div>`)}if(totals.waterM3>0){items.push(`<div class="summary-item" \n style="background: rgba(255,255,255,0.08); padding: 10px; border-radius: 8px;">\n <span class="summary-label" style="display: block; font-size: 13px; color: rgba(255,255,255,0.7);">Água Total:</span>\n <span class="summary-value" style="font-weight: bold; font-size: 14px;">${this._formatNumber(totals.waterM3)} m³</span>\n </div>`)}if(totals.tempC>0){items.push(`<div class="summary-item" \n style="background: rgba(255,255,255,0.08); padding: 10px; border-radius: 8px;">\n <span class="summary-label" style="display: block; font-size: 13px; color: rgba(255,255,255,0.7);">Temperatura Média:</span>\n <span class="summary-value" style="font-weight: bold; font-size: 14px;">${this._formatNumber(totals.tempC/totals.count)} °C</span>\n </div>`)}items.push(`<div class="summary-item" \n style="background: rgba(255,255,255,0.08); padding: 10px; border-radius: 8px;">\n <span class="summary-label" style="display: block; font-size: 13px; color: rgba(255,255,255,0.7);">Dispositivos:</span>\n <span class="summary-value" style="font-weight: bold; font-size: 14px;">${totals.count}</span>\n </div>`);return items.join("")}_generateSummaryHTML(totals){const items=[];if(totals.energyKwh>0){items.push(`<div class="summary-item">\n <span class="summary-label">Energia Total:</span>\n <span class="summary-value">${this._formatNumber(totals.energyKwh)} kWh</span>\n </div>`)}if(totals.waterM3>0){items.push(`<div class="summary-item">\n <span class="summary-label">Água Total:</span>\n <span class="summary-value">${this._formatNumber(totals.waterM3)} m³</span>\n </div>`)}if(totals.tempC>0){items.push(`<div class="summary-item">\n <span class="summary-label">Temperatura Média:</span>\n <span class="summary-value">${this._formatNumber(totals.tempC/totals.count)} °C</span>\n </div>`)}items.push(`<div class="summary-item">\n <span class="summary-label">Dispositivos:</span>\n <span class="summary-value">${totals.count}</span>\n </div>`);return items.join("")}_attachModalEventListeners(){if(!this.modalElement)return;const closeButtons=this.modalElement.querySelectorAll(".chart-modal-close, .chart-modal-close-btn");closeButtons.forEach(btn=>{btn.addEventListener("click",()=>this.close())});const typeSelect=this.modalElement.querySelector(".chart-type-select");if(typeSelect){typeSelect.addEventListener("change",e=>{this.chartConfig.type=e.target.value;this._renderChart();this._trackEvent("chart_modal.type_change",{newType:e.target.value,entityCount:this.currentData.count})})}const rangeSelect=this.modalElement.querySelector(".chart-range-select");if(rangeSelect){rangeSelect.addEventListener("change",e=>{this.chartConfig.timeRange=parseInt(e.target.value);this._renderChart();this._trackEvent("chart_modal.range_change",{newRange:e.target.value,entityCount:this.currentData.count})})}const csvBtn=this.modalElement.querySelector(".export-csv-btn");const pngBtn=this.modalElement.querySelector(".export-png-btn");const pdfBtn=this.modalElement.querySelector(".export-pdf-btn");if(csvBtn)csvBtn.addEventListener("click",()=>this.exportCsv());if(pngBtn)pngBtn.addEventListener("click",()=>this.exportPng());if(pdfBtn)pdfBtn.addEventListener("click",()=>this.exportPdf());this.modalElement.addEventListener("keydown",e=>{if(e.key==="Escape"){this.close()}});this.modalElement.addEventListener("click",e=>{if(e.target===this.modalElement){this.close()}})}async _loadChartJS(){if(typeof globalThis!=="undefined"&&globalThis.Chart){return}return new Promise((resolve,reject)=>{if(typeof document==="undefined"){resolve();return}const script=document.createElement("script");script.src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.js";script.onload=resolve;script.onerror=reject;document.head.appendChild(script)})}async _renderChart(){if(typeof globalThis==="undefined"||!globalThis.Chart){console.error("Chart.js not loaded");return}const canvas=this.modalElement?.querySelector("#comparison-chart");if(!canvas)return;if(this.chartInstance){this.chartInstance.destroy()}const store=this._getSelectionStore();const entityIds=this.currentData.entities.map(e=>e.id);const endDate=new Date;const startDate=new Date;startDate.setDate(startDate.getDate()-this.chartConfig.timeRange);let timeSeriesData={};if(store&&store.getTimeSeriesData){timeSeriesData=await store.getTimeSeriesData(entityIds,startDate,endDate)}const chartData=this._prepareChartData(timeSeriesData);this.chartInstance=new globalThis.Chart(canvas,{type:this.chartConfig.type,data:chartData,options:this._getChartOptions()})}_prepareChartData(timeSeriesData){const datasets=[];const colors=["#FF6384","#36A2EB","#FFCE56","#4BC0C0","#9966FF","#FF9F40","#FF6384","#C9CBCF","#4BC0C0","#FF6384"];this.currentData.entities.forEach((entity,index)=>{const data=timeSeriesData[entity.id]||[];const color=colors[index%colors.length];datasets.push({label:entity.name,data:data.map(point=>({x:point.timestamp,y:point.value})),borderColor:color,backgroundColor:color+"20",fill:this.chartConfig.type==="line"?false:true})});return{datasets:datasets}}_getChartOptions(){return{responsive:true,maintainAspectRatio:false,scales:{x:{type:"time",time:{unit:"day"},title:{display:true,text:"Data"}},y:{title:{display:true,text:"Valor"}}},plugins:{title:{display:true,text:`Comparativo - ${this.chartConfig.timeRange} dias`},legend:{display:true,position:"top"},tooltip:{mode:"index",intersect:false}},interaction:{mode:"nearest",axis:"x",intersect:false}}}_generateCsvData(){const headers=["Data","Dispositivo","Valor","Unidade"];const rows=[headers];this.currentData.entities.forEach(entity=>{const today=new Date;for(let i=0;i<this.chartConfig.timeRange;i++){const date=new Date(today);date.setDate(date.getDate()-i);const value=Math.random()*100+50;rows.push([date.toLocaleDateString("pt-BR"),entity.name,this._formatNumber(value),entity.unit||""])}});return rows.map(row=>row.join(";")).join("\n")}_downloadFile(data,filename,mimeType,isDataUrl=false){if(typeof document==="undefined")return;const link=document.createElement("a");if(isDataUrl){link.href=data}else{const blob=new Blob([data],{type:mimeType});link.href=URL.createObjectURL(blob)}link.download=filename;document.body.appendChild(link);link.click();document.body.removeChild(link);if(!isDataUrl){URL.revokeObjectURL(link.href)}}_showTooManyEntitiesWarning(data){if(typeof document==="undefined")return;const message=`Muitos dispositivos selecionados (${data.count}). Máximo permitido: ${this.chartConfig.maxEntities}.`;if(typeof globalThis!=="undefined"&&globalThis.alert){globalThis.alert(message)}this._announceToScreenReader(message)}_showNotImplementedNotice(feature){const message=`${feature} será implementado em uma versão futura.`;if(typeof globalThis!=="undefined"&&globalThis.alert){globalThis.alert(message)}this._announceToScreenReader(message)}_trapFocus(){if(!this.modalElement||typeof document==="undefined")return;const focusableElements=this.modalElement.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');const firstElement=focusableElements[0];const lastElement=focusableElements[focusableElements.length-1];if(firstElement){firstElement.focus()}this.modalElement.addEventListener("keydown",e=>{if(e.key==="Tab"&&typeof document!=="undefined"){if(e.shiftKey){if(document.activeElement===firstElement){e.preventDefault();lastElement?.focus()}}else{if(document.activeElement===lastElement){e.preventDefault();firstElement?.focus()}}}})}_getSelectionStore(){if(typeof globalThis!=="undefined"&&globalThis.window?.MyIOSelectionStore){return globalThis.window.MyIOSelectionStore}return null}_trackEvent(eventName,payload={}){const store=this._getSelectionStore();if(store&&store.trackEvent){store.trackEvent(eventName,payload)}}_announceToScreenReader(message){const store=this._getSelectionStore();if(store&&store.announceToScreenReader){store.announceToScreenReader(message)}}_formatNumber(value){if(typeof value!=="number"||isNaN(value))return"0";return new Intl.NumberFormat("pt-BR",{minimumFractionDigits:0,maximumFractionDigits:2}).format(value)}};var MyIOChartModal=new MyIOChartModalClass;if(typeof globalThis!=="undefined"&&typeof globalThis.window!=="undefined"){globalThis.window.MyIOChartModal=MyIOChartModal}if(typeof module!=="undefined"&&module.exports){module.exports={MyIOChartModal:MyIOChartModal}}var MyIOToast=function(){let toastContainer=null;let toastTimeout=null;const TOAST_CSS=`\n #myio-global-toast-container {\n position: fixed;\n top: 25px;\n right: 25px;\n z-index: 99999;\n min-width: 320px;\n max-width: 480px;\n padding: 16px 20px;\n background-color: #323232;\n color: white;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;\n font-size: 14px;\n border-radius: 8px;\n transform: translateX(400px);\n transition: transform 0.3s ease-in-out, opacity 0.3s ease-in-out;\n opacity: 0;\n box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);\n border-left: 5px solid transparent;\n display: flex;\n align-items: center;\n word-wrap: break-word;\n pointer-events: auto;\n }\n\n #myio-global-toast-container.show {\n transform: translateX(0);\n opacity: 1;\n }\n\n #myio-global-toast-container.info {\n background-color: #2196f3;\n border-color: #1976d2;\n }\n\n #myio-global-toast-container.success {\n background-color: #4caf50;\n border-color: #388e3c;\n }\n\n #myio-global-toast-container.warning {\n background-color: #ff9800;\n border-color: #f57c00;\n }\n\n #myio-global-toast-container.error {\n background-color: #d32f2f;\n border-color: #b71c1c;\n }\n\n #myio-global-toast-container::before {\n content: 'ℹ️';\n margin-right: 12px;\n font-size: 20px;\n flex-shrink: 0;\n }\n\n #myio-global-toast-container.success::before {\n content: '✅';\n }\n\n #myio-global-toast-container.warning::before {\n content: '⚠️';\n }\n\n #myio-global-toast-container.error::before {\n content: '🚫';\n }\n\n @media (max-width: 480px) {\n #myio-global-toast-container {\n top: 10px;\n right: 10px;\n left: 10px;\n min-width: auto;\n max-width: none;\n }\n }\n `;function createToastElement(){if(document.getElementById("myio-global-toast-container")){toastContainer=document.getElementById("myio-global-toast-container");return}if(!document.getElementById("myio-global-toast-styles")){const style=document.createElement("style");style.id="myio-global-toast-styles";style.textContent=TOAST_CSS;document.head.appendChild(style)}toastContainer=document.createElement("div");toastContainer.id="myio-global-toast-container";document.body.appendChild(toastContainer)}function show(message,type="info",duration=3500){if(!toastContainer){createToastElement()}clearTimeout(toastTimeout);const validTypes=["info","success","warning","error"];if(!validTypes.includes(type)){console.warn(`[MyIOToast] Invalid type "${type}". Using "info" instead.`);type="info"}toastContainer.textContent=message;toastContainer.className="";toastContainer.classList.add(type);setTimeout(()=>{toastContainer.classList.add("show")},10);if(duration>0){toastTimeout=setTimeout(()=>{hide()},duration)}return{hide:hide}}function hide(){if(toastContainer){toastContainer.classList.remove("show");clearTimeout(toastTimeout)}}function info(message,duration=3500){return show(message,"info",duration)}function success(message,duration=3500){return show(message,"success",duration)}function warning(message,duration=3500){return show(message,"warning",duration)}function error(message,duration=5e3){return show(message,"error",duration)}if(typeof window!=="undefined"){if(document.readyState==="loading"){document.addEventListener("DOMContentLoaded",createToastElement)}else{createToastElement()}}return{show:show,hide:hide,info:info,success:success,warning:warning,error:error}}();if(typeof module!=="undefined"&&module.exports){module.exports={MyIOToast:MyIOToast}}var TELEMETRY_CONFIG={voltage_a:{label:"Tensão Fase A",unit:"V",icon:"⚡",decimals:1},voltage_b:{label:"Tensão Fase B",unit:"V",icon:"⚡",decimals:1},voltage_c:{label:"Tensão Fase C",unit:"V",icon:"⚡",decimals:1},total_current:{label:"Corrente Total",unit:"A",icon:"🔌",decimals:2},current:{label:"Corrente",unit:"A",icon:"🔌",decimals:2},consumption:{label:"Potência",unit:"W",icon:"⚙️",decimals:0},power:{label:"Potência",unit:"W",icon:"⚙️",decimals:0},energy:{label:"Energia",unit:"kWh",icon:"📊",decimals:1},activePower:{label:"Potência Ativa",unit:"kW",icon:"⚙️",decimals:2},reactivePower:{label:"Potência Reativa",unit:"kVAr",icon:"🔄",decimals:2},apparentPower:{label:"Potência Aparente",unit:"kVA",icon:"📈",decimals:2},powerFactor:{label:"Fator de Potência",unit:"",icon:"📐",decimals:3},temperature:{label:"Temperatura",unit:"°C",icon:"🌡️",decimals:1}};var STRINGS={"pt-BR":{title:"Telemetrias em Tempo Real",close:"Fechar",pause:"Pausar",resume:"Retomar",export:"Exportar CSV",autoUpdate:"Atualização automática",lastUpdate:"Última atualização",noData:"Sem dados",loading:"Carregando...",error:"Erro ao carregar telemetrias",trend_up:"Aumentando",trend_down:"Diminuindo",trend_stable:"Estável"},"en-US":{title:"Real-Time Telemetry",close:"Close",pause:"Pause",resume:"Resume",export:"Export CSV",autoUpdate:"Auto-update",lastUpdate:"Last update",noData:"No data",loading:"Loading...",error:"Error loading telemetry",trend_up:"Increasing",trend_down:"Decreasing",trend_stable:"Stable"}};async function openRealTimeTelemetryModal(params){const{token:token,deviceId:deviceId,deviceLabel:deviceLabel="Dispositivo",telemetryKeys:telemetryKeys=["voltage_a","voltage_b","voltage_c","total_current","consumption"],refreshInterval:refreshInterval=8e3,historyPoints:historyPoints=50,onClose:onClose,locale:locale="pt-BR"}=params;const strings=STRINGS[locale]||STRINGS["pt-BR"];let refreshIntervalId=null;let isPaused=false;let telemetryHistory=new Map;let lastKnownValues=new Map;let chart=null;let selectedChartKey="consumption";const overlay=document.createElement("div");overlay.className="myio-realtime-telemetry-overlay";overlay.innerHTML=`\n <style>\n .myio-realtime-telemetry-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10000;\n padding: 20px;\n animation: fadeIn 0.2s ease;\n }\n\n @keyframes fadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n\n .myio-realtime-telemetry-container {\n background: #ffffff;\n border-radius: 12px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);\n max-width: 1200px;\n width: 100%;\n max-height: 90vh;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n animation: slideUp 0.3s ease;\n }\n\n @keyframes slideUp {\n from { transform: translateY(20px); opacity: 0; }\n to { transform: translateY(0); opacity: 1; }\n }\n\n .myio-realtime-telemetry-header {\n padding: 20px 24px;\n border-bottom: 1px solid #e0e0e0;\n display: flex;\n justify-content: space-between;\n align-items: center;\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n color: white;\n }\n\n .myio-realtime-telemetry-title {\n font-size: 20px;\n font-weight: 600;\n margin: 0;\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .myio-realtime-telemetry-close {\n background: rgba(255, 255, 255, 0.2);\n border: none;\n color: white;\n font-size: 24px;\n width: 32px;\n height: 32px;\n border-radius: 6px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.2s ease;\n }\n\n .myio-realtime-telemetry-close:hover {\n background: rgba(255, 255, 255, 0.3);\n transform: scale(1.1);\n }\n\n .myio-realtime-telemetry-body {\n padding: 24px;\n overflow-y: auto;\n flex: 1;\n }\n\n .myio-telemetry-cards-grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\n gap: 16px;\n margin-bottom: 24px;\n }\n\n .myio-telemetry-card {\n background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);\n border-radius: 12px;\n padding: 16px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n transition: all 0.3s ease;\n }\n\n .myio-telemetry-card:hover {\n transform: translateY(-4px);\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);\n }\n\n .myio-telemetry-card-header {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 12px;\n font-size: 14px;\n color: #555;\n font-weight: 500;\n }\n\n .myio-telemetry-card-icon {\n font-size: 20px;\n }\n\n .myio-telemetry-card-value {\n font-size: 28px;\n font-weight: 700;\n color: #2c3e50;\n margin-bottom: 4px;\n }\n\n .myio-telemetry-card-trend {\n font-size: 12px;\n color: #777;\n display: flex;\n align-items: center;\n gap: 4px;\n }\n\n .myio-telemetry-card-trend.up {\n color: #27ae60;\n }\n\n .myio-telemetry-card-trend.down {\n color: #e74c3c;\n }\n\n .myio-telemetry-chart-container {\n background: white;\n border-radius: 12px;\n padding: 20px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n margin-bottom: 20px;\n max-height: 520px;\n overflow: hidden;\n }\n\n .myio-telemetry-chart-title {\n font-size: 16px;\n font-weight: 600;\n margin: 0 0 16px 0;\n color: #2c3e50;\n }\n\n .myio-telemetry-chart {\n height: 300px;\n max-height: 450px;\n width: 100%;\n }\n\n .myio-telemetry-selector {\n padding: 8px 12px;\n border: 1px solid #ddd;\n border-radius: 6px;\n font-size: 14px;\n background: white;\n cursor: pointer;\n transition: border-color 0.2s;\n }\n\n .myio-telemetry-selector:hover {\n border-color: #667eea;\n }\n\n .myio-telemetry-selector:focus {\n outline: none;\n border-color: #667eea;\n box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);\n }\n\n .myio-realtime-telemetry-footer {\n padding: 16px 24px;\n border-top: 1px solid #e0e0e0;\n display: flex;\n justify-content: space-between;\n align-items: center;\n background: #f8f9fa;\n }\n\n .myio-telemetry-status {\n display: flex;\n align-items: center;\n gap: 12px;\n font-size: 14px;\n color: #555;\n }\n\n .myio-telemetry-status-indicator {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: #27ae60;\n animation: pulse 2s infinite;\n }\n\n .myio-telemetry-status-indicator.paused {\n background: #e74c3c;\n animation: none;\n }\n\n @keyframes pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.5; }\n }\n\n .myio-telemetry-actions {\n display: flex;\n gap: 12px;\n }\n\n .myio-telemetry-btn {\n padding: 8px 16px;\n border: none;\n border-radius: 6px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n display: flex;\n align-items: center;\n gap: 6px;\n }\n\n .myio-telemetry-btn-primary {\n background: #667eea;\n color: white;\n }\n\n .myio-telemetry-btn-primary:hover {\n background: #5568d3;\n transform: translateY(-1px);\n }\n\n .myio-telemetry-btn-secondary {\n background: #e0e0e0;\n color: #333;\n }\n\n .myio-telemetry-btn-secondary:hover {\n background: #d0d0d0;\n transform: translateY(-1px);\n }\n\n .myio-telemetry-loading {\n text-align: center;\n padding: 40px;\n color: #999;\n font-size: 16px;\n }\n\n .myio-telemetry-error {\n text-align: center;\n padding: 40px;\n color: #e74c3c;\n font-size: 16px;\n }\n </style>\n\n <div class="myio-realtime-telemetry-container">\n <div class="myio-realtime-telemetry-header">\n <h2 class="myio-realtime-telemetry-title">\n ⚡ ${strings.title} - ${deviceLabel}\n </h2>\n <button class="myio-realtime-telemetry-close" id="close-btn" title="${strings.close}">\n ×\n </button>\n </div>\n\n <div class="myio-realtime-telemetry-body">\n <div class="myio-telemetry-loading" id="loading-state">\n ${strings.loading}\n </div>\n\n <div id="telemetry-content" style="display: none;">\n <div class="myio-telemetry-cards-grid" id="telemetry-cards"></div>\n\n <div class="myio-telemetry-chart-container" id="chart-container" style="display: none;">\n <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px;">\n <h3 class="myio-telemetry-chart-title" id="chart-title" style="margin: 0;">Histórico de Telemetria</h3>\n <select id="chart-key-selector" class="myio-telemetry-selector">\n <option value="consumption">Potência</option>\n <option value="total_current">Corrente Total</option>\n <option value="voltage_a">Tensão Fase A</option>\n <option value="voltage_b">Tensão Fase B</option>\n <option value="voltage_c">Tensão Fase C</option>\n </select>\n </div>\n <canvas class="myio-telemetry-chart" id="telemetry-chart"></canvas>\n </div>\n </div>\n\n <div class="myio-telemetry-error" id="error-state" style="display: none;">\n ${strings.error}\n </div>\n </div>\n\n <div class="myio-realtime-telemetry-footer">\n <div class="myio-telemetry-status">\n <span class="myio-telemetry-status-indicator" id="status-indicator"></span>\n <span id="status-text">${strings.autoUpdate}: ON</span>\n <span>•</span>\n <span id="last-update-text">${strings.lastUpdate}: --:--:--</span>\n </div>\n\n <div class="myio-telemetry-actions">\n <button class="myio-telemetry-btn myio-telemetry-btn-secondary" id="pause-btn">\n <span id="pause-btn-icon">⏸️</span>\n <span id="pause-btn-text">${strings.pause}</span>\n </button>\n <button class="myio-telemetry-btn myio-telemetry-btn-primary" id="export-btn">\n ⬇️ ${strings.export}\n </button>\n </div>\n </div>\n </div>\n `;document.body.appendChild(overlay);const closeBtn=overlay.querySelector("#close-btn");const pauseBtn=overlay.querySelector("#pause-btn");const pauseBtnIcon=overlay.querySelector("#pause-btn-icon");const pauseBtnText=overlay.querySelector("#pause-btn-text");const exportBtn=overlay.querySelector("#export-btn");const loadingState=overlay.querySelector("#loading-state");const telemetryContent=overlay.querySelector("#telemetry-content");const errorState=overlay.querySelector("#error-state");const telemetryCards=overlay.querySelector("#telemetry-cards");const chartContainer=overlay.querySelector("#chart-container");const chartCanvas=overlay.querySelector("#telemetry-chart");const chartKeySelector=overlay.querySelector("#chart-key-selector");const statusIndicator=overlay.querySelector("#status-indicator");const statusText=overlay.querySelector("#status-text");const lastUpdateText=overlay.querySelector("#last-update-text");function closeModal(){if(refreshIntervalId){clearInterval(refreshIntervalId);refreshIntervalId=null}if(chart){chart.destroy();chart=null}overlay.remove();if(onClose){onClose()}}async function fetchLatestTelemetry(){const keys=telemetryKeys.join(",");const url=`/api/plugins/telemetry/DEVICE/${deviceId}/values/timeseries?keys=${keys}&limit=1&agg=NONE`;const response=await fetch(url,{headers:{"X-Authorization":`Bearer ${token}`}});if(!response.ok){throw new Error(`Failed to fetch telemetry: ${response.statusText}`)}return await response.json()}function processTelemetryData(data){const values=[];for(const key of telemetryKeys){const telemetryData=data[key];if(!telemetryData||telemetryData.length===0)continue;const latest=telemetryData[0];const config=TELEMETRY_CONFIG[key]||{label:key,unit:"",icon:"📊",decimals:2};let numValue=Number(latest.value)||0;if(key==="total_current"||key==="current"){numValue=numValue/1e3}const formatted=numValue.toFixed(config.decimals);values.push({key:key,value:numValue,timestamp:latest.ts,formatted:`${formatted} ${config.unit}`,unit:config.unit,icon:config.icon,label:config.label,trend:"stable"})}return values}function calculateTrend(key,currentValue){const history=telemetryHistory.get(key);if(!history||history.length<2)return"stable";const previousValue=history[history.length-2].y;const diff=currentValue-previousValue;const threshold=previousValue*.02;if(diff>threshold)return"up";if(diff<-threshold)return"down";return"stable"}function updateTelemetryCards(values){telemetryCards.innerHTML=values.map(tel=>{const trend=calculateTrend(tel.key,tel.value);const trendIcon=trend==="up"?"↗":trend==="down"?"↘":"→";const trendClass=trend;const trendLabel=strings[`trend_${trend}`]||"";return`\n <div class="myio-telemetry-card">\n <div class="myio-telemetry-card-header">\n <span class="myio-telemetry-card-icon">${tel.icon}</span>\n <span>${tel.label}</span>\n </div>\n <div class="myio-telemetry-card-value">${tel.formatted}</div>\n <div class="myio-telemetry-card-trend ${trendClass}">\n ${trendIcon} ${trendLabel}\n </div>\n </div>\n `}).join("")}function updateHistory(values){const now=Date.now();for(const tel of values){if(!telemetryHistory.has(tel.key)){telemetryHistory.set(tel.key,[])}const history=telemetryHistory.get(tel.key);history.push({x:now,y:tel.value});lastKnownValues.set(tel.key,tel.value);if(history.length>historyPoints){history.shift()}}for(const key of telemetryKeys){const receivedKeys=values.map(v=>v.key);if(!receivedKeys.includes(key)&&lastKnownValues.has(key)){if(!telemetryHistory.has(key)){telemetryHistory.set(key,[])}const history=telemetryHistory.get(key);const lastValue=lastKnownValues.get(key);history.push({x:now,y:lastValue});if(history.length>historyPoints){history.shift()}}}if(telemetryHistory.has(selectedChartKey)&&chart){const selectedHistory=telemetryHistory.get(selectedChartKey);chart.data.datasets[0].data=selectedHistory;chart.update("none")}}function getFormattedValue(key,value){const config=TELEMETRY_CONFIG[key];if(!config)return value.toFixed(2);if(key==="consumption"||key==="power"){if(value>=1e3){return`${(value/1e3).toFixed(2)} kW`}return`${value.toFixed(0)} W`}return`${value.toFixed(config.decimals)} ${config.unit}`}function initializeChart(){const Chart2=window.Chart;if(!Chart2){console.warn("[RealTimeTelemetry] Chart.js not loaded");return}chartContainer.style.display="block";const config=TELEMETRY_CONFIG[selectedChartKey]||{label:selectedChartKey,unit:""};chart=new Chart2(chartCanvas,{type:"line",data:{datasets:[{label:config.label,data:[],borderColor:"#667eea",backgroundColor:"rgba(102, 126, 234, 0.1)",borderWidth:2,fill:true,tension:.4,pointRadius:3,pointHoverRadius:5}]},options:{responsive:true,maintainAspectRatio:false,animation:false,plugins:{legend:{display:false},tooltip:{callbacks:{title:function(context){const timestamp=context[0].parsed.x;const date=new Date(timestamp);return date.toLocaleString(locale,{day:"2-digit",month:"2-digit",year:"numeric",hour:"2-digit",minute:"2-digit",second:"2-digit"})},label:function(context){const value=context.parsed.y;return getFormattedValue(selectedChartKey,value)}}}},scales:{x:{type:"linear",ticks:{maxRotation:45,minRotation:0,callback:function(value){const date=new Date(value);return date.toLocaleTimeString(locale,{hour:"2-digit",minute:"2-digit",second:"2-digit"})}},title:{display:true,text:"Hora"}},y:{beginAtZero:true,ticks:{callback:function(value){if(selectedChartKey==="consumption"||selectedChartKey==="power"){if(value>=1e3){return`${(value/1e3).toFixed(1)} kW`}return`${value} W`}return`${value} ${config.unit}`}},title:{display:true,text:selectedChartKey==="consumption"||selectedChartKey==="power"?"W":config.unit}}}}})}function updateChartKey(newKey){selectedChartKey=newKey;if(chart){chart.destroy();chart=null}initializeChart();if(telemetryHistory.has(selectedChartKey)){const selectedHistory=telemetryHistory.get(selectedChartKey);chart.data.datasets[0].data=selectedHistory;chart.update("none")}}async function refreshData(){try{const data=await fetchLatestTelemetry();const values=processTelemetryData(data);updateHistory(values);updateTelemetryCards(values);const now=new Date;lastUpdateText.textContent=`${strings.lastUpdate}: ${now.toLocaleTimeString(locale)}`;if(loadingState.style.display!=="none"){loadingState.style.display="none";telemetryContent.style.display="block";initializeChart()}}catch(error){console.error("[RealTimeTelemetry] Error fetching data:",error);errorState.style.display="block";loadingState.style.display="none";telemetryContent.style.display="none"}}function togglePause(){isPaused=!isPaused;if(isPaused){if(refreshIntervalId){clearInterval(refreshIntervalId);refreshIntervalId=null}pauseBtnIcon.textContent="▶️";pauseBtnText.textContent=strings.resume;statusIndicator.classList.add("paused");statusText.textContent=`${strings.autoUpdate}: OFF`}else{refreshIntervalId=window.setInterval(refreshData,refreshInterval);pauseBtnIcon.textContent="⏸️";pauseBtnText.textContent=strings.pause;statusIndicator.classList.remove("paused");statusText.textContent=`${strings.autoUpdate}: ON`}}function exportToCSV2(){const rows=[];rows.push("Timestamp,"+telemetryKeys.map(k=>TELEMETRY_CONFIG[k]?.label||k).join(","));let maxLength=0;for(const key of telemetryKeys){const history=telemetryHistory.get(key);if(history&&history.length>maxLength){maxLength=history.length}}for(let i=0;i<maxLength;i++){const row=[];let timestamp="";for(const key of telemetryKeys){const history=telemetryHistory.get(key);if(history&&history[i]){if(!timestamp){timestamp=new Date(history[i].x).toISOString()}row.push(history[i].y.toFixed(2))}else{row.push("")}}if(timestamp){rows.push(timestamp+","+row.join(","))}}const csv=rows.join("\n");const blob=new Blob([csv],{type:"text/csv"});const url=URL.createObjectURL(blob);const a=document.createElement("a");a.href=url;a.download=`telemetry_${deviceLabel}_${(new Date).toISOString()}.csv`;a.click();URL.revokeObjectURL(url)}closeBtn.addEventListener("click",closeModal);pauseBtn.addEventListener("click",togglePause);exportBtn.addEventListener("click",exportToCSV2);chartKeySelector.addEventListener("change",e=>{const newKey=e.target.value;updateChartKey(newKey)});overlay.addEventListener("click",e=>{if(e.target===overlay){closeModal()}});await refreshData();refreshIntervalId=window.setInterval(refreshData,refreshInterval);return{destroy:closeModal}}var CSS_TOKENS=`\n:root {\n /* Brand Colors */\n --myio-brand-700: #3e1a7d;\n --myio-brand-600: #2d1458;\n --myio-accent: #2d1458;\n --myio-danger: #d32f2f;\n --myio-success: #388E3C;\n --myio-warning: #f57c00;\n --myio-info: #1976d2;\n \n /* Neutral Colors */\n --myio-bg: #f7f7f7;\n --myio-card: #ffffff;\n --myio-border: #e0e0e0;\n --myio-text: #333333;\n --myio-text-muted: #666666;\n --myio-text-light: #999999;\n \n /* Layout */\n --myio-shadow: 0 2px 6px rgba(0,0,0,0.08);\n --myio-shadow-lg: 0 4px 12px rgba(0,0,0,0.15);\n --myio-radius: 10px;\n --myio-radius-sm: 6px;\n --myio-spacing: 16px;\n --myio-spacing-sm: 8px;\n --myio-spacing-lg: 24px;\n \n /* Typography */\n --myio-font: 'Roboto', Arial, sans-serif;\n --myio-font-size: 14px;\n --myio-font-size-sm: 12px;\n --myio-font-size-lg: 16px;\n --myio-line-height: 1.4;\n \n /* Z-index */\n --myio-z-modal: 9999;\n --myio-z-backdrop: 9998;\n --myio-z-sticky: 100;\n \n /* Animation */\n --myio-transition: 0.2s ease;\n --myio-transition-slow: 0.3s ease;\n}\n\n/* Dark theme tokens */\n[data-theme="dark"] {\n --myio-bg: #1a1a1a;\n --myio-card: #2d2d2d;\n --myio-border: #404040;\n --myio-text: #ffffff;\n --myio-text-muted: #cccccc;\n --myio-text-light: #999999;\n --myio-shadow: 0 2px 6px rgba(0,0,0,0.3);\n --myio-shadow-lg: 0 4px 12px rgba(0,0,0,0.4);\n}\n\n/* Reduced motion */\n@media (prefers-reduced-motion: reduce) {\n :root {\n --myio-transition: 0s;\n --myio-transition-slow: 0s;\n }\n}\n`;var MODAL_STYLES=`\n.myio-modal-scope {\n font-family: var(--myio-font);\n font-size: var(--myio-font-size);\n line-height: var(--myio-line-height);\n color: var(--myio-text);\n box-sizing: border-box;\n}\n\n.myio-modal-scope *,\n.myio-modal-scope *::before,\n.myio-modal-scope *::after {\n box-sizing: inherit;\n}\n\n.myio-modal-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n backdrop-filter: blur(2px);\n z-index: var(--myio-z-backdrop);\n display: flex;\n align-items: center;\n justify-content: center;\n padding: var(--myio-spacing);\n}\n\n.myio-modal {\n background: var(--myio-card);\n border-radius: var(--myio-radius);\n box-shadow: var(--myio-shadow-lg);\n max-width: 90vw;\n max-height: 90vh;\n overflow: hidden;\n display: flex;\n flex-direction: column;\n transform: scale(0.95);\n opacity: 0;\n transition: transform var(--myio-transition), opacity var(--myio-transition);\n}\n\n.myio-modal.myio-modal-open {\n transform: scale(1);\n opacity: 1;\n}\n\n.myio-modal-header {\n padding: 4px;\n border-bottom: none;\n display: flex;\n align-items: center;\n justify-content: space-between;\n background: var(--myio-brand-700);\n color: white;\n border-radius: var(--myio-radius) var(--myio-radius) 0 0;\n min-height: 20px;\n}\n\n.myio-modal-title {\n font-size: 18px !important;\n font-weight: 600;\n margin: 6px !important;\n color: white;\n line-height: 2 !important;\n}\n\n.myio-modal-close {\n background: none;\n border: none;\n font-size: 20px;\n cursor: pointer;\n padding: 4px 8px;\n border-radius: var(--myio-radius-sm);\n color: rgba(255, 255, 255, 0.8);\n transition: background-color var(--myio-transition);\n}\n\n.myio-modal-close:hover {\n background: rgba(255, 255, 255, 0.1);\n color: white;\n}\n\n.myio-modal-close:focus-visible {\n outline: 2px solid rgba(255, 255, 255, 0.5);\n outline-offset: 2px;\n}\n\n.myio-modal-body {\n flex: 1;\n overflow: auto;\n padding: var(--myio-spacing);\n}\n\n.myio-modal-footer {\n padding: var(--myio-spacing);\n border-top: 1px solid var(--myio-border);\n display: flex;\n gap: var(--myio-spacing-sm);\n justify-content: flex-end;\n}\n\n/* Button styles */\n.myio-btn {\n padding: 8px 16px;\n border: none;\n border-radius: var(--myio-radius-sm);\n font-family: inherit;\n font-size: var(--myio-font-size);\n cursor: pointer;\n transition: background-color var(--myio-transition);\n display: inline-flex;\n align-items: center;\n gap: var(--myio-spacing-sm);\n}\n\n.myio-btn-primary {\n background: var(--myio-brand-700);\n color: white;\n}\n\n.myio-btn-primary:hover {\n background: var(--myio-brand-600);\n}\n\n.myio-btn-secondary {\n background: var(--myio-bg);\n color: var(--myio-text);\n border: 1px solid var(--myio-border);\n}\n\n.myio-btn-secondary:hover {\n background: var(--myio-border);\n}\n\n.myio-btn:focus-visible {\n outline: 2px solid var(--myio-brand-700);\n outline-offset: 2px;\n}\n\n.myio-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n/* Form styles */\n.myio-form-group {\n margin-bottom: var(--myio-spacing);\n}\n\n.myio-label {\n display: block;\n margin-bottom: var(--myio-spacing-sm);\n font-weight: 500;\n color: var(--myio-text);\n}\n\n.myio-input {\n width: 100%;\n padding: var(--myio-spacing-sm);\n border: 1px solid var(--myio-border);\n border-radius: var(--myio-radius-sm);\n font-family: inherit;\n font-size: var(--myio-font-size);\n transition: border-color var(--myio-transition);\n}\n\n.myio-input:focus {\n outline: none;\n border-color: var(--myio-brand-700);\n}\n\n/* Table styles */\n.myio-table {\n width: 100%;\n border-collapse: collapse;\n font-size: var(--myio-font-size);\n}\n\n.myio-table th,\n.myio-table td {\n padding: var(--myio-spacing-sm) var(--myio-spacing);\n text-align: left;\n border-bottom: 1px solid var(--myio-border);\n}\n\n.myio-table th:first-child,\n.myio-table td:first-child {\n padding-left: var(--myio-spacing-lg);\n}\n\n.myio-table th:last-child,\n.myio-table td:last-child {\n padding-right: var(--myio-spacing-lg);\n}\n\n.myio-table th {\n background: var(--myio-brand-700);\n color: white;\n font-weight: 600;\n position: sticky;\n top: 0;\n z-index: var(--myio-z-sticky);\n}\n\n.myio-table tbody tr:nth-child(even) {\n background: var(--myio-bg);\n}\n\n.myio-table tbody tr:hover {\n background: var(--myio-border);\n}\n\n/* Loading spinner */\n.myio-spinner {\n width: 20px;\n height: 20px;\n border: 2px solid var(--myio-border);\n border-top: 2px solid var(--myio-brand-700);\n border-radius: 50%;\n animation: myio-spin 1s linear infinite;\n}\n\n@keyframes myio-spin {\n 0% { transform: rotate(0deg); }\n 100% { transform: rotate(360deg); }\n}\n\n/* Utility classes */\n.myio-text-center { text-align: center; }\n.myio-text-right { text-align: right; }\n.myio-text-muted { color: var(--myio-text-muted); }\n.myio-text-success { color: var(--myio-success); }\n.myio-text-danger { color: var(--myio-danger); }\n.myio-mb-0 { margin-bottom: 0; }\n.myio-mt-0 { margin-top: 0; }\n.myio-p-0 { padding: 0; }\n`;var DATERANGEPICKER_STYLES=`\n/* MyIO Premium DateRangePicker Styling */\n.myio-modal-scope .daterangepicker {\n font-family: var(--myio-font);\n border: 1px solid var(--myio-border);\n border-radius: var(--myio-radius);\n box-shadow: var(--myio-shadow-lg);\n background: var(--myio-card);\n z-index: var(--myio-z-popover, 10000);\n}\n\n.myio-modal-scope .daterangepicker .calendar-table {\n background: var(--myio-card);\n border: none;\n}\n\n.myio-modal-scope .daterangepicker .calendar-table th,\n.myio-modal-scope .daterangepicker .calendar-table td {\n border: none;\n padding: 8px;\n text-align: center;\n font-size: 13px;\n}\n\n.myio-modal-scope .daterangepicker .calendar-table th {\n background: var(--myio-bg);\n color: var(--myio-text);\n font-weight: 600;\n}\n\n.myio-modal-scope .daterangepicker .calendar-table td.available:hover {\n background: var(--myio-bg);\n color: var(--myio-text);\n}\n\n.myio-modal-scope .daterangepicker .calendar-table td.in-range {\n background: rgba(74, 20, 140, 0.1);\n color: var(--myio-text);\n}\n\n.myio-modal-scope .daterangepicker .calendar-table td.start-date,\n.myio-modal-scope .daterangepicker .calendar-table td.end-date {\n background: var(--myio-brand-700);\n color: white;\n border-radius: 4px;\n}\n\n.myio-modal-scope .daterangepicker .calendar-table td.active {\n background: var(--myio-brand-700);\n color: white;\n border-radius: 4px;\n}\n\n/* Premium button styling for Aplicar/Cancelar */\n.myio-modal-scope .daterangepicker .drp-buttons {\n border-top: 1px solid var(--myio-border);\n padding: 12px 16px;\n background: var(--myio-bg);\n text-align: right;\n}\n\n.myio-modal-scope .daterangepicker .drp-buttons .btn {\n margin-left: 8px;\n padding: 8px 16px;\n border-radius: var(--myio-radius-sm);\n font-family: var(--myio-font);\n font-size: 14px;\n font-weight: 500;\n border: none;\n cursor: pointer;\n transition: all var(--myio-transition);\n min-width: 80px;\n}\n\n/* Aplicar button - MyIO Primary */\n.myio-modal-scope .daterangepicker .drp-buttons .applyBtn {\n background: linear-gradient(135deg, var(--myio-brand-700) 0%, var(--myio-accent) 100%);\n color: white;\n box-shadow: 0 2px 4px rgba(74, 20, 140, 0.2);\n}\n\n.myio-modal-scope .daterangepicker .drp-buttons .applyBtn:hover {\n background: linear-gradient(135deg, var(--myio-brand-600) 0%, var(--myio-brand-700) 100%);\n box-shadow: 0 4px 8px rgba(74, 20, 140, 0.3);\n transform: translateY(-1px);\n}\n\n.myio-modal-scope .daterangepicker .drp-buttons .applyBtn:active {\n transform: translateY(0);\n box-shadow: 0 2px 4px rgba(74, 20, 140, 0.2);\n}\n\n/* Cancelar button - MyIO Secondary */\n.myio-modal-scope .daterangepicker .drp-buttons .cancelBtn {\n background: var(--myio-card);\n color: var(--myio-text);\n border: 1px solid var(--myio-border);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n}\n\n.myio-modal-scope .daterangepicker .drp-buttons .cancelBtn:hover {\n background: var(--myio-bg);\n border-color: var(--myio-brand-700);\n color: var(--myio-brand-700);\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);\n transform: translateY(-1px);\n}\n\n.myio-modal-scope .daterangepicker .drp-buttons .cancelBtn:active {\n transform: translateY(0);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n}\n\n/* Ranges (preset buttons) styling */\n.myio-modal-scope .daterangepicker .ranges {\n background: var(--myio-bg);\n border-right: 1px solid var(--myio-border);\n}\n\n.myio-modal-scope .daterangepicker .ranges li {\n padding: 10px 16px;\n margin: 2px 8px;\n border-radius: var(--myio-radius-sm);\n cursor: pointer;\n transition: all var(--myio-transition);\n font-size: 13px;\n color: var(--myio-text);\n}\n\n.myio-modal-scope .daterangepicker .ranges li:hover {\n background: rgba(74, 20, 140, 0.1);\n color: var(--myio-brand-700);\n}\n\n.myio-modal-scope .daterangepicker .ranges li.active {\n background: var(--myio-brand-700);\n color: white;\n font-weight: 500;\n}\n\n/* Time picker styling */\n.myio-modal-scope .daterangepicker .calendar-time {\n border-top: 1px solid var(--myio-border);\n background: var(--myio-bg);\n padding: 8px;\n}\n\n.myio-modal-scope .daterangepicker .calendar-time select {\n border: 1px solid var(--myio-border);\n border-radius: var(--myio-radius-sm);\n padding: 4px 8px;\n font-family: var(--myio-font);\n background: var(--myio-card);\n color: var(--myio-text);\n}\n\n.myio-modal-scope .daterangepicker .calendar-time select:focus {\n outline: none;\n border-color: var(--myio-brand-700);\n box-shadow: 0 0 0 2px rgba(74, 20, 140, 0.2);\n}\n\n/* Header styling */\n.myio-modal-scope .daterangepicker .drp-calendar {\n background: var(--myio-card);\n}\n\n.myio-modal-scope .daterangepicker .month {\n color: var(--myio-brand-700);\n font-weight: 600;\n font-size: 14px;\n}\n\n.myio-modal-scope .daterangepicker .prev,\n.myio-modal-scope .daterangepicker .next {\n color: var(--myio-brand-700);\n border: 1px solid var(--myio-border);\n border-radius: var(--myio-radius-sm);\n background: var(--myio-card);\n transition: all var(--myio-transition);\n}\n\n.myio-modal-scope .daterangepicker .prev:hover,\n.myio-modal-scope .daterangepicker .next:hover {\n background: var(--myio-brand-700);\n color: white;\n border-color: var(--myio-brand-700);\n}\n`;var ModalPremiumShell=class{constructor(options){this.options=options;this.originalActiveElement=document.activeElement;this.originalBodyOverflow=document.body.style.overflow;this.injectStyles();this.createElements();this.setupEventListeners();this.setupFocusTrap();this.lockBodyScroll();this.setInertBackground()}backdrop;modal;header;body;footer;closeButton;styleElement;focusTrap;originalBodyOverflow;originalActiveElement;closeHandlers=[];injectStyles(){this.styleElement=document.createElement("style");this.styleElement.textContent=CSS_TOKENS+MODAL_STYLES;document.head.appendChild(this.styleElement)}createElements(){this.backdrop=document.createElement("div");this.backdrop.className="myio-modal-backdrop myio-modal-scope";this.backdrop.setAttribute("role","dialog");this.backdrop.setAttribute("aria-modal","true");this.backdrop.setAttribute("aria-labelledby","myio-modal-title");if(this.options.theme==="dark"){this.backdrop.setAttribute("data-theme","dark")}this.modal=document.createElement("div");this.modal.className="myio-modal";if(this.options.width){const width=typeof this.options.width==="number"?`${this.options.width}px`:this.options.width;this.modal.style.width=width}if(this.options.height){const height=typeof this.options.height==="number"?`${this.options.height}px`:this.options.height;this.modal.style.height=height}this.header=document.createElement("div");this.header.className="myio-modal-header";const title=document.createElement("h2");title.id="myio-modal-title";title.className="myio-modal-title";title.textContent=this.options.title;this.closeButton=document.createElement("button");this.closeButton.className="myio-modal-close";this.closeButton.innerHTML="×";this.closeButton.setAttribute("aria-label","Fechar modal");this.closeButton.type="button";this.header.appendChild(title);this.header.appendChild(this.closeButton);this.body=document.createElement("div");this.body.className="myio-modal-body";this.footer=document.createElement("div");this.footer.className="myio-modal-footer";this.footer.style.display="none";this.modal.appendChild(this.header);this.modal.appendChild(this.body);this.modal.appendChild(this.footer);this.backdrop.appendChild(this.modal)}setupEventListeners(){this.closeButton.addEventListener("click",()=>this.close());if(this.options.closeOnBackdrop!==false){this.backdrop.addEventListener("click",e=>{if(e.target===this.backdrop){this.close()}})}if(this.options.closeOnEscape!==false){document.addEventListener("keydown",this.handleKeyDown)}}handleKeyDown=e=>{if(e.key==="Escape"){this.close()}};setupFocusTrap(){this.focusTrap=new FocusTrap(this.modal)}lockBodyScroll(){document.body.style.overflow="hidden"}unlockBodyScroll(){document.body.style.overflow=this.originalBodyOverflow}setInertBackground(){const topLevelElements=Array.from(document.body.children);topLevelElements.forEach(element=>{if(element!==this.backdrop&&element.tagName!=="SCRIPT"&&element.tagName!=="STYLE"){element.inert=true}})}removeInertBackground(){const topLevelElements=Array.from(document.body.children);topLevelElements.forEach(element=>{element.inert=false})}show(){document.body.appendChild(this.backdrop);requestAnimationFrame(()=>{this.modal.classList.add("myio-modal-open");this.focusTrap.activate()});return{element:this.body,close:()=>this.close(),setContent:content=>this.setContent(content),setFooter:footer=>this.setFooter(footer),on:(event,handler)=>this.on(event,handler)}}setContent(content){if(typeof content==="string"){this.body.innerHTML=content}else{this.body.innerHTML="";this.body.appendChild(content)}}setFooter(footer){if(typeof footer==="string"){this.footer.innerHTML=footer}else{this.footer.innerHTML="";this.footer.appendChild(footer)}this.footer.style.display="flex"}on(event,handler){if(event==="close"){this.closeHandlers.push(handler)}}close(){this.modal.classList.remove("myio-modal-open");setTimeout(()=>{this.cleanup();this.closeHandlers.forEach(handler=>handler())},200)}cleanup(){document.removeEventListener("keydown",this.handleKeyDown);if(this.originalActiveElement&&"focus"in this.originalActiveElement){this.originalActiveElement.focus()}this.focusTrap.deactivate();this.unlockBodyScroll();this.removeInertBackground();if(this.backdrop.parentNode){this.backdrop.parentNode.removeChild(this.backdrop)}if(this.styleElement.parentNode){this.styleElement.parentNode.removeChild(this.styleElement)}}};var FocusTrap=class{constructor(container){this.container=container}focusableElements=[];firstFocusable=null;lastFocusable=null;activate(){this.updateFocusableElements();this.container.addEventListener("keydown",this.handleTabKey);if(this.firstFocusable){this.firstFocusable.focus()}}deactivate(){this.container.removeEventListener("keydown",this.handleTabKey)}updateFocusableElements(){const focusableSelectors=["button:not([disabled])","input:not([disabled])","select:not([disabled])","textarea:not([disabled])","a[href]",'[tabindex]:not([tabindex="-1"])'].join(", ");this.focusableElements=Array.from(this.container.querySelectorAll(focusableSelectors));this.firstFocusable=this.focusableElements[0]||null;this.lastFocusable=this.focusableElements[this.focusableElements.length-1]||null}handleTabKey=e=>{if(e.key!=="Tab")return;if(e.shiftKey){if(document.activeElement===this.firstFocusable){e.preventDefault();this.lastFocusable?.focus()}}else{if(document.activeElement===this.lastFocusable){e.preventDefault();this.firstFocusable?.focus()}}}};function createModal(options){const shell=new ModalPremiumShell(options);return shell.show()}var AuthClient=class{constructor(cfg){this.cfg=cfg}token;async getBearer(){const now=Date.now()/1e3;if(this.token&&this.token.exp-now>60)return this.token.value;this.token={value:"",exp:now+300};return this.token.value}clearCache(){this.token=void 0}};function validateOptions(options){const mode=options.mode||"single";if(mode==="single"){if(!options.tbJwtToken){throw new Error("tbJwtToken is required for ThingsBoard API access in single mode")}if(!options.deviceId){throw new Error("deviceId is required for single mode")}}else if(mode==="comparison"){if(!options.dataSources||options.dataSources.length===0){throw new Error("dataSources is required for comparison mode")}if(!options.granularity){throw new Error("granularity is required for comparison mode")}}if(!options.startDate||!options.endDate){throw new Error("startDate and endDate are required")}if(mode==="single"){const hasIngestionToken=!!options.ingestionToken;const hasClientCredentials=!!(options.clientId&&options.clientSecret);if(!hasIngestionToken&&!hasClientCredentials){throw new Error("Either ingestionToken or clientId/clientSecret must be provided")}}else if(mode==="comparison"){if(!options.clientId||!options.clientSecret){throw new Error("clientId and clientSecret are required for comparison mode")}}}function normalizeToSaoPauloISO(dateLike,endOfDay=false){let date;if(typeof dateLike==="string"){if(/^\d{4}-\d{2}-\d{2}$/.test(dateLike)){date=new Date(dateLike+"T00:00:00-03:00")}else{date=new Date(dateLike)}}else{date=new Date(dateLike)}const saoPauloOffset=-3*60;const localOffset=date.getTimezoneOffset();const offsetDiff=saoPauloOffset-localOffset;if(offsetDiff!==0){date.setMinutes(date.getMinutes()+offsetDiff)}if(endOfDay){date.setHours(23,59,59,999)}else{date.setHours(0,0,0,0)}return date.toISOString().replace("Z","-03:00")}function resolveDeviceAttributes(attributes){return{ingestionId:attributes.ingestionId||attributes.INGESTION_ID,centralId:attributes.centralId||attributes.CENTRAL_ID,slaveId:attributes.slaveId||attributes.SLAVE_ID,customerId:attributes.customerId||attributes.CUSTOMER_ID,floor:attributes.floor||attributes.FLOOR,storeNumber:attributes.NumLoja||attributes.storeNumber}}function mapHttpError(status,body=""){switch(status){case 400:return{code:"VALIDATION_ERROR",message:"Invalid request parameters",userAction:"FIX_INPUT"};case 401:return{code:"TOKEN_EXPIRED",message:"Authentication token has expired",userAction:"RE_AUTH"};case 403:return{code:"AUTH_ERROR",message:"Insufficient permissions",userAction:"RE_AUTH"};case 404:return{code:"NETWORK_ERROR",message:"Device not found",userAction:"CONTACT_ADMIN"};case 409:return{code:"VALIDATION_ERROR",message:"Concurrent modification detected",userAction:"RETRY"};case 422:return{code:"VALIDATION_ERROR",message:"Server-side validation failed",userAction:"FIX_INPUT"};default:if(status>=500){return{code:"NETWORK_ERROR",message:"Server error occurred",userAction:"RETRY"}}return{code:"NETWORK_ERROR",message:"Network error occurred",userAction:"RETRY"}}}function generateDateRange(startISO,endISO,granularity){const dates=[];const start=new Date(startISO);const end=new Date(endISO);while(start<=end){dates.push(start.toISOString());switch(granularity){case"15m":start.setMinutes(start.getMinutes()+15);break;case"1h":start.setHours(start.getHours()+1);break;case"1d":default:start.setDate(start.getDate()+1);break}}return dates}function formatNumber(value){return new Intl.NumberFormat("pt-BR",{minimumFractionDigits:2,maximumFractionDigits:2}).format(value)}function formatDate(dateStr){const date=new Date(dateStr);return date.toLocaleDateString("pt-BR")}function validateJwtToken(token){if(!token||typeof token!=="string"){return false}const parts=token.split(".");if(parts.length!==3){return false}const base64Regex=/^[A-Za-z0-9_-]+$/;return parts.every(part=>base64Regex.test(part))}function createSafeErrorMessage(error){if(error instanceof Error){return error.message.replace(/Bearer\s+[^\s]+/gi,"Bearer [REDACTED]")}return"An unknown error occurred"}function createModalId(){return`myio-energy-modal-${Date.now()}-${Math.random().toString(36).substr(2,9)}`}var EnergyDataFetcher=class{config;authClient=null;constructor(config){this.config={dataApiHost:config.dataApiHost||"https://api.data.apps.myio-bas.com",...config};if(config.clientId&&config.clientSecret){this.authClient=new AuthClient({clientId:config.clientId,clientSecret:config.clientSecret,base:this.config.dataApiHost})}}async fetchEnergyData(params){try{const token=await this.getAuthToken();const url=this.buildEnergyApiUrl(params);console.log("[EnergyDataFetcher] Fetching energy data:",{url:url.replace(/Bearer\s+[^\s&]+/gi,"Bearer [REDACTED]"),ingestionId:params.ingestionId,granularity:params.granularity});const response=await fetch(url,{method:"GET",headers:{Authorization:`Bearer ${token}`,"Content-Type":"application/json"}});if(!response.ok){const errorText=await response.text().catch(()=>"");const error=mapHttpError(response.status,errorText);console.error("[EnergyDataFetcher] API request failed:",{status:response.status,statusText:response.statusText,error:error});throw new Error(`Energy data fetch failed: ${error.message}`)}const apiResponse=await response.json();console.log("[EnergyDataFetcher] API response received:",{hasData:!!apiResponse.data,dataLength:Array.isArray(apiResponse.data)?apiResponse.data.length:0});return this.processEnergyResponse(apiResponse,params)}catch(error){console.error("[EnergyDataFetcher] Error fetching energy data:",error);if(error instanceof Error&&"code"in error){throw error}throw new Error(createSafeErrorMessage(error))}}async getAuthToken(){if(this.config.ingestionToken){return this.config.ingestionToken}if(this.authClient){try{return await this.authClient.getBearer()}catch(error){console.error("[EnergyDataFetcher] AuthClient failed:",error);throw new Error("Failed to obtain authentication token via client credentials")}}throw new Error("No authentication method available. Provide either ingestionToken or clientId/clientSecret.")}buildEnergyApiUrl(params){const baseUrl=this.config.dataApiHost;const endpoint=`/api/v1/telemetry/devices/${params.ingestionId}/energy`;const queryParams=new URLSearchParams({startTime:params.startISO,endTime:params.endISO,granularity:params.granularity,page:"1",pageSize:"1000",deep:"0"});return`${baseUrl}${endpoint}?${queryParams.toString()}`}processEnergyResponse(apiResponse,params){const dataArray=Array.isArray(apiResponse)?apiResponse:apiResponse.data||[];if(!Array.isArray(dataArray)||dataArray.length===0){console.warn("[EnergyDataFetcher] Empty or invalid API response, creating zero-filled data");return this.createEmptyEnergyData(params)}const deviceData=dataArray[0];const consumption=deviceData.consumption||[];console.log("[EnergyDataFetcher] Processing device data:",{deviceId:deviceData.deviceId||params.ingestionId,consumptionPoints:consumption.length});const consumptionPoints=consumption.map(item=>({timestamp:item.timestamp,value:Number(item.value)||0})).filter(item=>item.timestamp).sort((a,b)=>new Date(a.timestamp).getTime()-new Date(b.timestamp).getTime());if(consumptionPoints.length===0){console.warn("[EnergyDataFetcher] No valid consumption points found, creating zero-filled data");return this.createEmptyEnergyData(params)}return{deviceId:deviceData.deviceId||params.ingestionId,consumption:consumptionPoints,granularity:params.granularity,dateRange:{start:params.startISO,end:params.endISO}}}createEmptyEnergyData(params){const dateRange=generateDateRange(params.startISO,params.endISO,params.granularity);return{deviceId:params.ingestionId,consumption:dateRange.map(date=>({timestamp:date,value:0})),granularity:params.granularity,dateRange:{start:params.startISO,end:params.endISO}}}clearCache(){if(this.authClient){this.authClient.clearCache()}}getConfig(){return{dataApiHost:this.config.dataApiHost,ingestionToken:this.config.ingestionToken?"[REDACTED]":void 0,clientId:this.config.clientId||void 0,clientSecret:this.config.clientSecret?"[REDACTED]":void 0}}};var toCsv=(rows,locale="pt-BR",sep=";")=>{const fmt=new Intl.NumberFormat(locale,{minimumFractionDigits:2,maximumFractionDigits:2});const esc=v=>(typeof v==="number"?fmt.format(v):String(v)).replace(/"/g,'""');return rows.map(r=>r.map(c=>`"${esc(c)}"`).join(sep)).join("\r\n")};var CDN_RESOURCES=[{id:"jquery-3.7.1",src:"https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js",integrity:"sha384-1H217gwSVyLSIfaLxHbE7dRb3v4mYCKbpQvzx0cegeju1MVsGrX5xXxAvs/HgeFs",crossorigin:"anonymous"},{id:"moment-2.29.4",src:"https://cdn.jsdelivr.net/npm/moment@2.29.4/min/moment.min.js",integrity:"sha384-2xoILS8hBHw+Atyv/qJLEdk8dFdW1hbGjfeQ3G0GU3pGNPlqck0chRqjMTZ5blGf",crossorigin:"anonymous"},{id:"daterangepicker-3.1.0",src:"https://cdn.jsdelivr.net/npm/daterangepicker@3.1.0/daterangepicker.min.js",integrity:"sha384-IbJFThFkdkMvvxP0U8wOffxBHPYEJE65UtA/l25/jJQUt/hft6OdAuKLxGjtOVnL",crossorigin:"anonymous"}];var CSS_RESOURCE={href:"https://cdn.jsdelivr.net/npm/daterangepicker@3.1.0/daterangepicker.css",integrity:"sha384-zLkQsiLfAQqGeIJeKLC+rcCR1YoYaQFLCL7cLDUoKE1ajKJzySpjzWGfYS2vjSG+"};var FALLBACK_PATHS=["/assets/vendor/jquery.min.js","/assets/vendor/moment.min.js","/assets/vendor/daterangepicker.min.js"];function getLocaleConfig(includeTime=false){return{format:includeTime?"DD/MM/YY HH:mm":"DD/MM/YYYY",separator:" até ",applyLabel:"Aplicar",cancelLabel:"Cancelar",fromLabel:"De",toLabel:"Até",customRangeLabel:"Personalizado",daysOfWeek:["Do","Se","Te","Qa","Qi","Se","Sa"],monthNames:["Jan","Fev","Mar","Abr","Mai","Jun","Jul","Ago","Set","Out","Nov","Dez"],firstDay:1}}var CDNLoader=class{static jQueryInstance=null;static loadingPromise=null;static loaded=false;static async ensureLoaded(){if(this.loaded&&this.jQueryInstance){return this.jQueryInstance}if(this.loadingPromise){return this.loadingPromise}this.loadingPromise=this.loadResources();return this.loadingPromise}static async loadResources(){if(window.jQuery&&window.moment&&window.jQuery.fn.daterangepicker){this.jQueryInstance=window.jQuery.noConflict(true);this.loaded=true;return this.jQueryInstance}try{await this.loadFromCDN();console.log("DateRangePicker: CDN loaded successfully")}catch(cdnError){console.warn("DateRangePicker: CDN failed, trying local assets:",cdnError);try{await this.loadFromLocal();console.log("DateRangePicker: Local assets loaded successfully")}catch(localError){console.warn("DateRangePicker: Local assets failed, using native inputs:",localError);throw new Error("DateRangePicker unavailable - using native inputs")}}this.jQueryInstance=window.jQuery?.noConflict(true);this.loaded=true;if(!this.jQueryInstance){throw new Error("jQuery not available after loading")}return this.jQueryInstance}static async loadFromCDN(){await this.loadCSS(CSS_RESOURCE.href,CSS_RESOURCE.integrity);for(const resource of CDN_RESOURCES){await this.loadScript(resource.src,resource.integrity)}}static async loadFromLocal(){for(const path of FALLBACK_PATHS){await this.loadScript(path)}}static loadScript(src,integrity){return new Promise((resolve,reject)=>{if(document.querySelector(`script[src="${src}"]`)){resolve();return}const script=document.createElement("script");script.src=src;script.async=false;if(integrity){script.integrity=integrity;script.crossOrigin="anonymous"}script.onload=()=>resolve();script.onerror=()=>reject(new Error(`Failed to load script: ${src}`));document.head.appendChild(script)})}static loadCSS(href,integrity){return new Promise((resolve,reject)=>{if(document.querySelector(`link[href="${href}"]`)){resolve();return}const link=document.createElement("link");link.rel="stylesheet";link.href=href;if(integrity){link.integrity=integrity;link.crossOrigin="anonymous"}link.onload=()=>resolve();link.onerror=()=>reject(new Error(`Failed to load CSS: ${href}`));document.head.appendChild(link)})}};function injectPremiumStyling(){const styleId="myio-daterangepicker-styles";if(document.getElementById(styleId)){return}const style=document.createElement("style");style.id=styleId;style.textContent=DATERANGEPICKER_STYLES;document.head.appendChild(style)}async function attach(input,opts={}){try{const $2=await CDNLoader.ensureLoaded();return createDateRangePicker($2,input,opts)}catch(error){console.error("DateRangePicker: Failed to load dependencies. Native date inputs are forbidden in this project.",error);throw new Error("DateRangePicker dependencies unavailable. Please ensure jQuery, moment.js, and daterangepicker are accessible.")}}function createDateRangePicker($2,input,opts){const $input=$2(input);const maxRangeDays=opts.maxRangeDays??31;injectPremiumStyling();input.readOnly=true;input.setAttribute("aria-label","Período de datas");const helpText=document.createElement("div");helpText.className="myio-text-muted";helpText.style.fontSize="12px";helpText.style.marginTop="4px";helpText.style.display="flex";helpText.style.alignItems="center";input.parentNode?.appendChild(helpText);const includeTime=opts.includeTime===true;const timePrecision=opts.timePrecision||"minute";const localeConfig=getLocaleConfig(includeTime);const moment=window.moment;let startDate,endDate;if(includeTime){startDate=opts.presetStart?moment(opts.presetStart):moment().startOf("day");endDate=opts.presetEnd?moment(opts.presetEnd):moment()}else{startDate=opts.presetStart?moment(opts.presetStart).startOf("day"):moment().startOf("month");endDate=opts.presetEnd?moment(opts.presetEnd).endOf("day"):moment().endOf("day")}let ranges;if(includeTime){const now=moment();ranges={"Última hora":[moment().subtract(1,"hours"),now.clone()],"Últimas 6 horas":[moment().subtract(6,"hours"),now.clone()],"Últimas 12 horas":[moment().subtract(12,"hours"),now.clone()],"Últimas 24 horas":[moment().subtract(24,"hours"),now.clone()],Hoje:[moment().startOf("day"),now.clone()],Ontem:[moment().subtract(1,"day").startOf("day"),moment().subtract(1,"day").endOf("day")],"Últimos 7 dias":[moment().subtract(6,"days").startOf("day"),now.clone()],"Este mês":[moment().startOf("month"),now.clone()]}}else{ranges={Hoje:[moment().startOf("day"),moment().endOf("day")],"Últimos 7 dias":[moment().subtract(6,"days").startOf("day"),moment().endOf("day")],"Últimos 30 dias":[moment().subtract(29,"days").startOf("day"),moment().endOf("day")],"Mês Anterior":[moment().subtract(1,"month").startOf("month"),moment().subtract(1,"month").endOf("month")]}}$input.daterangepicker({parentEl:opts.parentEl||document.body,timePicker:includeTime,timePicker24Hour:true,timePickerIncrement:timePrecision==="hour"?60:1,autoApply:true,autoUpdateInput:true,linkedCalendars:true,showCustomRangeLabel:true,maxSpan:{days:maxRangeDays},maxDate:moment().endOf("day"),startDate:startDate,endDate:endDate,opens:"right",drops:"down",locale:localeConfig,applyButtonClasses:"btn btn-primary",cancelClass:"btn btn-muted",ranges:ranges});updateInputDisplay();$input.on("apply.daterangepicker.myio",()=>{updateInputDisplay();if(opts.onApply){const result=getDates();opts.onApply(result)}});$input.on("cancel.daterangepicker.myio",()=>{$input.val("");if(opts.onApply){opts.onApply({startISO:"",endISO:"",startLabel:"",endLabel:""})}});function updateInputDisplay(){const picker=$input.data("daterangepicker");if(picker){const formatted=`${picker.startDate.format(localeConfig.format)}${localeConfig.separator}${picker.endDate.format(localeConfig.format)}`;$input.val(formatted)}}function getDates(){const picker=$input.data("daterangepicker");const startISO=picker.startDate.format("YYYY-MM-DD[T]HH:mm:ssZ");const endISO=picker.endDate.format("YYYY-MM-DD[T]HH:mm:ssZ");const startLabel=picker.startDate.format(localeConfig.format);const endLabel=picker.endDate.format(localeConfig.format);return{startISO:startISO,endISO:endISO,startLabel:startLabel,endLabel:endLabel}}function setDates(startISO,endISO){const picker=$input.data("daterangepicker");picker.setStartDate(moment(startISO));picker.setEndDate(moment(endISO));updateInputDisplay()}function destroy(){const picker=$input.data("daterangepicker");$input.off(".daterangepicker.myio");picker?.remove?.();$input.removeData("daterangepicker");helpText?.remove()}return{getDates:getDates,setDates:setDates,destroy:destroy}}var DateRangePickerJQ={attach:attach};function detectTelemetryType(keys){if(!keys){return TELEMETRY_TYPES.total_power}const keyStr=Array.isArray(keys)?keys.map(k=>k.trim()).join(","):keys.trim();for(const type of Object.values(TELEMETRY_TYPES)){const typeKeys=Array.isArray(type.keys)?type.keys.map(k=>k.trim()).join(","):type.keys.trim();if(typeKeys===keyStr){return type}}return TELEMETRY_TYPES.total_power}var telemetryCache=new Map;var DEFAULT_CACHE_TTL=5*60*1e3;var MAX_CACHE_SIZE=50;function getCacheKey(params){const keysStr=Array.isArray(params.keys)?params.keys.join(","):params.keys;const agg=params.agg||"MAX";const interval=params.interval||864e5;return`${params.deviceId}:${params.startDate}:${params.endDate}:${keysStr}:${agg}:${interval}`}function getCachedData(cacheKey){const entry=telemetryCache.get(cacheKey);if(!entry){return null}const now=Date.now();if(now-entry.timestamp>entry.ttl){telemetryCache.delete(cacheKey);return null}return entry.data}function setCachedData(cacheKey,data,ttl=DEFAULT_CACHE_TTL){if(telemetryCache.size>=MAX_CACHE_SIZE){const firstKey=telemetryCache.keys().next().value;if(firstKey){telemetryCache.delete(firstKey)}}telemetryCache.set(cacheKey,{data:data,timestamp:Date.now(),ttl:ttl})}var TELEMETRY_TYPES={total_power:{id:"total_power",label:"Potência Total",keys:"consumption",defaultAggregation:"MAX",unit:"kW",color:"#4A148C"},power_phases:{id:"power_phases",label:"Potência A, B, C",keys:["a","b","c"],defaultAggregation:"MAX",unit:"kW",color:["#FF5722","#4CAF50","#2196F3"]},current_phases:{id:"current_phases",label:"Corrente A, B, C",keys:["current_a","current_b","current_c"],defaultAggregation:"AVG",unit:"A",color:["#FF5722","#4CAF50","#2196F3"]},voltage_phases:{id:"voltage_phases",label:"Tensão A, B, C",keys:["voltage_a","voltage_b","voltage_c"],defaultAggregation:"AVG",unit:"V",color:["#FF5722","#4CAF50","#2196F3"]}};var DEFAULT_STYLES={primaryColor:"#4A148C",accentColor:"#EDE7F3",dangerColor:"#f44336",infoColor:"#2196F3",textPrimary:"#212121",textSecondary:"#757575",backgroundColor:"#ffffff",overlayColor:"rgba(0, 0, 0, 0.5)",borderRadius:"8px",buttonRadius:"6px",pillRadius:"20px",zIndex:1e4,spacingXs:"4px",spacingSm:"8px",spacingMd:"16px",spacingLg:"24px",spacingXl:"32px",fontFamily:'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',fontSizeXs:"12px",fontSizeSm:"14px",fontSizeMd:"16px",fontSizeLg:"18px",fontSizeXl:"20px",fontWeight:"400",fontWeightBold:"600"};var CHART_JS_CDN="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.js";var ZOOM_PLUGIN_CDN="https://cdn.jsdelivr.net/npm/chartjs-plugin-zoom@2.0.1/dist/chartjs-plugin-zoom.min.js";var JSPDF_VERSION="2.5.1";var JSPDF_CDN=`https://cdnjs.cloudflare.com/ajax/libs/jspdf/${JSPDF_VERSION}/jspdf.umd.min.js`;var chartJsLoaded=false;var zoomPluginLoaded=false;var jsPdfLoaded=false;var _jspdfPromise=null;var cssInjected=false;var STRINGS2={"pt-BR":{title:"Demanda",period:"Período",maximum:"Máxima",at:"em",exportPdf:"Exportar PDF",exportCsv:"Exportar CSV",fullscreen:"Tela cheia",close:"Fechar",resetZoom:"Reset Zoom",loading:"Carregando dados...",noData:"Sem pontos de demanda no período selecionado",error:"Erro ao carregar dados",zoomHelp:"Scroll: zoom | Arraste: mover | Ctrl+Arraste: selecionar",demand:"Demanda (kW)",reportTitle:"Relatório de Demanda",reportFooter:"MyIO Energy Management System",startDate:"Data Inicial",endDate:"Data Final",updatePeriod:"Atualizar",invalidDateRange:"Data final deve ser maior que data inicial",maxRangeExceeded:"Período máximo de 30 dias",telemetryType:"Tipo de Telemetria"},"en-US":{title:"Demand",period:"Period",maximum:"Maximum",at:"on",exportPdf:"Export PDF",exportCsv:"Export CSV",fullscreen:"Fullscreen",close:"Close",resetZoom:"Reset Zoom",loading:"Loading data...",noData:"No demand points in the selected period",error:"Error loading data",zoomHelp:"Scroll: zoom | Drag: pan | Ctrl+Drag: select",demand:"Demand (kW)",reportTitle:"Demand Report",reportFooter:"MyIO Energy Management System",startDate:"Start Date",endDate:"End Date",updatePeriod:"Update",invalidDateRange:"End date must be greater than start date",maxRangeExceeded:"Maximum range of 30 days",telemetryType:"Telemetry Type"}};async function loadScript(url,checkGlobal){return new Promise((resolve,reject)=>{if(window[checkGlobal]){resolve();return}const existingScript=document.querySelector(`script[src="${url}"]`);if(existingScript){existingScript.addEventListener("load",()=>{if(window[checkGlobal]){resolve()}else{reject(new Error(`Library ${checkGlobal} not available after loading ${url}`))}});existingScript.addEventListener("error",()=>reject(new Error(`Failed to load ${url}`)));return}const script=document.createElement("script");script.src=url;script.onload=()=>{if(window[checkGlobal]){resolve()}else{reject(new Error(`Library ${checkGlobal} not available after loading ${url}`))}};script.onerror=()=>reject(new Error(`Failed to load ${url}`));document.head.appendChild(script)})}async function loadExternalLibraries(){try{if(!chartJsLoaded){await loadScript(CHART_JS_CDN,"Chart");chartJsLoaded=true}if(!zoomPluginLoaded){await loadScript(ZOOM_PLUGIN_CDN,"ChartZoom");zoomPluginLoaded=true}if(!jsPdfLoaded){await ensureJsPDF();jsPdfLoaded=true}}catch(error){throw new Error(`Failed to load external libraries: ${error}`)}}function ensureJsPDF(){if(window.jspdf?.jsPDF){console.info("jsPDF already loaded.");return Promise.resolve()}if(_jspdfPromise){console.info("jsPDF loading already in progress.");return _jspdfPromise}_jspdfPromise=new Promise((resolve,reject)=>{const existing=document.querySelector('script[data-lib="jspdf"]');if(existing){existing.addEventListener("load",()=>{if(window.jspdf?.jsPDF){console.info("jsPDF loaded via existing script.");resolve()}else{reject(new Error("jsPDF loaded but window.jspdf.jsPDF missing"))}});existing.addEventListener("error",()=>reject(new Error("Failed to load jsPDF via existing script")));return}const s=document.createElement("script");s.src=JSPDF_CDN;s.async=true;s.defer=true;s.dataset.lib="jspdf";s.onload=()=>{if(window.jspdf?.jsPDF){console.info("jsPDF loaded successfully.");resolve()}else{reject(new Error("jsPDF loaded but window.jspdf.jsPDF missing"))}};s.onerror=()=>reject(new Error("Failed to load jsPDF from CDN"));document.head.appendChild(s)}).finally(()=>{_jspdfPromise=null});return _jspdfPromise}function getJsPDFCtor(){if(window.jspdf?.jsPDF)return window.jspdf.jsPDF;if(window.jsPDF?.jsPDF)return window.jsPDF.jsPDF;if(window.jsPDF)return window.jsPDF;throw new Error("jsPDF constructor not found on window")}function savePdfSafe(doc,filename){try{doc.save(filename)}catch(e){console.warn("doc.save() failed, attempting Blob URL fallback:",e);const blob=doc.output("blob");const url=URL.createObjectURL(blob);window.open(url,"_blank")||alert("Pop-up blocked. Allow pop-ups to download the PDF.");setTimeout(()=>URL.revokeObjectURL(url),3e4)}}function addCanvasToPdf(doc,canvas,x=10,y=20,maxWmm=190){const img=canvas.toDataURL("image/png",1);const pageWmm=doc.internal.pageSize.getWidth();const mmW=Math.min(maxWmm,pageWmm-x*2);const mmH=canvas.height/canvas.width*mmW;doc.addImage(img,"PNG",x,y,mmW,mmH,void 0,"FAST");return y+mmH}function ensureRoom(doc,nextY,minRoom=40){const h=doc.internal.pageSize.getHeight();if(nextY+minRoom>h){doc.addPage();return 20}return nextY}function injectCSS(styles){if(cssInjected)return;const css=`\n .myio-demand-modal-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: ${styles.overlayColor};\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: ${styles.zIndex};\n font-family: ${styles.fontFamily};\n font-size: ${styles.fontSizeMd};\n color: ${styles.textPrimary};\n }\n\n .myio-demand-modal-card {\n background: ${styles.backgroundColor};\n border-radius: ${styles.borderRadius};\n box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);\n max-width: 90vw;\n max-height: 90vh;\n width: 1040px;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n }\n\n .myio-demand-modal-card.fullscreen {\n width: 100vw;\n height: 100vh;\n max-width: 100vw;\n max-height: 100vh;\n border-radius: 0;\n }\n\n .myio-demand-modal-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: ${styles.spacingMd};\n background: #4A148C;\n color: white;\n }\n\n .myio-demand-modal-title-section {\n display: flex;\n align-items: center;\n gap: ${styles.spacingSm};\n }\n\n .myio-demand-modal-icon {\n font-size: ${styles.fontSizeLg};\n }\n\n .myio-demand-modal-title {\n margin: 0;\n font-size: ${styles.fontSizeLg};\n font-weight: ${styles.fontWeightBold};\n }\n\n .myio-demand-modal-actions {\n display: flex;\n gap: ${styles.spacingSm};\n }\n\n .myio-demand-modal-btn {\n background: rgba(255, 255, 255, 0.2);\n border: 1px solid rgba(255, 255, 255, 0.3);\n color: white;\n padding: ${styles.spacingSm} ${styles.spacingMd};\n border-radius: ${styles.buttonRadius};\n cursor: pointer;\n font-size: ${styles.fontSizeSm};\n font-family: inherit;\n transition: background-color 0.2s;\n }\n\n .myio-demand-modal-btn:hover {\n background: rgba(255, 255, 255, 0.3);\n }\n\n .myio-demand-modal-btn:focus {\n outline: 2px solid white;\n outline-offset: 2px;\n }\n\n .myio-demand-modal-btn-close {\n font-size: ${styles.fontSizeLg};\n padding: ${styles.spacingSm};\n min-width: 32px;\n background: ${styles.dangerColor} !important;\n }\n\n .myio-demand-modal-btn-close:hover {\n background: #d32f2f !important;\n }\n\n .myio-demand-modal-period {\n padding: ${styles.spacingMd} ${styles.spacingLg};\n background: #f5f5f5;\n font-size: ${styles.fontSizeSm};\n color: ${styles.textSecondary};\n border-bottom: 1px solid #e0e0e0;\n }\n\n .myio-demand-modal-period-selector {\n display: flex;\n align-items: center;\n gap: ${styles.spacingMd};\n padding: ${styles.spacingMd} ${styles.spacingLg};\n background: #f9f9f9;\n border-bottom: 1px solid #e0e0e0;\n flex-wrap: wrap;\n }\n\n .myio-demand-modal-period-selector label {\n display: flex;\n flex-direction: column;\n gap: ${styles.spacingXs};\n font-size: ${styles.fontSizeSm};\n color: ${styles.textPrimary};\n font-weight: ${styles.fontWeightBold};\n }\n\n .myio-demand-modal-date-input {\n padding: ${styles.spacingSm};\n border: 1px solid #ccc;\n border-radius: ${styles.buttonRadius};\n font-size: ${styles.fontSizeSm};\n font-family: inherit;\n min-width: 150px;\n }\n\n .myio-demand-modal-date-input:focus {\n outline: 2px solid ${styles.primaryColor};\n outline-offset: 1px;\n }\n\n /* RFC-0061: Telemetry selector styles */\n .myio-demand-modal-select {\n padding-right: 32px;\n background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="%23333"><path d="M4 6l4 4 4-4z"/></svg>');\n background-repeat: no-repeat;\n background-position: right 8px center;\n background-size: 16px;\n cursor: pointer;\n appearance: none;\n -webkit-appearance: none;\n -moz-appearance: none;\n }\n\n .myio-demand-modal-select:hover {\n border-color: ${styles.primaryColor};\n background-color: #fafafa;\n }\n\n .myio-demand-modal-select:focus {\n outline: 2px solid ${styles.primaryColor};\n outline-offset: 1px;\n box-shadow: 0 0 0 3px rgba(74, 20, 140, 0.1);\n }\n\n .myio-demand-modal-select:disabled {\n background-color: #f5f5f5;\n cursor: not-allowed;\n opacity: 0.6;\n }\n\n .myio-demand-modal-btn-update {\n background: ${styles.primaryColor};\n border: none;\n color: white;\n padding: ${styles.spacingSm} ${styles.spacingMd};\n border-radius: ${styles.buttonRadius};\n cursor: pointer;\n font-size: ${styles.fontSizeSm};\n font-family: inherit;\n font-weight: ${styles.fontWeightBold};\n align-self: flex-end;\n margin-bottom: 2px;\n }\n\n .myio-demand-modal-btn-update:hover {\n background: #6A1B9A;\n }\n\n .myio-demand-modal-btn-update:disabled {\n background: #ccc;\n cursor: not-allowed;\n }\n\n /* RFC-0082: Real-time button styles */\n .myio-demand-modal-btn-realtime {\n background: transparent;\n border: 2px solid #666;\n color: #666;\n padding: ${styles.spacingSm} ${styles.spacingMd};\n border-radius: ${styles.buttonRadius};\n cursor: pointer;\n font-size: ${styles.fontSizeSm};\n font-family: inherit;\n font-weight: ${styles.fontWeightBold};\n align-self: flex-end;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n transition: all 0.3s ease;\n white-space: nowrap;\n }\n\n .myio-demand-modal-btn-realtime:hover {\n border-color: #999;\n color: #999;\n }\n\n .myio-demand-modal-btn-realtime.active {\n background: linear-gradient(135deg, #d32f2f 0%, #f44336 100%);\n border-color: #d32f2f;\n color: white;\n box-shadow: 0 0 12px rgba(244, 67, 54, 0.5);\n }\n\n .myio-demand-modal-btn-realtime.active:hover {\n background: linear-gradient(135deg, #c62828 0%, #e53935 100%);\n }\n\n .realtime-indicator {\n display: inline-block;\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: #666;\n transition: all 0.3s ease;\n }\n\n .myio-demand-modal-btn-realtime.active .realtime-indicator {\n background: white;\n animation: pulse 1.5s ease-in-out infinite;\n }\n\n @keyframes pulse {\n 0%, 100% { opacity: 1; transform: scale(1); }\n 50% { opacity: 0.6; transform: scale(1.2); }\n }\n\n .myio-demand-modal-btn-realtime.active .realtime-text::before {\n content: "AO VIVO";\n }\n\n .myio-demand-modal-btn-realtime:not(.active) .realtime-text::before {\n content: "REAL TIME";\n }\n\n .realtime-text {\n font-size: 0; /* Hide original text, use ::before pseudo-element */\n }\n\n .myio-demand-modal-period-error {\n flex-basis: 100%;\n color: ${styles.dangerColor};\n font-size: ${styles.fontSizeXs};\n margin-top: -${styles.spacingSm};\n }\n\n .myio-demand-modal-peak {\n margin: ${styles.spacingMd} ${styles.spacingLg} 0;\n padding: ${styles.spacingSm} ${styles.spacingMd};\n background: ${styles.accentColor};\n color: #333;\n border-radius: ${styles.pillRadius};\n font-size: ${styles.fontSizeSm};\n font-weight: ${styles.fontWeightBold};\n display: inline-block;\n width: fit-content;\n }\n\n .myio-demand-modal-content {\n flex: 1;\n padding: ${styles.spacingLg};\n display: flex;\n flex-direction: column;\n min-height: 400px;\n }\n\n .myio-demand-modal-chart-container {\n flex: 1;\n position: relative;\n min-height: 300px;\n }\n\n .myio-demand-modal-chart {\n width: 100% !important;\n height: 100% !important;\n }\n\n .myio-demand-modal-zoom-controls {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-top: ${styles.spacingMd};\n padding-top: ${styles.spacingMd};\n border-top: 1px solid #e0e0e0;\n }\n\n .myio-demand-modal-btn-reset {\n background: ${styles.infoColor};\n border: none;\n color: white;\n padding: ${styles.spacingSm} ${styles.spacingMd};\n border-radius: ${styles.buttonRadius};\n cursor: pointer;\n font-size: ${styles.fontSizeSm};\n font-family: inherit;\n }\n\n .myio-demand-modal-btn-reset:hover {\n background: #1976D2;\n }\n\n .myio-demand-modal-zoom-help {\n font-size: ${styles.fontSizeXs};\n color: ${styles.textSecondary};\n }\n\n .myio-demand-modal-loading,\n .myio-demand-modal-error {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: ${styles.spacingXl};\n text-align: center;\n }\n\n .myio-demand-modal-spinner {\n font-size: 2rem;\n animation: spin 1s linear infinite;\n margin-bottom: ${styles.spacingMd};\n }\n\n @keyframes spin {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n }\n\n .myio-demand-modal-loading-text {\n color: ${styles.textSecondary};\n }\n\n .myio-demand-modal-error-icon {\n font-size: 2rem;\n color: ${styles.dangerColor};\n margin-bottom: ${styles.spacingMd};\n }\n\n .myio-demand-modal-error-text {\n color: ${styles.dangerColor};\n }\n\n /* Responsive design */\n @media (max-width: 768px) {\n .myio-demand-modal-card {\n width: 95vw;\n height: 90vh;\n max-height: 90vh;\n }\n\n .myio-demand-modal-header {\n padding: ${styles.spacingMd};\n }\n\n .myio-demand-modal-title {\n font-size: ${styles.fontSizeMd};\n }\n\n .myio-demand-modal-actions {\n gap: ${styles.spacingXs};\n }\n\n .myio-demand-modal-btn {\n padding: ${styles.spacingXs} ${styles.spacingSm};\n font-size: ${styles.fontSizeXs};\n }\n\n .myio-demand-modal-content {\n padding: ${styles.spacingMd};\n }\n }\n `;const style=document.createElement("style");style.textContent=css;document.head.appendChild(style);cssInjected=true}function formatDate2(date,locale){return date.toLocaleDateString(locale,{day:"2-digit",month:"2-digit",year:"numeric"})}function formatDateTime(date,locale){return date.toLocaleDateString(locale,{day:"2-digit",month:"2-digit",year:"numeric"})}function interpolateTemperatureData(rawPoints){if(rawPoints.length===0)return[];const interpolated=[];const interval=30*60*1e3;const sorted=[...rawPoints].sort((a,b)=>a.x-b.x);const startTime=sorted[0].x;const endTime=sorted[sorted.length-1].x;let lastKnownValue=sorted[0].y;let dataIndex=0;for(let time=startTime;time<=endTime;time+=interval){const actualPoint=sorted.find((p,idx)=>{if(idx>=dataIndex&&Math.abs(p.x-time)<5*60*1e3){dataIndex=idx+1;return true}return false});if(actualPoint){lastKnownValue=actualPoint.y;interpolated.push({x:time,y:lastKnownValue})}else{interpolated.push({x:time,y:lastKnownValue})}}return interpolated}async function fetchTelemetryData(token,deviceId,startDate,endDate,queryParams){const startTs=new Date(startDate).getTime();const endTs=new Date(endDate).getTime();const keys=queryParams?.keys||"consumption";const intervalType=queryParams?.intervalType||"MILLISECONDS";const interval=queryParams?.interval||864e5;const agg=queryParams?.agg||"MAX";const orderBy=queryParams?.orderBy||"ASC";let url=`/api/plugins/telemetry/DEVICE/${deviceId}/values/timeseries?keys=${keys}&startTs=${startTs}&endTs=${endTs}&intervalType=${intervalType}&interval=${interval}&agg=${agg}&orderBy=${orderBy}`;if(agg==="NONE"&&queryParams?.limit){url+=`&limit=${queryParams.limit}`}const response=await fetch(url,{headers:{"X-Authorization":`Bearer ${token}`,"Content-Type":"application/json"}});if(!response.ok){throw new Error(`HTTP ${response.status}: ${response.statusText}`)}const data=await response.json();return data}function processMultiSeriesChartData(rawData,keys,correctionFactor,locale,aggregation,timezoneOffset){const seriesKeys=keys.split(",").map(k=>k.trim());const seriesData=[];let globalPeak=null;let isEmpty=true;const tzOffset=timezoneOffset!==void 0?timezoneOffset:-3;const tzOffsetMs=tzOffset*60*60*1e3;const colors=["#4A148C","#2196F3","#4CAF50","#FF9800","#F44336","#9C27B0","#795548","#607D8B"];seriesKeys.forEach((key,index)=>{const rawSeries=rawData[key]||[];if(rawSeries.length===0){seriesData.push({key:key,label:key,points:[],peak:null,color:colors[index%colors.length]});return}isEmpty=false;const sortedData=rawSeries.sort((a,b)=>a.ts-b.ts);const points=[];const isAggregated=aggregation!=="NONE";if(isAggregated){for(let i=0;i<sortedData.length;i++){const current=sortedData[i];const value=parseFloat(current.value)*correctionFactor;const timestamp=current.ts+tzOffsetMs;points.push({x:timestamp,y:value})}}else{let previousValue=0;let previousTs=0;for(let i=0;i<sortedData.length;i++){const current=sortedData[i];const currentValue=parseFloat(current.value);const currentTs=current.ts;if(i>0){const deltaWh=currentValue-previousValue;const deltaHours=(currentTs-previousTs)/(1e3*60*60);if(deltaWh>0&&deltaHours>0){const demandKw=deltaWh/1e3/deltaHours*correctionFactor;const timestamp=currentTs+tzOffsetMs;points.push({x:timestamp,y:demandKw})}}previousValue=currentValue;previousTs=currentTs}}let seriesPeak=null;if(points.length>0){const maxPoint=points.reduce((max,point)=>point.y>max.y?point:max);seriesPeak={value:maxPoint.y,timestamp:maxPoint.x,formattedValue:maxPoint.y.toFixed(2),formattedTime:formatDateTime(new Date(maxPoint.x),locale),key:key};if(!globalPeak||seriesPeak.value>globalPeak.value){globalPeak=seriesPeak}}seriesData.push({key:key,label:key,points:points,peak:seriesPeak,color:colors[index%colors.length]})});return{series:seriesData,globalPeak:globalPeak,isEmpty:isEmpty}}function createFocusTrap(container){const focusableElements=container.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');const firstElement=focusableElements[0];const lastElement=focusableElements[focusableElements.length-1];function handleTabKey(e){if(e.key!=="Tab")return;if(e.shiftKey){if(document.activeElement===firstElement){lastElement.focus();e.preventDefault()}}else{if(document.activeElement===lastElement){firstElement.focus();e.preventDefault()}}}container.addEventListener("keydown",handleTabKey);firstElement?.focus();return()=>{container.removeEventListener("keydown",handleTabKey)}}async function openDemandModal(params){if(!params.token||!params.deviceId||!params.startDate||!params.endDate){throw new Error("Missing required parameters: token, deviceId, startDate, endDate")}const styles={...DEFAULT_STYLES,...params.styles};const locale=params.locale||"pt-BR";const strings=STRINGS2[locale]||STRINGS2["pt-BR"];await loadExternalLibraries();injectCSS(styles);const container=typeof params.container==="string"?document.querySelector(params.container):params.container||document.body;if(!container){throw new Error("Container element not found")}const overlay=document.createElement("div");overlay.className="myio-demand-modal-overlay";overlay.setAttribute("role","dialog");overlay.setAttribute("aria-modal","true");overlay.setAttribute("aria-labelledby","modal-title");const label=params.label||"Dispositivo";const allowTelemetrySwitch=params.allowTelemetrySwitch!==false;const currentTelemetryType=detectTelemetryType(params.telemetryQuery?.keys);const availableTypes=params.availableTelemetryTypes?Object.values(TELEMETRY_TYPES).filter(type=>params.availableTelemetryTypes.includes(type.id)):Object.values(TELEMETRY_TYPES);const telemetrySelectorOptions=availableTypes.map(type=>`<option value="${type.id}" ${type.id===currentTelemetryType.id?"selected":""}>${type.label}</option>`).join("");const telemetrySelectorHTML=allowTelemetrySwitch?`\n <label>\n ${strings.telemetryType}:\n <select id="telemetry-type-select" class="myio-demand-modal-select myio-demand-modal-date-input" aria-label="${strings.telemetryType}">\n ${telemetrySelectorOptions}\n </select>\n </label>\n `:"";overlay.innerHTML=`\n <div class="myio-demand-modal-card">\n <div class="myio-demand-modal-header">\n <div class="myio-demand-modal-title-section">\n <span class="myio-demand-modal-icon">⚡</span>\n <h2 id="modal-title" class="myio-demand-modal-title">${strings.title} – ${label}</h2>\n </div>\n <div class="myio-demand-modal-actions">\n <button class="myio-demand-modal-btn myio-demand-modal-btn-pdf" type="button" style="display: none;">\n ${strings.exportPdf}\n </button>\n <button class="myio-demand-modal-btn myio-demand-modal-btn-csv" type="button">\n ${strings.exportCsv}\n </button>\n <button class="myio-demand-modal-btn myio-demand-modal-btn-fullscreen" type="button" aria-label="${strings.fullscreen}">\n ⛶\n </button>\n <button class="myio-demand-modal-btn myio-demand-modal-btn-close" type="button" aria-label="${strings.close}">\n ×\n </button>\n </div>\n </div>\n\n <div class="myio-demand-modal-period-selector">\n <label>\n ${strings.startDate}:\n <input type="date" class="myio-demand-modal-date-input myio-demand-modal-date-start" />\n </label>\n <label>\n ${strings.endDate}:\n <input type="date" class="myio-demand-modal-date-input myio-demand-modal-date-end" />\n </label>\n ${telemetrySelectorHTML}\n <label>\n Intervalo:\n <select id="demand-interval-select" class="myio-demand-modal-select myio-demand-modal-date-input" aria-label="Intervalo">\n <option value="86400000">24 horas</option>\n <option value="3600000">1 hora</option>\n <option value="60000">1 minuto (60s)</option>\n </select>\n </label>\n <label>\n Agregador:\n <select id="demand-agg-select" class="myio-demand-modal-select myio-demand-modal-date-input" aria-label="Agregador">\n <option value="MAX">Máximo</option>\n <option value="AVG">Média</option>\n </select>\n </label>\n <button class="myio-demand-modal-btn-update" type="button">\n ${strings.updatePeriod}\n </button>\n \x3c!-- RFC-0084: REAL TIME button removed - use RealTimeTelemetryModal instead --\x3e\n <div class="myio-demand-modal-period-error" style="display: none;"></div>\n </div>\n\n <div class="myio-demand-modal-peak" style="display: none;"></div>\n \n <div class="myio-demand-modal-content">\n <div class="myio-demand-modal-chart-container">\n <canvas class="myio-demand-modal-chart"></canvas>\n </div>\n \n <div class="myio-demand-modal-zoom-controls">\n <button class="myio-demand-modal-btn-reset" type="button">\n ${strings.resetZoom}\n </button>\n <div class="myio-demand-modal-zoom-help">\n <small>${strings.zoomHelp}</small>\n </div>\n </div>\n </div>\n \n <div class="myio-demand-modal-loading">\n <div class="myio-demand-modal-spinner">⧗</div>\n <div class="myio-demand-modal-loading-text">${strings.loading}</div>\n </div>\n \n <div class="myio-demand-modal-error" style="display: none;">\n <div class="myio-demand-modal-error-icon">⚠</div>\n <div class="myio-demand-modal-error-text"></div>\n </div>\n </div>\n `;container.appendChild(overlay);const card=overlay.querySelector(".myio-demand-modal-card");const closeBtn=overlay.querySelector(".myio-demand-modal-btn-close");const fullscreenBtn=overlay.querySelector(".myio-demand-modal-btn-fullscreen");const pdfBtn=overlay.querySelector(".myio-demand-modal-btn-pdf");const csvBtn=overlay.querySelector(".myio-demand-modal-btn-csv");const resetBtn=overlay.querySelector(".myio-demand-modal-btn-reset");const chartCanvas=overlay.querySelector(".myio-demand-modal-chart");const loadingEl=overlay.querySelector(".myio-demand-modal-loading");const errorEl=overlay.querySelector(".myio-demand-modal-error");const errorText=overlay.querySelector(".myio-demand-modal-error-text");const peakEl=overlay.querySelector(".myio-demand-modal-peak");const contentEl=overlay.querySelector(".myio-demand-modal-content");const dateStartInput=overlay.querySelector(".myio-demand-modal-date-start");const dateEndInput=overlay.querySelector(".myio-demand-modal-date-end");const updateBtn=overlay.querySelector(".myio-demand-modal-btn-update");const periodErrorEl=overlay.querySelector(".myio-demand-modal-period-error");const telemetryTypeSelect=overlay.querySelector("#telemetry-type-select");const intervalSelect=overlay.querySelector("#demand-interval-select");const aggSelect=overlay.querySelector("#demand-agg-select");let chart=null;let chartData=null;let isFullscreen=false;let currentStartDate=params.startDate;let currentEndDate=params.endDate;let activeTelemetryType=currentTelemetryType;const originalOverflow=document.body.style.overflow;document.body.style.overflow="hidden";const releaseFocusTrap=createFocusTrap(overlay);function closeModal(){if(chart){chart.destroy()}overlay.remove();document.body.style.overflow=originalOverflow;releaseFocusTrap();params.onClose?.()}function toggleFullscreen(){isFullscreen=!isFullscreen;card.classList.toggle("fullscreen",isFullscreen);if(chart){setTimeout(()=>chart.resize(),100)}}function resetZoom(){if(chart){chart.resetZoom()}}function exportCsv(){if(!chartData){alert("Nenhum dado disponível para exportar");return}const btn=csvBtn;const originalHtml=btn.innerHTML;btn.disabled=true;btn.innerHTML="<span>⏳</span> Gerando CSV...";try{const BOM="\ufeff";let csv=BOM+"Data,Série,Valor (kW)\n";chartData.series.forEach(series=>{series.points.forEach(point=>{const dateStr2=formatDate2(new Date(point.x),locale);const value=point.y.toFixed(2);csv+=`${dateStr2},${series.label},${value}\n`})});const blob=new Blob([csv],{type:"text/csv;charset=utf-8;"});const url=URL.createObjectURL(blob);const link=document.createElement("a");link.href=url;const labelSafe=label.replace(/\s+/g,"_");const dateStr=(new Date).toISOString().slice(0,10);link.download=`demanda_${labelSafe}_${dateStr}.csv`;document.body.appendChild(link);link.click();document.body.removeChild(link);URL.revokeObjectURL(url)}catch(error){console.error("[CSV Export] Error:",error);alert("Erro ao gerar CSV. Por favor, tente novamente.")}finally{btn.disabled=false;btn.innerHTML=originalHtml}}async function exportPdf(){if(!chartData||!chart){alert("Nenhum dado disponível para exportar");return}const btn=pdfBtn;const originalHtml=btn.innerHTML;btn.disabled=true;btn.innerHTML="<span>⏳</span> Gerando PDF...";try{await ensureJsPDF();await(document.fonts?.ready?.catch(e=>console.warn("Font loading interrupted or failed:",e)));const JsPDF=getJsPDFCtor();const doc=new JsPDF("p","mm","a4");doc.setFontSize(20);doc.setTextColor(74,20,140);doc.text(strings.reportTitle,20,20);doc.setFontSize(14);doc.setTextColor(0,0,0);const label2=params.label||"Dispositivo";doc.text(`${strings.title} - ${label2}`,20,35);const startDate=formatDate2(new Date(params.startDate),params.locale||"pt-BR");const endDate=formatDate2(new Date(params.endDate),params.locale||"pt-BR");doc.text(`${strings.period}: ${startDate} - ${endDate}`,20,45);let currentY=55;if(chartData.globalPeak){const peak=chartData.globalPeak;doc.setFontSize(12);doc.setTextColor(255,152,0);doc.text(`${strings.maximum}: ${peak.formattedValue} kW ${peak.key?`(${peak.key}) `:""}${strings.at} ${peak.formattedTime}`,20,currentY);currentY+=10}currentY=ensureRoom(doc,currentY,120);currentY=addCanvasToPdf(doc,chartCanvas,20,currentY);currentY+=10;if(chartData.series.length>0&&chartData.series[0].points.length>0){currentY=ensureRoom(doc,currentY,60);doc.setFontSize(12);doc.setTextColor(0,0,0);doc.text("Amostra de Dados:",20,currentY);currentY+=10;const samplePoints=chartData.series[0].points.slice(0,10);doc.setFontSize(10);doc.text("Data",20,currentY);doc.text(params.yAxisLabel||strings.demand,100,currentY);currentY+=7;samplePoints.forEach(point=>{currentY=ensureRoom(doc,currentY,10);const dateStr=formatDate2(new Date(point.x),params.locale||"pt-BR");doc.text(dateStr,20,currentY);doc.text(point.y.toFixed(2),100,currentY);currentY+=7})}currentY=ensureRoom(doc,currentY,20);doc.setFontSize(8);doc.setTextColor(128,128,128);doc.text(`${strings.reportFooter}`,20,doc.internal.pageSize.getHeight()-15);doc.text(`Gerado em: ${(new Date).toLocaleString(params.locale||"pt-BR")}`,20,doc.internal.pageSize.getHeight()-10);const fileName=params.pdf?.fileName||`demanda_${label2.replace(/\s+/g,"_")}_${(new Date).toISOString().slice(0,10)}.pdf`;savePdfSafe(doc,fileName)}catch(error){console.error("[PDF Export] Error:",error);alert("Erro ao gerar PDF. Por favor, tente novamente. Verifique o console para mais detalhes.")}finally{btn.disabled=false;btn.innerHTML=originalHtml}}function initializeDateInputs(){const startDate=new Date(currentStartDate);const endDate=new Date(currentEndDate);const formatLocalDate=date=>{const year=date.getFullYear();const month=String(date.getMonth()+1).padStart(2,"0");const day=String(date.getDate()).padStart(2,"0");return`${year}-${month}-${day}`};dateStartInput.value=formatLocalDate(startDate);dateEndInput.value=formatLocalDate(endDate)}function initializeQuerySelects(){if(intervalSelect&¶ms.telemetryQuery?.interval){intervalSelect.value=params.telemetryQuery.interval.toString()}if(aggSelect&¶ms.telemetryQuery?.agg){aggSelect.value=params.telemetryQuery.agg}}function debounce(func,wait){let timeout=null;return function(...args){const context=this;if(timeout)clearTimeout(timeout);timeout=setTimeout(()=>func.apply(context,args),wait)}}async function switchTelemetryType(newTypeId){const newType=TELEMETRY_TYPES[newTypeId];if(!newType||newType.id===activeTelemetryType.id){return}try{loadingEl.style.display="flex";contentEl.style.display="none";errorEl.style.display="none";peakEl.style.display="none";activeTelemetryType=newType;await loadData()}catch(error){console.error("[DemandModal] Error switching telemetry type:",error);errorEl.style.display="flex";errorText.textContent=`${strings.error}: ${error instanceof Error?error.message:"Unknown error"}`;loadingEl.style.display="none"}}async function updatePeriod(){periodErrorEl.style.display="none";const newStartDate=dateStartInput.value;const newEndDate=dateEndInput.value;if(!newStartDate||!newEndDate){periodErrorEl.textContent=strings.error;periodErrorEl.style.display="block";return}const startDateObj=new Date(newStartDate);const endDateObj=new Date(newEndDate);if(endDateObj<startDateObj){periodErrorEl.textContent=strings.invalidDateRange;periodErrorEl.style.display="block";return}const diffTime=Math.abs(endDateObj.getTime()-startDateObj.getTime());const diffDays=Math.ceil(diffTime/(1e3*60*60*24));if(diffDays>30){periodErrorEl.textContent=strings.maxRangeExceeded;periodErrorEl.style.display="block";return}currentStartDate=startDateObj.toISOString();currentEndDate=endDateObj.toISOString();await loadData()}closeBtn.addEventListener("click",closeModal);fullscreenBtn.addEventListener("click",toggleFullscreen);resetBtn.addEventListener("click",resetZoom);pdfBtn.addEventListener("click",exportPdf);csvBtn.addEventListener("click",exportCsv);updateBtn.addEventListener("click",updatePeriod);if(telemetryTypeSelect&&allowTelemetrySwitch){const debouncedSwitch=debounce(switchTelemetryType,300);telemetryTypeSelect.addEventListener("change",e=>{const newTypeId=e.target.value;debouncedSwitch(newTypeId)})}if(intervalSelect){intervalSelect.addEventListener("change",()=>{loadData()})}if(aggSelect){aggSelect.addEventListener("change",()=>{loadData()})}overlay.addEventListener("click",e=>{if(e.target===overlay){closeModal()}});overlay.addEventListener("keydown",e=>{if(e.key==="Escape"){closeModal()}});async function loadData(){try{loadingEl.style.display="flex";contentEl.style.display="none";errorEl.style.display="none";peakEl.style.display="none";const startDateObj=new Date(currentStartDate);const endDateObj=new Date(currentEndDate);const diffTime=Math.abs(endDateObj.getTime()-startDateObj.getTime());const diffDays=Math.ceil(diffTime/(1e3*60*60*24));if(diffDays>30){loadingEl.style.display="none";errorEl.style.display="flex";errorText.textContent="O Limite de busca é de 30 dias de intervalo.";return}const keysStr=Array.isArray(activeTelemetryType.keys)?activeTelemetryType.keys.join(","):activeTelemetryType.keys;const selectedInterval=intervalSelect?parseInt(intervalSelect.value):864e5;const selectedAgg=aggSelect?aggSelect.value:"MAX";let queryLimit=params.telemetryQuery?.limit||1e4;if(selectedInterval===6e4){queryLimit=1440}const telemetryQuery={...params.telemetryQuery,keys:keysStr,interval:selectedInterval,agg:selectedAgg,limit:queryLimit};const cacheKey=getCacheKey({deviceId:params.deviceId,startDate:currentStartDate,endDate:currentEndDate,keys:keysStr,agg:telemetryQuery.agg,interval:telemetryQuery.interval||864e5});let rawData=getCachedData(cacheKey);if(!rawData){rawData=params.fetcher?await params.fetcher({token:params.token,deviceId:params.deviceId,startDate:currentStartDate,endDate:currentEndDate,telemetryQuery:telemetryQuery}):await fetchTelemetryData(params.token,params.deviceId,currentStartDate,currentEndDate,telemetryQuery);setCachedData(cacheKey,rawData)}chartData=processMultiSeriesChartData(rawData,keysStr,params.correctionFactor||1,locale,telemetryQuery.agg||"MAX",params.timezoneOffset);if(params.readingType==="temperature"&&!chartData.isEmpty){chartData.series=chartData.series.map(series=>({...series,points:interpolateTemperatureData(series.points)}))}if(chartData.isEmpty){errorEl.style.display="flex";errorText.textContent=strings.noData;return}if(chartData.globalPeak){const peak=chartData.globalPeak;const date=new Date(peak.timestamp);const dateStr=date.toLocaleDateString(locale,{day:"2-digit",month:"2-digit",year:"numeric"});peakEl.textContent=`${strings.maximum}: ${peak.formattedValue} kW ${peak.key?`(${peak.key}) `:""}${strings.at} ${dateStr}`;peakEl.style.display="block"}const Chart2=window.Chart;Chart2.register(window.ChartZoom);if(chart){chart.data.datasets=chartData.series.map(series=>({label:series.label,data:series.points,borderColor:series.color,backgroundColor:series.color+"CC",borderWidth:1}));chart.options.plugins.legend.display=chartData.series.length>1;chart.options.plugins.tooltip.callbacks={title:function(context){const timestamp=context[0].parsed.x;const date=new Date(timestamp);if(params.readingType==="temperature"){return date.toLocaleString(locale,{day:"2-digit",month:"2-digit",year:"numeric",hour:"2-digit",minute:"2-digit"})}else{return date.toLocaleDateString(locale,{day:"2-digit",month:"2-digit",year:"numeric"})}},label:function(context){const seriesLabel=context.dataset.label||"";const value=context.parsed.y;return`${seriesLabel}: ${value.toFixed(2)} kW`}};chart.options.scales.x.ticks.callback=function(value){const date=new Date(value);if(params.readingType==="temperature"){return date.toLocaleTimeString(locale,{hour:"2-digit",minute:"2-digit"})}else{return date.toLocaleDateString(locale,{day:"2-digit",month:"2-digit"})}};chart.update()}else{chart=new Chart2(chartCanvas,{type:"line",data:{datasets:chartData.series.map(series=>({label:series.label,data:series.points,borderColor:series.color,backgroundColor:series.color+"33",borderWidth:2,fill:true,tension:.4}))},options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{display:chartData.series.length>1,position:"top"},tooltip:{callbacks:{title:function(context){const timestamp=context[0].parsed.x;const date=new Date(timestamp);if(params.readingType==="temperature"){return date.toLocaleString(locale,{day:"2-digit",month:"2-digit",year:"numeric",hour:"2-digit",minute:"2-digit"})}else{return date.toLocaleDateString(locale,{day:"2-digit",month:"2-digit",year:"numeric"})}},label:function(context){const seriesLabel=context.dataset.label||"";const value=context.parsed.y;return`${seriesLabel}: ${value.toFixed(2)} kW`}}},zoom:{zoom:{wheel:{enabled:true},pinch:{enabled:true},drag:{enabled:true,modifierKey:"ctrl"},mode:"x"},pan:{enabled:true,mode:"x"}}},scales:{x:{type:"linear",position:"bottom",title:{display:true,text:"Tempo"},ticks:{callback:function(value){const date=new Date(value);if(params.readingType==="temperature"){return date.toLocaleTimeString(locale,{hour:"2-digit",minute:"2-digit"})}else{return date.toLocaleDateString(locale,{day:"2-digit",month:"2-digit"})}}}},y:{title:{display:true,text:params.yAxisLabel||strings.demand},beginAtZero:true}},interaction:{intersect:false,mode:"index"}}})}loadingEl.style.display="none";contentEl.style.display="flex"}catch(error){console.error("Error loading demand data:",error);loadingEl.style.display="none";errorEl.style.display="flex";errorText.textContent=`${strings.error}: ${error instanceof Error?error.message:"Unknown error"}`}}initializeDateInputs();initializeQuerySelects();loadData();return{destroy:closeModal}}var DEFAULT_I18N2={title:"Dashboard - Gráfico",loading:"Carregando dados...",error:"Ocorreu um erro ao carregar os dados",noData:"Nenhum dado disponível para o período selecionado",exportCsv:"Exportar CSV",close:"Fechar",totalConsumption:"Consumo total",averageDaily:"Média diária",peakDay:"Pico do dia",dateRange:"Período",deviceSummary:"Detalhes do dispositivo",energyChart:"Consumo de Energia",kwhUnit:"kWh"};var DEFAULT_STYLES2={primaryColor:"#6366f1",backgroundColor:"#ffffff",textColor:"#1f2937",borderColor:"#e5e7eb",borderRadius:"8px",fontFamily:'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',modalWidth:"90vw",modalHeight:"90vh"};var EnergyModalView=class{modal;container=null;chartContainer=null;config;currentEnergyData=null;dateRangePicker=null;isLoading=false;currentTheme="dark";currentBarMode="stacked";currentGranularity="1d";constructor(modal,config){this.modal=modal;this.config=config;this.initializeTheme();this.initializeBarMode();this.initializeGranularity();this.validateConfiguration();this.render()}initializeTheme(){const savedTheme=localStorage.getItem("myio-modal-theme");this.currentTheme=savedTheme||this.config.params.theme||"dark"}initializeBarMode(){const savedBarMode=localStorage.getItem("myio-modal-bar-mode");this.currentBarMode=savedBarMode||"stacked"}initializeGranularity(){const savedGranularity=localStorage.getItem("myio-modal-granularity");const configGranularity=this.config.params.granularity;const candidate=savedGranularity||configGranularity||"1d";this.currentGranularity=candidate==="1h"||candidate==="1d"?candidate:"1d"}setGranularity(granularity){if(this.currentGranularity===granularity)return;this.currentGranularity=granularity;const buttons=document.querySelectorAll(".myio-btn-granularity");buttons.forEach(btn=>{const btnEl=btn;if(btnEl.dataset.granularity===granularity){btnEl.classList.add("active")}else{btnEl.classList.remove("active")}});localStorage.setItem("myio-modal-granularity",granularity);this.reRenderChart();console.log("[EnergyModalView] [RFC-0097] Granularity changed to:",granularity)}calculateSuggestedGranularity(startDate,endDate){const start=new Date(startDate);const end=new Date(endDate);const diffDays=Math.ceil((end.getTime()-start.getTime())/(1e3*60*60*24));if(diffDays<=1)return"1h";return"1d"}applyGranularityUI(){const buttons=document.querySelectorAll(".myio-btn-granularity");buttons.forEach(btn=>{const btnEl=btn;if(btnEl.dataset.granularity===this.currentGranularity){btnEl.classList.add("active")}else{btnEl.classList.remove("active")}})}toggleTheme(){this.currentTheme=this.currentTheme==="dark"?"light":"dark";localStorage.setItem("myio-modal-theme",this.currentTheme);this.applyTheme();console.log("[EnergyModalView] Theme toggled to:",this.currentTheme)}toggleBarMode(){this.currentBarMode=this.currentBarMode==="stacked"?"grouped":"stacked";localStorage.setItem("myio-modal-bar-mode",this.currentBarMode);this.applyBarMode();console.log("[EnergyModalView] Bar mode toggled to:",this.currentBarMode)}applyTheme(){const themeToggleBtn=document.getElementById("theme-toggle-btn");const modalContent=this.container;const externalBody=this.container?.closest(".myio-modal-body")||document.querySelector(".myio-modal-body");const rootDiv=document.querySelector("#root > div");if(themeToggleBtn){const sunIcon=themeToggleBtn.querySelector(".myio-theme-icon-sun");const moonIcon=themeToggleBtn.querySelector(".myio-theme-icon-moon");if(this.currentTheme==="light"){if(sunIcon){sunIcon.style.opacity="1";sunIcon.style.transform="translate(-50%, -50%) rotate(0deg) scale(1)"}if(moonIcon){moonIcon.style.opacity="0";moonIcon.style.transform="translate(-50%, -50%) rotate(90deg) scale(0)"}if(externalBody){externalBody.style.backgroundColor="#ffffff";externalBody.style.color="#1f2937"}if(rootDiv){rootDiv.style.backgroundColor="#ffffff";rootDiv.style.color="#1f2937"}}else{if(sunIcon){sunIcon.style.opacity="0";sunIcon.style.transform="translate(-50%, -50%) rotate(-90deg) scale(0)"}if(moonIcon){moonIcon.style.opacity="1";moonIcon.style.transform="translate(-50%, -50%) rotate(0deg) scale(1)"}if(externalBody){externalBody.style.backgroundColor="#1f1f1f";externalBody.style.color="#f3f4f6"}if(rootDiv){rootDiv.style.backgroundColor="#1f1f1f";rootDiv.style.color="#f3f4f6"}}}if(modalContent){modalContent.setAttribute("data-theme",this.currentTheme)}this.reRenderChart()}applyBarMode(){const barModeToggleBtn=document.getElementById("bar-mode-toggle-btn");if(barModeToggleBtn){const stackedIcon=barModeToggleBtn.querySelector(".myio-bar-mode-icon-stacked");const groupedIcon=barModeToggleBtn.querySelector(".myio-bar-mode-icon-grouped");if(this.currentBarMode==="grouped"){if(groupedIcon){groupedIcon.style.opacity="1";groupedIcon.style.transform="translate(-50%, -50%) scale(1)"}if(stackedIcon){stackedIcon.style.opacity="0";stackedIcon.style.transform="translate(-50%, -50%) scale(0)"}}else{if(stackedIcon){stackedIcon.style.opacity="1";stackedIcon.style.transform="translate(-50%, -50%) scale(1)"}if(groupedIcon){groupedIcon.style.opacity="0";groupedIcon.style.transform="translate(-50%, -50%) scale(0)"}}}this.reRenderChart()}reRenderChart(){const mode=this.config.params.mode||"single";console.log("[EnergyModalView] reRenderChart called, mode:",mode,"barMode:",this.currentBarMode,"theme:",this.currentTheme);if(mode==="comparison"){console.log("[EnergyModalView] Calling renderComparisonChart...");const result=this.renderComparisonChart();console.log("[EnergyModalView] renderComparisonChart result:",result)}else{if(this.currentEnergyData){this.renderChart(this.currentEnergyData)}}}validateConfiguration(){const mode=this.config.params.mode||"single";if(mode==="single"){if(!this.config.params.deviceId){console.error("[EnergyModalView] deviceId is required for single mode");throw new Error("deviceId is required for single mode")}}else if(mode==="comparison"){if(!this.config.params.dataSources||this.config.params.dataSources.length===0){console.error("[EnergyModalView] dataSources is required for comparison mode");throw new Error("dataSources is required for comparison mode with at least 1 device")}if(this.config.params.dataSources.length<2){console.warn("[EnergyModalView] Comparison with less than 2 devices")}if(!this.config.params.granularity){console.error("[EnergyModalView] granularity is required for comparison mode");throw new Error("granularity is required for comparison mode")}}}getModalTitle(){const mode=this.config.params.mode||"single";if(mode==="comparison"){const count=this.config.params.dataSources?.length||0;return`Comparação de ${count} Dispositivos`}else{const{device:device}=this.config.context;const label=device.label||device.id||"Dispositivo";return`Consumo - ${label}`}}render(){const content=this.createModalContent();this.modal.setContent(content);this.setupEventListeners()}createModalContent(){const container=document.createElement("div");container.className="myio-energy-modal-scope";this.getModalTitle();container.innerHTML=`\n <style>\n ${this.getModalStyles()}\n </style>\n <div class="myio-modal-scope" style="height: 100%; display: flex; flex-direction: column;">\n \x3c!-- Controls Section --\x3e\n <div style="margin-bottom: 16px; flex-shrink: 0;">\n <div style="display: flex; gap: 16px; align-items: end; margin-bottom: 16px;">\n <div class="myio-form-group" style="margin-bottom: 0;">\n <label class="myio-label" for="date-range">Período</label>\n <input type="text" id="date-range" class="myio-input" readonly placeholder="Selecione o período" style="width: 300px;">\n </div>\n <button id="load-btn" class="myio-btn myio-btn-primary">\n <span class="myio-spinner" id="load-spinner" style="display: none;"></span>\n Carregar\n </button>\n <button id="export-csv-btn" class="myio-btn myio-btn-secondary" disabled>\n Exportar CSV\n </button>\n ${this.config.params.readingType==="energy"&&this.config.params.mode!=="comparison"?`\n <button id="view-demand-btn" class="myio-btn myio-btn-secondary" style="\n background: linear-gradient(135deg, #1976D2 0%, #2196F3 100%);\n color: white;\n border: none;\n transition: all 0.3s ease;\n box-shadow: 0 2px 8px rgba(25, 118, 210, 0.3);\n ">\n <span style="font-size: 16px; margin-right: 4px;">📊</span>\n Pico de Demanda\n </button>\n <button id="view-telemetry-btn" class="myio-btn myio-btn-secondary" style="\n background: linear-gradient(135deg, #4A148C 0%, #6A1B9A 100%);\n color: white;\n border: none;\n transition: all 0.3s ease;\n box-shadow: 0 2px 8px rgba(74, 20, 140, 0.3);\n ">\n <span style="font-size: 16px; margin-right: 4px;">⚡</span>\n Telemetrias Instantâneas\n </button>\n `:""}\n <button id="theme-toggle-btn" class="myio-btn myio-btn-secondary" title="Alternar tema (claro/escuro)" style="\n position: relative;\n width: 40px;\n height: 40px;\n padding: 0;\n overflow: hidden;\n ">\n <svg class="myio-theme-icon-sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%) rotate(-90deg) scale(0);\n width: 18px;\n height: 18px;\n opacity: 0;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n ">\n <circle cx="12" cy="12" r="5"></circle>\n <line x1="12" y1="1" x2="12" y2="3"></line>\n <line x1="12" y1="21" x2="12" y2="23"></line>\n <line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>\n <line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>\n <line x1="1" y1="12" x2="3" y2="12"></line>\n <line x1="21" y1="12" x2="23" y2="12"></line>\n <line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>\n <line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>\n </svg>\n <svg class="myio-theme-icon-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%) rotate(0deg) scale(1);\n width: 18px;\n height: 18px;\n opacity: 1;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n ">\n <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>\n </svg>\n </button>\n ${this.config.params.mode==="comparison"&&this.config.params.readingType!=="temperature"?`\n <button id="bar-mode-toggle-btn" class="myio-btn myio-btn-secondary" title="Alternar modo (empilhado/agrupado)" style="\n position: relative;\n width: 40px;\n height: 40px;\n padding: 0;\n overflow: hidden;\n ">\n <svg class="myio-bar-mode-icon-stacked" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%) scale(1);\n width: 18px;\n height: 18px;\n opacity: 1;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n ">\n <rect x="3" y="13" width="18" height="8"></rect>\n <rect x="3" y="7" width="18" height="5"></rect>\n <rect x="3" y="3" width="18" height="3"></rect>\n </svg>\n <svg class="myio-bar-mode-icon-grouped" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%) scale(0);\n width: 18px;\n height: 18px;\n opacity: 0;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n ">\n <rect x="2" y="10" width="5" height="11"></rect>\n <rect x="9.5" y="5" width="5" height="16"></rect>\n <rect x="17" y="8" width="5" height="13"></rect>\n </svg>\n </button>\n `:""}\n ${this.config.params.mode==="comparison"?`\n \x3c!-- RFC-0097: Granularity Selector (only 1h and 1d supported) --\x3e\n <div class="myio-granularity-selector" style="display: flex; align-items: center; gap: 4px; margin-left: 8px; padding: 4px 8px; background: rgba(0,0,0,0.05); border-radius: 8px;">\n <span class="myio-label-secondary" style="font-size: 11px; margin-right: 4px; white-space: nowrap;">Granularidade:</span>\n <button class="myio-btn myio-btn-granularity ${this.currentGranularity==="1h"?"active":""}" data-granularity="1h" title="Hora">1h</button>\n <button class="myio-btn myio-btn-granularity ${this.currentGranularity==="1d"?"active":""}" data-granularity="1d" title="Dia">1d</button>\n </div>\n `:""}\n <button id="close-btn" class="myio-btn myio-btn-secondary">\n Fechar\n </button>\n </div>\n </div>\n \n \x3c!-- Error Container --\x3e\n <div id="energy-error" class="myio-energy-error" style="display: none; flex-shrink: 0;">\n \x3c!-- Error messages will be displayed here --\x3e\n </div>\n \n \x3c!-- Main Chart Container - Full Width --\x3e\n <div id="energy-chart-container" class="myio-energy-chart-container" style="width: 100%; flex: 1; box-sizing: border-box;">\n <div class="myio-loading-state">\n <div class="myio-spinner"></div>\n <p>${this.getI18nText("loading")}</p>\n </div>\n </div>\n \n \x3c!-- KPI Button Section --\x3e\n <div id="energy-kpi-btn" style="display: none; margin-top: 16px; text-align: center; flex-shrink: 0;">\n <button id="show-kpis-btn" class="myio-btn myio-btn-secondary" title="Show detailed metrics" style="display: none;">\n <span style="font-size: 16px; font-weight: bold;">+</span>\n <span style="margin-left: 8px;">Show Metrics</span>\n </button>\n </div>\n \n </div>\n `;this.container=container;this.chartContainer=container.querySelector("#energy-chart-container");return container}showLoadingState(){if(this.chartContainer){this.chartContainer.innerHTML=`\n <div class="myio-loading-state">\n <div class="myio-spinner"></div>\n <p>${this.getI18nText("loading")}</p>\n </div>\n `}this.hideError();this.hideKPIButton()}showError(message){const errorContainer=document.getElementById("energy-error");if(errorContainer){errorContainer.innerHTML=`\n <div class="myio-error-content">\n <div class="myio-error-icon">⚠️</div>\n <div class="myio-error-message">${message}</div>\n </div>\n `;errorContainer.style.display="block"}this.hideLoadingState()}renderEnergyData(energyData){this.currentEnergyData=energyData;this.hideLoadingState();this.hideError();this.renderChart(energyData);this.showKPIButton();const exportBtn=document.getElementById("export-csv-btn");if(exportBtn){exportBtn.disabled=false}}renderChart(energyData){if(!this.chartContainer)return;if(this.tryRenderWithSDK(energyData)){return}this.renderFallbackChart(energyData)}tryRenderWithSDK(energyData){const mode=this.config.params.mode||"single";if(mode==="single"){return this.renderSingleDeviceChart(energyData)}else if(mode==="comparison"){return this.renderComparisonChart()}return false}renderSingleDeviceChart(energyData){try{if(this.chartInstance&&typeof this.chartInstance.destroy==="function"){this.chartInstance.destroy();this.chartInstance=null}if(this.chartContainer){this.chartContainer.innerHTML=""}let renderTelemetryChart;if(typeof window!=="undefined"&&window.EnergyChartSDK&&typeof window.EnergyChartSDK.renderTelemetryChart==="function"){renderTelemetryChart=window.EnergyChartSDK.renderTelemetryChart}else{console.error("[EnergyModalView] EnergyChartSDK v2 (renderTelemetryChart) not loaded!");if(this.chartContainer){this.chartContainer.innerHTML='<div style="padding: 20px; text-align: center; color: red;">EnergyChartSDK v2 (renderTelemetryChart) not loaded. Check widget configuration and browser console.</div>'}return false}let startISO,endISO;if(this.dateRangePicker){const dates=this.dateRangePicker.getDates();startISO=dates.startISO;endISO=dates.endISO}else{startISO=this.normalizeToSaoPauloISO(this.config.params.startDate,false);endISO=this.normalizeToSaoPauloISO(this.config.params.endDate,true)}const tzIdentifier=this.config.params.timezone||"America/Sao_Paulo";const granularity=this.config.params.granularity||"1d";const ingestionId=this.config.context.resolved.ingestionId;console.log(`[EnergyModalView] Initializing v2 chart with: deviceId=${ingestionId}, startDate=${startISO}, endDate=${endISO}, granularity=${granularity}, theme=${this.currentTheme}, timezone=${tzIdentifier}`);const chartConfig={version:"v2",clientId:this.config.params.clientId||"ADMIN_DASHBOARD_CLIENT",clientSecret:this.config.params.clientSecret||"admin_dashboard_secret_2025",deviceId:ingestionId,readingType:this.config.params.readingType||"energy",startDate:startISO,endDate:endISO,granularity:granularity,theme:this.currentTheme,timezone:tzIdentifier,iframeBaseUrl:this.config.params.chartsBaseUrl||"https://graphs.apps.myio-bas.com",apiBaseUrl:this.config.params.dataApiHost||"https://api.data.apps.myio-bas.com"};this.chartInstance=renderTelemetryChart(this.chartContainer,chartConfig);if(this.chartInstance&&typeof this.chartInstance.on==="function"){this.chartInstance.on("drilldown",data=>{console.log("[EnergyModalView] v2 SDK Drilldown Event:",data)});this.chartInstance.on("error",errorData=>{console.error("[EnergyModalView] v2 SDK Error Event:",errorData);if(this.chartContainer){this.chartContainer.innerHTML=`<div style="padding: 20px; text-align: center; color: red;">v2 Chart Error: ${errorData.message||"Unknown error"}</div>`}})}else if(this.chartInstance){console.warn("[EnergyModalView] EnergyChartSDK v2 instance does not have an 'on' method for event listeners.")}return true}catch(error){console.warn("[EnergyModalView] EnergyChartSDK failed, using fallback:",error)}return false}renderComparisonChart(){const readingType=this.config.params.readingType||"energy";if(readingType==="temperature"){return this.renderTemperatureComparisonChart()}try{if(this.chartInstance&&typeof this.chartInstance.destroy==="function"){this.chartInstance.destroy();this.chartInstance=null}if(this.chartContainer){this.chartContainer.innerHTML=""}let renderTelemetryStackedChart;if(typeof window!=="undefined"&&window.EnergyChartSDK&&typeof window.EnergyChartSDK.renderTelemetryStackedChart==="function"){renderTelemetryStackedChart=window.EnergyChartSDK.renderTelemetryStackedChart}else{console.error("[EnergyModalView] renderTelemetryStackedChart not available in SDK");if(this.chartContainer){this.chartContainer.innerHTML='<div style="padding: 20px; text-align: center; color: red;">EnergyChartSDK renderTelemetryStackedChart not loaded. Check SDK version.</div>'}return false}let startDateStr,endDateStr;if(this.dateRangePicker){const dates=this.dateRangePicker.getDates();startDateStr=dates.startISO.split("T")[0];endDateStr=dates.endISO.split("T")[0]}else{const startDate=new Date(this.config.params.startDate);const endDate=new Date(this.config.params.endDate);startDateStr=startDate.toISOString().split("T")[0];endDateStr=endDate.toISOString().split("T")[0]}const tzIdentifier=this.config.params.timezone||"America/Sao_Paulo";const chartConfig={version:"v2",clientId:this.config.params.clientId||"ADMIN_DASHBOARD_CLIENT",clientSecret:this.config.params.clientSecret||"admin_dashboard_secret_2025",dataSources:this.config.params.dataSources,readingType:this.config.params.readingType||"energy",startDate:startDateStr,endDate:endDateStr,granularity:this.currentGranularity,theme:this.currentTheme,bar_mode:this.currentBarMode,timezone:tzIdentifier,iframeBaseUrl:this.config.params.chartsBaseUrl||"https://graphs.apps.myio-bas.com",apiBaseUrl:this.config.params.dataApiHost||"https://api.data.apps.myio-bas.com",deep:this.config.params.deep||false};console.log("[EnergyModalView] Rendering comparison chart with SDK:",chartConfig);this.chartInstance=renderTelemetryStackedChart(this.chartContainer,chartConfig);if(this.chartInstance&&typeof this.chartInstance.on==="function"){this.chartInstance.on("error",errorData=>{console.error("[EnergyModalView] Comparison chart error:",errorData);if(this.chartContainer){this.chartContainer.innerHTML=`<div style="padding: 20px; text-align: center; color: red;">Comparison Chart Error: ${errorData.message||"Unknown error"}</div>`}})}return true}catch(error){console.error("[EnergyModalView] Error rendering comparison chart:",error);if(this.chartContainer){this.chartContainer.innerHTML=`<div style="padding: 20px; text-align: center; color: red;">Error: ${error.message}</div>`}}return false}renderTemperatureComparisonChart(){try{if(this.chartInstance&&typeof this.chartInstance.destroy==="function"){this.chartInstance.destroy();this.chartInstance=null}if(this.chartContainer){this.chartContainer.innerHTML=""}let renderTelemetryLineChart;if(typeof window!=="undefined"&&window.EnergyChartSDK&&typeof window.EnergyChartSDK.renderTelemetryLineChart==="function"){renderTelemetryLineChart=window.EnergyChartSDK.renderTelemetryLineChart}else{console.error("[EnergyModalView] renderTelemetryLineChart not available in SDK");if(this.chartContainer){this.chartContainer.innerHTML='<div style="padding: 20px; text-align: center; color: red;">EnergyChartSDK renderTelemetryLineChart not loaded. Check SDK version.</div>'}return false}let startDateStr,endDateStr;if(this.dateRangePicker){const dates=this.dateRangePicker.getDates();startDateStr=dates.startISO.split("T")[0];endDateStr=dates.endISO.split("T")[0]}else{const startDate=new Date(this.config.params.startDate);const endDate=new Date(this.config.params.endDate);startDateStr=startDate.toISOString().split("T")[0];endDateStr=endDate.toISOString().split("T")[0]}const tzIdentifier=this.config.params.timezone||"America/Sao_Paulo";const chartConfig={version:"v2",clientId:this.config.params.clientId||"ADMIN_DASHBOARD_CLIENT",clientSecret:this.config.params.clientSecret||"admin_dashboard_secret_2025",dataSources:this.config.params.dataSources,readingType:"temperature",startDate:startDateStr,endDate:endDateStr,granularity:this.currentGranularity,theme:this.currentTheme,timezone:tzIdentifier,iframeBaseUrl:this.config.params.chartsBaseUrl||"https://graphs.apps.myio-bas.com",apiBaseUrl:this.config.params.dataApiHost||"https://api.data.apps.myio-bas.com",deep:this.config.params.deep||false,showMinMax:false,yAxisTitle:"Temperatura (°C)"};console.log("[EnergyModalView] Rendering temperature comparison chart with SDK:",chartConfig);this.chartInstance=renderTelemetryLineChart(this.chartContainer,chartConfig);if(this.chartInstance&&typeof this.chartInstance.on==="function"){this.chartInstance.on("error",errorData=>{console.error("[EnergyModalView] Temperature chart error:",errorData);if(this.chartContainer){this.chartContainer.innerHTML=`<div style="padding: 20px; text-align: center; color: red;">Temperature Chart Error: ${errorData.message||"Unknown error"}</div>`}})}return true}catch(error){console.error("[EnergyModalView] Error rendering temperature comparison chart:",error);if(this.chartContainer){this.chartContainer.innerHTML=`<div style="padding: 20px; text-align: center; color: red;">Error: ${error.message}</div>`}return false}}normalizeToSaoPauloISO(dateLike,endOfDay=false){let date;if(typeof dateLike==="string"){if(/^\d{4}-\d{2}-\d{2}$/.test(dateLike)){date=new Date(dateLike+"T00:00:00-03:00")}else{date=new Date(dateLike)}}else{date=new Date(dateLike)}if(endOfDay){date.setHours(23,59,59,999)}else{date.setHours(0,0,0,0)}return date.toISOString().replace("Z","-03:00")}renderFallbackChart(energyData){if(!this.chartContainer)return;const maxValue=Math.max(...energyData.consumption.map(p=>p.value));const chartHeight=300;const chartHTML=`\n <div class="myio-fallback-chart">\n <h4>${this.getI18nText("energyChart")}</h4>\n <div class="myio-chart-container" style="height: ${chartHeight}px;">\n <svg width="100%" height="100%" viewBox="0 0 800 ${chartHeight}">\n ${energyData.consumption.map((point,index)=>{const x=index/(energyData.consumption.length-1)*750+25;const y=chartHeight-50-point.value/maxValue*(chartHeight-100);const barWidth=Math.max(2,750/energyData.consumption.length-2);return`\n <rect x="${x-barWidth/2}" y="${y}" width="${barWidth}" height="${chartHeight-50-y}" \n fill="var(--myio-energy-primary, #6366f1)" opacity="0.7">\n <title>${formatDate(point.timestamp)}: ${formatNumber(point.value)} kWh</title>\n </rect>\n `}).join("")}\n \n \x3c!-- Y-axis --\x3e\n <line x1="25" y1="25" x2="25" y2="${chartHeight-25}" stroke="#ccc" stroke-width="1"/>\n \x3c!-- X-axis --\x3e\n <line x1="25" y1="${chartHeight-25}" x2="775" y2="${chartHeight-25}" stroke="#ccc" stroke-width="1"/>\n \n \x3c!-- Y-axis labels --\x3e\n <text x="15" y="30" text-anchor="end" font-size="12" fill="#666">${formatNumber(maxValue)}</text>\n <text x="15" y="${chartHeight-30}" text-anchor="end" font-size="12" fill="#666">0</text>\n </svg>\n </div>\n <p class="myio-chart-note">\n ${this.getI18nText("kwhUnit")} consumption over time. Hover over bars for details.\n </p>\n </div>\n `;this.chartContainer.innerHTML=chartHTML}showKPIButton(){const kpiButtonContainer=document.getElementById("energy-kpi-btn");if(kpiButtonContainer){kpiButtonContainer.style.display="block"}}exportToCsv(){if(!this.currentEnergyData){throw new Error("No data available for export")}const{device:device}=this.config.context;const totalConsumption=this.currentEnergyData.consumption.reduce((sum,item)=>sum+item.value,0);const now=new Date;const timestamp=now.toLocaleDateString("pt-BR")+" - "+now.toLocaleTimeString("pt-BR",{hour:"2-digit",minute:"2-digit"});const csvData=[["ENERGY REPORT - DEVICE DETAILS","",""],["Device",device.label,""],["Device ID",device.id,""],["Export Date",timestamp,""],["Total Consumption",formatNumber(totalConsumption),"kWh"],["","",""],["Date","Consumption (kWh)",""],...this.currentEnergyData.consumption.map(row=>[formatDate(row.timestamp),formatNumber(row.value),""])];const csvContent=toCsv(csvData);this.downloadCSV(csvContent,`energy-report-${device.id}-${(new Date).toISOString().split("T")[0]}.csv`)}downloadCSV(content,filename){const BOM="\ufeff";const csvWithBOM=BOM+content;const blob=new Blob([csvWithBOM],{type:"text/csv;charset=utf-8;"});const link=document.createElement("a");const url=URL.createObjectURL(blob);link.setAttribute("href",url);link.setAttribute("download",filename);link.style.visibility="hidden";document.body.appendChild(link);link.click();document.body.removeChild(link);URL.revokeObjectURL(url)}getDefaultStartDate(){const date=new Date;date.setDate(1);return date.toISOString().split("T")[0]}getDefaultEndDate(){return(new Date).toISOString().split("T")[0]}async loadData(){if(this.isLoading)return;const mode=this.config.params.mode||"single";const loadBtn=document.getElementById("load-btn");const exportBtn=document.getElementById("export-csv-btn");const spinner=document.getElementById("load-spinner");if(!this.dateRangePicker){this.showError("Seletor de data não inicializado");return}this.isLoading=true;loadBtn.disabled=true;exportBtn.disabled=true;spinner.style.display="inline-block";try{const{startISO:startISO,endISO:endISO}=this.dateRangePicker.getDates();if(!startISO||!endISO){this.showError("Selecione um período válido");return}this.showLoadingState();if(mode==="comparison"){console.log("[EnergyModalView] Comparison mode: rendering chart directly");const success=this.tryRenderWithSDK(null);if(success){this.hideLoadingState();this.hideError()}else{this.showError("Erro ao carregar gráfico de comparação")}return}if(this.config.onDateRangeChange){await this.config.onDateRangeChange(startISO,endISO)}}catch(error){this.showError("Erro ao carregar dados: "+error.message);console.error("Error loading data:",error)}finally{this.isLoading=false;loadBtn.disabled=false;spinner.style.display="none"}}async setupEventListeners(){const exportBtn=document.getElementById("export-csv-btn");const closeBtn=document.getElementById("close-btn");const loadBtn=document.getElementById("load-btn");const showKpisBtn=document.getElementById("show-kpis-btn");const dateRangeInput=document.getElementById("date-range");if(exportBtn){exportBtn.addEventListener("click",()=>{try{this.exportToCsv()}catch(error){console.error("[EnergyModalView] Export error:",error);this.config.onError({code:"UNKNOWN_ERROR",message:"Failed to export data",cause:error})}})}if(closeBtn){closeBtn.addEventListener("click",()=>{this.modal.close()})}if(loadBtn){loadBtn.addEventListener("click",()=>this.loadData())}const viewDemandBtn=document.getElementById("view-demand-btn");if(viewDemandBtn){viewDemandBtn.addEventListener("click",async()=>{try{console.log("[EnergyModalView] Opening demand modal (Pico de Demanda)");const jwtToken=localStorage.getItem("jwt_token");if(!jwtToken){throw new Error("Token de autenticação não encontrado")}let startDate;let endDate;if(this.dateRangePicker){const dates=this.dateRangePicker.getDates();startDate=dates.startISO;endDate=dates.endISO}else{startDate=this.config.params.startDate instanceof Date?this.config.params.startDate.toISOString():this.config.params.startDate;endDate=this.config.params.endDate instanceof Date?this.config.params.endDate.toISOString():this.config.params.endDate}await openDemandModal({token:jwtToken,deviceId:this.config.params.deviceId,startDate:startDate,endDate:endDate,label:this.config.params.deviceLabel||"Dispositivo",locale:"pt-BR",readingType:this.config.params.readingType||"energy",enableRealTimeMode:true,realTimeInterval:8e3,realTimeAutoScroll:true})}catch(error){console.error("[EnergyModalView] Error opening demand modal:",error);this.showError("Erro ao abrir pico de demanda: "+error.message)}})}const viewTelemetryBtn=document.getElementById("view-telemetry-btn");if(viewTelemetryBtn){viewTelemetryBtn.addEventListener("click",async()=>{try{console.log("[EnergyModalView] Opening real-time telemetry modal");const jwtToken=localStorage.getItem("jwt_token");if(!jwtToken){throw new Error("Token de autenticação não encontrado")}await openRealTimeTelemetryModal({token:jwtToken,deviceId:this.config.params.deviceId,deviceLabel:this.config.params.deviceLabel||"Dispositivo",telemetryKeys:["voltage_a","voltage_b","voltage_c","total_current","consumption"],refreshInterval:8e3,historyPoints:50,locale:"pt-BR"})}catch(error){console.error("[EnergyModalView] Error opening real-time telemetry modal:",error);this.showError("Erro ao abrir telemetrias: "+error.message)}})}if(showKpisBtn){showKpisBtn.addEventListener("click",()=>{console.log("[EnergyModalView] Show KPIs modal clicked");alert("KPI modal functionality to be implemented")})}const themeToggleBtn=document.getElementById("theme-toggle-btn");if(themeToggleBtn){this.applyTheme();themeToggleBtn.addEventListener("click",()=>{this.toggleTheme()})}const barModeToggleBtn=document.getElementById("bar-mode-toggle-btn");if(barModeToggleBtn){this.applyBarMode();barModeToggleBtn.addEventListener("click",()=>{this.toggleBarMode()})}const granularityButtons=document.querySelectorAll(".myio-btn-granularity");if(granularityButtons.length>0){this.applyGranularityUI();granularityButtons.forEach(btn=>{btn.addEventListener("click",e=>{const target=e.currentTarget;const newGranularity=target.dataset.granularity;if(newGranularity){this.setGranularity(newGranularity)}})});console.log("[EnergyModalView] [RFC-0097] Granularity selector initialized with:",this.currentGranularity)}try{this.dateRangePicker=await attach(dateRangeInput,{presetStart:this.config.params.startDate instanceof Date?this.config.params.startDate.toISOString().split("T")[0]:this.config.params.startDate,presetEnd:this.config.params.endDate instanceof Date?this.config.params.endDate.toISOString().split("T")[0]:this.config.params.endDate,maxRangeDays:31,parentEl:this.modal.element,onApply:({startISO:startISO,endISO:endISO})=>{this.hideError();console.log("Date range selected:",{startISO:startISO,endISO:endISO})}})}catch(error){console.warn("DateRangePicker initialization failed, using fallback:",error)}}hideLoadingState(){const loadingState=this.chartContainer?.querySelector(".myio-loading-state");if(loadingState){loadingState.remove()}}hideError(){const errorContainer=document.getElementById("energy-error");if(errorContainer){errorContainer.style.display="none"}}hideKPIButton(){const kpiButtonContainer=document.getElementById("energy-kpi-btn");if(kpiButtonContainer){kpiButtonContainer.style.display="none"}}getI18nText(key){const i18n=this.config.params.i18n||DEFAULT_I18N2;return i18n[key]||DEFAULT_I18N2[key]}getModalStyles(){const styles=this.config.params.styles||{};const defaultPrimary=styles.primaryColor||"#4A148C";const defaultFont=styles.fontFamily||'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';return`\n /* --- VARIÁVEIS DE TEMA (LIGHT MODE - PADRÃO) --- */\n .myio-energy-modal-scope {\n --myio-energy-primary: ${defaultPrimary};\n --myio-energy-font: ${defaultFont};\n --myio-energy-radius: 8px;\n \n /* Cores Gerais (Light Mode) */\n --myio-energy-bg: #ffffff;\n --myio-energy-text: #1f2937;\n --myio-energy-text-secondary: #6b7280;\n \n /* Borda GERAL (para botões e inputs) - Cinza suave */\n --myio-energy-border: #e5e7eb;\n \n /* Borda ESPECÍFICA DO GRÁFICO (Aqui está a correção) */\n /* No Light Mode, definimos como transparente ou branco para "sumir" */\n --myio-chart-border: transparent; \n\n --myio-energy-btn-bg: #f3f4f6;\n --myio-energy-btn-hover: #e5e7eb;\n --myio-energy-input-bg: #ffffff;\n --myio-granularity-bg: #f9fafb;\n \n font-family: var(--myio-energy-font);\n background-color: var(--myio-energy-bg);\n color: var(--myio-energy-text);\n height: -webkit-fill-available;\n transition: background-color 0.3s ease, color 0.3s ease;\n }\n\n /* --- DARK MODE OVERRIDES --- */\n .myio-energy-modal-scope[data-theme="dark"] {\n --myio-energy-bg: #1f1f1f;\n --myio-energy-text: #f3f4f6;\n --myio-energy-text-secondary: #9ca3af;\n \n /* No Dark Mode, as bordas precisam aparecer */\n --myio-energy-border: #374151;\n --myio-chart-border: #374151; /* Borda visível no escuro */\n\n --myio-energy-btn-bg: #374151;\n --myio-energy-btn-hover: #4b5563;\n --myio-energy-input-bg: #111827;\n --myio-granularity-bg: #111827;\n }\n\n /* --- COMPONENTES --- */\n\n .myio-energy-chart-container {\n flex: 1 !important;\n min-height: 353px !important;\n height: 353px !important;\n background: var(--myio-energy-bg);\n border-radius: var(--myio-energy-radius);\n \n /* USO DA VARIÁVEL ESPECÍFICA AQUI */\n border: 1px solid var(--myio-chart-border);\n \n padding: 10px !important;\n display: block !important;\n overflow: hidden !important;\n }\n\n .myio-chart-container {\n /* Aplica a mesma lógica para o gráfico de fallback */\n border: 1px solid var(--myio-chart-border);\n border-radius: var(--myio-energy-radius);\n overflow: hidden;\n }\n\n /* --- Resto dos estilos (Botões, Labels, etc.) --- */\n \n .myio-label-secondary {\n color: var(--myio-energy-text-secondary);\n font-weight: 500;\n }\n\n .myio-granularity-selector {\n display: flex; \n align-items: center; \n gap: 4px; \n margin-left: 8px; \n padding: 4px 8px; \n border-radius: 8px;\n background: var(--myio-granularity-bg);\n border: 1px solid var(--myio-energy-border); /* Granularidade mantém borda suave */\n }\n\n .myio-btn-granularity {\n padding: 4px 10px;\n font-size: 12px;\n font-weight: 600;\n border-radius: 6px;\n cursor: pointer;\n transition: all 0.2s ease;\n min-width: 36px;\n background: transparent;\n border: 1px solid transparent;\n color: var(--myio-energy-text-secondary);\n }\n\n .myio-btn-granularity:hover:not(.active) {\n background: var(--myio-energy-btn-hover);\n color: var(--myio-energy-text);\n }\n\n .myio-btn-granularity.active {\n background: var(--myio-energy-primary);\n color: white;\n border-color: var(--myio-energy-primary);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);\n }\n\n .myio-btn {\n padding: 8px 16px;\n border-radius: var(--myio-energy-radius);\n border: 1px solid var(--myio-energy-border);\n background: var(--myio-energy-btn-bg);\n color: var(--myio-energy-text);\n cursor: pointer;\n font-size: 14px;\n transition: all 0.2s;\n }\n\n .myio-btn:hover:not(:disabled) {\n background: var(--myio-energy-btn-hover);\n border-color: var(--myio-energy-border);\n }\n\n .myio-btn-primary {\n background: var(--myio-energy-primary);\n color: white;\n border-color: var(--myio-energy-primary);\n }\n\n .myio-btn-primary:hover:not(:disabled) {\n opacity: 0.9;\n }\n\n .myio-btn-secondary {\n background: var(--myio-energy-btn-bg);\n color: var(--myio-energy-text);\n border-color: var(--myio-energy-border);\n }\n\n .myio-modal-scope {\n height: 100% !important;\n display: flex !important;\n flex-direction: column !important;\n }\n\n .myio-form-group {\n display: flex;\n flex-direction: column;\n }\n\n .myio-label {\n font-weight: 500;\n margin-bottom: 5px;\n color: var(--myio-energy-text);\n }\n\n .myio-input {\n padding: 8px 12px;\n border: 1px solid var(--myio-energy-border);\n border-radius: var(--myio-energy-radius);\n font-size: 14px;\n background: var(--myio-energy-input-bg);\n color: var(--myio-energy-text);\n }\n\n .myio-input:focus {\n outline: none;\n border-color: var(--myio-energy-primary);\n box-shadow: 0 0 0 1px var(--myio-energy-primary);\n }\n\n .myio-spinner {\n width: 40px;\n height: 40px;\n border: 4px solid var(--myio-energy-border);\n border-top: 4px solid var(--myio-energy-primary);\n border-radius: 50%;\n animation: spin 1s linear infinite;\n margin: 0 auto 16px;\n }\n \n @keyframes spin {\n 0% { transform: rotate(0deg); }\n 100% { transform: rotate(360deg); }\n }\n\n .myio-loading-state p {\n color: var(--myio-energy-text);\n }\n\n .myio-energy-error {\n background: rgba(254, 202, 202, 0.15);\n border: 1px solid rgba(248, 113, 113, 0.5);\n border-radius: var(--myio-energy-radius);\n padding: 16px;\n }\n \n .myio-error-message {\n color: #ef4444;\n font-weight: 500;\n }\n\n .myio-chart-note {\n margin: 12px 0 0 0;\n font-size: 12px;\n color: var(--myio-energy-text);\n opacity: 0.7;\n text-align: center;\n }\n\n .myio-energy-chart-container > iframe {\n width: 100% !important;\n height: 100% !important;\n border: none !important;\n }\n\n @media (max-width: 768px) {\n .myio-energy-modal-layout {\n grid-template-columns: 1fr;\n grid-template-rows: auto 1fr;\n }\n }\n `}destroy(){if(this.chartInstance&&typeof this.chartInstance.destroy==="function"){this.chartInstance.destroy();this.chartInstance=null}if(this.dateRangePicker){this.dateRangePicker.destroy();this.dateRangePicker=null}if(this.chartContainer){this.chartContainer.innerHTML=""}this.currentEnergyData=null;this.container=null;this.chartContainer=null}};var EnergyModal=class{modal;view=null;dataFetcher;params;context=null;modalId;eventHandlers={};constructor(params){validateOptions(params);this.params=this.normalizeParams(params);this.modalId=createModalId();this.dataFetcher=new EnergyDataFetcher({dataApiHost:this.params.dataApiHost,ingestionToken:this.params.ingestionToken,clientId:this.params.clientId,clientSecret:this.params.clientSecret});console.log("[EnergyModal] Initialized with params:",{deviceId:this.params.deviceId,hasIngestionToken:!!this.params.ingestionToken,hasClientCredentials:!!(this.params.clientId&&this.params.clientSecret),dataApiHost:this.params.dataApiHost,modalId:this.modalId})}async show(){try{console.log("[EnergyModal] Starting modal show process");const mode=this.params.mode||"single";console.log(`[EnergyModal] Mode: ${mode}`);if(mode==="single"){this.context=await this.fetchDeviceContext();const identifier=this.context.device.attributes.identifier||"SEM IDENTIFICADOR";const label=this.context.device.label||"SEM ETIQUETA";this.modal=createModal({title:`Energy Report - ${identifier} - ${label}`,width:"80vw",height:"90vh",theme:this.params.theme==="dark"?"dark":"light"})}else if(mode==="comparison"){this.context=this.createComparisonContext();const deviceCount=this.params.dataSources?.length||0;this.modal=createModal({title:`Comparação de ${deviceCount} Dispositivos`,width:"80vw",height:"90vh",theme:this.params.theme==="dark"?"dark":"light"})}this.view=new EnergyModalView(this.modal,{context:this.context,params:this.params,onExport:()=>this.handleExport(),onError:error=>this.handleEnergyModalError(error),onDateRangeChange:(startISO,endISO)=>this.handleDateRangeChange(startISO,endISO)});this.setupModalEventHandlers();if(mode==="single"){await this.loadEnergyData()}else if(mode==="comparison"){console.log("[EnergyModal] Triggering comparison chart render");const success=this.view.tryRenderWithSDK(null);if(!success){const error=new Error("Failed to render comparison chart. Check if EnergyChartSDK is loaded.");this.handleError(error)}}if(this.params.onOpen){try{this.params.onOpen(this.context)}catch(error){console.warn("[EnergyModal] onOpen callback error:",error)}}console.log("[EnergyModal] Modal successfully opened");return{close:()=>this.close()}}catch(error){console.error("[EnergyModal] Error showing modal:",error);this.handleError(error);throw error}}normalizeParams(params){if(!validateJwtToken(params.tbJwtToken)){throw new Error("Invalid JWT token format")}return{...params,dataApiHost:params.dataApiHost||"https://api.data.apps.myio-bas.com",chartsBaseUrl:params.chartsBaseUrl||"https://graphs.apps.myio-bas.com",timezone:params.timezone||"America/Sao_Paulo",theme:params.theme||"light",granularity:params.granularity||"1d",closeOnEsc:params.closeOnEsc!==false,zIndex:params.zIndex||1e4,i18n:{...DEFAULT_I18N2,...params.i18n},styles:{...DEFAULT_STYLES2,...params.styles}}}createComparisonContext(){const deviceCount=this.params.dataSources?.length||0;return{device:{id:"comparison",label:`Comparação (${deviceCount} dispositivos)`,attributes:{}},resolved:{ingestionId:null,centralId:null,slaveId:null,customerId:null}}}async fetchDeviceContext(){console.log("[EnergyModal] Fetching device context for:",this.params.deviceId);try{const[entityInfo,attributes]=await Promise.all([this.fetchEntityInfo(),this.fetchEntityAttributes()]);const resolvedAttributes=resolveDeviceAttributes(attributes);const context={device:{id:this.params.deviceId,label:this.params.label||entityInfo.label||entityInfo.name||"Unknown Device",attributes:attributes},resolved:{ingestionId:this.params.ingestionId||resolvedAttributes.ingestionId,centralId:this.params.centralId||resolvedAttributes.centralId,slaveId:this.params.slaveId||resolvedAttributes.slaveId,customerId:this.params.customerId||resolvedAttributes.customerId}};console.log("[EnergyModal] Device context resolved:",{deviceLabel:context.device.label,hasIngestionId:!!context.resolved.ingestionId,attributeCount:Object.keys(attributes).length});return context}catch(error){console.error("[EnergyModal] Error fetching device context:",error);throw new Error(`Failed to fetch device information: ${createSafeErrorMessage(error)}`)}}async fetchEntityInfo(){const url=`/api/device/${this.params.deviceId}`;const response=await fetch(url,{method:"GET",headers:{"X-Authorization":`Bearer ${this.params.tbJwtToken}`,"Content-Type":"application/json"}});if(!response.ok){const error=mapHttpError(response.status);throw new Error(`Failed to fetch device entity: ${error.message}`)}return response.json()}async fetchEntityAttributes(){const url=`/api/plugins/telemetry/DEVICE/${this.params.deviceId}/values/attributes?scope=SERVER_SCOPE`;const response=await fetch(url,{method:"GET",headers:{"X-Authorization":`Bearer ${this.params.tbJwtToken}`,"Content-Type":"application/json"}});if(!response.ok){const error=mapHttpError(response.status);throw new Error(`Failed to fetch device attributes: ${error.message}`)}const attributes=await response.json();return attributes.reduce((acc,attr)=>{acc[attr.key]=attr.value;return acc},{})}async loadEnergyData(){const mode=this.params.mode||"single";if(mode!=="single"){console.log("[EnergyModal] Skipping loadEnergyData in comparison mode");return}if(!this.context?.resolved.ingestionId){const error=new Error("ingestionId not found in device attributes. Please configure the device properly.");this.handleError(error);return}if(!this.view){throw new Error("View not initialized")}try{console.log("[EnergyModal] Loading energy data");this.view.showLoadingState();const startISO=normalizeToSaoPauloISO(this.params.startDate,false);const endISO=normalizeToSaoPauloISO(this.params.endDate,true);const energyData=await this.dataFetcher.fetchEnergyData({ingestionId:this.context.resolved.ingestionId,startISO:startISO,endISO:endISO,granularity:this.params.granularity||"1d"});console.log("[EnergyModal] Energy data loaded:",{dataPoints:energyData.consumption.length,totalConsumption:energyData.consumption.reduce((sum,point)=>sum+point.value,0)});this.view.renderEnergyData(energyData)}catch(error){console.error("[EnergyModal] Error loading energy data:",error);this.handleError(error)}}buildModalTitle(){const i18n=this.params.i18n||DEFAULT_I18N2;const deviceLabel=this.context?.device.label||this.params.label||"Device";return`${i18n.title} - ${deviceLabel}`}setupModalEventHandlers(){if(!this.modal)return;this.modal.on("close",()=>{console.log("[EnergyModal] Modal closing");this.cleanup();this.emit("close");if(this.params.onClose){try{this.params.onClose()}catch(error){console.warn("[EnergyModal] onClose callback error:",error)}}});if(this.params.closeOnEsc){const handleKeyDown=event=>{if(event.key==="Escape"){event.preventDefault();this.close()}};document.addEventListener("keydown",handleKeyDown);this.on("close",()=>{document.removeEventListener("keydown",handleKeyDown)})}}async handleDateRangeChange(startISO,endISO){const mode=this.params.mode||"single";if(!this.view){return}try{console.log("[EnergyModal] Date range changed:",{startISO:startISO,endISO:endISO,mode:mode});if(mode==="comparison"){console.log("[EnergyModal] Comparison mode: re-rendering chart with new dates");this.params.startDate=startISO;this.params.endDate=endISO;this.view.showLoadingState();const success=this.view.tryRenderWithSDK(null);if(success){this.view.hideLoadingState();this.view.hideError()}else{this.view.showError("Erro ao recarregar gráfico de comparação")}return}if(!this.context?.resolved.ingestionId){return}this.view.showLoadingState();const energyData=await this.dataFetcher.fetchEnergyData({ingestionId:this.context.resolved.ingestionId,startISO:startISO,endISO:endISO,granularity:this.params.granularity||"1d"});console.log("[EnergyModal] Energy data reloaded:",{dataPoints:energyData.consumption.length,totalConsumption:energyData.consumption.reduce((sum,point)=>sum+point.value,0)});this.view.renderEnergyData(energyData)}catch(error){console.error("[EnergyModal] Error reloading energy data:",error);this.handleError(error)}}handleExport(){const mode=this.params.mode||"single";if(!this.view){console.warn("[EnergyModal] Cannot export: view not initialized");return}try{if(mode==="comparison"){alert("Export não disponível no modo de comparação");return}this.view.exportToCsv();console.log("[EnergyModal] CSV export completed")}catch(error){console.error("[EnergyModal] Export error:",error);this.handleError(new Error("Failed to export data to CSV"))}}handleEnergyModalError(error){console.error("[EnergyModal] EnergyModalError occurred:",error);if(this.view){this.view.showError(error.message)}if(this.params.onError){try{this.params.onError(error)}catch(callbackError){console.warn("[EnergyModal] onError callback error:",callbackError)}}this.emit("error",{message:error.message,error:error})}handleError(error){console.error("[EnergyModal] Error occurred:",error);if(this.view){this.view.showError(error.message)}if(this.params.onError){try{const modalError={code:"UNKNOWN_ERROR",message:error.message,cause:error};this.params.onError(modalError)}catch(callbackError){console.warn("[EnergyModal] onError callback error:",callbackError)}}this.emit("error",{message:error.message,error:error})}close(){if(this.modal){this.modal.close()}}cleanup(){console.log("[EnergyModal] Cleaning up resources");this.dataFetcher.clearCache();if(this.view){this.view.destroy();this.view=null}this.context=null;this.eventHandlers={}}on(event,handler){if(!this.eventHandlers[event]){this.eventHandlers[event]=[]}this.eventHandlers[event].push(handler)}emit(event,payload){if(this.eventHandlers[event]){this.eventHandlers[event].forEach(handler=>{try{handler()}catch(error){console.warn(`[EnergyModal] Event handler error for ${event}:`,error)}})}}};async function openDashboardPopupEnergy(options){try{validateOptions(options);console.log("[openDashboardPopupEnergy] Opening energy modal with options:",{deviceId:options.deviceId,hasIngestionToken:!!options.ingestionToken,hasClientCredentials:!!(options.clientId&&options.clientSecret),startDate:options.startDate,endDate:options.endDate,theme:options.theme||"light"});const modal=new EnergyModal(options);const modalHandle=await modal.show();console.log("[openDashboardPopupEnergy] Energy modal opened successfully");return modalHandle}catch(error){console.error("[openDashboardPopupEnergy] Error opening modal:",error);if(options.onError){try{options.onError({code:error instanceof Error&&error.message.includes("validation")?"VALIDATION_ERROR":"UNKNOWN_ERROR",message:error instanceof Error?error.message:"Unknown error occurred",cause:error})}catch(callbackError){console.warn("[openDashboardPopupEnergy] onError callback failed:",callbackError)}}throw error}}var rangeDaysInclusive=(start,end)=>{const out=[];let cur=new Date(start);const last=new Date(end);while(cur<=last){out.push(cur.toISOString().slice(0,10));cur.setDate(cur.getDate()+1)}return out};var fmtPt=n=>new Intl.NumberFormat("pt-BR",{minimumFractionDigits:2,maximumFractionDigits:2}).format(n);var DOMAIN_CONFIG={energy:{endpoint:"energy",unit:"kWh",label:"Consumo (kWh)",formatter:v=>fmtPt(v),summaryType:"total",summaryLabel:"Total"},water:{endpoint:"water",unit:"m³",label:"Consumo (m³)",formatter:v=>fmtPt(v),summaryType:"total",summaryLabel:"Total"},temperature:{endpoint:"temperature",unit:"°C",label:"Temperatura (°C)",formatter:v=>fmtPt(v),summaryType:"average",summaryLabel:"Média"}};var createDefaultEnergyFetcher=params=>async({baseUrl:baseUrl,ingestionId:ingestionId,startISO:startISO,endISO:endISO})=>{const domain=params.domain||"energy";const endpoint=DOMAIN_CONFIG[domain].endpoint;const url=`${baseUrl}/api/v1/telemetry/devices/${ingestionId}/${endpoint}?startTime=${encodeURIComponent(startISO)}&endTime=${encodeURIComponent(endISO)}&granularity=1d&page=1&pageSize=1000&deep=0`;const token=params.api.ingestionToken;if(!token){throw new Error("ingestionToken is required for Data API calls to data.apps.myio-bas.com")}const response=await fetch(url,{headers:{Authorization:`Bearer ${token}`,"Content-Type":"application/json"}});if(!response.ok){throw new Error(`HTTP ${response.status}: ${response.statusText}`)}return response.json()};var DeviceReportModal=class{constructor(params){this.params=params;this.authClient=new AuthClient({clientId:params.api.clientId,clientSecret:params.api.clientSecret,base:params.api.dataApiBaseUrl});const domain=params.domain||"energy";this.domainConfig=DOMAIN_CONFIG[domain];this.energyFetcher=params.fetcher||createDefaultEnergyFetcher(params)}modal;authClient;energyFetcher;data=[];isLoading=false;eventHandlers={};dateRangePicker=null;sortState={key:null,direction:"asc"};domainConfig;show(){this.modal=createModal({title:`Relatório - ${this.params.identifier||"SEM IDENTIFICADOR"} - ${this.params.label||"SEM ETIQUETA"}`,width:"80vw",height:"90vh",theme:this.params.ui?.theme||"light"});this.renderContent();this.modal.on("close",()=>{if(this.dateRangePicker){this.dateRangePicker.destroy();this.dateRangePicker=null}this.authClient.clearCache();this.emit("close")});return{close:()=>this.modal.close(),on:(event,handler)=>this.on(event,handler)}}renderContent(){const content=document.createElement("div");content.innerHTML=`\n <div class="myio-modal-scope">\n <div style="margin-bottom: 16px;">\n <div style="display: flex; gap: 16px; align-items: end; margin-bottom: 16px;">\n <div class="myio-form-group" style="margin-bottom: 0;">\n <label class="myio-label" for="date-range">Período</label>\n <input type="text" id="date-range" class="myio-input" readonly placeholder="Selecione o período" style="width: 300px;">\n </div>\n <button id="load-btn" class="myio-btn myio-btn-primary">\n <span class="myio-spinner" id="load-spinner" style="display: none;"></span>\n Carregar\n </button>\n <button id="export-btn" class="myio-btn myio-btn-secondary" disabled>\n Exportar CSV\n </button>\n </div>\n </div>\n \n <div id="error-container" style="display: none; background: #ffebee; color: #c62828; padding: 12px; border-radius: 6px; margin-bottom: 16px;">\n </div>\n \n <div id="table-container">\n <div style="text-align: center; padding: 40px; color: var(--myio-text-muted);">\n Selecione um período e clique em "Carregar" para visualizar os dados.\n </div>\n </div>\n </div>\n `;this.modal.setContent(content);this.setupEventListeners()}async setupEventListeners(){const loadBtn=document.getElementById("load-btn");const exportBtn=document.getElementById("export-btn");const dateRangeInput=document.getElementById("date-range");loadBtn?.addEventListener("click",()=>this.loadData());exportBtn?.addEventListener("click",()=>this.exportCSV());try{this.dateRangePicker=await attach(dateRangeInput,{presetStart:this.getDefaultStartDate(),presetEnd:this.getDefaultEndDate(),maxRangeDays:31,parentEl:this.modal.element,onApply:({startISO:startISO,endISO:endISO})=>{this.hideError();console.log("Date range selected:",{startISO:startISO,endISO:endISO})}})}catch(error){console.warn("DateRangePicker initialization failed, using fallback:",error)}}async loadData(){if(this.isLoading)return;const loadBtn=document.getElementById("load-btn");const exportBtn=document.getElementById("export-btn");const spinner=document.getElementById("load-spinner");if(!this.dateRangePicker){this.showError("Seletor de data não inicializado");return}this.isLoading=true;loadBtn.disabled=true;exportBtn.disabled=true;spinner.style.display="inline-block";try{const{startISO:startISO,endISO:endISO}=this.dateRangePicker.getDates();if(!startISO||!endISO){this.showError("Selecione um período válido");return}const startDate=startISO.split("T")[0];const endDate=endISO.split("T")[0];const dateRange=rangeDaysInclusive(startDate,endDate);const apiResponse=await this.energyFetcher({baseUrl:this.params.api.dataApiBaseUrl||"https://api.data.apps.myio-bas.com",ingestionId:this.params.ingestionId,startISO:startISO,endISO:endISO});this.data=this.processApiResponse(apiResponse,dateRange);this.renderTable();exportBtn.disabled=false;this.emit("loaded",{date:{start:startDate,end:endDate},count:this.data.length,total:this.calculateTotal()})}catch(error){this.showError("Erro ao carregar dados: "+error.message);console.error("Error loading data:",error);this.emit("error",{message:error.message,context:"loadData"})}finally{this.isLoading=false;loadBtn.disabled=false;spinner.style.display="none"}}processApiResponse(apiResponse,dateRange){const dataArray=Array.isArray(apiResponse)?apiResponse:apiResponse.data||[];if(!Array.isArray(dataArray)||dataArray.length===0){console.warn("[DeviceReportModal] API returned empty or invalid response, zero-filling date range");return dateRange.map(date=>({date:date,consumption:0}))}const deviceData=dataArray[0];const consumption=deviceData.consumption||[];const dailyMap={};consumption.forEach(item=>{if(item.timestamp&&item.value!=null){const date=item.timestamp.slice(0,10);const value=Number(item.value);if(!dailyMap[date])dailyMap[date]=0;dailyMap[date]+=value}});return dateRange.map(date=>({date:date,consumption:dailyMap[date]||0}))}generateMockData(dateRange){return dateRange.map(date=>({date:date,consumption:Math.random()*50+10}))}renderTable(){const container=document.getElementById("table-container");if(!container)return;const total=this.calculateTotal();const summaryValue=this.domainConfig.summaryType==="average"?this.data.length>0?total/this.data.length:0:total;const getSortIndicator=columnKey=>{if(this.sortState.key===columnKey){return this.sortState.direction==="asc"?"↑":"↓"}return"↕"};container.innerHTML=`\n <div style="margin-bottom: 16px; padding: 12px; background: var(--myio-bg); border-radius: 6px;">\n <strong>${this.domainConfig.summaryLabel}: ${this.domainConfig.formatter(summaryValue)} ${this.domainConfig.unit}</strong>\n </div>\n\n <div style="max-height: 400px; overflow-y: auto; border: 1px solid var(--myio-border); border-radius: 6px;">\n <table class="myio-table">\n <thead>\n <tr>\n <th style="cursor: pointer;" data-sort="date">\n Data\n <span style="margin-left: 4px; opacity: ${this.sortState.key==="date"?"1":"0.5"};">${getSortIndicator("date")}</span>\n </th>\n <th style="cursor: pointer; text-align: right;" data-sort="consumption">\n ${this.domainConfig.label}\n <span style="margin-left: 4px; opacity: ${this.sortState.key==="consumption"?"1":"0.5"};">${getSortIndicator("consumption")}</span>\n </th>\n </tr>\n </thead>\n <tbody>\n ${this.data.map(row=>`\n <tr>\n <td>${this.formatDate(row.date)}</td>\n <td style="text-align: right;">${this.domainConfig.formatter(row.consumption)}</td>\n </tr>\n `).join("")}\n </tbody>\n </table>\n </div>\n `;this.setupTableSorting()}setupTableSorting(){const headers=document.querySelectorAll("[data-sort]");headers.forEach(header=>{header.addEventListener("click",()=>{const sortKey=header.getAttribute("data-sort");this.sortData(sortKey);this.renderTable()})})}sortData(key){if(this.sortState.key===key){this.sortState.direction=this.sortState.direction==="asc"?"desc":"asc"}else{this.sortState.key=key;this.sortState.direction="asc"}this.data.sort((a,b)=>{let comparison=0;if(key==="date"){comparison=new Date(a.date).getTime()-new Date(b.date).getTime()}else{comparison=a.consumption-b.consumption}return this.sortState.direction==="desc"?-comparison:comparison})}calculateTotal(){return this.data.reduce((sum,row)=>sum+row.consumption,0)}formatDate(dateStr){const date=new Date(dateStr+"T00:00:00");return date.toLocaleDateString("pt-BR")}exportCSV(){const total=this.calculateTotal();const summaryValue=this.domainConfig.summaryType==="average"?this.data.length>0?total/this.data.length:0:total;const now=new Date;const timestamp=now.toLocaleDateString("pt-BR")+" - "+now.toLocaleTimeString("pt-BR",{hour:"2-digit",minute:"2-digit"});const csvData=[["Dispositivo/Loja",this.params.identifier||"N/A",this.params.label||""],["DATA EMISSÃO",timestamp,""],[this.domainConfig.summaryLabel,this.domainConfig.formatter(summaryValue),this.domainConfig.unit],["Data",this.domainConfig.label,""],...this.data.map(row=>[this.formatDate(row.date),this.domainConfig.formatter(row.consumption)])];const csvContent=toCsv(csvData);this.downloadCSV(csvContent,`relatorio-${this.params.identifier||"dispositivo"}-${(new Date).toISOString().split("T")[0]}.csv`)}downloadCSV(content,filename){const BOM="\ufeff";const csvWithBOM=BOM+content;const blob=new Blob([csvWithBOM],{type:"text/csv;charset=utf-8;"});const link=document.createElement("a");const url=URL.createObjectURL(blob);link.setAttribute("href",url);link.setAttribute("download",filename);link.style.visibility="hidden";document.body.appendChild(link);link.click();document.body.removeChild(link);URL.revokeObjectURL(url)}getDefaultStartDate(){const date=new Date;date.setDate(1);return date.toISOString().split("T")[0]}getDefaultEndDate(){return(new Date).toISOString().split("T")[0]}showError(message){const container=document.getElementById("error-container");if(container){container.textContent=message;container.style.display="block"}}hideError(){const container=document.getElementById("error-container");if(container){container.style.display="none"}}on(event,handler){if(!this.eventHandlers[event]){this.eventHandlers[event]=[]}this.eventHandlers[event].push(handler)}emit(event,payload){if(this.eventHandlers[event]){this.eventHandlers[event].forEach(handler=>handler())}}};function openDashboardPopupReport(params){const modal=new DeviceReportModal(params);return modal.show()}var defaultI18n={selectAll:"Selecionar todas",clear:"Limpar",searchPlaceholder:"Buscar lojas...",sortingTitle:"Ordenação",consumptionDesc:"Consumo ↓ (padrão)",consumptionAsc:"Consumo ↑",aToZ:"A → Z",zToA:"Z → A",tieNote:"Caso o consumo seja o mesmo é considerada a ordem alfabética.",apply:"Aplicar",reset:"Resetar",totalLabel:"Lojas",selectedLabel:"Selecionadas",closeLabel:"Fechar"};var FilterOrderingModal=class{state;props;i18n;dom=null;searchTimeout=null;itemsById;initialState;openerEl=null;constructor(props){this.props=props;this.i18n={...defaultI18n,...props.i18n};this.itemsById=new Map(props.items.map(item=>[item.id,item]));const initialSelected=new Set(props.initialSelected||[]);const initialSort=props.initialSort||"CONSUMPTION_DESC";this.state={allIds:props.items.map(item=>item.id),selected:new Set(initialSelected),query:"",sort:initialSort};this.initialState={selected:new Set(initialSelected),sort:initialSort}}open(){if(!this.dom){this.createDOM()}this.openerEl=document.activeElement instanceof HTMLElement?document.activeElement:null;this.updateCounters();this.renderList();this.dom.overlay.setAttribute("aria-hidden","false");this.dom.root.setAttribute("aria-hidden","false");this.lockBodyScroll();this.dom.root.addEventListener("keydown",this.trapFocus);setTimeout(()=>{const search=this.dom.searchInput||this.dom.root.querySelector("button, input");if(search)search.focus()},200);this.emit("myio:filter:open",{})}close(){if(this.dom){this.dom.overlay.setAttribute("aria-hidden","true");this.dom.root.setAttribute("aria-hidden","true");if(this.openerEl&&document.contains(this.openerEl)){this.openerEl.focus()}this.openerEl=null;this.unlockBodyScroll();this.dom.root.removeEventListener("keydown",this.trapFocus)}this.emit("myio:filter:close",{});this.props.onClose?.()}lockBodyScroll(){const hasScrollbar=document.documentElement.scrollHeight>document.documentElement.clientHeight;if(hasScrollbar){const scrollBarW=window.innerWidth-document.documentElement.clientWidth;document.documentElement.style.paddingRight=`${scrollBarW}px`}document.body.classList.add("body--myio-modal-open")}unlockBodyScroll(){document.body.classList.remove("body--myio-modal-open");document.documentElement.style.paddingRight=""}getState(){return{selected:Array.from(this.state.selected),sort:this.state.sort}}setSelection(ids){this.state.selected=new Set(ids);this.updateCounters();this.renderList()}setSort(sort){this.state.sort=sort;this.renderList();if(this.dom){this.dom.sortRadios.forEach(radio=>{radio.checked=radio.value===sort})}}destroy(){if(this.dom){this.close();this.dom.overlay.remove();this.dom.root.remove();this.dom=null}}trapFocus=e=>{if(e.isComposing)return;if(!this.dom||e.key!=="Tab"||this.dom.root.getAttribute("aria-hidden")==="true")return;const focusable=[...this.dom.root.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])')].filter(el=>{const htmlEl=el;return!htmlEl.hasAttribute("disabled")&&htmlEl.tabIndex!==-1&&htmlEl.offsetParent!==null});if(!focusable.length)return;const first=focusable[0];const last=focusable[focusable.length-1];if(e.shiftKey&&document.activeElement===first){last.focus();e.preventDefault()}else if(!e.shiftKey&&document.activeElement===last){first.focus();e.preventDefault()}};createDOM(){const existing=document.querySelector(".myio-modal-root");if(existing){existing.remove()}const overlay=document.createElement("div");overlay.className="myio-modal-overlay";overlay.setAttribute("aria-hidden","true");const root=document.createElement("div");root.className="myio-modal-root myio-modal-portal";root.setAttribute("aria-hidden","true");root.setAttribute("role","dialog");root.setAttribute("aria-modal","true");root.setAttribute("aria-labelledby","myioFilterTitle");const card=document.createElement("div");card.className="myio-modal-card";card.setAttribute("role","document");root.appendChild(card);card.addEventListener("click",e=>e.stopPropagation());card.innerHTML=`\n <header class="myio-header">\n <h2 id="myioFilterTitle">${this.props.title||"Filtros & Ordenação"}</h2>\n <div class="myio-counters" aria-live="polite">\n <span class="counter">\n <strong class="value" data-total>0</strong> <span>${this.i18n.totalLabel}</span>\n </span>\n <span class="sep">•</span>\n <span class="counter">\n <strong class="value" data-selected>0</strong> <span>${this.i18n.selectedLabel}</span>\n </span>\n </div>\n <button class="icon-btn close" aria-label="${this.i18n.closeLabel}" data-close>×</button>\n </header>\n\n <section class="myio-toolbar">\n <div class="actions">\n <button class="btn ghost" data-select-all>${this.i18n.selectAll}</button>\n <button class="btn ghost" data-clear>${this.i18n.clear}</button>\n </div>\n <div class="search">\n <div class="search-input-wrapper">\n <span class="search-icon">🔍</span>\n <input type="text" placeholder="${this.i18n.searchPlaceholder}" aria-label="Buscar" data-search />\n <button class="icon-btn clear-search" aria-label="Limpar busca" data-clear-search hidden>✕</button>\n </div>\n </div>\n </section>\n\n <section class="myio-list" role="listbox" aria-multiselectable="true" data-virtualized>\n \x3c!-- Items will be rendered here --\x3e\n </section>\n\n <section class="myio-sorting">\n <h3>${this.i18n.sortingTitle}</h3>\n <div class="radio-group">\n <label><input type="radio" name="sort" value="CONSUMPTION_DESC" checked /> ${this.i18n.consumptionDesc}</label>\n <label><input type="radio" name="sort" value="CONSUMPTION_ASC" /> ${this.i18n.consumptionAsc}</label>\n <label><input type="radio" name="sort" value="ALPHA_ASC" /> ${this.i18n.aToZ}</label>\n <label><input type="radio" name="sort" value="ALPHA_DESC" /> ${this.i18n.zToA}</label>\n </div>\n <p class="hint">${this.i18n.tieNote}</p>\n </section>\n\n <footer class="myio-footer">\n <button class="btn secondary" data-reset>${this.i18n.reset}</button>\n <button class="btn primary" data-apply>${this.i18n.apply}</button>\n </footer>\n `;this.addStyles();document.body.appendChild(overlay);document.body.appendChild(root);const totalCounter=card.querySelector("[data-total]");const selectedCounter=card.querySelector("[data-selected]");const searchInput=card.querySelector("[data-search]");const listContainer=card.querySelector(".myio-list");const sortRadios=card.querySelectorAll('input[name="sort"]');const clearSearchBtn=card.querySelector("[data-clear-search]");this.dom={overlay:overlay,root:root,card:card,totalCounter:totalCounter,selectedCounter:selectedCounter,searchInput:searchInput,listContainer:listContainer,sortRadios:sortRadios,clearSearchBtn:clearSearchBtn};this.attachEventListeners()}addStyles(){if(document.getElementById("myio-filter-styles"))return;const style=document.createElement("style");style.id="myio-filter-styles";style.textContent=`\n /* === Overlay / Scrim ==================================================== */\n .myio-modal-overlay {\n position: fixed !important;\n top: 0 !important;\n left: 0 !important;\n right: 0 !important;\n bottom: 0 !important;\n background: rgba(0, 0, 0, 0.50); /* 50% dim */\n backdrop-filter: saturate(100%) blur(2px);\n z-index: 10000; /* above app UI */\n opacity: 0;\n pointer-events: none;\n transition: opacity 160ms ease-out;\n margin: 0 !important; /* Reset any inherited margins */\n width: auto !important; /* Reset any inherited width */\n height: auto !important; /* Reset any inherited height */\n touch-action: none; /* iOS overscroll prevention */\n }\n .myio-modal-overlay[aria-hidden="false"] {\n opacity: 1;\n pointer-events: auto;\n }\n\n /* === Card container ====================================================== */\n .myio-modal-root {\n position: fixed !important; /* Force fixed positioning relative to viewport */\n top: 0 !important;\n left: 0 !important;\n right: 0 !important;\n bottom: 0 !important;\n display: flex !important;\n align-items: center !important;\n justify-content: center !important;\n z-index: 10001; /* above overlay */\n opacity: 0;\n pointer-events: none;\n transition: opacity 160ms ease-out;\n padding: 20px; /* Add padding for better mobile positioning */\n margin: 0 !important; /* Reset any inherited margins */\n width: auto !important; /* Reset any inherited width */\n height: auto !important; /* Reset any inherited height */\n touch-action: none; /* iOS overscroll prevention */\n }\n .myio-modal-root[aria-hidden="false"] {\n opacity: 1;\n pointer-events: auto;\n }\n\n /* === Card ================================================================ */\n .myio-modal-card {\n width: min(960px, 94vw);\n max-height: calc(100vh - 40px); /* Account for padding */\n overflow: auto;\n background: #fff;\n border-radius: 16px;\n box-shadow: 0 24px 64px rgba(0,0,0,.28);\n transform: translateY(-12px) scale(0.98);\n transition: transform 160ms ease-out;\n }\n .myio-modal-root[aria-hidden="false"] .myio-modal-card {\n transform: translateY(0) scale(1);\n }\n\n /* Reduced motion support */\n @media (prefers-reduced-motion: reduce) {\n .myio-modal-overlay,\n .myio-modal-root,\n .myio-modal-card {\n transition: none !important;\n transform: none !important;\n }\n .myio-modal-root[aria-hidden="false"] .myio-modal-card {\n transform: none !important;\n }\n }\n\n /* Header stays purple (MYIO palette) and sticky */\n .myio-modal-card header.myio-header {\n position: sticky;\n top: 0;\n z-index: 1;\n padding: 16px 20px;\n background: var(--myio-purple-600, #4A148C);\n color: #fff;\n border-radius: 16px 16px 0 0;\n display: flex;\n align-items: center;\n gap: 12px;\n }\n\n .myio-modal-card footer.myio-footer {\n position: sticky;\n bottom: 0;\n background: #fff;\n border-top: 1px solid var(--myio-border, #E5E7EB);\n padding: 12px 20px;\n border-radius: 0 0 16px 16px;\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n }\n\n /* Prevent accidental stacking-context issues inside transformed parents */\n .myio-modal-portal {\n position: relative;\n z-index: 10000;\n }\n\n /* Optional: body lock when open */\n .body--myio-modal-open {\n overflow: hidden;\n overscroll-behavior: contain;\n }\n\n .myio-header {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 16px 20px;\n color: #fff;\n background: #4A148C;\n position: sticky;\n top: 0;\n z-index: 1;\n border-radius: 16px 16px 0 0;\n }\n\n .myio-header h2 {\n font-size: 18px;\n font-weight: 700;\n margin: 0;\n flex: 1;\n }\n\n .myio-counters {\n display: flex;\n align-items: center;\n gap: 8px;\n opacity: 0.95;\n }\n\n .myio-header .close {\n background: transparent;\n color: #fff;\n font-size: 20px;\n border: none;\n cursor: pointer;\n padding: 4px 8px;\n border-radius: 4px;\n }\n\n .myio-header .close:hover {\n background: rgba(255,255,255,0.1);\n }\n\n .myio-toolbar {\n display: flex;\n gap: 12px;\n align-items: center;\n padding: 12px 20px;\n flex-wrap: wrap;\n border-bottom: 1px solid #E5E7EB;\n }\n\n .myio-toolbar .actions {\n display: flex;\n gap: 8px;\n }\n\n .myio-toolbar .search {\n margin-left: auto;\n flex: 1 1 320px;\n max-width: 400px;\n }\n\n .search-input-wrapper {\n position: relative;\n display: flex;\n align-items: center;\n }\n\n .search-icon {\n position: absolute;\n left: 12px;\n z-index: 1;\n opacity: 0.5;\n }\n\n .myio-toolbar .search input {\n width: 100%;\n height: 40px;\n padding: 0 40px;\n border-radius: 12px;\n border: 1px solid #E5E7EB;\n font-size: 14px;\n }\n\n .clear-search {\n position: absolute;\n right: 8px;\n background: transparent;\n border: none;\n cursor: pointer;\n padding: 4px;\n opacity: 0.5;\n }\n\n .clear-search:hover {\n opacity: 1;\n }\n\n .myio-list {\n display: grid;\n grid-template-columns: repeat(2, minmax(0,1fr));\n gap: 12px;\n padding: 16px 20px;\n max-height: 300px;\n overflow-y: auto;\n }\n\n @media (max-width: 768px) {\n .myio-list {\n grid-template-columns: 1fr;\n }\n }\n\n .chip {\n display: flex;\n align-items: center;\n gap: 10px;\n height: 44px;\n border-radius: 12px;\n border: 2px solid #4A148C;\n background: #fff;\n padding: 0 14px;\n cursor: pointer;\n font-size: 14px;\n transition: all 0.2s ease;\n }\n\n .chip:hover {\n background: #EDE7F3;\n }\n\n .chip.selected {\n background: #EDE7F3;\n border-color: #4A148C;\n }\n\n .chip .checkbox {\n width: 22px;\n height: 22px;\n border-radius: 6px;\n border: 2px solid #4A148C;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 14px;\n font-weight: bold;\n color: #4A148C;\n }\n\n .chip.selected .checkbox {\n background: #4A148C;\n color: white;\n }\n\n .myio-sorting {\n padding: 16px 20px;\n border-top: 1px solid #E5E7EB;\n }\n\n .myio-sorting h3 {\n margin: 0 0 12px 0;\n font-size: 16px;\n font-weight: 600;\n color: #1F2937;\n }\n\n .radio-group {\n display: flex;\n flex-wrap: wrap;\n gap: 16px;\n margin-bottom: 8px;\n }\n\n .myio-sorting label {\n display: flex;\n gap: 8px;\n align-items: center;\n cursor: pointer;\n font-size: 14px;\n }\n\n .myio-sorting input[type="radio"] {\n margin: 0;\n }\n\n .hint {\n font-size: 12px;\n color: #6B7280;\n margin: 8px 0 0 0;\n font-style: italic;\n }\n\n .myio-footer {\n position: sticky;\n bottom: 0;\n background: #fff;\n border-top: 1px solid #E5E7EB;\n padding: 12px 20px;\n border-radius: 0 0 16px 16px;\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n }\n\n .btn {\n border: none;\n border-radius: 8px;\n padding: 10px 16px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n }\n\n .btn.primary {\n background: #4A148C;\n color: #fff;\n }\n\n .btn.primary:hover {\n background: #3A0E5C;\n }\n\n .btn.secondary {\n background: #fff;\n border: 1px solid #D1D5DB;\n color: #374151;\n }\n\n .btn.secondary:hover {\n background: #F9FAFB;\n }\n\n .btn.ghost {\n background: transparent;\n border: 1px solid #D1D5DB;\n color: #374151;\n padding: 8px 12px;\n font-size: 13px;\n }\n\n .btn.ghost:hover {\n background: #F3F4F6;\n }\n\n .empty-state {\n text-align: center;\n padding: 40px 20px;\n color: #6B7280;\n }\n `;document.head.appendChild(style)}attachEventListeners(){if(!this.dom)return;const{overlay:overlay,root:root,searchInput:searchInput,clearSearchBtn:clearSearchBtn,sortRadios:sortRadios}=this.dom;overlay.addEventListener("click",()=>this.close());root.querySelector("[data-close]")?.addEventListener("click",()=>this.close());root.addEventListener("keydown",e=>{if(e.key==="Escape"){this.close()}});searchInput.addEventListener("input",e=>{const query=e.target.value;this.debounceSearch(query);if(query){clearSearchBtn.hidden=false}else{clearSearchBtn.hidden=true}});clearSearchBtn.addEventListener("click",()=>{searchInput.value="";clearSearchBtn.hidden=true;this.updateQuery("")});root.querySelector("[data-select-all]")?.addEventListener("click",()=>{this.selectAllVisible()});root.querySelector("[data-clear]")?.addEventListener("click",()=>{this.clearSelection()});sortRadios.forEach(radio=>{radio.addEventListener("change",()=>{if(radio.checked){this.setSort(radio.value)}})});root.querySelector("[data-apply]")?.addEventListener("click",()=>{this.apply()});root.querySelector("[data-reset]")?.addEventListener("click",()=>{this.reset()})}debounceSearch(query){if(this.searchTimeout){clearTimeout(this.searchTimeout)}this.searchTimeout=window.setTimeout(()=>{this.updateQuery(query)},200)}updateQuery(query){this.state.query=query;this.renderList()}selectAllVisible(){const visibleIds=this.getVisibleItems();visibleIds.forEach(id=>this.state.selected.add(id));this.updateCounters();this.renderList();this.emit("myio:filter:select_all",{scope:this.state.query?"visible":"all"})}clearSelection(){const visibleIds=this.getVisibleItems();const queryActive=this.state.query.length>0;if(queryActive){visibleIds.forEach(id=>this.state.selected.delete(id))}else{this.state.selected.clear()}this.updateCounters();this.renderList();this.emit("myio:filter:clear",{scope:queryActive?"visible":"all"})}apply(){const startTime=performance.now();this.props.onApply({selected:Array.from(this.state.selected),sort:this.state.sort});this.emit("myio:filter:apply",{selectedCount:this.state.selected.size,sort:this.state.sort,queryLength:this.state.query.length,durationMs:performance.now()-startTime});if(this.props.autoCloseOnApply!==false){this.close()}}reset(){this.state.selected=new Set(this.initialState.selected);this.state.sort=this.initialState.sort;this.state.query="";if(this.dom){this.dom.searchInput.value="";this.dom.clearSearchBtn.hidden=true;this.dom.sortRadios.forEach(radio=>{radio.checked=radio.value===this.state.sort})}this.updateCounters();this.renderList()}getVisibleItems(){const sorted=this.sortItems([...this.itemsById.values()],this.state.sort);if(!this.state.query){return sorted.map(x=>x.id)}const query=this.normalize(this.state.query);return sorted.filter(x=>this.normalize(x.label).includes(query)).map(x=>x.id)}normalize(s){return s.normalize("NFD").replace(/\p{Diacritic}/gu,"").toLowerCase().trim()}sortItems(list,mode){const byLabelAsc=(a,b)=>a.label.localeCompare(b.label,void 0,{sensitivity:"base"})||(a.id>b.id?1:-1);const byLabelDesc=(a,b)=>-byLabelAsc(a,b);const num=x=>x==null?Number.NEGATIVE_INFINITY:x;switch(mode){case"CONSUMPTION_DESC":return[...list].sort((a,b)=>num(b.consumption)-num(a.consumption)||byLabelAsc(a,b));case"CONSUMPTION_ASC":return[...list].sort((a,b)=>num(a.consumption)-num(b.consumption)||byLabelAsc(a,b));case"ALPHA_ASC":return[...list].sort(byLabelAsc);case"ALPHA_DESC":return[...list].sort(byLabelDesc);default:return[...list]}}updateCounters(){if(!this.dom)return;this.dom.totalCounter.textContent=this.state.allIds.length.toString();this.dom.selectedCounter.textContent=this.state.selected.size.toString()}renderList(){if(!this.dom)return;const visibleIds=this.getVisibleItems();if(visibleIds.length===0){this.dom.listContainer.innerHTML=`\n <div class="empty-state">\n ${this.state.query?"Nenhuma loja encontrada com o filtro aplicado.":"Nenhuma loja disponível."}\n </div>\n `;return}this.dom.listContainer.innerHTML=visibleIds.map(id=>this.renderChip(id)).join("");this.dom.listContainer.querySelectorAll(".chip").forEach(chip=>{chip.addEventListener("click",()=>{const id=chip.getAttribute("data-id");this.toggleItem(id)})})}renderChip(id){const item=this.itemsById.get(id);const isSelected=this.state.selected.has(id);return`\n <button role="option" aria-selected="${isSelected}"\n class="chip ${isSelected?"selected":""}"\n data-id="${id}">\n <span class="checkbox" aria-hidden="true">${isSelected?"✓":""}</span>\n <span class="label" title="${item.label}">${item.label}</span>\n </button>\n `}toggleItem(id){if(this.state.selected.has(id)){this.state.selected.delete(id)}else{this.state.selected.add(id)}this.updateCounters();this.renderList()}emit(eventType,detail){if(this.dom){const event=new CustomEvent(eventType,{detail:detail});this.dom.card.dispatchEvent(event)}}};function attachFilterOrderingModal(props){const modal=new FilterOrderingModal(props);return{open:()=>modal.open(),close:()=>modal.close(),getState:()=>modal.getState(),setSelection:ids=>modal.setSelection(ids),setSort:sort=>modal.setSort(sort),destroy:()=>modal.destroy()}}var DOMAIN_CONFIG2={energy:{endpoint:"energy",unit:"kWh",label:"Consumption (kWh)",totalLabel:"Total kWh"},water:{endpoint:"water",unit:"m³",label:"Consumo (m³)",totalLabel:"Total m³"}};var AllReportModal=class{constructor(params){this.params=params;this.authClient=new AuthClient({clientId:params.api.clientId,clientSecret:params.api.clientSecret,base:params.api.dataApiBaseUrl});const domain=params.domain||"energy";this.domainConfig=DOMAIN_CONFIG2[domain];this.debugEnabled=params.debug===1;this.debugLog("🚀 AllReportModal initialized",{customerId:params.customerId,itemsListLength:params.itemsList?.length||0,itemsList:params.itemsList,debugEnabled:this.debugEnabled,debugParam:params.debug,apiConfig:{hasIngestionToken:!!params.api.ingestionToken,dataApiBaseUrl:params.api.dataApiBaseUrl}})}modal;authClient;data=[];isLoading=false;eventHandlers={};dateRangePicker=null;currentPage=1;itemsPerPage=10;sortField="consumption";sortDirection="desc";searchFilter="";filterModal=null;selectedStoreIds=new Set;currentSortMode="CONSUMPTION_DESC";debugEnabled;domainConfig;debugLog(message,data){if(this.debugEnabled){console.log(`[AllReportModal DEBUG] ${message}`,data||"")}}normalizeId(v){return(v||"").toString().normalize("NFKC").toUpperCase().replace(/\s+/g,"").replace(/[^A-Z0-9]/g,"")}resolveStoreIdentifierFromApi(item){if(item?.assetName){return item.assetName}const name=item?.name||"";if(!name)return null;const tokens=name.trim().split(/\s+/);const last=tokens[tokens.length-1]||"";if(/[A-Za-z0-9]{3,}/.test(last)){return last}const maybe=tokens.find(t=>/[A-Za-z0-9]{3,}/.test(t));return maybe||null}pickConsumption(item){const fields=["total_value","totalValue","consumption","value","total","energy","kwh"];for(const f of fields){if(item?.[f]!==void 0&&item?.[f]!==null){const n=typeof item[f]==="string"?parseFloat(item[f].replace(",",".")):Number(item[f]);if(!Number.isNaN(n))return n}}return 0}show(){this.debugLog("🎭 Modal show() called - creating modal UI");this.modal=createModal({title:`Relatório Geral - Todas as Lojas${this.debugEnabled?" [DEBUG MODE]":""}`,width:"85vw",height:"90vh",theme:this.params.ui?.theme||"light"});this.renderContent();this.modal.on("close",()=>{this.debugLog("🚪 Modal closing - cleaning up resources");if(this.dateRangePicker){this.dateRangePicker.destroy();this.dateRangePicker=null}if(this.filterModal){this.filterModal.destroy();this.filterModal=null}this.authClient.clearCache();this.emit("close")});this.debugLog("✅ Modal created and ready to use");return{close:()=>this.modal.close(),on:(event,handler)=>this.on(event,handler)}}renderContent(){const content=document.createElement("div");content.innerHTML=`\n <div class="myio-modal-scope">\n <div style="margin-bottom: 16px;">\n <div style="display: flex; gap: 16px; align-items: end; margin-bottom: 16px; flex-wrap: wrap;">\n <div class="myio-form-group" style="margin-bottom: 0;">\n <label class="myio-label" for="date-range">Período</label>\n <input type="text" id="date-range" class="myio-input" readonly placeholder="Selecione o período" style="width: 300px;">\n </div>\n <button id="load-btn" class="myio-btn myio-btn-primary">\n <span class="myio-spinner" id="load-spinner" style="display: none;"></span>\n Carregar\n </button>\n <button id="export-btn" class="myio-btn myio-btn-secondary" disabled>\n Exportar CSV\n </button>\n <button id="filter-btn" class="myio-btn myio-btn-secondary" style="background: var(--myio-brand-700); color: white;">\n 🔍 Filtros & Ordenação\n </button>\n <div class="myio-form-group" style="margin-bottom: 0; margin-left: auto;">\n <label class="myio-label" for="search-input">Busca rápida</label>\n <input type="text" id="search-input" class="myio-input" placeholder="Digite para filtrar..." style="width: 200px;">\n </div>\n </div>\n </div>\n\n <div id="error-container" style="display: none; background: #ffebee; color: #c62828; padding: 12px; border-radius: 6px; margin-bottom: 16px;">\n </div>\n\n <div id="summary-container" style="display: none; margin-bottom: 16px;">\n </div>\n\n <div id="table-container">\n <div style="text-align: center; padding: 40px; color: var(--myio-text-muted);">\n Selecione um período e clique em "Carregar" para visualizar os dados de todas as lojas.\n </div>\n </div>\n\n <div id="pagination-container" style="display: none; margin-top: 16px; text-align: center;">\n </div>\n </div>\n `;this.modal.setContent(content);this.setupEventListeners()}async setupEventListeners(){const loadBtn=document.getElementById("load-btn");const exportBtn=document.getElementById("export-btn");const filterBtn=document.getElementById("filter-btn");const dateRangeInput=document.getElementById("date-range");const searchInput=document.getElementById("search-input");loadBtn?.addEventListener("click",()=>this.loadData());exportBtn?.addEventListener("click",()=>this.exportCSV());filterBtn?.addEventListener("click",()=>this.openFilterModal());if(searchInput){searchInput.addEventListener("input",e=>{this.searchFilter=e.target.value.toLowerCase();this.currentPage=1;this.renderTable()});searchInput.addEventListener("keyup",e=>{this.searchFilter=e.target.value.toLowerCase();this.currentPage=1;this.renderTable()})}try{this.dateRangePicker=await attach(dateRangeInput,{presetStart:this.getDefaultStartDate(),presetEnd:this.getDefaultEndDate(),maxRangeDays:31,parentEl:this.modal.element,onApply:({startISO:startISO,endISO:endISO})=>{this.hideError();this.debugLog("Date range selected:",{startISO:startISO,endISO:endISO})}})}catch(error){this.debugLog("DateRangePicker initialization failed, using fallback:",error)}}async loadData(){if(this.isLoading)return;this.debugLog("📊 Starting loadData process");const loadBtn=document.getElementById("load-btn");const exportBtn=document.getElementById("export-btn");const spinner=document.getElementById("load-spinner");if(!this.dateRangePicker){this.showError("Seletor de data não inicializado");return}this.isLoading=true;loadBtn.disabled=true;exportBtn.disabled=true;spinner.style.display="inline-block";try{const{startISO:startISO,endISO:endISO}=this.dateRangePicker.getDates();this.debugLog("📅 Date range selected",{startISO:startISO,endISO:endISO});if(!startISO||!endISO){this.showError("Selecione um período válido");return}const startDate=startISO.split("T")[0];const endDate=endISO.split("T")[0];this.debugLog("🌐 Fetching customer totals from API...");const customerTotalsData=await this.fetchCustomerTotals(startISO,endISO);this.debugLog("✅ API response received",customerTotalsData);this.debugLog("🔄 Processing API response...");this.data=this.mapCustomerTotalsResponse(customerTotalsData);this.debugLog("✅ Data mapping completed",{mappedDataLength:this.data.length,mappedData:this.data,totalConsumption:this.calculateTotalConsumption()});this.selectedStoreIds=new Set(this.data.map(store=>this.generateStoreId(store.identifier)));this.debugLog("🎯 Store IDs initialized",{selectedStoreIdsSize:this.selectedStoreIds.size,selectedStoreIds:Array.from(this.selectedStoreIds)});this.currentPage=1;this.debugLog("🎨 Rendering UI components...");this.renderSummary();this.renderTable();exportBtn.disabled=false;this.debugLog("🎉 Load process completed successfully");this.emit("loaded",{date:{start:startDate,end:endDate},stores:this.data.length,totalConsumption:this.calculateTotalConsumption()})}catch(error){this.debugLog("❌ Error in loadData",error);this.showError("Erro ao carregar dados: "+error.message);this.debugLog("Error loading data:",error);this.emit("error",{message:error.message,context:"loadData"})}finally{this.isLoading=false;loadBtn.disabled=false;spinner.style.display="none"}}getFilteredData(){let filtered=this.data;if(this.selectedStoreIds.size>0){filtered=this.data.filter(store=>{const storeId=this.generateStoreId(store.identifier);return this.selectedStoreIds.has(storeId)})}if(this.searchFilter){filtered=filtered.filter(store=>{const name=(store.name||"").toString().toLowerCase();const identifier=(store.identifier||"").toString().toLowerCase();return name.includes(this.searchFilter)||identifier.includes(this.searchFilter)})}return filtered.sort((a,b)=>{const aVal=a[this.sortField];const bVal=b[this.sortField];if(typeof aVal==="string"&&typeof bVal==="string"){return this.sortDirection==="asc"?aVal.localeCompare(bVal):bVal.localeCompare(aVal)}else{return this.sortDirection==="asc"?aVal-bVal:bVal-aVal}})}getPaginatedData(){return this.getFilteredData()}renderSummary(){const container=document.getElementById("summary-container");if(!container)return;const totalConsumption=this.calculateTotalConsumption();const storeCount=Math.max(1,this.data.length);container.innerHTML=`\n <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px; padding: 16px; background: var(--myio-bg); border-radius: 6px;">\n <div style="text-align: center;">\n <div style="font-size: 24px; font-weight: bold; color: var(--myio-primary);">${storeCount}</div>\n <div style="color: var(--myio-text-muted);">Lojas</div>\n </div>\n <div style="text-align: center;">\n <div style="font-size: 24px; font-weight: bold; color: var(--myio-primary);">${fmtPt(totalConsumption)}</div>\n <div style="color: var(--myio-text-muted);">${this.domainConfig.totalLabel}</div>\n </div>\n <div style="text-align: center;">\n <div style="font-size: 24px; font-weight: bold; color: var(--myio-primary);">${fmtPt(totalConsumption/storeCount)}</div>\n <div style="color: var(--myio-text-muted);">Média por Loja</div>\n </div>\n </div>\n `;container.style.display="block"}renderTable(){const container=document.getElementById("table-container");if(!container)return;const paginatedData=this.getPaginatedData();this.getFilteredData();if(paginatedData.length===0){container.innerHTML=`\n <div style="text-align: center; padding: 40px; color: var(--myio-text-muted);">\n ${this.searchFilter?"Nenhuma loja encontrada com o filtro aplicado.":"Nenhum dado encontrado."}\n </div>\n `;return}container.innerHTML=`\n <div style="max-height: 500px; overflow-y: auto; border: 1px solid var(--myio-border); border-radius: 6px;">\n <style>\n @media (max-width: 768px) {\n .myio-table-mobile {\n display: block !important;\n }\n .myio-table-mobile thead,\n .myio-table-mobile tbody,\n .myio-table-mobile th,\n .myio-table-mobile td,\n .myio-table-mobile tr {\n display: block !important;\n }\n .myio-table-mobile thead tr {\n position: absolute !important;\n top: -9999px !important;\n left: -9999px !important;\n }\n .myio-table-mobile tbody tr {\n border: 1px solid var(--myio-border) !important;\n border-radius: 8px !important;\n margin-bottom: 16px !important;\n padding: 16px !important;\n background: white !important;\n }\n .myio-table-mobile tbody td {\n border: none !important;\n padding: 8px 0 !important;\n position: relative !important;\n }\n .myio-table-mobile tbody td:before {\n content: attr(data-label) ": " !important;\n font-weight: bold !important;\n display: inline-block !important;\n width: 120px !important;\n color: var(--myio-text-muted) !important;\n }\n }\n </style>\n <table class="myio-table myio-table-mobile" style="table-layout: fixed; width: 100%;">\n <thead style="position: sticky; top: 0; background: var(--myio-bg); z-index: 1;">\n <tr>\n <th style="cursor: pointer; width: 25%;" data-sort="identifier">\n Identifier\n <span style="margin-left: 4px; opacity: ${this.getSortOpacity("identifier")};">${this.getSortIcon("identifier")}</span>\n </th>\n <th style="cursor: pointer; width: 45%;" data-sort="name">\n Name\n <span style="margin-left: 4px; opacity: ${this.getSortOpacity("name")};">${this.getSortIcon("name")}</span>\n </th>\n <th style="cursor: pointer; text-align: right; width: 30%;" data-sort="consumption">\n ${this.domainConfig.label}\n <span style="margin-left: 4px; opacity: ${this.getSortOpacity("consumption")};">${this.getSortIcon("consumption")}</span>\n </th>\n </tr>\n </thead>\n <tbody>\n ${paginatedData.map(row=>`\n <tr>\n <td data-label="Identifier" style="font-family: monospace; font-weight: bold; text-transform: uppercase;">${row.identifier}</td>\n <td data-label="Name"><strong>${row.name}</strong></td>\n <td data-label="${this.domainConfig.label}" style="text-align: right; font-weight: bold;">${row.consumption.toFixed(2)}</td>\n </tr>\n `).join("")}\n </tbody>\n </table>\n </div>\n `;this.setupTableSorting()}getSortIcon(field){if(this.sortField!==field)return"↕";return this.sortDirection==="asc"?"↑":"↓"}getSortOpacity(field){return this.sortField===field?"1":"0.5"}setupTableSorting(){const headers=document.querySelectorAll("[data-sort]");headers.forEach(header=>{header.addEventListener("click",()=>{const sortKey=header.getAttribute("data-sort");if(this.sortField===sortKey){this.sortDirection=this.sortDirection==="asc"?"desc":"asc"}else{this.sortField=sortKey;this.sortDirection=sortKey==="identifier"||sortKey==="name"?"asc":"desc"}this.currentPage=1;this.renderTable()})})}renderPagination(){return}calculateTotalConsumption(){return this.data.reduce((sum,row)=>sum+row.consumption,0)}openFilterModal(){if(!this.filterModal){this.filterModal=attachFilterOrderingModal({title:"Filtros & Ordenação - Lojas",items:this.convertToStoreItems(),initialSelected:Array.from(this.selectedStoreIds),initialSort:this.currentSortMode,onApply:({selected:selected,sort:sort})=>{this.applyFiltersAndSort(selected,sort)},onClose:()=>{}})}else{this.filterModal.setSelection(Array.from(this.selectedStoreIds));this.filterModal.setSort(this.currentSortMode)}this.filterModal.open()}convertToStoreItems(){const labelCounts=new Map;this.data.forEach(store=>{const count=labelCounts.get(store.name)||0;labelCounts.set(store.name,count+1)});return this.data.map(store=>{const isDuplicate=labelCounts.get(store.name)>1;const label=isDuplicate?`${store.name} <span style="font-size: 7px; font-style: italic; color: #666;">(${store.identifier})</span>`:store.name;return{id:this.generateStoreId(store.identifier),identifier:store.identifier,label:label,consumption:store.consumption}})}generateStoreId(storeName){const name=(storeName||"SEM-ID").toString();return name.toLowerCase().replace(/\s+/g,"-").replace(/[^a-z0-9-]/g,"")}applyFiltersAndSort(selectedIds,sortMode){this.selectedStoreIds=new Set(selectedIds);this.currentSortMode=sortMode;switch(sortMode){case"CONSUMPTION_DESC":this.sortField="consumption";this.sortDirection="desc";break;case"CONSUMPTION_ASC":this.sortField="consumption";this.sortDirection="asc";break;case"ALPHA_ASC":this.sortField="name";this.sortDirection="asc";break;case"ALPHA_DESC":this.sortField="name";this.sortDirection="desc";break}this.currentPage=1;this.renderSummary();this.renderTable();const filterBtn=document.getElementById("filter-btn");if(filterBtn&&selectedIds.length>0&&selectedIds.length<this.data.length){filterBtn.innerHTML=`🔍 Filtros & Ordenação (${selectedIds.length})`;filterBtn.style.background="var(--myio-brand-600)"}else{filterBtn.innerHTML="🔍 Filtros & Ordenação";filterBtn.style.background="var(--myio-brand-700)"}}exportCSV(){const sortedData=[...this.data].sort((a,b)=>b.consumption-a.consumption);const csvData=[["Identificador","Nome",`Consumo (${this.domainConfig.unit})`],...sortedData.map(row=>[row.identifier,row.name,row.consumption.toFixed(2)])];const csvContent=toCsv(csvData);this.downloadCSV(csvContent,`relatorio-geral-lojas-${(new Date).toISOString().split("T")[0]}.csv`)}async fetchCustomerTotals(startISO,endISO){if(this.params.fetcher){const token2=this.params.api.ingestionToken||await this.authClient.getBearer();return await this.params.fetcher({baseUrl:this.params.api.dataApiBaseUrl||"https://api.data.apps.myio-bas.com",token:token2,customerId:this.params.customerId,startISO:startISO,endISO:endISO})}const token=this.params.api.ingestionToken;if(!token){throw new Error("ingestionToken is required for Data API calls to data.apps.myio-bas.com")}const baseUrl=this.params.api.dataApiBaseUrl||"https://api.data.apps.myio-bas.com";const startTime=encodeURIComponent(startISO);const endTime=encodeURIComponent(endISO);const endpoint=this.domainConfig.endpoint;const url=`${baseUrl}/api/v1/telemetry/customers/${this.params.customerId}/${endpoint}/devices/totals?startTime=${startTime}&endTime=${endTime}`;this.debugLog("[AllReportModal] Fetching customer totals:",{url:url,customerId:this.params.customerId,domain:this.params.domain||"energy"});const response=await fetch(url,{method:"GET",headers:{Authorization:`Bearer ${token}`,"Content-Type":"application/json"}});if(!response.ok){throw new Error(`Erro na API: ${response.status} ${response.statusText}`)}const data=await response.json();this.debugLog("[AllReportModal] Customer totals response:",data);return data}mapCustomerTotalsResponse(apiResponse){this.debugLog("🔍 Starting mapCustomerTotalsResponse",{apiResponse:apiResponse});const apiArray=Array.isArray(apiResponse?.data)?apiResponse.data:Array.isArray(apiResponse)?apiResponse:[];this.debugLog("[AllReportModal] NEW MAPPING - API array length:",apiArray.length);this.debugLog("[AllReportModal] NEW MAPPING - ItemsList length:",this.params.itemsList.length);this.debugLog("📋 API data array extracted",{isDataProperty:!!apiResponse?.data,isDirectArray:Array.isArray(apiResponse),apiArrayLength:apiArray.length,firstFewItems:apiArray.slice(0,3)});if(!apiArray.length){this.debugLog("⚠️ Empty API array, returning empty result");this.debugLog("[AllReportModal] Empty/invalid API response:",apiResponse);return[]}const sumByApiId=new Map;let apiItemsWithoutId=0;let totalApiConsumption=0;this.debugLog("🔨 Building ID index from API data...");for(const[index,item]of apiArray.entries()){const consumption=this.pickConsumption(item);totalApiConsumption+=consumption;if(index<3){this.debugLog(`[AllReportModal] NEW MAPPING - API item ${index}:`,{id:item?.id,name:item?.name,assetName:item?.assetName,total_value:item?.total_value,extractedConsumption:consumption})}this.debugLog(`📊 Processing API item ${index}`,{item:item,extractedConsumption:consumption,hasId:!!item?.id});if(item?.id){const id=String(item.id);const previousSum=sumByApiId.get(id)||0;sumByApiId.set(id,previousSum+consumption);this.debugLog(`✅ Added to ID index: ${id} = ${previousSum} + ${consumption} = ${previousSum+consumption}`)}else{apiItemsWithoutId++;this.debugLog(`❌ API item without ID:`,item)}}this.debugLog("[AllReportModal] NEW MAPPING - Total API consumption:",totalApiConsumption);this.debugLog("[AllReportModal] NEW MAPPING - Unique API IDs:",sumByApiId.size);this.debugLog("📊 ID index built",{sumByApiIdSize:sumByApiId.size,sumByApiIdEntries:Array.from(sumByApiId.entries()),apiItemsWithoutId:apiItemsWithoutId});let matchedById=0,matchedBySubstring=0;let totalMappedConsumption=0;this.debugLog("🎯 Starting itemsList mapping...");const rows=this.params.itemsList.map((listItem,index)=>{this.debugLog(`🔍 Processing listItem ${index}`,listItem);let consumption=sumByApiId.get(listItem.id)??0;this.debugLog(`🎯 Primary ID match for ${listItem.id}: ${consumption}`);if(index<3){this.debugLog(`[AllReportModal] NEW MAPPING - ItemsList item ${index}:`,{id:listItem.id,identifier:listItem.identifier,label:listItem.label,idMatchConsumption:consumption})}if(consumption>0){matchedById++;this.debugLog(`✅ Matched by ID: ${listItem.id} -> ${consumption}`)}else{this.debugLog(`🔄 No ID match, trying substring fallback for identifier: ${listItem.identifier}`);for(const[apiIndex,apiItem]of apiArray.entries()){const assetName=apiItem?.assetName||"";const name=apiItem?.name||"";const assetNameMatch=assetName.includes(listItem.identifier);const nameMatch=name.includes(listItem.identifier);if(assetNameMatch||nameMatch){const itemConsumption=this.pickConsumption(apiItem);consumption+=itemConsumption;if(index<3){this.debugLog(`[AllReportModal] NEW MAPPING - Substring match for ${listItem.identifier}:`,{apiItemName:name,apiItemAssetName:assetName,itemConsumption:itemConsumption,totalConsumption:consumption})}this.debugLog(`✅ Substring match found in API item ${apiIndex}`,{listItemIdentifier:listItem.identifier,apiItemAssetName:assetName,apiItemName:name,assetNameMatch:assetNameMatch,nameMatch:nameMatch,itemConsumption:itemConsumption,totalConsumption:consumption})}}if(consumption>0){matchedBySubstring++;this.debugLog(`✅ Matched by substring: ${listItem.identifier} -> ${consumption}`)}else{this.debugLog(`❌ No match found for: ${listItem.identifier}`)}}const result={identifier:listItem.identifier,name:listItem.label,consumption:Math.round(consumption*100)/100};totalMappedConsumption+=result.consumption;this.debugLog(`📝 Final row for ${listItem.identifier}:`,result);return result});const stats={apiItems:apiArray.length,uniqueApiIds:sumByApiId.size,itemsInList:this.params.itemsList.length,matchedById:matchedById,matchedBySubstring:matchedBySubstring,unmatched:this.params.itemsList.length-matchedById-matchedBySubstring,apiItemsWithoutId:apiItemsWithoutId,totalApiConsumption:totalApiConsumption,totalMappedConsumption:totalMappedConsumption};this.debugLog("[AllReportModal] NEW MAPPING - Final stats:",stats);this.debugLog("📊 Final mapping stats:",stats);this.debugLog("[AllReportModal] Mapping stats:",stats);return rows}parseConsumptionValue(item){const possibleFields=["total_value","totalValue","consumption","value","total","energy","kwh"];for(const field of possibleFields){if(item[field]!==void 0&&item[field]!==null){const value=typeof item[field]==="string"?parseFloat(item[field].replace(",",".")):Number(item[field]);if(!isNaN(value)){return Math.round(value*100)/100}}}this.debugLog("[AllReportModal] No valid consumption value found in item:",item);return 0}downloadCSV(content,filename){const BOM="\ufeff";const csvWithBOM=BOM+content;const blob=new Blob([csvWithBOM],{type:"text/csv;charset=utf-8;"});const link=document.createElement("a");const url=URL.createObjectURL(blob);link.setAttribute("href",url);link.setAttribute("download",filename);link.style.visibility="hidden";document.body.appendChild(link);link.click();document.body.removeChild(link);URL.revokeObjectURL(url)}getDefaultStartDate(){const date=new Date;date.setDate(1);return date.toISOString().split("T")[0]}getDefaultEndDate(){return(new Date).toISOString().split("T")[0]}showError(message){const container=document.getElementById("error-container");if(container){container.textContent=message;container.style.display="block"}}hideError(){const container=document.getElementById("error-container");if(container){container.style.display="none"}}on(event,handler){if(!this.eventHandlers[event]){this.eventHandlers[event]=[]}this.eventHandlers[event].push(handler)}emit(event,payload){if(this.eventHandlers[event]){this.eventHandlers[event].forEach(handler=>handler())}}};function openDashboardPopupAllReport(params){const modal=new AllReportModal(params);return modal.show()}function openDashboardPopup(params){console.log("Opening settings modal with params:",params);return{close:()=>{console.log("Closing settings modal")},on:(event,handler)=>{console.log(`Registering ${event} handler for settings modal`)}}}var WaterTankModalView=class{config;overlay=null;modal=null;i18n;constructor(config){this.config=config;this.i18n=this.getI18n()}getI18n(){const defaults={title:"Water Tank",loading:"Loading...",error:"Error loading data",noData:"No data available",exportCsv:"Export CSV",close:"Close",currentLevel:"Current Level",averageLevel:"Average Level",minLevel:"Minimum Level",maxLevel:"Maximum Level",dateRange:"Date Range",deviceInfo:"Device Information",levelChart:"Water Level History (m.c.a)",percentUnit:"%",status:{critical:"Critical",low:"Low",medium:"Medium",good:"Good",full:"Full"}};return{...defaults,...this.config.params.i18n}}getLevelStatus(level){if(level<20){return{status:"critical",color:"#e74c3c",label:this.i18n.status.critical}}else if(level<40){return{status:"low",color:"#e67e22",label:this.i18n.status.low}}else if(level<70){return{status:"medium",color:"#f39c12",label:this.i18n.status.medium}}else if(level<90){return{status:"good",color:"#27ae60",label:this.i18n.status.good}}else{return{status:"full",color:"#3498db",label:this.i18n.status.full}}}getTankImageUrl(percentage){if(percentage>=70){return"https://dashboard.myio-bas.com/api/images/public/3t6WVhMQJFsrKA8bSZmrngDsNPkZV7fq"}else if(percentage>=40){return"https://dashboard.myio-bas.com/api/images/public/4UBbShfXCVWR9wcw6IzVMNran4x1EW5n"}else if(percentage>=20){return"https://dashboard.myio-bas.com/api/images/public/aB9nX28F54fBBQs1Ht8jKUdYAMcq9QSm"}else{return"https://dashboard.myio-bas.com/api/images/public/qLdwhV4qw295poSCa7HinpnmXoN7dAPO"}}formatDate(ts,includeTime=true){const date=new Date(ts);const dateStr=date.toLocaleDateString("pt-BR");if(includeTime){const timeStr=date.toLocaleTimeString("pt-BR");return`${dateStr} ${timeStr}`}return dateStr}formatDateForInput(ts){const date=new Date(ts);return date.toISOString().split("T")[0]}render(){const{params:params}=this.config;this.overlay=document.createElement("div");this.overlay.className="myio-water-tank-modal-overlay";this.overlay.style.cssText=`\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: ${params.zIndex||1e4};\n opacity: 0;\n transition: opacity 0.3s ease;\n `;this.modal=document.createElement("div");this.modal.className="myio-water-tank-modal";this.modal.style.cssText=`\n background: white;\n border-radius: 12px;\n box-shadow: 0 4px 24px rgba(0, 0, 0, 0.15);\n width: ${params.ui?.width||700}px;\n max-width: 95vw;\n max-height: 90vh;\n display: flex;\n flex-direction: column;\n transform: scale(0.9);\n transition: transform 0.3s ease;\n `;this.modal.innerHTML=`\n ${this.renderHeader()}\n ${this.renderBody()}\n `;this.overlay.appendChild(this.modal);document.body.appendChild(this.overlay);this.attachEventListeners()}renderHeader(){const{context:context,params:params}=this.config;const title=params.ui?.title||`${this.i18n.title} - ${context.device.label}`;return`\n <div class="myio-water-tank-modal-header" style="\n padding: 20px 24px;\n border-bottom: 1px solid #e0e0e0;\n display: flex;\n align-items: center;\n justify-content: space-between;\n ">\n <h2 style="\n margin: 0;\n font-size: 20px;\n font-weight: 600;\n color: #2c3e50;\n ">${title}</h2>\n <button class="myio-water-tank-modal-close" style="\n background: none;\n border: none;\n font-size: 24px;\n color: #7f8c8d;\n cursor: pointer;\n padding: 0;\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 4px;\n transition: background 0.2s ease;\n " title="${this.i18n.close}">\n ×\n </button>\n </div>\n `}renderBody(){return`\n <div class="myio-water-tank-modal-body" style="\n padding: 24px;\n overflow-y: auto;\n flex: 1;\n ">\n ${this.renderDateRangePicker()}\n ${this.renderTankVisualization()}\n ${this.renderChart()}\n </div>\n ${this.renderFooter()}\n `}renderDateRangePicker(){const{params:params}=this.config;const startDate=this.formatDateForInput(params.startTs);const endDate=this.formatDateForInput(params.endTs);return`\n <div style="\n background: #f8f9fa;\n border: 1px solid #e0e0e0;\n border-radius: 8px;\n padding: 16px;\n margin-bottom: 20px;\n display: flex;\n align-items: center;\n gap: 16px;\n flex-wrap: wrap;\n ">\n <div style="display: flex; align-items: center; gap: 8px;">\n <label style="font-size: 14px; font-weight: 500; color: #2c3e50;">From:</label>\n <input type="date" id="myio-water-tank-start-date" value="${startDate}" style="\n padding: 8px 12px;\n border: 1px solid #ddd;\n border-radius: 6px;\n font-size: 14px;\n color: #2c3e50;\n cursor: pointer;\n "/>\n </div>\n <div style="display: flex; align-items: center; gap: 8px;">\n <label style="font-size: 14px; font-weight: 500; color: #2c3e50;">To:</label>\n <input type="date" id="myio-water-tank-end-date" value="${endDate}" style="\n padding: 8px 12px;\n border: 1px solid #ddd;\n border-radius: 6px;\n font-size: 14px;\n color: #2c3e50;\n cursor: pointer;\n "/>\n </div>\n <button id="myio-water-tank-apply-dates" style="\n background: linear-gradient(135deg, #3498db 0%, #2980b9 100%);\n color: white;\n border: none;\n padding: 8px 20px;\n border-radius: 6px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n ">\n Apply\n </button>\n </div>\n `}renderTankVisualization(){const{data:data,context:context}=this.config;let percentage=0;const percentagePoints=data.telemetry.filter(p=>p.key==="water_percentage");if(percentagePoints.length>0){const latestPercentage=percentagePoints[percentagePoints.length-1].value;percentage=latestPercentage<=1?latestPercentage*100:latestPercentage}else if(context.device.currentLevel!==void 0){const level=context.device.currentLevel;percentage=level<=1?level*100:level}const levelStatus=this.getLevelStatus(percentage);const tankImageUrl=this.getTankImageUrl(percentage);return`\n <div style="\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 32px;\n padding: 24px;\n background: linear-gradient(135deg, ${levelStatus.color}10 0%, ${levelStatus.color}05 100%);\n border: 1px solid ${levelStatus.color}30;\n border-radius: 12px;\n margin-bottom: 24px;\n ">\n <div style="\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 12px;\n ">\n <img src="${tankImageUrl}" alt="Water Tank" style="\n width: 120px;\n height: auto;\n filter: drop-shadow(0 4px 8px rgba(0,0,0,0.1));\n "/>\n <div style="\n background: ${levelStatus.color};\n color: white;\n padding: 4px 12px;\n border-radius: 20px;\n font-size: 12px;\n font-weight: 600;\n text-transform: uppercase;\n ">${levelStatus.label}</div>\n </div>\n\n <div style="\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 8px;\n ">\n <div style="\n font-size: 48px;\n font-weight: 700;\n color: ${levelStatus.color};\n line-height: 1;\n ">${percentage.toFixed(1)}%</div>\n <div style="\n font-size: 14px;\n color: #7f8c8d;\n font-weight: 500;\n ">${this.i18n.currentLevel}</div>\n </div>\n </div>\n `}renderChart(){const{data:data}=this.config;const waterLevelPoints=data.telemetry.filter(p=>p.key==="water_level"||p.key==="waterLevel"||p.key==="nivel"||p.key==="level");if(waterLevelPoints.length===0){return`\n <div style="\n background: #f8f9fa;\n border: 1px solid #e0e0e0;\n border-radius: 8px;\n padding: 48px;\n text-align: center;\n ">\n <div style="font-size: 48px; margin-bottom: 16px; opacity: 0.3;">📊</div>\n <div style="color: #7f8c8d; font-size: 16px;">${this.i18n.noData}</div>\n <div style="color: #bdc3c7; font-size: 13px; margin-top: 8px;">\n No water_level (m.c.a) data available for this period\n </div>\n </div>\n `}const firstTs=waterLevelPoints[0]?.ts;const lastTs=waterLevelPoints[waterLevelPoints.length-1]?.ts;return`\n <div style="\n background: white;\n border: 1px solid #e0e0e0;\n border-radius: 8px;\n padding: 20px;\n ">\n <h3 style="\n margin: 0 0 16px 0;\n font-size: 16px;\n font-weight: 600;\n color: #2c3e50;\n ">${this.i18n.levelChart}</h3>\n <canvas id="myio-water-tank-chart" style="width: 100%; height: 280px;"></canvas>\n ${firstTs&&lastTs?`\n <div style="\n margin-top: 12px;\n font-size: 12px;\n color: #7f8c8d;\n text-align: center;\n ">\n ${this.formatDate(firstTs,false)} — ${this.formatDate(lastTs,false)}\n (${waterLevelPoints.length} readings)\n </div>\n `:""}\n </div>\n `}renderFooter(){const{params:params}=this.config;if(!params.ui?.showExport){return""}return`\n <div class="myio-water-tank-modal-footer" style="\n padding: 16px 24px;\n border-top: 1px solid #e0e0e0;\n display: flex;\n justify-content: flex-end;\n gap: 12px;\n ">\n <button class="myio-water-tank-export-btn" style="\n background: #3498db;\n color: white;\n border: none;\n padding: 10px 20px;\n border-radius: 6px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: background 0.2s ease;\n ">\n ${this.i18n.exportCsv}\n </button>\n </div>\n `}attachEventListeners(){if(!this.overlay||!this.modal)return;const closeBtn=this.modal.querySelector(".myio-water-tank-modal-close");if(closeBtn){closeBtn.addEventListener("click",()=>this.config.onClose())}const exportBtn=this.modal.querySelector(".myio-water-tank-export-btn");if(exportBtn){exportBtn.addEventListener("click",()=>this.config.onExport())}const applyDatesBtn=this.modal.querySelector("#myio-water-tank-apply-dates");if(applyDatesBtn){applyDatesBtn.addEventListener("click",()=>this.handleDateRangeChange())}this.overlay.addEventListener("click",e=>{if(e.target===this.overlay){this.config.onClose()}});if(this.config.params.closeOnEsc){this.handleEscapeKey=this.handleEscapeKey.bind(this);document.addEventListener("keydown",this.handleEscapeKey)}requestAnimationFrame(()=>{this.renderCanvasChart()})}handleDateRangeChange(){if(!this.modal)return;const startInput=this.modal.querySelector("#myio-water-tank-start-date");const endInput=this.modal.querySelector("#myio-water-tank-end-date");if(startInput&&endInput){const startTs=new Date(startInput.value).setHours(0,0,0,0);const endTs=new Date(endInput.value).setHours(23,59,59,999);if(startTs>=endTs){alert("Start date must be before end date");return}console.log("[WaterTankModalView] Date range changed:",{startTs:startTs,endTs:endTs,startDate:new Date(startTs).toISOString(),endDate:new Date(endTs).toISOString()});if(this.config.onDateRangeChange){this.config.onDateRangeChange(startTs,endTs)}}}handleEscapeKey(e){if(e.key==="Escape"){this.config.onClose()}}renderCanvasChart(){const canvas=document.getElementById("myio-water-tank-chart");if(!canvas)return;const{data:data}=this.config;const points=data.telemetry.filter(p=>p.key==="water_level"||p.key==="waterLevel"||p.key==="nivel"||p.key==="level");if(points.length<2)return;const ctx=canvas.getContext("2d");if(!ctx)return;const rect=canvas.getBoundingClientRect();canvas.width=rect.width*window.devicePixelRatio;canvas.height=280*window.devicePixelRatio;ctx.scale(window.devicePixelRatio,window.devicePixelRatio);const width=rect.width;const height=280;const padding={top:20,right:20,bottom:40,left:60};ctx.clearRect(0,0,width,height);const values=points.map(p=>p.value);const minValue=Math.min(...values);const maxValue=Math.max(...values);const valueRange=maxValue-minValue||1;const valuePadding=valueRange*.1;const chartMinY=minValue-valuePadding;const chartMaxY=maxValue+valuePadding;const chartRangeY=chartMaxY-chartMinY;ctx.fillStyle="#fafafa";ctx.fillRect(padding.left,padding.top,width-padding.left-padding.right,height-padding.top-padding.bottom);ctx.strokeStyle="#e8e8e8";ctx.lineWidth=1;ctx.fillStyle="#666";ctx.font="11px Arial";ctx.textAlign="right";const ySteps=5;for(let i=0;i<=ySteps;i++){const y=padding.top+(height-padding.top-padding.bottom)*(i/ySteps);const value=chartMaxY-chartRangeY*i/ySteps;ctx.beginPath();ctx.moveTo(padding.left,y);ctx.lineTo(width-padding.right,y);ctx.stroke();ctx.fillText(`${value.toFixed(2)}`,padding.left-8,y+4)}ctx.save();ctx.translate(15,height/2);ctx.rotate(-Math.PI/2);ctx.textAlign="center";ctx.fillStyle="#666";ctx.font="12px Arial";ctx.fillText("m.c.a",0,0);ctx.restore();ctx.strokeStyle="#ccc";ctx.lineWidth=1;ctx.beginPath();ctx.moveTo(padding.left,padding.top);ctx.lineTo(padding.left,height-padding.bottom);ctx.lineTo(width-padding.right,height-padding.bottom);ctx.stroke();const chartWidth=width-padding.left-padding.right;const chartHeight=height-padding.top-padding.bottom;const xScale=chartWidth/(points.length-1);ctx.beginPath();ctx.moveTo(padding.left,height-padding.bottom);points.forEach((point,index)=>{const x=padding.left+index*xScale;const y=padding.top+chartHeight-(point.value-chartMinY)/chartRangeY*chartHeight;ctx.lineTo(x,y)});ctx.lineTo(padding.left+(points.length-1)*xScale,height-padding.bottom);ctx.closePath();const gradient=ctx.createLinearGradient(0,padding.top,0,height-padding.bottom);gradient.addColorStop(0,"rgba(52, 152, 219, 0.3)");gradient.addColorStop(1,"rgba(52, 152, 219, 0.05)");ctx.fillStyle=gradient;ctx.fill();ctx.strokeStyle="#3498db";ctx.lineWidth=2;ctx.lineJoin="round";ctx.lineCap="round";ctx.beginPath();points.forEach((point,index)=>{const x=padding.left+index*xScale;const y=padding.top+chartHeight-(point.value-chartMinY)/chartRangeY*chartHeight;if(index===0){ctx.moveTo(x,y)}else{ctx.lineTo(x,y)}});ctx.stroke();if(points.length<=50){ctx.fillStyle="#3498db";points.forEach((point,index)=>{const x=padding.left+index*xScale;const y=padding.top+chartHeight-(point.value-chartMinY)/chartRangeY*chartHeight;ctx.beginPath();ctx.arc(x,y,3,0,2*Math.PI);ctx.fill()})}ctx.fillStyle="#888";ctx.font="10px Arial";ctx.textAlign="center";const xLabelCount=Math.min(6,points.length);const xLabelStep=Math.floor(points.length/xLabelCount);for(let i=0;i<points.length;i+=xLabelStep){const x=padding.left+i*xScale;const date=new Date(points[i].ts);const label=`${date.getDate()}/${date.getMonth()+1}`;ctx.fillText(label,x,height-padding.bottom+16)}if(points.length>1){const lastX=padding.left+(points.length-1)*xScale;const lastDate=new Date(points[points.length-1].ts);const lastLabel=`${lastDate.getDate()}/${lastDate.getMonth()+1}`;ctx.fillText(lastLabel,lastX,height-padding.bottom+16)}}updateData(data){this.config.data=data;if(this.modal){const bodyEl=this.modal.querySelector(".myio-water-tank-modal-body");if(bodyEl){bodyEl.innerHTML=`\n ${this.renderDateRangePicker()}\n ${this.renderTankVisualization()}\n ${this.renderChart()}\n `;const applyDatesBtn=this.modal.querySelector("#myio-water-tank-apply-dates");if(applyDatesBtn){applyDatesBtn.addEventListener("click",()=>this.handleDateRangeChange())}requestAnimationFrame(()=>{this.renderCanvasChart()})}}}show(){if(!this.overlay||!this.modal)return;requestAnimationFrame(()=>{if(this.overlay&&this.modal){this.overlay.style.opacity="1";this.modal.style.transform="scale(1)"}})}destroy(){if(this.config.params.closeOnEsc){document.removeEventListener("keydown",this.handleEscapeKey)}if(this.overlay&&this.modal){this.overlay.style.opacity="0";this.modal.style.transform="scale(0.9)";setTimeout(()=>{if(this.overlay){document.body.removeChild(this.overlay);this.overlay=null;this.modal=null}},300)}}};var WaterTankModal=class{view=null;options;context;data=null;constructor(options){this.options=this.normalizeOptions(options);this.context=this.buildContext(this.options)}normalizeOptions(options){return{...options,tbApiHost:options.tbApiHost||window.location.origin,timezone:options.timezone||"America/Sao_Paulo",theme:options.theme||"light",closeOnEsc:options.closeOnEsc!==false,zIndex:options.zIndex||1e4,telemetryKeys:options.telemetryKeys||["waterLevel","nivel","level"],aggregation:options.aggregation||"NONE",limit:options.limit||1e3,ui:{title:options.ui?.title||`Water Tank - ${options.label||options.deviceId}`,width:options.ui?.width||900,height:options.ui?.height||600,showExport:options.ui?.showExport!==false,showLevelIndicator:options.ui?.showLevelIndicator!==false}}}buildContext(options){return{device:{id:options.deviceId,label:options.label||options.deviceId,type:options.deviceType,currentLevel:options.currentLevel},metadata:{slaveId:options.slaveId,centralId:options.centralId,ingestionId:options.ingestionId},timeRange:{startTs:options.startTs,endTs:options.endTs,timezone:options.timezone}}}async fetchTelemetryData(){const config={tbApiHost:this.options.tbApiHost,tbJwtToken:this.options.tbJwtToken,deviceId:this.options.deviceId,keys:this.options.telemetryKeys,startTs:this.options.startTs,endTs:this.options.endTs,aggregation:this.options.aggregation,limit:this.options.limit};console.log("[WaterTankModal] Fetching telemetry data:",{deviceId:config.deviceId,keys:config.keys,timeRange:{start:new Date(config.startTs).toISOString(),end:new Date(config.endTs).toISOString()}});try{const keysParam=config.keys.join(",");const url=`${config.tbApiHost}/api/plugins/telemetry/DEVICE/${config.deviceId}/values/timeseries?keys=${keysParam}&startTs=${config.startTs}&endTs=${config.endTs}&limit=${config.limit}`;if(config.aggregation&&config.aggregation!=="NONE"){}const response=await fetch(url,{method:"GET",headers:{"Content-Type":"application/json","X-Authorization":`Bearer ${config.tbJwtToken}`}});if(!response.ok){if(response.status===401){throw this.createError("AUTH_ERROR","Authentication failed. Please login again.")}if(response.status===404){throw this.createError("NO_DATA","Device not found or no telemetry data available.")}throw this.createError("NETWORK_ERROR",`Failed to fetch data: ${response.statusText}`)}const rawData=await response.json();console.log("[WaterTankModal] Raw telemetry response:",rawData);const telemetryData=this.transformTelemetryData(rawData,config.keys);const summary=this.calculateSummary(telemetryData);const result={deviceId:config.deviceId,telemetry:telemetryData,summary:summary,metadata:{keys:config.keys,aggregation:config.aggregation||"NONE",limit:config.limit||1e3}};console.log("[WaterTankModal] Processed telemetry data:",{pointCount:result.telemetry.length,summary:result.summary});return result}catch(error){console.error("[WaterTankModal] Error fetching telemetry:",error);if(error instanceof Error&&"code"in error){throw error}throw this.createError("NETWORK_ERROR",`Failed to fetch telemetry data: ${error instanceof Error?error.message:"Unknown error"}`,error)}}transformTelemetryData(rawData,keys){const allPoints=[];for(const key of keys){if(rawData[key]&&Array.isArray(rawData[key])){const keyPoints=rawData[key].map(point=>({ts:point.ts,value:typeof point.value==="string"?parseFloat(point.value):point.value,key:key}));allPoints.push(...keyPoints)}}allPoints.sort((a,b)=>a.ts-b.ts);const uniquePoints=[];const seenTimestamps=new Set;for(const point of allPoints){if(!seenTimestamps.has(point.ts)){seenTimestamps.add(point.ts);uniquePoints.push(point)}}return uniquePoints}calculateSummary(telemetry){if(telemetry.length===0){return{currentLevel:this.options.currentLevel,avgLevel:0,minLevel:0,maxLevel:0,totalReadings:0}}const values=telemetry.map(p=>p.value);const sum=values.reduce((acc,val)=>acc+val,0);const avg=sum/values.length;const min=Math.min(...values);const max=Math.max(...values);const lastPoint=telemetry[telemetry.length-1];return{currentLevel:lastPoint.value,avgLevel:avg,minLevel:min,maxLevel:max,totalReadings:telemetry.length,firstReadingTs:telemetry[0].ts,lastReadingTs:lastPoint.ts}}createError(code,message,cause){const error={code:code,message:message,cause:cause};switch(code){case"AUTH_ERROR":case"TOKEN_EXPIRED":error.userAction="RE_AUTH";break;case"NETWORK_ERROR":error.userAction="RETRY";break;case"VALIDATION_ERROR":error.userAction="FIX_INPUT";break;default:error.userAction="CONTACT_ADMIN"}return error}async show(){try{console.log("[WaterTankModal] Initializing modal...");this.data=await this.fetchTelemetryData();if(this.options.onDataLoaded){try{this.options.onDataLoaded(this.data)}catch(callbackError){console.warn("[WaterTankModal] onDataLoaded callback error:",callbackError)}}this.view=new WaterTankModalView({context:this.context,params:this.options,data:this.data,onExport:()=>this.handleExport(),onError:error=>this.handleError(error),onClose:()=>this.close(),onDateRangeChange:(startTs,endTs)=>this.handleDateRangeChange(startTs,endTs)});this.view.render();this.view.show();if(this.options.onOpen){try{this.options.onOpen(this.context)}catch(callbackError){console.warn("[WaterTankModal] onOpen callback error:",callbackError)}}console.log("[WaterTankModal] Modal opened successfully");return{close:()=>this.close()}}catch(error){console.error("[WaterTankModal] Failed to show modal:",error);this.handleError(error);throw error}}close(){console.log("[WaterTankModal] Closing modal");if(this.view){this.view.destroy();this.view=null}this.handleClose()}async handleDateRangeChange(startTs,endTs){console.log("[WaterTankModal] Date range changed:",{startTs:startTs,endTs:endTs,startDate:new Date(startTs).toISOString(),endDate:new Date(endTs).toISOString()});this.options.startTs=startTs;this.options.endTs=endTs;this.context.timeRange.startTs=startTs;this.context.timeRange.endTs=endTs;try{console.log("[WaterTankModal] Fetching new data for date range...");this.data=await this.fetchTelemetryData();if(this.view){this.view.updateData(this.data)}if(this.options.onDataLoaded){try{this.options.onDataLoaded(this.data)}catch(callbackError){console.warn("[WaterTankModal] onDataLoaded callback error:",callbackError)}}console.log("[WaterTankModal] Data refreshed successfully")}catch(error){console.error("[WaterTankModal] Failed to fetch data for new date range:",error);this.handleError(error)}}handleExport(){if(!this.data){console.warn("[WaterTankModal] No data to export");return}console.log("[WaterTankModal] Exporting CSV...");try{const headers=["Timestamp","Date","Time","Level (%)","Key"];const rows=this.data.telemetry.map(point=>{const date=new Date(point.ts);return[point.ts.toString(),date.toLocaleDateString("pt-BR"),date.toLocaleTimeString("pt-BR"),point.value.toFixed(2),point.key||"waterLevel"]});const csvContent=[headers.join(","),...rows.map(row=>row.join(","))].join("\n");const blob=new Blob([csvContent],{type:"text/csv;charset=utf-8;"});const link=document.createElement("a");const url=URL.createObjectURL(blob);const filename=`water-tank-${this.options.deviceId}-${Date.now()}.csv`;link.setAttribute("href",url);link.setAttribute("download",filename);link.style.visibility="hidden";document.body.appendChild(link);link.click();document.body.removeChild(link);console.log("[WaterTankModal] CSV exported successfully:",filename)}catch(error){console.error("[WaterTankModal] Export failed:",error);this.handleError(this.createError("UNKNOWN_ERROR","Failed to export CSV"))}}handleError(error){console.error("[WaterTankModal] Error:",error);if(this.options.onError){try{this.options.onError(error)}catch(callbackError){console.warn("[WaterTankModal] onError callback error:",callbackError)}}}handleClose(){if(this.options.onClose){try{this.options.onClose()}catch(callbackError){console.warn("[WaterTankModal] onClose callback error:",callbackError)}}}};async function openDashboardPopupWaterTank(options){try{validateOptions2(options);console.log("[openDashboardPopupWaterTank] Opening water tank modal with options:",{deviceId:options.deviceId,label:options.label,currentLevel:options.currentLevel,timeRange:{start:new Date(options.startTs).toISOString(),end:new Date(options.endTs).toISOString()},telemetryKeys:options.telemetryKeys||["waterLevel","nivel","level"]});const modal=new WaterTankModal(options);const modalHandle=await modal.show();console.log("[openDashboardPopupWaterTank] Water tank modal opened successfully");return modalHandle}catch(error){console.error("[openDashboardPopupWaterTank] Error opening modal:",error);if(options.onError){try{options.onError({code:error instanceof Error&&error.message.includes("validation")?"VALIDATION_ERROR":error instanceof Error&&error.message.includes("auth")?"AUTH_ERROR":"UNKNOWN_ERROR",message:error instanceof Error?error.message:"Unknown error occurred",cause:error})}catch(callbackError){console.warn("[openDashboardPopupWaterTank] onError callback failed:",callbackError)}}throw error}}function validateOptions2(options){const errors=[];if(!options.deviceId||typeof options.deviceId!=="string"){errors.push("deviceId is required and must be a string")}if(!options.tbJwtToken||typeof options.tbJwtToken!=="string"){errors.push("tbJwtToken is required and must be a string")}if(typeof options.startTs!=="number"||isNaN(options.startTs)){errors.push("startTs is required and must be a valid timestamp number")}if(typeof options.endTs!=="number"||isNaN(options.endTs)){errors.push("endTs is required and must be a valid timestamp number")}if(options.startTs&&options.endTs&&options.startTs>=options.endTs){errors.push("startTs must be before endTs")}if(options.currentLevel!==void 0){const level=Number(options.currentLevel);if(isNaN(level)||level<0||level>100){errors.push("currentLevel must be a number between 0 and 100")}}if(options.limit!==void 0){const limit=Number(options.limit);if(isNaN(limit)||limit<1||limit>1e4){errors.push("limit must be a number between 1 and 10000")}}if(options.aggregation!==void 0){const validAggregations=["NONE","MIN","MAX","AVG","SUM","COUNT"];if(!validAggregations.includes(options.aggregation)){errors.push(`aggregation must be one of: ${validAggregations.join(", ")}`)}}if(errors.length>0){throw new Error(`Validation failed:\n- ${errors.join("\n- ")}`)}}var SettingsModalView=class{container;modal;form;config;focusTrapElements=[];originalActiveElement=null;constructor(config){this.config=config;this.createModal()}render(initialData){this.originalActiveElement=document.activeElement;document.body.appendChild(this.container);let formData={...initialData};if(initialData.deviceMapInstaneousPower&&typeof initialData.deviceMapInstaneousPower==="object"){console.log("[SettingsModalView] Configuração salva encontrada (Device Scope). Processando...");const flatLimits=this.parseDeviceSavedLimits(initialData.deviceMapInstaneousPower);formData={...formData,...flatLimits}}this.populateForm(formData);this.attachEventListeners();this.setupAccessibility();this.setupFocusTrap();this.applyTheme();this.fetchLatestConsumptionTelemetry()}close(){this.teardownFocusTrap();if(this.originalActiveElement&&"focus"in this.originalActiveElement){this.originalActiveElement.focus()}if(this.container.parentNode){this.container.parentNode.removeChild(this.container)}}updateMapInstantaneousPower(mapInstantaneousPower){this.config.mapInstantaneousPower=mapInstantaneousPower;console.log("[SettingsModalView] RFC-0080: Updated mapInstantaneousPower config")}showError(message){const errorEl=this.modal.querySelector(".error-message");if(errorEl){errorEl.textContent=message;errorEl.style.display="block";errorEl.setAttribute("role","alert");errorEl.setAttribute("aria-live","polite")}}hideError(){const errorEl=this.modal.querySelector(".error-message");if(errorEl){errorEl.style.display="none";errorEl.removeAttribute("role");errorEl.removeAttribute("aria-live")}}showLoadingState(isLoading){const saveBtn=this.modal.querySelector(".btn-save");const cancelBtn=this.modal.querySelector(".btn-cancel");const formInputs=this.modal.querySelectorAll("input, select, textarea");if(saveBtn){saveBtn.disabled=isLoading;saveBtn.textContent=isLoading?"Salvando...":"Salvar"}if(cancelBtn){cancelBtn.disabled=isLoading}formInputs.forEach(input=>{input.disabled=isLoading})}formatDomainLabel(domain){const MAP={energy:"de energia",water:"de água",temperature:"de temperatura"};return MAP[domain]}getTelemetryLabelByDomain(){const domain=this.config.domain||"energy";const MAP={energy:"de Consumo",water:"de Água",temperature:"de Temperatura"};return MAP[domain]||"de Consumo"}getFormData(){const formData=new FormData(this.form);const data={};for(const[key,value]of formData.entries()){if(typeof value==="string"){if(["maxDailyKwh","maxNightKwh","maxBusinessKwh","minTemperature","maxTemperature","minWaterLevel","maxWaterLevel"].includes(key)){const num=parseFloat(value);if(!isNaN(num)){if(key.includes("Kwh")&&num<0){continue}if(key.includes("WaterLevel")){if(num<0||num>100){continue}}data[key]=num}}else if(value.trim()){data[key]=value.trim()}}}return data}createModal(){this.container=document.createElement("div");this.container.className="myio-settings-modal-overlay";this.container.innerHTML=this.getModalHTML();this.modal=this.container.querySelector(".myio-settings-modal");this.form=this.modal.querySelector("form")}getModalHTML(){const width=typeof this.config.width==="number"?`${this.config.width}px`:this.config.width;return`\n <div class="myio-settings-modal-overlay" role="dialog" aria-modal="true" aria-labelledby="modal-title">\n <div class="myio-settings-modal" style="width: ${width}">\n <div class="modal-header">\n <h3 id="modal-title">Configurações</h3>\n <button type="button" class="close-btn" aria-label="Fechar">×</button>\n </div>\n <div class="modal-body">\n <div class="error-message" style="display: none;" role="alert" aria-live="polite"></div>\n <form novalidate>\n ${this.getFormHTML()}\n </form>\n </div>\n <div class="modal-footer">\n <button type="button" class="btn-cancel">Fechar</button>\n <button type="button" class="btn-save btn-primary">Salvar</button>\n </div>\n </div>\n </div>\n ${this.getModalCSS()}\n `}getFormHTML(){const deviceType=this.config.deviceType;const customerName=this.config.customerName;const hasCustomerName=customerName&&customerName.trim()!=="";return`\n <div class="form-layout">\n ${hasCustomerName?`\n \x3c!-- RFC-0077/0078: Shopping name display with device type icon --\x3e\n <div class="customer-name-container">\n <div class="customer-info-row">\n <div class="device-type-icon-wrapper">\n ${this.getDeviceTypeIcon(deviceType)}\n </div>\n <div class="customer-info-content">\n <div class="customer-name-label">Shopping</div>\n <div class="customer-name-value">\n <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="shopping-icon"><path d="M4 22h16"/><path d="M7 22V4a2 2 0 0 1 2-2h6a2 2 0 0 1 2 2v18"/></svg>\n <span class="customer-name-text">${customerName}</span>\n </div>\n </div>\n </div>\n </div>\n `:""}\n\n \x3c!-- Top Row: Two cards side by side --\x3e\n <div class="form-columns">\n \x3c!-- Left Column: Device Label --\x3e\n <div class="form-column">\n <div class="form-card">\n <h4 class="section-title device-label-title">${this.config.deviceLabel||"NÃO INFORMADO"}</h4>\n\n <div class="form-group">\n <label for="label">Etiqueta</label>\n <input type="text" id="label" name="label" required maxlength="255">\n </div>\n\n <div class="form-group">\n <label for="floor">Andar</label>\n <input type="text" id="floor" name="floor" maxlength="50">\n </div>\n\n <div class="form-group">\n <label for="identifier">Identificador / LUC / SUC</label>\n <input type="text" id="identifier" name="identifier" maxlength="20" readonly>\n </div>\n </div>\n </div>\n\n \x3c!-- Right Column: Alarms --\x3e\n <div class="form-column">\n ${this.getAlarmsHTML(deviceType)}\n </div>\n </div>\n\n \x3c!-- Bottom Row: Connection Info spanning full width --\x3e\n ${this.getConnectionInfoHTML()}\n\n \x3c!-- RFC-0077: Power Limits Configuration (only for energy domain and when deviceType is available) --\x3e\n ${this.config.domain==="energy"&&this.config.deviceType?this.getPowerLimitsHTML():""}\n </div>\n `}getAlarmsHTML(deviceType){switch(deviceType){case"TERMOSTATO":return this.getThermostatAlarmsHTML();case"CAIXA_DAGUA":return this.getWaterTankAlarmsHTML();default:return this.getConsumptionAlarmsHTML()}}getConsumptionAlarmsHTML(){const unit=this.config.domain==="water"?"L":"kWh";return`\n <div class="form-card">\n <h4 class="section-title">Alarmes ${this.formatDomainLabel(this.config.domain)}</h4>\n\n <div class="form-group">\n <label for="maxDailyKwh">Consumo Máximo Diário (${unit})</label>\n <input type="number" id="maxDailyKwh" name="maxDailyKwh" min="0" step="0.1">\n </div>\n\n <div class="form-group">\n <label for="maxNightKwh">Consumo Máximo na Madrugada (0h–06h)</label>\n <input type="number" id="maxNightKwh" name="maxNightKwh" min="0" step="0.1">\n </div>\n\n <div class="form-group">\n <label for="maxBusinessKwh">Consumo Máximo Horário Comercial (09h–22h)</label>\n <input type="number" id="maxBusinessKwh" name="maxBusinessKwh" min="0" step="0.1">\n </div>\n </div>\n `}getThermostatAlarmsHTML(){return`\n <div class="form-card">\n <h4 class="section-title">Alarmes de Temperatura</h4>\n\n <div class="form-group">\n <label for="minTemperature">Temperatura Mínima (°C)</label>\n <input type="number" id="minTemperature" name="minTemperature" step="0.1">\n </div>\n\n <div class="form-group">\n <label for="maxTemperature">Temperatura Máxima (°C)</label>\n <input type="number" id="maxTemperature" name="maxTemperature" step="0.1">\n </div>\n </div>\n `}getWaterTankAlarmsHTML(){return`\n <div class="form-card">\n <h4 class="section-title">Alarmes de Nível</h4>\n\n <div class="form-group">\n <label for="minWaterLevel">Nível Mínimo (%)</label>\n <input type="number" id="minWaterLevel" name="minWaterLevel" min="0" max="100" step="0.1" placeholder="Risco de falta d'água">\n </div>\n\n <div class="form-group">\n <label for="maxWaterLevel">Nível Máximo (%)</label>\n <input type="number" id="maxWaterLevel" name="maxWaterLevel" min="0" max="100" step="0.1" placeholder="Risco de transbordar">\n </div>\n </div>\n `}getDeviceTypeIcon(deviceType){let normalizedType=(deviceType||"").toUpperCase();if(normalizedType==="3F_MEDIDOR"){const deviceProfile=this.config.deviceProfile;if(deviceProfile&&deviceProfile!=="N/D"&&deviceProfile.trim()!==""){normalizedType=deviceProfile.toUpperCase()}}const energyDevices=["COMPRESSOR","VENTILADOR","ESCADA_ROLANTE","ELEVADOR","MOTOR","3F_MEDIDOR","RELOGIO","ENTRADA","SUBESTACAO","BOMBA","CHILLER","AR_CONDICIONADO","HVAC","FANCOIL"];const waterDevices=["HIDROMETRO","CAIXA_DAGUA","TANK"];if(energyDevices.includes(normalizedType)){return`\n <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="device-type-icon energy-icon">\n <path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/>\n </svg>\n `}else if(waterDevices.includes(normalizedType)){return`\n <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="device-type-icon water-icon">\n <path d="M12 2.69l5.66 5.66a8 8 0 1 1-11.31 0z"/>\n </svg>\n `}else{return`\n <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="device-type-icon generic-icon">\n <rect x="4" y="4" width="16" height="16" rx="2" ry="2"/>\n <rect x="9" y="9" width="6" height="6"/>\n <line x1="9" y1="1" x2="9" y2="4"/>\n <line x1="15" y1="1" x2="15" y2="4"/>\n <line x1="9" y1="20" x2="9" y2="23"/>\n <line x1="15" y1="20" x2="15" y2="23"/>\n <line x1="20" y1="9" x2="23" y2="9"/>\n <line x1="20" y1="14" x2="23" y2="14"/>\n <line x1="1" y1="9" x2="4" y2="9"/>\n <line x1="1" y1="14" x2="4" y2="14"/>\n </svg>\n `}}getConsumptionLimits(){const mapPower=this.config.mapInstantaneousPower||{};const limitsByType=mapPower.limitsByInstantaneoustPowerType||[];const consumptionGroup=limitsByType.find(group=>group.telemetryType==="consumption");const targetDeviceType=this.config.deviceType;const itemsByDevice=consumptionGroup?.itemsByDeviceType||[];const deviceSettings=itemsByDevice.find(item=>item.deviceType===targetDeviceType);const limitsList=deviceSettings?.limitsByDeviceStatus||[];const getValues=statusName=>{const statusObj=limitsList.find(l=>l.deviceStatusName===statusName);return statusObj?.limitsValues||{baseValue:"",topValue:""}};return{hasConfig:!!deviceSettings,description:deviceSettings?.description||"Padrão do Sistema",standby:getValues("standBy"),normal:getValues("normal"),alert:getValues("alert"),failure:getValues("failure")}}getPowerLimitsHTML(){const globalData=this.getConsumptionLimits();const fmtRange=val=>{if(val.baseValue===""||val.baseValue===void 0)return"—";return`${val.baseValue} a ${val.topValue} W`};return`\n <div class="form-card power-limits-card">\n <div class="power-limits-header">\n <h4 class="section-title">Configuração de Limites de Telemetrias Instantâneas</h4>\n <div class="power-limits-subtitle">\n Monitoramento de Consumo (W) para: <strong>${this.config.deviceType||"N/D"}</strong>\n </div>\n </div>\n\n <div class="power-limits-controls-row" style="display:flex; gap:20px; align-items: flex-end; margin-bottom: 15px;">\n <div class="telemetry-selector-group" style="flex:1">\n <label for="telemetryType" style="display:block; margin-bottom:5px; font-weight:500;">Tipo de Telemetria</label>\n <select id="telemetryType" name="telemetryType" class="form-select">\n <option value="consumption" selected>Potência Ativa (W)</option>\n </select>\n </div>\n </div>\n\n <div class="global-reference-container">\n <div class="global-ref-header">\n <span>🌐 Referência Global (${globalData.description})</span>\n </div>\n <div class="global-values-grid">\n <div class="global-value-item">\n <span class="g-status">StandBy 🔌</span>\n <span class="g-range">${fmtRange(globalData.standby)}</span>\n </div>\n <div class="global-value-item">\n <span class="g-status">Normal ⚡</span>\n <span class="g-range">${fmtRange(globalData.normal)}</span>\n </div>\n <div class="global-value-item">\n <span class="g-status">Alerta ⚠️</span>\n <span class="g-range">${fmtRange(globalData.alert)}</span>\n </div>\n <div class="global-value-item">\n <span class="g-status">Falha 🚨</span>\n <span class="g-range">${fmtRange(globalData.failure)}</span>\n </div>\n </div>\n </div>\n\n <div class="power-limits-table-wrapper">\n <table class="power-limits-table">\n <thead>\n <tr>\n <th style="width: 30%">Status</th>\n <th style="width: 35%">Mínimo (W)</th>\n <th style="width: 35%">Máximo (W)</th>\n </tr>\n </thead>\n <tbody>\n <tr class="limit-row">\n <td class="status-label"><span class="status-icon">🔌</span> StandBy</td>\n <td>\n <input type="number" \n name="standbyLimitDownConsumption" \n class="limit-input js-limit-input" \n min="0" step="1" \n placeholder="Vazio" \n data-global-value="${globalData.standby.baseValue}">\n </td>\n <td>\n <input type="number" \n name="standbyLimitUpConsumption" \n class="limit-input js-limit-input" \n min="0" step="1" \n placeholder="Vazio" \n data-global-value="${globalData.standby.topValue}">\n </td>\n </tr>\n \n <tr class="limit-row">\n <td class="status-label"><span class="status-icon">⚡</span> Normal</td>\n <td>\n <input type="number" \n name="normalLimitDownConsumption" \n class="limit-input js-limit-input" \n min="0" step="1" \n placeholder="Vazio" \n data-global-value="${globalData.normal.baseValue}">\n </td>\n <td>\n <input type="number" \n name="normalLimitUpConsumption" \n class="limit-input js-limit-input" \n min="0" step="1" \n placeholder="Vazio" \n data-global-value="${globalData.normal.topValue}">\n </td>\n </tr>\n\n <tr class="limit-row">\n <td class="status-label"><span class="status-icon">⚠️</span> Alerta</td>\n <td>\n <input type="number" \n name="alertLimitDownConsumption" \n class="limit-input js-limit-input" \n min="0" step="1" \n placeholder="Vazio" \n data-global-value="${globalData.alert.baseValue}">\n </td>\n <td>\n <input type="number" \n name="alertLimitUpConsumption" \n class="limit-input js-limit-input" \n min="0" step="1" \n placeholder="Vazio" \n data-global-value="${globalData.alert.topValue}">\n </td>\n </tr>\n\n <tr class="limit-row">\n <td class="status-label"><span class="status-icon">🚨</span> Falha</td>\n <td>\n <input type="number" \n name="failureLimitDownConsumption" \n class="limit-input js-limit-input" \n min="0" step="1" \n placeholder="Vazio" \n data-global-value="${globalData.failure.baseValue}">\n </td>\n <td>\n <input type="number" \n name="failureLimitUpConsumption" \n class="limit-input js-limit-input" \n min="0" step="1" \n placeholder="Vazio" \n data-global-value="${globalData.failure.topValue}">\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n\n <div class="power-limits-actions">\n <button type="button" class="btn-copy-global" id="btnCopyFromGlobal">\n ⬇️ Copiar valores da Referência Global\n </button>\n \n <button type="button" class="btn-clear-overrides" id="btnClearInputs">\n 🗑️ Limpar Campos\n </button>\n </div>\n </div>\n `}calculateTimeBetweenDates(data1,data2){if(!(data1 instanceof Date)||!(data2 instanceof Date)){console.error("Entradas inválidas. As duas entradas devem ser objetos Date.");return"Datas inválidas"}const diffMs=Math.abs(data1.getTime()-data2.getTime());const msPorMinuto=1e3*60;const msPorHora=msPorMinuto*60;const msPorDia=msPorHora*24;if(diffMs>=msPorDia){const dias=Math.floor(diffMs/msPorDia);return`${dias} ${dias===1?"dia":"dias"}`}if(diffMs>=msPorHora){const horas=Math.floor(diffMs/msPorHora);return`${horas} ${horas===1?"hora":"horas"}`}const minutos=Math.round(diffMs/msPorMinuto);return`${minutos} ${minutos===1?"minuto":"minutos"}`}getConnectionInfoHTML(){if(!this.config.connectionData){return""}const{centralName:centralName,connectionStatusTime:connectionStatusTime,timeVal:timeVal,deviceStatus:deviceStatus,lastDisconnectTime:lastDisconnectTime}=this.config.connectionData;let disconnectionIntervalFormatted="N/A";if(lastDisconnectTime&&connectionStatusTime){try{const disconnectDate=new Date(lastDisconnectTime);const reconnectDate=new Date(connectionStatusTime);const disconnectFormatted=disconnectDate.toLocaleString("pt-BR",{day:"2-digit",month:"2-digit",year:"numeric",hour:"2-digit",minute:"2-digit",second:"2-digit"});const reconnectFormatted=reconnectDate.toLocaleString("pt-BR",{day:"2-digit",month:"2-digit",year:"numeric",hour:"2-digit",minute:"2-digit",second:"2-digit"});const diffMs=reconnectDate.getTime()-disconnectDate.getTime();const diffSeconds=Math.floor(diffMs/1e3);const diffMinutes=Math.floor(diffSeconds/60);const diffHours=Math.floor(diffMinutes/60);const diffDays=Math.floor(diffHours/24);let durationText="";if(diffDays>0){const remainingHours=diffHours%24;durationText=`${diffDays} dia${diffDays>1?"s":""}${remainingHours>0?` e ${remainingHours} hora${remainingHours>1?"s":""}`:""}`}else if(diffHours>0){const remainingMinutes=diffMinutes%60;durationText=`${diffHours} hora${diffHours>1?"s":""}${remainingMinutes>0?` e ${remainingMinutes} minuto${remainingMinutes>1?"s":""}`:""}`}else if(diffMinutes>0){const remainingSeconds=diffSeconds%60;durationText=`${diffMinutes} minuto${diffMinutes>1?"s":""}${remainingSeconds>0?` e ${remainingSeconds} segundo${remainingSeconds>1?"s":""}`:""}`}else{durationText=`${diffSeconds} segundo${diffSeconds!==1?"s":""}`}disconnectionIntervalFormatted=`${disconnectFormatted} até ${reconnectFormatted} (${durationText})`}catch(e){disconnectionIntervalFormatted="Formato inválido"}}let connectionTimeFormatted="—";let timeSinceLastConnection="";let isCurrentlyConnected=false;if(connectionStatusTime){try{const connectDate=new Date(connectionStatusTime);const disconnectDate=lastDisconnectTime?new Date(lastDisconnectTime):null;if(!disconnectDate||connectDate.getTime()>disconnectDate.getTime()){isCurrentlyConnected=true;connectionTimeFormatted=connectDate.toLocaleString("pt-BR",{day:"2-digit",month:"2-digit",year:"numeric",hour:"2-digit",minute:"2-digit"});const now=new Date;const diffMs=now.getTime()-connectDate.getTime();const diffMinutes=Math.floor(diffMs/(1e3*60));const diffHours=Math.floor(diffMinutes/60);const diffDays=Math.floor(diffHours/24);const remainingMinutes=diffMinutes%60;if(diffDays>0){const remainingHours=diffHours%24;timeSinceLastConnection=`(${diffDays}d:${remainingHours.toString().padStart(2,"0")}hs:${remainingMinutes.toString().padStart(2,"0")}mins atrás)`}else if(diffHours>0){timeSinceLastConnection=`(${diffHours}hs:${remainingMinutes.toString().padStart(2,"0")}mins atrás)`}else if(diffMinutes>0){timeSinceLastConnection=`(${diffMinutes}mins atrás)`}else{timeSinceLastConnection="(agora)"}}}catch(e){connectionTimeFormatted="—"}}let telemetryTimeFormatted="N/A";let timeSinceLastTelemetry="";if(timeVal){try{const telemetryDate=new Date(timeVal);telemetryTimeFormatted=telemetryDate.toLocaleString("pt-BR",{day:"2-digit",month:"2-digit",year:"numeric",hour:"2-digit",minute:"2-digit"});const now=new Date;const diffMs=now.getTime()-telemetryDate.getTime();const diffMinutes=Math.floor(diffMs/(1e3*60));const diffHours=Math.floor(diffMinutes/60);const diffDays=Math.floor(diffHours/24);if(diffDays>0){timeSinceLastTelemetry=`(${diffDays}d atrás)`}else if(diffHours>0){timeSinceLastTelemetry=`(${diffHours}h atrás)`}else if(diffMinutes>0){timeSinceLastTelemetry=`(${diffMinutes}min atrás)`}else{timeSinceLastTelemetry="(agora)"}}catch(e){telemetryTimeFormatted="Formato inválido"}}const statusMap={ok:{text:"ONLINE",color:"#22c55e"},alert:{text:"Atenção",color:"#f59e0b"},fail:{text:"OFFLINE",color:"#ef4444"},not_installed:{text:"Não instalado",color:"#94a3b8"},unknown:{text:"Sem informação",color:"#94a3b8"}};const statusInfo=statusMap[mapDeviceStatusToCardStatus(deviceStatus)]||{text:"Desconhecido",color:"#6b7280"};return`\n <div class="form-card info-card-wide">\n <h4 class="section-title">\n <svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" style="vertical-align: text-bottom; margin-right: 6px;">\n <path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z"/>\n <path d="m8.93 6.588-2.29.287-.082.38.45.083c.294.07.352.176.288.469l-.738 3.468c-.194.897.105 1.319.808 1.319.545 0 1.178-.252 1.465-.598l.088-.416c-.2.176-.492.246-.686.246-.275 0-.375-.193-.304-.533L8.93 6.588zM9 4.5a1 1 0 1 1-2 0 1 1 0 0 1 2 0z"/>\n </svg>\n Informações de Conexão\n </h4>\n\n <div class="info-grid">\n <div class="info-row">\n <span class="info-label">Central:</span>\n <span class="info-value">${centralName||"N/A"}${this.config.customerName?` (${this.config.customerName})`:""}</span>\n </div>\n\n <div class="info-row">\n <span class="info-label">Status:</span>\n <span class="info-value" style="color: ${statusInfo.color}; font-weight: 600;">\n ${statusInfo.text}\n </span>\n </div>\n\n <div class="info-row">\n <span class="info-label">Conectado desde:</span>\n <span class="info-value">\n ${connectionTimeFormatted}\n ${timeSinceLastConnection?`<span class="time-since">${timeSinceLastConnection}</span>`:""}\n </span>\n </div>\n\n <div class="info-row">\n <span class="info-label">Último check status:</span>\n <span class="info-value">\n ${telemetryTimeFormatted}\n ${timeSinceLastTelemetry?`<span class="time-since">${timeSinceLastTelemetry}</span>`:""}\n </span>\n </div>\n <div class="info-row full-width">\n <span class="info-label">Último intervalo desconectado:</span>\n <span class="info-value disconnect-interval">\n ${disconnectionIntervalFormatted}\n </span>\n </div>\n <div class="info-row full-width">\n <span class="info-label">Última Telemetria ${this.getTelemetryLabelByDomain()}:</span>\n <span class="info-value" id="lastConsumptionTelemetry">\n <span class="loading-text">Carregando...</span>\n </span>\n </div>\n </div>\n </div>\n `}getModalCSS(){return`\n <style>\n .myio-settings-modal-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10000;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n }\n \n .myio-settings-modal {\n background: white;\n border-radius: 8px;\n box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);\n max-width: 95vw;\n max-height: 90vh;\n width: 1000px;\n overflow: hidden;\n display: flex;\n flex-direction: column;\n }\n \n .modal-header {\n background: #3e1a7d;\n color: white;\n padding: 20px 24px;\n display: flex;\n justify-content: space-between;\n align-items: center;\n }\n \n .modal-header h3 {\n margin: 0;\n font-size: 18px;\n font-weight: 600;\n color: white;\n }\n \n .close-btn {\n background: none;\n border: none;\n font-size: 24px;\n cursor: pointer;\n color: white;\n padding: 0;\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 4px;\n }\n \n .close-btn:hover {\n background: rgba(255, 255, 255, 0.1);\n }\n \n .modal-body {\n padding: 24px;\n overflow-y: auto;\n flex: 1;\n background: #f8f9fa;\n }\n \n .error-message {\n background: #fee;\n border: 1px solid #fcc;\n color: #c33;\n padding: 12px;\n border-radius: 4px;\n margin-bottom: 16px;\n font-size: 14px;\n }\n \n .form-layout {\n display: flex;\n flex-direction: column;\n gap: 20px;\n }\n\n /* RFC-0077/0078: Customer name display styles with device type icon */\n .customer-name-container {\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n border-radius: 8px;\n padding: 16px 20px;\n box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);\n }\n\n .customer-info-row {\n display: flex;\n align-items: center;\n gap: 16px;\n }\n\n .device-type-icon-wrapper {\n flex-shrink: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 48px;\n height: 48px;\n background: rgba(255, 255, 255, 0.15);\n border-radius: 12px;\n backdrop-filter: blur(4px);\n }\n\n .device-type-icon {\n stroke: white;\n opacity: 0.95;\n }\n\n .device-type-icon.energy-icon {\n stroke: #ffd700;\n }\n\n .device-type-icon.water-icon {\n stroke: #00bfff;\n }\n\n .device-type-icon.generic-icon {\n stroke: white;\n }\n\n .customer-info-content {\n flex: 1;\n min-width: 0;\n }\n\n .customer-name-label {\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: rgba(255, 255, 255, 0.75);\n margin-bottom: 4px;\n }\n\n .customer-name-value {\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .shopping-icon {\n stroke: rgba(255, 255, 255, 0.9);\n flex-shrink: 0;\n }\n\n .customer-name-text {\n font-size: 16px;\n font-weight: 600;\n color: white;\n line-height: 1.2;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n /* RFC-0077: Device label with monospace font */\n .device-label-title {\n font-family: 'Courier New', Courier, monospace;\n font-size: 15px;\n letter-spacing: 0.5px;\n }\n\n .form-columns {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 20px;\n }\n\n .form-column {\n display: flex;\n flex-direction: column;\n }\n\n .form-card {\n background: white;\n border-radius: 8px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n padding: 20px;\n height: fit-content;\n }\n\n .section-title {\n margin: 0 0 20px 0;\n font-size: 16px;\n font-weight: 600;\n color: #3e1a7d;\n }\n \n .form-group {\n display: flex;\n flex-direction: column;\n margin-bottom: 16px;\n }\n \n .form-group:last-child {\n margin-bottom: 0;\n }\n \n .form-group label {\n font-weight: 500;\n margin-bottom: 6px;\n color: #333;\n font-size: 14px;\n }\n \n .form-group input {\n width: 100%;\n padding: 10px 12px;\n border: 1px solid #ddd;\n border-radius: 6px;\n font-size: 14px;\n transition: border-color 0.2s, box-shadow 0.2s;\n box-sizing: border-box;\n }\n \n .form-group input:focus {\n outline: none;\n border-color: #3e1a7d;\n box-shadow: 0 0 0 2px rgba(62, 26, 125, 0.25);\n }\n \n .form-group input:invalid {\n border-color: #dc3545;\n }\n \n .form-group input[readonly] {\n background-color: #f8f9fa;\n color: #6c757d;\n cursor: not-allowed;\n }\n \n .modal-footer {\n padding: 16px 24px;\n border-top: 1px solid #e0e0e0;\n display: flex;\n justify-content: flex-end;\n gap: 12px;\n background: white;\n }\n \n .modal-footer button {\n padding: 10px 20px;\n border: none;\n border-radius: 6px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s;\n }\n \n .btn-cancel {\n background: #6c757d;\n color: white;\n }\n \n .btn-cancel:hover:not(:disabled) {\n background: #545b62;\n }\n \n .btn-primary {\n background: #3e1a7d;\n color: white;\n }\n \n .btn-primary:hover:not(:disabled) {\n background: #2d1458;\n }\n \n .modal-footer button:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n .global-reference-container {\n background-color: #f8f9fa;\n border: 1px dashed #cbd5e1;\n border-radius: 6px;\n padding: 12px 16px;\n margin-top: 8px;\n margin-bottom: 16px;\n }\n\n .global-ref-header {\n font-size: 12px;\n font-weight: 700;\n color: #64748b;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n margin-bottom: 8px;\n display: flex;\n align-items: center;\n gap: 6px;\n }\n\n .global-values-grid {\n display: grid;\n grid-template-columns: repeat(4, 1fr); /* 4 Colunas para os 4 status */\n gap: 12px;\n }\n\n .global-value-item {\n background: white;\n border: 1px solid #e2e8f0;\n border-radius: 4px;\n padding: 6px 8px;\n display: flex;\n flex-direction: column;\n align-items: center;\n text-align: center;\n }\n\n .g-status {\n font-size: 11px;\n font-weight: 600;\n color: #475569;\n margin-bottom: 2px;\n }\n\n .g-range {\n font-family: 'Courier New', monospace;\n font-size: 13px;\n font-weight: 700;\n color: #3e1a7d;\n }\n\n /* Responsividade para telas pequenas */\n @media (max-width: 768px) {\n .global-values-grid {\n grid-template-columns: 1fr 1fr; /* 2 colunas em mobile */\n }\n }\n \n /* Responsive design */\n @media (max-width: 1700px) {\n .myio-settings-modal {\n width: 95vw !important;\n }\n }\n \n @media (max-width: 1024px) {\n .myio-settings-modal {\n width: 90vw !important;\n }\n \n .form-columns {\n gap: 16px;\n }\n \n .form-card {\n padding: 16px;\n }\n }\n \n @media (max-width: 768px) {\n .myio-settings-modal {\n width: 95vw !important;\n margin: 10px;\n }\n\n .form-columns {\n grid-template-columns: 1fr;\n gap: 16px;\n }\n\n .info-grid {\n grid-template-columns: 1fr;\n gap: 12px;\n }\n\n .modal-header, .modal-body, .modal-footer {\n padding-left: 16px;\n padding-right: 16px;\n }\n\n .form-card {\n padding: 16px;\n }\n }\n \n /* Scrollbar styling for modal body */\n .modal-body::-webkit-scrollbar {\n width: 6px;\n }\n \n .modal-body::-webkit-scrollbar-track {\n background: #f1f1f1;\n border-radius: 3px;\n }\n \n .modal-body::-webkit-scrollbar-thumb {\n background: #c1c1c1;\n border-radius: 3px;\n }\n \n .modal-body::-webkit-scrollbar-thumb:hover {\n background: #a8a8a8;\n }\n\n /* Connection Info Card Styles - Wide layout spanning 2 columns */\n .info-card-wide {\n margin-top: 20px;\n background: linear-gradient(135deg, #f8fafc 0%, #f0f9ff 100%);\n border: 1px solid #e0e7ff;\n grid-column: 1 / -1; /* Span all columns */\n }\n\n .info-card-wide .section-title {\n color: #2563eb;\n display: flex;\n align-items: center;\n margin-bottom: 12px;\n }\n\n .info-grid {\n display: grid;\n grid-template-columns: repeat(2, 1fr);\n gap: 12px 24px;\n }\n\n .info-row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 8px 12px;\n background: rgba(255, 255, 255, 0.6);\n border-radius: 6px;\n border: 1px solid rgba(0, 0, 0, 0.05);\n }\n\n .info-label {\n font-weight: 600;\n color: #475569;\n font-size: 13px;\n flex-shrink: 0;\n }\n\n .info-value {\n text-align: right;\n color: #1e293b;\n font-size: 13px;\n word-break: break-word;\n margin-left: 12px;\n }\n\n .time-since {\n display: inline-block;\n margin-left: 6px;\n color: #64748b;\n font-size: 12px;\n font-style: italic;\n }\n\n .disconnect-interval {\n font-size: 12px;\n line-height: 1.4;\n }\n\n .info-row.full-width {\n grid-column: 1 / -1; /* Span all columns */\n }\n\n .loading-text {\n color: #64748b;\n font-style: italic;\n }\n\n .consumption-value-display {\n font-weight: 600;\n color: #3e1a7d;\n }\n\n .consumption-date {\n color: #475569;\n }\n\n .telemetry-error {\n color: #dc2626;\n font-style: italic;\n }\n\n .telemetry-no-data {\n color: #94a3b8;\n font-style: italic;\n }\n\n /* RFC-0078: Power Limits Configuration Styles */\n .power-limits-card {\n grid-column: 1 / -1; /* Span full width */\n margin-top: 20px;\n }\n\n .power-limits-header {\n margin-bottom: 20px;\n }\n\n .power-limits-subtitle {\n font-size: 13px;\n color: #6c757d;\n margin-top: 8px;\n }\n\n /* RFC-0078: Telemetry Type Selector Styles */\n .telemetry-selector {\n margin-bottom: 16px;\n }\n\n .telemetry-selector label {\n display: block;\n font-weight: 500;\n margin-bottom: 6px;\n color: #333;\n font-size: 14px;\n }\n\n .form-select {\n width: 100%;\n padding: 10px 12px;\n border: 1px solid #ddd;\n border-radius: 6px;\n font-size: 14px;\n background-color: white;\n cursor: pointer;\n transition: border-color 0.2s, box-shadow 0.2s;\n }\n\n .form-select:focus {\n outline: none;\n border-color: #3e1a7d;\n box-shadow: 0 0 0 2px rgba(62, 26, 125, 0.25);\n }\n\n .form-select:hover {\n border-color: #3e1a7d;\n }\n\n .power-limits-table-wrapper {\n overflow-x: auto;\n margin-bottom: 16px;\n }\n\n .power-limits-table {\n width: 100%;\n border-collapse: collapse;\n font-size: 14px;\n }\n\n .power-limits-table thead {\n background: #f8f9fa;\n }\n\n .power-limits-table th {\n padding: 12px;\n text-align: left;\n font-weight: 600;\n color: #495057;\n border-bottom: 2px solid #dee2e6;\n }\n\n .power-limits-table td {\n padding: 10px 12px;\n border-bottom: 1px solid #e9ecef;\n }\n\n .limit-row:hover {\n background: #f8f9fa;\n }\n\n .status-label {\n display: flex;\n align-items: center;\n gap: 8px;\n font-weight: 500;\n }\n\n .status-icon {\n font-size: 18px;\n }\n\n .limit-input {\n width: 100%;\n padding: 8px 10px;\n border: 1px solid #ced4da;\n border-radius: 4px;\n font-size: 14px;\n transition: border-color 0.2s, box-shadow 0.2s;\n }\n\n .limit-input:focus {\n outline: none;\n border-color: #3e1a7d;\n box-shadow: 0 0 0 2px rgba(62, 26, 125, 0.15);\n }\n\n .power-limits-source-info {\n display: flex;\n align-items: center;\n gap: 12px;\n margin-bottom: 16px;\n padding: 12px 16px;\n background: #f8f9fa;\n border-radius: 8px;\n border: 1px solid #dee2e6;\n }\n\n .source-label {\n font-weight: 600;\n color: #495057;\n font-size: 14px;\n }\n\n .source-badge {\n display: inline-block;\n padding: 4px 10px;\n border-radius: 12px;\n font-size: 12px;\n font-weight: 600;\n background: #e9ecef;\n color: #495057;\n }\n\n .source-badge.global-source {\n background: #3e1a7d;\n color: #fff;\n padding: 6px 14px;\n font-size: 13px;\n }\n\n .source-badge.source-device {\n background: #cfe2ff;\n color: #084298;\n }\n\n .source-badge.source-global {\n background: #d1e7dd;\n color: #0f5132;\n }\n\n .source-badge.source-hardcoded {\n background: #f8d7da;\n color: #842029;\n }\n\n .power-limits-actions {\n display: flex;\n gap: 12px;\n margin-bottom: 16px;\n }\n\n .btn-copy-global,\n .btn-clear-overrides {\n padding: 10px 16px;\n border: none;\n border-radius: 6px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s;\n }\n\n .btn-copy-global {\n background: #198754;\n color: white;\n }\n\n .btn-copy-global:hover {\n background: #157347;\n }\n\n .btn-clear-overrides {\n background: #0d6efd;\n color: white;\n }\n\n .btn-clear-overrides:hover {\n background: #0b5ed7;\n }\n\n .btn-view-json {\n background: #6c757d;\n color: white;\n }\n\n .btn-view-json:hover {\n background: #5a6268;\n }\n\n /* RFC-0078: JSON Preview Panel Styles */\n .json-preview-panel {\n background: #1e1e1e;\n border-radius: 6px;\n margin-bottom: 16px;\n overflow: hidden;\n }\n\n .json-preview-header {\n background: #2d2d2d;\n padding: 10px 16px;\n display: flex;\n justify-content: space-between;\n align-items: center;\n }\n\n .json-preview-header h5 {\n margin: 0;\n color: #e0e0e0;\n font-size: 14px;\n font-weight: 600;\n }\n\n .btn-close-json {\n background: none;\n border: none;\n color: #999;\n cursor: pointer;\n font-size: 16px;\n padding: 4px 8px;\n border-radius: 4px;\n }\n\n .btn-close-json:hover {\n background: rgba(255, 255, 255, 0.1);\n color: #fff;\n }\n\n .json-content {\n margin: 0;\n padding: 16px;\n color: #d4d4d4;\n font-family: 'Courier New', Courier, monospace;\n font-size: 12px;\n line-height: 1.5;\n overflow-x: auto;\n max-height: 300px;\n overflow-y: auto;\n }\n\n .power-limits-legend {\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 12px;\n background: #f8f9fa;\n border-radius: 6px;\n border-left: 3px solid #3e1a7d;\n }\n\n .legend-item {\n display: flex;\n align-items: center;\n gap: 10px;\n }\n\n .legend-text {\n font-size: 13px;\n color: #495057;\n }\n\n @media (max-width: 768px) {\n .power-limits-table {\n font-size: 12px;\n }\n\n .power-limits-table th,\n .power-limits-table td {\n padding: 8px;\n }\n\n .limit-input {\n padding: 6px 8px;\n font-size: 12px;\n }\n\n .power-limits-actions {\n flex-direction: column;\n }\n\n .btn-copy-global,\n .btn-clear-overrides {\n width: 100%;\n }\n }\n </style>\n `}populateForm(data){for(const[key,value]of Object.entries(data)){const input=this.form.querySelector(`[name="${key}"]`);if(input&&value!==void 0&&value!==null){input.value=String(value)}}}setupAccessibility(){const firstInput=this.modal.querySelector("input");if(firstInput){setTimeout(()=>firstInput.focus(),100)}this.modal.setAttribute("aria-labelledby","modal-title")}setupFocusTrap(){this.focusTrapElements=Array.from(this.modal.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'));this.modal.addEventListener("keydown",this.handleKeyDown.bind(this))}teardownFocusTrap(){this.modal.removeEventListener("keydown",this.handleKeyDown.bind(this))}handleKeyDown(event){if(event.key==="Escape"&&this.config.closeOnBackdrop!==false){event.preventDefault();this.config.onClose();return}if(event.key==="Tab"){const firstElement=this.focusTrapElements[0];const lastElement=this.focusTrapElements[this.focusTrapElements.length-1];if(event.shiftKey){if(document.activeElement===firstElement){event.preventDefault();lastElement.focus()}}else{if(document.activeElement===lastElement){event.preventDefault();firstElement.focus()}}}}parseDeviceSavedLimits(deviceJson){const extracted={};try{if(!deviceJson||!deviceJson.limitsByInstantaneoustPowerType)return extracted;const consumptionGroup=deviceJson.limitsByInstantaneoustPowerType.find(g=>g.telemetryType==="consumption");const deviceItem=consumptionGroup?.itemsByDeviceType?.[0];if(!deviceItem?.limitsByDeviceStatus)return extracted;const mapPrefix={standBy:"standby",normal:"normal",alert:"alert",failure:"failure"};deviceItem.limitsByDeviceStatus.forEach(status=>{const prefix=mapPrefix[status.deviceStatusName];if(prefix&&status.limitsValues){const{baseValue:baseValue,topValue:topValue}=status.limitsValues;if(baseValue!==null&&baseValue!==void 0){extracted[`${prefix}LimitDownConsumption`]=baseValue}if(topValue!==null&&topValue!==void 0){extracted[`${prefix}LimitUpConsumption`]=topValue}}})}catch(e){console.warn("[SettingsModalView] Erro ao processar deviceMapInstaneousPower:",e)}return extracted}attachEventListeners(){this.form.addEventListener("submit",event=>{event.preventDefault();this.hideError();const formData=this.getFormData();this.config.onSave(formData)});const closeBtn=this.modal.querySelector(".close-btn");if(closeBtn){closeBtn.addEventListener("click",event=>{event.preventDefault();event.stopPropagation();this.config.onClose()})}const cancelBtn=this.modal.querySelector(".btn-cancel");if(cancelBtn){cancelBtn.addEventListener("click",event=>{event.preventDefault();event.stopPropagation();this.config.onClose()})}const saveBtn=this.modal.querySelector(".btn-save");if(saveBtn){saveBtn.addEventListener("click",event=>{event.preventDefault();event.stopPropagation();this.hideError();const formData=this.getFormData();this.config.onSave(formData)})}const btnCopy=this.modal.querySelector("#btnCopyFromGlobal");if(btnCopy){btnCopy.addEventListener("click",e=>{e.preventDefault();e.stopPropagation();const inputs=this.modal.querySelectorAll(".js-limit-input");inputs.forEach(el=>{const input=el;const globalVal=input.getAttribute("data-global-value");if(globalVal!==null&&globalVal!==""&&globalVal!=="undefined"){input.value=globalVal;input.dispatchEvent(new Event("input",{bubbles:true}))}})})}const btnClear=this.modal.querySelector("#btnClearInputs");if(btnClear){btnClear.addEventListener("click",e=>{e.preventDefault();e.stopPropagation();const inputs=this.modal.querySelectorAll(".js-limit-input");inputs.forEach(el=>{const input=el;input.value="";input.dispatchEvent(new Event("input",{bubbles:true}))})})}this.container.addEventListener("click",event=>{const target=event.target;if(target.classList.contains("myio-settings-modal-overlay")&&this.config.closeOnBackdrop!==false){this.config.onClose()}});this.form.addEventListener("input",this.handleInputValidation.bind(this))}handleInputValidation(event){const input=event.target;input.classList.remove("is-invalid");if(input.name==="guid"&&input.value){const guidPattern=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;if(!guidPattern.test(input.value)){input.classList.add("is-invalid");input.setCustomValidity("Invalid GUID format")}else{input.setCustomValidity("")}}if(input.type==="number"&&input.value){const num=parseFloat(input.value);if(isNaN(num)||num<0){input.classList.add("is-invalid");input.setCustomValidity("Must be a positive number")}else{input.setCustomValidity("")}}}applyTheme(){if(this.config.themeTokens){const style=document.createElement("style");let css="";for(const[property,value]of Object.entries(this.config.themeTokens)){css+=`--myio-${property}: ${value};\n`}style.textContent=`.myio-settings-modal { ${css} }`;this.container.appendChild(style)}}getI18nText(key,defaultText){return this.config.i18n?.t(key,defaultText)||defaultText}async fetchLatestConsumptionTelemetry(){const telemetryElement=this.modal.querySelector("#lastConsumptionTelemetry");if(!telemetryElement)return;const deviceId=this.config.deviceId;const jwtToken=this.config.jwtToken;const domain=this.config.domain||"energy";if(!deviceId||!jwtToken){telemetryElement.innerHTML='<span class="telemetry-error">N/A</span>';return}const telemetryConfigByDomain={energy:{key:"consumption",unit:"kW",label:"Consumo",formatter:v=>(v/1e3).toFixed(2)},temperature:{key:"temperature",unit:"°C",label:"Temperatura",formatter:v=>v.toFixed(1)},water:{key:"pulses",unit:"L",label:"Pulsos",formatter:v=>v.toFixed(0)}};const telemetryConfig=telemetryConfigByDomain[domain]||telemetryConfigByDomain.energy;try{const endTs=Date.now();const startTs=endTs-24*60*60*1e3;const url=`/api/plugins/telemetry/DEVICE/${deviceId}/values/timeseries?keys=${telemetryConfig.key}&startTs=${startTs}&endTs=${endTs}&limit=1&orderBy=DESC`;const response=await fetch(url,{headers:{"X-Authorization":`Bearer ${jwtToken}`,"Content-Type":"application/json"}});if(!response.ok){throw new Error(`HTTP ${response.status}`)}const data=await response.json();const telemetryData=data[telemetryConfig.key];if(telemetryData&&telemetryData.length>0){const latestPoint=telemetryData[0];const rawValue=parseFloat(latestPoint.value);const timestamp=latestPoint.ts;const formattedValue=telemetryConfig.formatter(rawValue);const date=new Date(timestamp);const formattedDate=date.toLocaleString("pt-BR",{day:"2-digit",month:"2-digit",year:"numeric",hour:"2-digit",minute:"2-digit",second:"2-digit"});const now=new Date;const diffMs=now.getTime()-date.getTime();const diffMinutes=Math.floor(diffMs/(1e3*60));const diffHours=Math.floor(diffMinutes/60);const remainingMinutes=diffMinutes%60;let timeSince="";if(diffHours>0){timeSince=`(${diffHours}hs:${remainingMinutes.toString().padStart(2,"0")}mins atrás)`}else if(diffMinutes>0){timeSince=`(${diffMinutes}mins atrás)`}else{timeSince="(agora)"}telemetryElement.innerHTML=`\n <span class="consumption-value-display">${formattedValue} ${telemetryConfig.unit}</span>\n <span class="consumption-date">- ${formattedDate}</span>\n <span class="time-since">${timeSince}</span>\n `}else{telemetryElement.innerHTML='<span class="telemetry-no-data">Sem dados</span>'}}catch(error){console.error("[SettingsModal] Failed to fetch telemetry:",error);telemetryElement.innerHTML='<span class="telemetry-error">Erro ao carregar</span>'}}};var DefaultSettingsPersister=class{jwtToken;tbBaseUrl;deviceType;deviceProfile;existingMapInstantaneousPower;constructor(jwtToken,apiConfig){this.jwtToken=jwtToken;this.tbBaseUrl=apiConfig?.tbBaseUrl||window.location.origin;this.deviceType=apiConfig?.deviceType||"ELEVADOR";this.deviceProfile=apiConfig?.deviceProfile||null;this.existingMapInstantaneousPower=apiConfig?.mapInstantaneousPower||null}getEffectiveDeviceType(){const normalizedType=(this.deviceType||"").toUpperCase();if(normalizedType==="3F_MEDIDOR"){const profile=(this.deviceProfile||"").toUpperCase();if(profile&&profile!=="N/D"&&profile.trim()!==""){console.log(`[SettingsPersister] RFC-0086: Resolved 3F_MEDIDOR → ${profile}`);return profile}}return normalizedType||"ELEVADOR"}async saveEntityLabel(deviceId,label){try{const getRes=await fetch(`${this.tbBaseUrl}/api/device/${deviceId}`,{headers:{"X-Authorization":`Bearer ${this.jwtToken}`}});if(!getRes.ok){throw this.createHttpError(getRes.status,await getRes.text().catch(()=>""))}const device=await getRes.json();const postRes=await fetch(`${this.tbBaseUrl}/api/device`,{method:"POST",headers:{"X-Authorization":`Bearer ${this.jwtToken}`,"Content-Type":"application/json"},body:JSON.stringify({...device,label:this.sanitizeLabel(label)})});if(!postRes.ok){throw this.createHttpError(postRes.status,await postRes.text().catch(()=>""))}return{ok:true}}catch(error){console.error("[SettingsPersister] Entity label save failed:",error);return{ok:false,error:this.mapError(error)}}}async saveServerScopeAttributes(deviceId,attributes){try{const payload={...attributes};const effectiveDeviceType=this.getEffectiveDeviceType();const deviceJson=this.buildDevicePowerJson(payload,effectiveDeviceType);if(deviceJson){payload.deviceMapInstaneousPower=deviceJson}const flatKeysToRemove=["telemetryType","standbyLimitDownConsumption","standbyLimitUpConsumption","normalLimitDownConsumption","normalLimitUpConsumption","alertLimitDownConsumption","alertLimitUpConsumption","failureLimitDownConsumption","failureLimitUpConsumption"];flatKeysToRemove.forEach(key=>delete payload[key]);console.log("[SettingsPersister] Saving Server Scope Attributes:",payload);const res=await fetch(`${this.tbBaseUrl}/api/plugins/telemetry/DEVICE/${deviceId}/attributes/SERVER_SCOPE`,{method:"POST",headers:{"X-Authorization":`Bearer ${this.jwtToken}`,"Content-Type":"application/json"},body:JSON.stringify(payload)});if(!res.ok){throw this.createHttpError(res.status,await res.text().catch(()=>""))}return{ok:true,updatedKeys:Object.keys(payload)}}catch(error){console.error("[SettingsPersister] Attributes save failed:",error);return{ok:false,error:this.mapError(error)}}}buildDevicePowerJson(formData,deviceType){const statuses=["standby","normal","alert","failure"];const hasPowerData=statuses.some(status=>formData[`${status}LimitDownConsumption`]!==void 0&&formData[`${status}LimitDownConsumption`]!==""||formData[`${status}LimitUpConsumption`]!==void 0&&formData[`${status}LimitUpConsumption`]!=="");if(!hasPowerData)return null;const statusMap={standby:"standBy",normal:"normal",alert:"alert",failure:"failure"};const limitsList=[];statuses.forEach(statusKey=>{const down=formData[`${statusKey}LimitDownConsumption`];const up=formData[`${statusKey}LimitUpConsumption`];if(down!==void 0&&down!==""||up!==void 0&&up!==""){limitsList.push({deviceStatusName:statusMap[statusKey],limitsValues:{baseValue:down!==""&&down!==void 0?Number(down):null,topValue:up!==""&&up!==void 0?Number(up):null}})}});if(limitsList.length===0)return null;return{version:"1.0.0",limitsByInstantaneoustPowerType:[{telemetryType:"consumption",itemsByDeviceType:[{deviceType:deviceType,name:`deviceMapInstaneousPower${this.formatDeviceTypeName(deviceType)}`,description:"Override manual configurado via Dashboard",limitsByDeviceStatus:limitsList}]}]}}formatDeviceTypeName(deviceType){if(!deviceType)return"";return deviceType.charAt(0).toUpperCase()+deviceType.slice(1).toLowerCase()}buildMapInstantaneousPower(formData){const effectiveDeviceType=this.getEffectiveDeviceType();const telemetryType=String(formData.telemetryType||"consumption");const result={version:"1.0.0",limitsByInstantaneoustPowerType:[{telemetryType:telemetryType,itemsByDeviceType:[{deviceType:effectiveDeviceType,name:`mapInstantaneousPower${this.formatDeviceTypeName(effectiveDeviceType)}`,description:formData.identifier?`Limites customizados para ${formData.identifier}`:`Limites de potência customizados para ${effectiveDeviceType}`,limitsByDeviceStatus:[{deviceStatusName:"standBy",limitsValues:{baseValue:Number(formData.standbyLimitDownConsumption)||0,topValue:Number(formData.standbyLimitUpConsumption)||0}},{deviceStatusName:"normal",limitsValues:{baseValue:Number(formData.normalLimitDownConsumption)||0,topValue:Number(formData.normalLimitUpConsumption)||0}},{deviceStatusName:"alert",limitsValues:{baseValue:Number(formData.alertLimitDownConsumption)||0,topValue:Number(formData.alertLimitUpConsumption)||0}},{deviceStatusName:"failure",limitsValues:{baseValue:Number(formData.failureLimitDownConsumption)||0,topValue:Number(formData.failureLimitUpConsumption)||0}}]}]}]};console.log(`[SettingsPersister] RFC-0086: Built mapInstantaneousPower for deviceType=${effectiveDeviceType}:`,result);return result}sanitizeLabel(label){return label.trim().slice(0,255).replace(/[\x00-\x1F\x7F]/g,"")}createHttpError(status,body){const error=new Error(`HTTP ${status}: ${body}`);error.status=status;error.body=body;return error}mapError(error){const status=error.status;if(status===400){return{code:"VALIDATION_ERROR",message:"Invalid input data",userAction:"FIX_INPUT",cause:error}}if(status===401){return{code:"TOKEN_EXPIRED",message:"Authentication token has expired",userAction:"RE_AUTH",cause:error}}if(status===403){return{code:"AUTH_ERROR",message:"Insufficient permissions",userAction:"RE_AUTH",cause:error}}if(status===404){return{code:"NETWORK_ERROR",message:"Device not found",userAction:"CONTACT_ADMIN",cause:error}}if(status===409){return{code:"VALIDATION_ERROR",message:"Concurrent modification detected",userAction:"RETRY",cause:error}}if(status>=500){return{code:"NETWORK_ERROR",message:"Server error occurred",userAction:"RETRY",cause:error}}return{code:"UNKNOWN_ERROR",message:error.message||"Unknown error occurred",userAction:"CONTACT_ADMIN",cause:error}}};var DefaultSettingsFetcher=class{jwtToken;tbBaseUrl;constructor(jwtToken,apiConfig){this.jwtToken=jwtToken;this.tbBaseUrl=apiConfig?.tbBaseUrl||window.location.origin}async fetchCurrentSettings(deviceId,jwtToken,scope="SERVER_SCOPE"){try{const[entityResult,attributesResult]=await Promise.allSettled([this.fetchDeviceEntity(deviceId),this.fetchDeviceAttributes(deviceId,scope)]);const result={};if(entityResult.status==="fulfilled"){result.entity=entityResult.value}else{console.warn("[SettingsFetcher] Failed to fetch device entity:",entityResult.reason)}if(attributesResult.status==="fulfilled"){result.attributes=attributesResult.value}else{console.warn("[SettingsFetcher] Failed to fetch device attributes:",attributesResult.reason)}return result}catch(error){console.error("[SettingsFetcher] Failed to fetch current settings:",error);return{}}}async fetchDeviceEntity(deviceId){const response=await fetch(`${this.tbBaseUrl}/api/device/${deviceId}`,{headers:{"X-Authorization":`Bearer ${this.jwtToken}`}});if(!response.ok){throw new Error(`Failed to fetch device entity: ${response.status} ${response.statusText}`)}const device=await response.json();return{label:device.label||device.name||""}}async fetchDeviceAttributes(deviceId,scope){const response=await fetch(`${this.tbBaseUrl}/api/plugins/telemetry/DEVICE/${deviceId}/values/attributes/${scope}`,{headers:{"X-Authorization":`Bearer ${this.jwtToken}`}});if(!response.ok){throw new Error(`Failed to fetch device attributes: ${response.status} ${response.statusText}`)}const attributesArray=await response.json();const attributes={};for(const attr of attributesArray){if(attr.key&&attr.value!==void 0&&attr.value!==null&&attr.value!==""){if(attr.key==="floor"){attributes.floor=attr.value}else if(attr.key==="identifier"){attributes.identifier=attr.value}else if(attr.key==="mapInstantaneousPower"){attributes.mapInstantaneousPower=attr.value}else if(attr.key==="deviceMapInstaneousPower"){attributes.deviceMapInstaneousPower=attr.value}}}return attributes}static mergeWithSeed(fetchedData,seedData){const merged={};if(seedData){Object.assign(merged,seedData)}if(fetchedData.entity?.label){merged.label=fetchedData.entity.label}if(fetchedData.attributes){Object.assign(merged,fetchedData.attributes)}return merged}static sanitizeFetchedData(data){const sanitized={};const stringFields=["label","floor","identifier"];for(const field of stringFields){if(data[field]&&typeof data[field]==="string"){sanitized[field]=data[field].trim()}}const numericFields=["maxDailyKwh","maxNightKwh","maxBusinessKwh"];for(const field of numericFields){if(data[field]!==void 0&&data[field]!==null){const num=Number(data[field]);if(!isNaN(num)&&num>=0){sanitized[field]=num}}}const objectFields=["mapInstantaneousPower","deviceMapInstaneousPower"];for(const field of objectFields){if(data[field]&&typeof data[field]==="object"){sanitized[field]=data[field]}}return sanitized}};var SettingsController=class{view;persister;fetcher;params;constructor(params){this.params=params;this.validateParams();const apiConfigWithDeviceInfo={...params.api,deviceType:params.deviceType,deviceProfile:params.deviceProfile,mapInstantaneousPower:params.mapInstantaneousPower};this.persister=params.persister||new DefaultSettingsPersister(params.jwtToken,apiConfigWithDeviceInfo);this.fetcher=params.fetcher||new DefaultSettingsFetcher(params.jwtToken,params.api);this.view=new SettingsModalView({title:params.ui?.title||`Settings - ${params.label||params.deviceId}`,width:params.ui?.width||600,theme:"light",closeOnBackdrop:params.ui?.closeOnBackdrop!==false,domain:params.domain||"energy",deviceType:params.deviceType,deviceProfile:params.deviceProfile,customerName:params.customerName,customerId:params.customerId,deviceId:params.deviceId,jwtToken:params.jwtToken,themeTokens:params.ui?.themeTokens,i18n:params.ui?.i18n,deviceLabel:params.label,connectionData:params.connectionData,onSave:this.handleSave.bind(this),onClose:this.handleClose.bind(this),mapInstantaneousPower:params.mapInstantaneousPower,deviceMapInstaneousPower:params.deviceMapInstaneousPower})}async show(){console.info("[SettingsModal] Opening modal",{deviceId:this.params.deviceId,deviceType:this.params.deviceType,deviceProfile:this.params.deviceProfile});this.emitEvent("modal_opened");if(!this.params.mapInstantaneousPower&&this.params.customerId){try{const globalMap=await this.fetchGlobalMapInstantaneousPower();if(globalMap){this.params.mapInstantaneousPower=globalMap;console.log("[SettingsModal] RFC-0080: Loaded GLOBAL mapInstantaneousPower from CUSTOMER");this.persister=new DefaultSettingsPersister(this.params.jwtToken,{...this.params.api,deviceType:this.params.deviceType,deviceProfile:this.params.deviceProfile,mapInstantaneousPower:globalMap});this.view.updateMapInstantaneousPower(globalMap)}}catch(error){console.warn("[SettingsModal] RFC-0080: Failed to fetch GLOBAL mapInstantaneousPower:",error)}}let initialData=this.params.seed||{};if(!this.params.seed){try{const fetchedData=await this.fetcher.fetchCurrentSettings(this.params.deviceId,this.params.jwtToken,this.params.scope||"SERVER_SCOPE");initialData=DefaultSettingsFetcher.mergeWithSeed(fetchedData,this.params.seed);initialData=DefaultSettingsFetcher.sanitizeFetchedData(initialData)}catch(error){console.warn("[SettingsModal] Failed to fetch current settings:",error);if(this.params.onError){this.params.onError({code:"NETWORK_ERROR",message:"Failed to load current settings",cause:error})}}}this.view.render(initialData)}async fetchGlobalMapInstantaneousPower(){const customerId=this.params.customerId;const jwtToken=this.params.jwtToken;if(!customerId||!jwtToken){console.warn("[SettingsModal] RFC-0080: Cannot fetch GLOBAL - missing customerId or token");return null}try{const tbBaseUrl=this.params.api?.tbBaseUrl||window.location.origin;const url=`${tbBaseUrl}/api/plugins/telemetry/CUSTOMER/${customerId}/values/attributes/SERVER_SCOPE?keys=mapInstantaneousPower`;console.log("[SettingsModal] RFC-0080: Fetching GLOBAL from:",url);const response=await fetch(url,{headers:{"X-Authorization":`Bearer ${jwtToken}`,"Content-Type":"application/json"}});if(!response.ok){throw new Error(`HTTP ${response.status}`)}const attrs=await response.json();const powerLimitsAttr=attrs.find(a=>a.key==="mapInstantaneousPower");if(!powerLimitsAttr){console.log("[SettingsModal] RFC-0080: No GLOBAL mapInstantaneousPower found on CUSTOMER");return null}const value=typeof powerLimitsAttr.value==="string"?JSON.parse(powerLimitsAttr.value):powerLimitsAttr.value;console.log("[SettingsModal] RFC-0080: Loaded GLOBAL mapInstantaneousPower:",value);return value}catch(error){console.error("[SettingsModal] RFC-0080: Failed to fetch GLOBAL mapInstantaneousPower:",error);return null}}validateParams(){if(!this.params.jwtToken){throw new Error("jwtToken is required for settings persistence")}if(!this.params.deviceId){throw new Error("deviceId is required")}}async handleSave(formData){console.info("[SettingsModal] Save initiated",{deviceId:this.params.deviceId,formData:formData});this.emitEvent("save_started",{formData:formData});this.view.showLoadingState(true);try{const result=await this.saveSettings(formData);if(result.ok){console.info("[SettingsModal] Settings saved successfully",result);this.emitEvent("save_completed",{result:result});if(this.params.onSaved){this.params.onSaved(result)}setTimeout(()=>{this.view.close()},500)}else{console.error("[SettingsModal] Save failed:",result);this.emitEvent("save_failed",{result:result});const errorMessage=this.getErrorMessage(result);this.view.showError(errorMessage);if(this.params.onError){this.params.onError({code:"VALIDATION_ERROR",message:errorMessage,cause:result})}}}catch(error){console.error("[SettingsModal] Save error:",error);this.emitEvent("save_failed",{error:error.message});const errorMessage="Network error occurred while saving settings";this.view.showError(errorMessage);if(this.params.onError){this.params.onError({code:"NETWORK_ERROR",message:errorMessage,cause:error})}}finally{this.view.showLoadingState(false)}}async saveSettings(formData){const result={ok:true,timestamp:(new Date).toISOString()};if(formData.label){try{const labelResult=await this.persister.saveEntityLabel(this.params.deviceId,formData.label);result.entity={ok:labelResult.ok,updated:labelResult.ok?["label"]:void 0,error:labelResult.error?{code:labelResult.error.code,message:labelResult.error.message,cause:labelResult.error.cause}:void 0};if(!labelResult.ok){result.ok=false}}catch(error){result.entity={ok:false,error:{code:"UNKNOWN_ERROR",message:error.message||"Failed to save device label",cause:error}};result.ok=false}}const attributes=this.extractAttributes(formData);if(Object.keys(attributes).length>0){try{const attributesResult=await this.persister.saveServerScopeAttributes(this.params.deviceId,attributes);result.serverScope={ok:attributesResult.ok,updatedKeys:attributesResult.updatedKeys,error:attributesResult.error?{code:attributesResult.error.code,message:attributesResult.error.message,cause:attributesResult.error.cause}:void 0};if(!attributesResult.ok){result.ok=false}}catch(error){result.serverScope={ok:false,error:{code:"UNKNOWN_ERROR",message:error.message||"Failed to save device attributes",cause:error}};result.ok=false}}return result}extractAttributes(formData){const attributes={};for(const[key,value]of Object.entries(formData)){if(key!=="label"&&value!==void 0&&value!==null&&value!==""){attributes[key]=value}}return attributes}getErrorMessage(result){const errors=[];if(result.entity?.error){errors.push(`Device label: ${result.entity.error.message}`)}if(result.serverScope?.error){errors.push(`Settings: ${result.serverScope.error.message}`)}return errors.length>0?errors.join("; "):"Failed to save settings"}handleClose(){console.info("[SettingsModal] Modal closed");this.emitEvent("modal_closed");if(this.params.onClose){this.params.onClose()}}emitEvent(type,data){if(this.params.onEvent){const event={type:type,deviceId:this.params.deviceId,timestamp:(new Date).toISOString(),data:data};try{this.params.onEvent(event)}catch(error){console.warn("[SettingsModal] Event handler error:",error)}}}validateFormData(formData){const errors=[];if(formData.guid){const guidPattern=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;if(!guidPattern.test(formData.guid)){errors.push("GUID must be in valid UUID format")}}const numericFields=["maxDailyKwh","maxNightKwh","maxBusinessKwh","minTemperature","maxTemperature","minWaterLevel","maxWaterLevel"];for(const field of numericFields){if(formData[field]!==void 0){const num=Number(formData[field]);if(isNaN(num)||field.includes("Kwh")&&num<0){errors.push(`${field} must be a valid number`)}if(field.includes("WaterLevel")){if(num<0||num>100){errors.push(`${field} must be between 0 and 100`)}}}}if(formData.minTemperature!==void 0&&formData.maxTemperature!==void 0){const minTemp=Number(formData.minTemperature);const maxTemp=Number(formData.maxTemperature);if(!isNaN(minTemp)&&!isNaN(maxTemp)&&minTemp>=maxTemp){errors.push("Minimum temperature must be less than maximum temperature")}}if(formData.minWaterLevel!==void 0&&formData.maxWaterLevel!==void 0){const minLevel=Number(formData.minWaterLevel);const maxLevel=Number(formData.maxWaterLevel);if(!isNaN(minLevel)&&!isNaN(maxLevel)&&minLevel>=maxLevel){errors.push("Minimum water level must be less than maximum water level")}}if(formData.label&&formData.label.length>255){errors.push("Device label must be 255 characters or less")}if(formData.floor&&formData.floor.length>50){errors.push("Floor must be 50 characters or less")}if(formData.storeNumber&&formData.storeNumber.length>20){errors.push("Store number must be 20 characters or less")}return{valid:errors.length===0,errors:errors}}closeModal(){this.view.close()}getCurrentFormData(){return this.view.getFormData()}};async function openDashboardPopupSettings(params){if(!params.jwtToken){throw new Error("jwtToken is required for settings persistence")}if(!params.deviceId){throw new Error("deviceId is required")}console.info("[openDashboardPopupSettings] Initializing settings modal",{deviceId:params.deviceId,ingestionId:params.ingestionId,hasJwtToken:!!params.jwtToken,hasSeedData:!!params.seed,apiConfig:params.api?Object.keys(params.api):void 0});const controller=new SettingsController(params);try{await controller.show()}catch(error){console.error("[openDashboardPopupSettings] Error:",error);if(params.onError){params.onError({code:"UNKNOWN_ERROR",message:error.message||"Unknown error occurred while opening settings modal",cause:error})}throw error}}async function createDateRangePicker2(input,options={}){const defaultOptions={maxRangeDays:31,...options};return await DateRangePickerJQ.attach(input,defaultOptions)}var PREMIUM_STYLES=`\n .myio-daterange-wrapper {\n font-family: 'Roboto', Arial, sans-serif;\n background: #f9f9f9;\n padding: 20px;\n border-radius: 8px;\n margin-bottom: 20px;\n box-shadow: 0 2px 4px rgba(0,0,0,0.05);\n transition: all 0.2s ease;\n }\n \n .myio-daterange-wrapper:hover {\n box-shadow: 0 4px 8px rgba(0,0,0,0.1);\n }\n \n .myio-daterange-label {\n display: block;\n margin-bottom: 8px;\n font-weight: 500;\n color: #333;\n font-size: 14px;\n line-height: 1.4;\n }\n \n .myio-daterange-input {\n width: 100%;\n max-width: 300px;\n padding: 8px 12px;\n border: 1px solid #ddd;\n border-radius: 4px;\n background: white;\n font-size: 14px;\n color: #333;\n cursor: pointer;\n transition: all 0.2s ease;\n font-family: inherit;\n line-height: 1.4;\n }\n \n .myio-daterange-input:hover {\n border-color: #4A148C;\n box-shadow: 0 0 0 2px rgba(74, 20, 140, 0.1);\n }\n \n .myio-daterange-input:focus {\n outline: none;\n border-color: #4A148C;\n box-shadow: 0 0 0 3px rgba(74, 20, 140, 0.2);\n }\n \n .myio-daterange-input::placeholder {\n color: #999;\n opacity: 1;\n }\n \n .myio-daterange-helper {\n font-size: 12px;\n color: #666;\n margin-top: 4px;\n line-height: 1.3;\n transition: color 0.2s ease;\n }\n \n .myio-daterange-helper.success {\n color: #28a745;\n font-weight: 500;\n }\n \n .myio-daterange-helper.error {\n color: #dc3545;\n font-weight: 500;\n }\n \n /* Responsive design */\n @media (max-width: 768px) {\n .myio-daterange-wrapper {\n padding: 16px;\n margin-bottom: 16px;\n }\n \n .myio-daterange-input {\n max-width: 100%;\n font-size: 16px; /* Prevents zoom on iOS */\n }\n }\n \n /* High contrast mode support */\n @media (prefers-contrast: high) {\n .myio-daterange-wrapper {\n border: 2px solid #000;\n }\n \n .myio-daterange-input {\n border: 2px solid #000;\n }\n }\n \n /* Reduced motion support */\n @media (prefers-reduced-motion: reduce) {\n .myio-daterange-wrapper,\n .myio-daterange-input,\n .myio-daterange-helper {\n transition: none;\n }\n }\n`;function injectPremiumStyles(){const styleId="myio-daterange-premium-styles";if(document.getElementById(styleId)){return}const styleEl=document.createElement("style");styleEl.id=styleId;styleEl.textContent=PREMIUM_STYLES;document.head.appendChild(styleEl);console.log("[MyIO] Premium date range styles injected")}function validateId(id,context){if(!id||typeof id!=="string"){throw new Error(`[createInputDateRangePickerInsideDIV] ${context} must be a non-empty string`)}if(!/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(id)){throw new Error(`[createInputDateRangePickerInsideDIV] ${context} '${id}' is not a valid HTML ID`)}}async function createInputDateRangePickerInsideDIV(params){const{containerId:containerId,inputId:inputId,label:label="Período de Datas",placeholder:placeholder="Clique para selecionar período",pickerOptions:pickerOptions={},classNames:classNames={},injectStyles:injectStyles=true,showHelper:showHelper=true}=params;validateId(containerId,"containerId");validateId(inputId,"inputId");const container=document.getElementById(containerId);if(!container){throw new Error(`[createInputDateRangePickerInsideDIV] Container '#${containerId}' not found`)}if(injectStyles){injectPremiumStyles()}let inputEl=document.getElementById(inputId);if(inputEl&&inputEl.tagName.toLowerCase()!=="input"){throw new Error(`[createInputDateRangePickerInsideDIV] Element '#${inputId}' exists but is not an input element`)}const wrapper=document.createElement("div");wrapper.className=classNames.wrapper||"myio-daterange-wrapper";wrapper.setAttribute("data-myio-component","daterange-input");wrapper.setAttribute("data-version","1.0.0");let labelEl=null;if(label){labelEl=document.createElement("label");labelEl.className=classNames.label||"myio-daterange-label";labelEl.textContent=label;labelEl.setAttribute("for",inputId);wrapper.appendChild(labelEl)}if(!inputEl){inputEl=document.createElement("input");inputEl.type="text";inputEl.id=inputId;inputEl.name=inputId}inputEl.className=classNames.input||"myio-daterange-input";inputEl.readOnly=true;inputEl.placeholder=placeholder;inputEl.autocomplete="off";inputEl.setAttribute("aria-label",label||"Date range selector");if(showHelper){inputEl.setAttribute("aria-describedby",`${inputId}-helper`)}let helperEl=null;if(showHelper){helperEl=document.createElement("div");helperEl.id=`${inputId}-helper`;helperEl.className=classNames.helper||"myio-daterange-helper";helperEl.setAttribute("aria-live","polite");helperEl.style.display="flex";helperEl.style.alignItems="center"}wrapper.appendChild(inputEl);if(helperEl){wrapper.appendChild(helperEl)}const existingWrapper=container.querySelector('[data-myio-component="daterange-input"]');if(existingWrapper){console.warn(`[createInputDateRangePickerInsideDIV] Replacing existing daterange input in container '#${containerId}'`);existingWrapper.remove()}container.appendChild(wrapper);const enhancedOptions={maxRangeDays:31,onApply:result=>{if(helperEl){const startDate=new Date(result.startISO);const endDate=new Date(result.endISO);const days=Math.ceil((endDate.getTime()-startDate.getTime())/(1e3*60*60*24))+1;helperEl.textContent=`Período selecionado: ${days} dia${days!==1?"s":""}`;helperEl.className=(classNames.helper||"myio-daterange-helper")+" success";setTimeout(()=>{if(helperEl){helperEl.className=classNames.helper||"myio-daterange-helper"}},3e3)}if(pickerOptions.onApply){pickerOptions.onApply(result)}},...pickerOptions};let picker;try{picker=await createDateRangePicker2(inputEl,enhancedOptions);console.log(`[createInputDateRangePickerInsideDIV] Successfully initialized for input '#${inputId}'`)}catch(error){wrapper.remove();throw new Error(`[createInputDateRangePickerInsideDIV] Failed to initialize date picker: ${error.message}`)}const controller={input:inputEl,container:container,wrapper:wrapper,picker:picker,getDisplayValue:()=>inputEl.value,getDates:()=>picker.getDates(),setDates:(startISO,endISO)=>{try{picker.setDates(startISO,endISO)}catch(error){console.error(`[createInputDateRangePickerInsideDIV] Error setting dates:`,error);throw error}},setHelperText:(text,type="default")=>{if(helperEl){helperEl.textContent=text;const baseClass=classNames.helper||"myio-daterange-helper";helperEl.className=type==="default"?baseClass:`${baseClass} ${type}`}},destroy:()=>{try{picker.destroy();console.log(`[createInputDateRangePickerInsideDIV] Date picker destroyed for input '#${inputId}'`)}catch(error){console.warn(`[createInputDateRangePickerInsideDIV] Error destroying picker:`,error)}try{wrapper.remove();console.log(`[createInputDateRangePickerInsideDIV] Wrapper removed for input '#${inputId}'`)}catch(error){console.warn(`[createInputDateRangePickerInsideDIV] Error removing wrapper:`,error)}}};return controller}function openGoalsPanel(params){if(typeof window==="undefined"||typeof document==="undefined"){throw new Error("GoalsPanel requires browser environment")}const{customerId:customerId,token:token,api:api={},data:data=null,shoppingList:shoppingList=[],onSave:onSave=null,onClose:onClose=null,styles:styles={},locale:locale="pt-BR"}=params;if(!customerId){throw new Error("customerId is required")}if(!token&&!data){throw new Error("token is required when not using mock data")}const theme={primaryColor:styles.primaryColor||"#4A148C",accentColor:styles.accentColor||"#FFC107",successColor:styles.successColor||"#28a745",errorColor:styles.errorColor||"#dc3545",warningColor:styles.warningColor||"#fd7e14",borderRadius:styles.borderRadius||"8px",fontFamily:styles.fontFamily||"'Roboto', Arial, sans-serif",zIndex:styles.zIndex||1e4};const i18n=locale==="en-US"?getEnglishStrings():getPortugueseStrings();let modalState={currentTab:"shopping",currentYear:(new Date).getFullYear(),selectedShoppingId:shoppingList.length>0?shoppingList[0].value:null,goalsData:data||null,isDirty:false,isSaving:false,validationErrors:[]};const instance={close:()=>closeModal(),getState:()=>({...modalState}),setYear:year=>setCurrentYear(year),refresh:()=>loadGoalsData()};initializeModal();return instance;function initializeModal(){if(data){modalState.goalsData=data;renderModal4()}else{renderModal4();loadGoalsData()}}function renderModal4(){const existing=document.getElementById("myio-goals-panel-modal");if(existing){existing.remove()}const modal=document.createElement("div");modal.id="myio-goals-panel-modal";modal.className="myio-goals-modal-overlay";modal.setAttribute("role","dialog");modal.setAttribute("aria-modal","true");modal.setAttribute("aria-labelledby","goals-modal-title");modal.innerHTML=generateModalHTML();document.body.appendChild(modal);injectStyles();attachEventListeners();trapFocus(modal);renderTabContent()}function generateModalHTML(){return`\n <div class="myio-goals-modal-backdrop" aria-hidden="true"></div>\n <div class="myio-goals-modal-container">\n <div class="myio-goals-modal-card">\n \x3c!-- Header --\x3e\n <div class="myio-goals-modal-header">\n <div class="myio-goals-header-content">\n <div class="myio-goals-header-icon">\n <svg width="24" height="24" viewBox="0 0 24 24" fill="none">\n <path d="M12 2L15.09 8.26L22 9.27L17 14.14L18.18 21.02L12 17.77L5.82 21.02L7 14.14L2 9.27L8.91 8.26L12 2Z"\n fill="currentColor" stroke="currentColor" stroke-width="1.5"/>\n </svg>\n </div>\n <h2 id="goals-modal-title" class="myio-goals-modal-title">${i18n.modalTitle}</h2>\n </div>\n <button class="myio-goals-close-btn" aria-label="${i18n.close}" data-action="close">\n <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\n <path d="M6 18L18 6M6 6l12 12"/>\n </svg>\n </button>\n </div>\n\n \x3c!-- Year Selector --\x3e\n <div class="myio-goals-year-selector">\n <button class="myio-goals-year-btn" data-action="prev-year" aria-label="${i18n.previousYear}">\n <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\n <path d="M15 18l-6-6 6-6"/>\n </svg>\n </button>\n <div class="myio-goals-year-display">${modalState.currentYear}</div>\n <button class="myio-goals-year-btn" data-action="next-year" aria-label="${i18n.nextYear}">\n <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\n <path d="M9 18l6-6-6-6"/>\n </svg>\n </button>\n </div>\n\n \x3c!-- Tabs --\x3e\n <div class="myio-goals-tabs" role="tablist">\n <button class="myio-goals-tab ${modalState.currentTab==="shopping"?"active":""}"\n role="tab"\n aria-selected="${modalState.currentTab==="shopping"}"\n aria-controls="shopping-panel"\n data-tab="shopping">\n ${i18n.shoppingTab}\n </button>\n <button class="myio-goals-tab ${modalState.currentTab==="assets"?"active":""}"\n role="tab"\n aria-selected="${modalState.currentTab==="assets"}"\n aria-controls="assets-panel"\n data-tab="assets">\n ${i18n.assetsTab}\n </button>\n </div>\n\n \x3c!-- Tab Content --\x3e\n <div class="myio-goals-content">\n <div id="tab-content-area"></div>\n </div>\n\n \x3c!-- Validation Errors --\x3e\n <div id="validation-errors" class="myio-goals-errors" style="display: none;"></div>\n\n \x3c!-- Footer --\x3e\n <div class="myio-goals-modal-footer">\n <div class="myio-goals-meta-info">\n <small id="last-update-info"></small>\n </div>\n <div class="myio-goals-footer-actions">\n <button class="myio-goals-btn myio-goals-btn-secondary" data-action="cancel">\n ${i18n.cancel}\n </button>\n <button class="myio-goals-btn myio-goals-btn-primary" data-action="save" ${modalState.isSaving?"disabled":""}>\n ${modalState.isSaving?i18n.saving:i18n.save}\n </button>\n </div>\n </div>\n </div>\n </div>\n `}function renderTabContent(){const container=document.getElementById("tab-content-area");if(!container)return;if(modalState.currentTab==="shopping"){container.innerHTML=generateShoppingTabHTML();attachShoppingTabListeners()}else{container.innerHTML=generateAssetsTabHTML();attachAssetsTabListeners()}updateLastUpdateInfo()}function generateShoppingTabHTML(){const yearData=getYearData(modalState.currentYear);const annual=yearData?.annual||{total:0,unit:"kWh"};const monthly=yearData?.monthly||{};const monthlySum=Object.values(monthly).reduce((sum,val)=>sum+parseFloat(val||0),0);const progress=annual.total>0?monthlySum/annual.total*100:0;return`\n <div class="myio-goals-shopping-panel" role="tabpanel" id="shopping-panel">\n \x3c!-- Shopping Selector --\x3e\n ${shoppingList.length>0?`\n <div class="myio-goals-section">\n <div class="myio-goals-shopping-selector">\n <label for="shopping-select" class="myio-goals-shopping-label">\n <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\n <path d="M3 9l9-7 9 7v11a2 2 0 01-2 2H5a2 2 0 01-2-2z"/>\n <path d="M9 22V12h6v10"/>\n </svg>\n ${i18n.selectShopping}\n </label>\n <select id="shopping-select" class="myio-goals-input myio-goals-shopping-select">\n ${shoppingList.map(shopping=>`\n <option value="${shopping.value}" ${modalState.selectedShoppingId===shopping.value?"selected":""}>\n ${shopping.name}\n </option>\n `).join("")}\n </select>\n </div>\n </div>\n `:""}\n\n \x3c!-- Annual Goal Section --\x3e\n <div class="myio-goals-section">\n <h3 class="myio-goals-section-title">${i18n.annualGoal}</h3>\n <div class="myio-goals-form-row">\n <div class="myio-goals-form-group">\n <label for="unit-select">${i18n.unit}</label>\n <select id="unit-select" class="myio-goals-input">\n <option value="kWh" ${annual.unit==="kWh"?"selected":""}>kWh</option>\n <option value="m3" ${annual.unit==="m3"?"selected":""}>m³</option>\n </select>\n </div>\n <div class="myio-goals-form-group myio-goals-form-group-large">\n <label for="annual-total">${i18n.annualTotal}</label>\n <input type="number"\n id="annual-total"\n class="myio-goals-input"\n value="${annual.total}"\n min="0"\n step="0.01"\n placeholder="0.00">\n </div>\n </div>\n </div>\n\n \x3c!-- Monthly Distribution Section --\x3e\n <div class="myio-goals-section">\n <div class="myio-goals-section-header">\n <h3 class="myio-goals-section-title">${i18n.monthlyDistribution}</h3>\n <button class="myio-goals-btn-link" data-action="auto-fill">\n ${i18n.autoFill}\n </button>\n </div>\n\n \x3c!-- Progress Bar --\x3e\n <div class="myio-goals-progress-bar">\n <div class="myio-goals-progress-fill" style="width: ${Math.min(progress,100)}%"></div>\n </div>\n <div class="myio-goals-progress-text">\n <span>${formatNumber3(monthlySum,locale)} ${annual.unit}</span>\n <span>${formatNumber3(annual.total,locale)} ${annual.unit}</span>\n </div>\n\n \x3c!-- Monthly Grid --\x3e\n <div class="myio-goals-monthly-grid">\n ${generateMonthlyInputsHTML(monthly,annual.unit)}\n </div>\n </div>\n </div>\n `}function generateMonthlyInputsHTML(monthly,unit){const months=[{key:"01",label:i18n.jan},{key:"02",label:i18n.feb},{key:"03",label:i18n.mar},{key:"04",label:i18n.apr},{key:"05",label:i18n.may},{key:"06",label:i18n.jun},{key:"07",label:i18n.jul},{key:"08",label:i18n.aug},{key:"09",label:i18n.sep},{key:"10",label:i18n.oct},{key:"11",label:i18n.nov},{key:"12",label:i18n.dec}];return months.map(month=>`\n <div class="myio-goals-month-input">\n <label for="month-${month.key}">${month.label}</label>\n <input type="number"\n id="month-${month.key}"\n class="myio-goals-input myio-goals-input-small"\n data-month="${month.key}"\n value="${monthly[month.key]||""}"\n min="0"\n step="0.01"\n placeholder="0">\n <span class="myio-goals-unit">${unit}</span>\n </div>\n `).join("")}function generateAssetsTabHTML(){const yearData=getYearData(modalState.currentYear);const assets=yearData?.assets||{};return`\n <div class="myio-goals-assets-panel" role="tabpanel" id="assets-panel">\n <div class="myio-goals-assets-header">\n <input type="text"\n id="asset-search"\n class="myio-goals-input"\n placeholder="${i18n.searchAssets}"\n aria-label="${i18n.searchAssets}">\n <button class="myio-goals-btn myio-goals-btn-primary" data-action="add-asset">\n + ${i18n.addAsset}\n </button>\n </div>\n\n <div class="myio-goals-assets-list">\n ${Object.keys(assets).length>0?generateAssetItemsHTML(assets):`<div class="myio-goals-empty-state">${i18n.noAssets}</div>`}\n </div>\n </div>\n `}function generateAssetItemsHTML(assets){return Object.entries(assets).map(([assetId,assetData])=>`\n <div class="myio-goals-asset-item" data-asset-id="${assetId}">\n <div class="myio-goals-asset-header" data-action="toggle-asset">\n <div class="myio-goals-asset-title">\n <svg class="myio-goals-asset-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\n <path d="M9 18l6-6-6-6"/>\n </svg>\n <span>${assetData.label||assetId}</span>\n </div>\n <div class="myio-goals-asset-total">\n ${formatNumber3(assetData.annual?.total||0,locale)} ${assetData.annual?.unit||"kWh"}\n </div>\n <button class="myio-goals-btn-icon" data-action="delete-asset" data-asset-id="${assetId}" aria-label="${i18n.deleteAsset}">\n <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\n <path d="M3 6h18M19 6v14a2 2 0 01-2 2H7a2 2 0 01-2-2V6m3 0V4a2 2 0 012-2h4a2 2 0 012 2v2"/>\n </svg>\n </button>\n </div>\n <div class="myio-goals-asset-content" style="display: none;">\n ${generateAssetDetailHTML(assetId,assetData)}\n </div>\n </div>\n `).join("")}function generateAssetDetailHTML(assetId,assetData){const annual=assetData.annual||{total:0,unit:"kWh"};const monthly=assetData.monthly||{};return`\n <div class="myio-goals-asset-detail">\n <div class="myio-goals-form-row">\n <div class="myio-goals-form-group">\n <label for="asset-${assetId}-unit">${i18n.unit}</label>\n <select id="asset-${assetId}-unit" class="myio-goals-input" data-asset-id="${assetId}">\n <option value="kWh" ${annual.unit==="kWh"?"selected":""}>kWh</option>\n <option value="m3" ${annual.unit==="m3"?"selected":""}>m³</option>\n </select>\n </div>\n <div class="myio-goals-form-group myio-goals-form-group-large">\n <label for="asset-${assetId}-total">${i18n.annualTotal}</label>\n <input type="number"\n id="asset-${assetId}-total"\n class="myio-goals-input"\n data-asset-id="${assetId}"\n value="${annual.total}"\n min="0"\n step="0.01">\n </div>\n </div>\n\n <div class="myio-goals-monthly-grid">\n ${generateAssetMonthlyInputsHTML(assetId,monthly,annual.unit)}\n </div>\n </div>\n `}function generateAssetMonthlyInputsHTML(assetId,monthly,unit){const months=[{key:"01",label:i18n.jan},{key:"02",label:i18n.feb},{key:"03",label:i18n.mar},{key:"04",label:i18n.apr},{key:"05",label:i18n.may},{key:"06",label:i18n.jun},{key:"07",label:i18n.jul},{key:"08",label:i18n.aug},{key:"09",label:i18n.sep},{key:"10",label:i18n.oct},{key:"11",label:i18n.nov},{key:"12",label:i18n.dec}];return months.map(month=>`\n <div class="myio-goals-month-input">\n <label for="asset-${assetId}-month-${month.key}">${month.label}</label>\n <input type="number"\n id="asset-${assetId}-month-${month.key}"\n class="myio-goals-input myio-goals-input-small"\n data-asset-id="${assetId}"\n data-month="${month.key}"\n value="${monthly[month.key]||""}"\n min="0"\n step="0.01"\n placeholder="0">\n <span class="myio-goals-unit">${unit}</span>\n </div>\n `).join("")}function attachEventListeners(){const modal=document.getElementById("myio-goals-panel-modal");if(!modal)return;modal.addEventListener("click",e=>{const action=e.target.closest("[data-action]")?.dataset.action;if(action==="close"||action==="cancel"){if(modalState.isDirty){if(confirm(i18n.unsavedChanges)){closeModal()}}else{closeModal()}}else if(action==="save"){handleSave()}else if(action==="prev-year"){setCurrentYear(modalState.currentYear-1)}else if(action==="next-year"){setCurrentYear(modalState.currentYear+1)}});modal.addEventListener("click",e=>{const tab=e.target.closest("[data-tab]");if(tab){switchTab(tab.dataset.tab)}});modal.querySelector(".myio-goals-modal-backdrop")?.addEventListener("click",()=>{if(modalState.isDirty){if(confirm(i18n.unsavedChanges)){closeModal()}}else{closeModal()}});const handleEscape=e=>{if(e.key==="Escape"){if(modalState.isDirty){if(confirm(i18n.unsavedChanges)){closeModal()}}else{closeModal()}}};document.addEventListener("keydown",handleEscape);modal._escapeHandler=handleEscape}function attachShoppingTabListeners(){const container=document.getElementById("tab-content-area");if(!container)return;container.querySelector("#shopping-select")?.addEventListener("change",e=>{modalState.selectedShoppingId=e.target.value;modalState.isDirty=true;loadGoalsDataForShopping(e.target.value)});container.querySelector("#annual-total")?.addEventListener("input",e=>{modalState.isDirty=true;updateProgressBar()});container.querySelector("#unit-select")?.addEventListener("change",e=>{modalState.isDirty=true;updateMonthlyUnits(e.target.value)});container.querySelectorAll("[data-month]").forEach(input=>{input.addEventListener("input",()=>{modalState.isDirty=true;updateProgressBar()})});container.querySelector('[data-action="auto-fill"]')?.addEventListener("click",()=>{autoFillMonthly()})}function attachAssetsTabListeners(){const container=document.getElementById("tab-content-area");if(!container)return;container.addEventListener("click",e=>{const toggleBtn=e.target.closest('[data-action="toggle-asset"]');if(toggleBtn){const assetItem=toggleBtn.closest(".myio-goals-asset-item");const content=assetItem?.querySelector(".myio-goals-asset-content");const icon=assetItem?.querySelector(".myio-goals-asset-icon");if(content){const isHidden=content.style.display==="none";content.style.display=isHidden?"block":"none";if(icon){icon.style.transform=isHidden?"rotate(90deg)":"rotate(0deg)"}}}const deleteBtn=e.target.closest('[data-action="delete-asset"]');if(deleteBtn){const assetId=deleteBtn.dataset.assetId;if(confirm(i18n.confirmDeleteAsset)){deleteAsset(assetId)}}const addBtn=e.target.closest('[data-action="add-asset"]');if(addBtn){showAddAssetDialog()}});container.addEventListener("input",e=>{if(e.target.dataset.assetId){modalState.isDirty=true}})}function getYearData(year){if(!modalState.goalsData?.years)return null;return modalState.goalsData.years[year.toString()]}function setYearData(year,data2){if(!modalState.goalsData){modalState.goalsData={version:1,history:[],years:{}}}if(!modalState.goalsData.years){modalState.goalsData.years={}}modalState.goalsData.years[year.toString()]=data2}function loadGoalsData(){if(!modalState.goalsData){modalState.goalsData={version:1,history:[],years:{}};setYearData(modalState.currentYear,{annual:{total:0,unit:"kWh"},monthly:{},assets:{},metaTag:`${(new Date).toISOString()}|user`})}renderTabContent()}function loadGoalsDataForShopping(shoppingId){console.log("[GoalsPanel] Loading goals for shopping:",shoppingId);renderTabContent()}async function handleSave(){const errors=validateGoalsData();if(errors.length>0){displayValidationErrors(errors);return}modalState.isSaving=true;updateSaveButton();try{const goalsData=collectGoalsDataFromInputs();if(!modalState.goalsData.version){modalState.goalsData.version=1}else{modalState.goalsData.version++}if(!modalState.goalsData.history){modalState.goalsData.history=[]}modalState.goalsData.history.unshift({tag:`${(new Date).toISOString()}|user`,reason:"Manual update from Goals Panel",diff:{year:modalState.currentYear,changed:["manual_update"]}});setYearData(modalState.currentYear,goalsData);if(onSave){await onSave(modalState.goalsData)}modalState.isDirty=false;showSuccessMessage(i18n.saveSuccess);setTimeout(()=>{closeModal()},1500)}catch(error){console.error("Error saving goals:",error);displayValidationErrors([i18n.saveError+": "+error.message])}finally{modalState.isSaving=false;updateSaveButton()}}function collectGoalsDataFromInputs(){if(modalState.currentTab==="shopping"){return collectShoppingData()}else{return collectAssetsData()}}function collectShoppingData(){const unitSelect=document.getElementById("unit-select");const annualTotal=document.getElementById("annual-total");const unit=unitSelect?.value||"kWh";const total=parseFloat(annualTotal?.value||0);const monthly={};for(let i=1;i<=12;i++){const monthKey=i.toString().padStart(2,"0");const input=document.getElementById(`month-${monthKey}`);if(input&&input.value){monthly[monthKey]=parseFloat(input.value)}}const yearData=getYearData(modalState.currentYear)||{assets:{}};return{annual:{total:total,unit:unit},monthly:monthly,assets:yearData.assets||{},metaTag:`${(new Date).toISOString()}|user`}}function collectAssetsData(){const yearData=getYearData(modalState.currentYear);return yearData||{annual:{total:0,unit:"kWh"},monthly:{},assets:{},metaTag:`${(new Date).toISOString()}|user`}}function validateGoalsData(){const errors=[];if(modalState.currentTab==="shopping"){const annualTotal=parseFloat(document.getElementById("annual-total")?.value||0);if(annualTotal<0){errors.push(i18n.errorNegativeAnnual)}let monthlySum=0;for(let i=1;i<=12;i++){const monthKey=i.toString().padStart(2,"0");const input=document.getElementById(`month-${monthKey}`);const value=parseFloat(input?.value||0);if(value<0){errors.push(`${i18n.errorNegativeMonth} ${monthKey}`)}monthlySum+=value}if(monthlySum>annualTotal&&annualTotal>0){errors.push(`${i18n.errorMonthlyExceedsAnnual} (${formatNumber3(monthlySum,locale)} > ${formatNumber3(annualTotal,locale)})`)}}return errors}function displayValidationErrors(errors){const container=document.getElementById("validation-errors");if(!container)return;if(errors.length===0){container.style.display="none";return}container.innerHTML=`\n <div class="myio-goals-error-header">\n <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\n <circle cx="12" cy="12" r="10"/>\n <line x1="12" y1="8" x2="12" y2="12"/>\n <line x1="12" y1="16" x2="12.01" y2="16"/>\n </svg>\n ${i18n.validationErrors}\n </div>\n <ul class="myio-goals-error-list">\n ${errors.map(err=>`<li>${err}</li>`).join("")}\n </ul>\n `;container.style.display="block";container.scrollIntoView({behavior:"smooth",block:"nearest"})}function showSuccessMessage(message){const container=document.getElementById("validation-errors");if(!container)return;container.innerHTML=`\n <div class="myio-goals-success-message">\n <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\n <path d="M20 6L9 17l-5-5"/>\n </svg>\n ${message}\n </div>\n `;container.style.display="block"}function switchTab(tab){if(modalState.currentTab===tab)return;modalState.currentTab=tab;const modal=document.getElementById("myio-goals-panel-modal");modal.querySelectorAll(".myio-goals-tab").forEach(btn=>{const isActive=btn.dataset.tab===tab;btn.classList.toggle("active",isActive);btn.setAttribute("aria-selected",isActive.toString())});renderTabContent()}function setCurrentYear(year){modalState.currentYear=year;const yearDisplay=document.querySelector(".myio-goals-year-display");if(yearDisplay){yearDisplay.textContent=year}if(!getYearData(year)){setYearData(year,{annual:{total:0,unit:"kWh"},monthly:{},assets:{},metaTag:`${(new Date).toISOString()}|user`})}renderTabContent()}function updateProgressBar(){const annualTotal=parseFloat(document.getElementById("annual-total")?.value||0);let monthlySum=0;for(let i=1;i<=12;i++){const monthKey=i.toString().padStart(2,"0");const input=document.getElementById(`month-${monthKey}`);monthlySum+=parseFloat(input?.value||0)}const progress=annualTotal>0?monthlySum/annualTotal*100:0;const progressFill=document.querySelector(".myio-goals-progress-fill");const progressTexts=document.querySelectorAll(".myio-goals-progress-text span");if(progressFill){progressFill.style.width=Math.min(progress,100)+"%";if(progress>100){progressFill.style.background=theme.errorColor}else if(progress>95){progressFill.style.background=theme.warningColor}else{progressFill.style.background=theme.successColor}}if(progressTexts.length===2){const unit=document.getElementById("unit-select")?.value||"kWh";progressTexts[0].textContent=`${formatNumber3(monthlySum,locale)} ${unit}`;progressTexts[1].textContent=`${formatNumber3(annualTotal,locale)} ${unit}`}}function updateMonthlyUnits(unit){document.querySelectorAll(".myio-goals-unit").forEach(span=>{span.textContent=unit})}function autoFillMonthly(){const annualTotal=parseFloat(document.getElementById("annual-total")?.value||0);if(annualTotal<=0)return;const monthlyValue=Math.round(annualTotal/12*100)/100;for(let i=1;i<=12;i++){const monthKey=i.toString().padStart(2,"0");const input=document.getElementById(`month-${monthKey}`);if(input){input.value=monthlyValue}}modalState.isDirty=true;updateProgressBar()}function deleteAsset(assetId){const yearData=getYearData(modalState.currentYear);if(yearData?.assets?.[assetId]){delete yearData.assets[assetId];modalState.isDirty=true;renderTabContent()}}function showAddAssetDialog(){const assetLabel=prompt(i18n.enterAssetName);if(!assetLabel)return;const yearData=getYearData(modalState.currentYear);if(!yearData.assets)yearData.assets={};const assetId=`asset-${Date.now()}`;yearData.assets[assetId]={label:assetLabel,annual:{total:0,unit:"kWh"},monthly:{}};modalState.isDirty=true;renderTabContent()}function updateLastUpdateInfo(){const infoElement=document.getElementById("last-update-info");if(!infoElement)return;const yearData=getYearData(modalState.currentYear);if(yearData?.metaTag){const[timestamp,author]=yearData.metaTag.split("|");const date=new Date(timestamp);infoElement.textContent=`${i18n.lastUpdate}: ${date.toLocaleString(locale)} - ${author}`}else{infoElement.textContent=""}}function updateSaveButton(){const saveBtn=document.querySelector('[data-action="save"]');if(saveBtn){saveBtn.disabled=modalState.isSaving;saveBtn.textContent=modalState.isSaving?i18n.saving:i18n.save}}function closeModal(){const modal=document.getElementById("myio-goals-panel-modal");if(!modal)return;if(modal._escapeHandler){document.removeEventListener("keydown",modal._escapeHandler)}modal.remove();if(onClose){onClose()}}function trapFocus(modal){const focusableElements=modal.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');if(focusableElements.length===0)return;const firstElement=focusableElements[0];const lastElement=focusableElements[focusableElements.length-1];const handleTab=e=>{if(e.key!=="Tab")return;if(e.shiftKey){if(document.activeElement===firstElement){e.preventDefault();lastElement.focus()}}else{if(document.activeElement===lastElement){e.preventDefault();firstElement.focus()}}};modal.addEventListener("keydown",handleTab);firstElement.focus()}function formatNumber3(value,locale2){return new Intl.NumberFormat(locale2,{minimumFractionDigits:0,maximumFractionDigits:2}).format(value)}function injectStyles(){const styleId="myio-goals-panel-styles";if(document.getElementById(styleId))return;const style=document.createElement("style");style.id=styleId;style.textContent=`\n .myio-goals-modal-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n z-index: ${theme.zIndex};\n display: flex;\n align-items: center;\n justify-content: center;\n font-family: ${theme.fontFamily};\n }\n\n .myio-goals-modal-backdrop {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n backdrop-filter: blur(4px);\n }\n\n .myio-goals-modal-container {\n position: relative;\n width: 90%;\n max-width: 1000px;\n max-height: 90vh;\n display: flex;\n flex-direction: column;\n }\n\n .myio-goals-modal-card {\n background: white;\n border-radius: ${theme.borderRadius};\n box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);\n display: flex;\n flex-direction: column;\n max-height: 90vh;\n overflow: hidden;\n }\n\n .myio-goals-modal-header {\n background: ${theme.primaryColor};\n color: white;\n padding: 20px 24px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n flex-shrink: 0;\n }\n\n .myio-goals-header-content {\n display: flex;\n align-items: center;\n gap: 12px;\n }\n\n .myio-goals-header-icon {\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .myio-goals-modal-title {\n margin: 0;\n font-size: 20px;\n font-weight: 600;\n }\n\n .myio-goals-close-btn {\n background: transparent;\n border: none;\n color: white;\n cursor: pointer;\n padding: 8px;\n border-radius: 4px;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: background 0.2s;\n }\n\n .myio-goals-close-btn:hover {\n background: rgba(255, 255, 255, 0.1);\n }\n\n .myio-goals-year-selector {\n padding: 16px 24px;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 24px;\n background: #f8f9fa;\n border-bottom: 1px solid #dee2e6;\n }\n\n .myio-goals-year-btn {\n background: white;\n border: 1px solid #dee2e6;\n border-radius: 50%;\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n transition: all 0.2s;\n }\n\n .myio-goals-year-btn:hover {\n background: ${theme.primaryColor};\n color: white;\n border-color: ${theme.primaryColor};\n }\n\n .myio-goals-year-display {\n font-size: 24px;\n font-weight: 700;\n color: ${theme.primaryColor};\n min-width: 80px;\n text-align: center;\n }\n\n .myio-goals-tabs {\n display: flex;\n border-bottom: 1px solid #dee2e6;\n background: white;\n flex-shrink: 0;\n }\n\n .myio-goals-tab {\n flex: 1;\n padding: 16px 24px;\n background: transparent;\n border: none;\n border-bottom: 3px solid transparent;\n cursor: pointer;\n font-size: 15px;\n font-weight: 500;\n color: #6c757d;\n transition: all 0.2s;\n }\n\n .myio-goals-tab:hover {\n background: #f8f9fa;\n color: ${theme.primaryColor};\n }\n\n .myio-goals-tab.active {\n color: ${theme.primaryColor};\n border-bottom-color: ${theme.primaryColor};\n }\n\n .myio-goals-content {\n flex: 1;\n overflow-y: auto;\n padding: 24px;\n }\n\n .myio-goals-section {\n margin-bottom: 32px;\n }\n\n .myio-goals-section-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 16px;\n }\n\n .myio-goals-section-title {\n font-size: 16px;\n font-weight: 600;\n color: #212529;\n margin: 0 0 16px 0;\n }\n\n .myio-goals-shopping-selector {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 20px;\n background: linear-gradient(135deg, ${theme.primaryColor}15 0%, ${theme.primaryColor}05 100%);\n border: 2px solid ${theme.primaryColor}30;\n border-radius: ${theme.borderRadius};\n margin-bottom: 24px;\n }\n\n .myio-goals-shopping-label {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 15px;\n font-weight: 600;\n color: ${theme.primaryColor};\n margin: 0;\n }\n\n .myio-goals-shopping-label svg {\n flex-shrink: 0;\n }\n\n .myio-goals-shopping-select {\n font-size: 15px;\n font-weight: 500;\n padding: 12px 16px;\n border: 2px solid ${theme.primaryColor};\n background: white;\n color: ${theme.primaryColor};\n cursor: pointer;\n }\n\n .myio-goals-shopping-select:hover {\n background: ${theme.primaryColor}10;\n }\n\n .myio-goals-shopping-select:focus {\n border-color: ${theme.primaryColor};\n box-shadow: 0 0 0 4px ${theme.primaryColor}20;\n }\n\n .myio-goals-btn-link {\n background: transparent;\n border: none;\n color: ${theme.primaryColor};\n cursor: pointer;\n font-size: 14px;\n font-weight: 500;\n padding: 4px 8px;\n border-radius: 4px;\n transition: background 0.2s;\n }\n\n .myio-goals-btn-link:hover {\n background: rgba(74, 20, 140, 0.1);\n }\n\n .myio-goals-form-row {\n display: flex;\n gap: 16px;\n margin-bottom: 16px;\n }\n\n .myio-goals-form-group {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 8px;\n }\n\n .myio-goals-form-group-large {\n flex: 2;\n }\n\n .myio-goals-form-group label {\n font-size: 14px;\n font-weight: 500;\n color: #495057;\n }\n\n .myio-goals-input {\n padding: 10px 12px;\n border: 1px solid #ced4da;\n border-radius: 6px;\n font-size: 14px;\n transition: border-color 0.2s;\n }\n\n .myio-goals-input:focus {\n outline: none;\n border-color: ${theme.primaryColor};\n box-shadow: 0 0 0 3px rgba(74, 20, 140, 0.1);\n }\n\n .myio-goals-progress-bar {\n width: 100%;\n height: 8px;\n background: #e9ecef;\n border-radius: 4px;\n overflow: hidden;\n margin-bottom: 8px;\n }\n\n .myio-goals-progress-fill {\n height: 100%;\n background: ${theme.successColor};\n transition: width 0.3s, background 0.3s;\n }\n\n .myio-goals-progress-text {\n display: flex;\n justify-content: space-between;\n font-size: 14px;\n color: #6c757d;\n margin-bottom: 16px;\n }\n\n .myio-goals-monthly-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));\n gap: 16px;\n }\n\n .myio-goals-month-input {\n display: flex;\n flex-direction: column;\n gap: 6px;\n }\n\n .myio-goals-month-input label {\n font-size: 13px;\n font-weight: 500;\n color: #495057;\n }\n\n .myio-goals-input-small {\n padding: 8px 10px;\n font-size: 13px;\n }\n\n .myio-goals-unit {\n font-size: 12px;\n color: #6c757d;\n margin-top: -4px;\n }\n\n .myio-goals-assets-header {\n display: flex;\n gap: 16px;\n margin-bottom: 24px;\n }\n\n .myio-goals-assets-header .myio-goals-input {\n flex: 1;\n }\n\n .myio-goals-assets-list {\n display: flex;\n flex-direction: column;\n gap: 12px;\n }\n\n .myio-goals-asset-item {\n border: 1px solid #dee2e6;\n border-radius: ${theme.borderRadius};\n overflow: hidden;\n }\n\n .myio-goals-asset-header {\n padding: 16px;\n background: #f8f9fa;\n display: flex;\n align-items: center;\n justify-content: space-between;\n cursor: pointer;\n transition: background 0.2s;\n }\n\n .myio-goals-asset-header:hover {\n background: #e9ecef;\n }\n\n .myio-goals-asset-title {\n display: flex;\n align-items: center;\n gap: 8px;\n font-weight: 500;\n color: #212529;\n }\n\n .myio-goals-asset-icon {\n transition: transform 0.3s;\n }\n\n .myio-goals-asset-total {\n font-size: 14px;\n color: #6c757d;\n }\n\n .myio-goals-btn-icon {\n background: transparent;\n border: none;\n color: ${theme.errorColor};\n cursor: pointer;\n padding: 6px;\n border-radius: 4px;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: background 0.2s;\n }\n\n .myio-goals-btn-icon:hover {\n background: rgba(220, 53, 69, 0.1);\n }\n\n .myio-goals-asset-content {\n padding: 16px;\n border-top: 1px solid #dee2e6;\n }\n\n .myio-goals-asset-detail {\n /* Inherits styles from shopping tab */\n }\n\n .myio-goals-empty-state {\n text-align: center;\n padding: 48px 24px;\n color: #6c757d;\n font-size: 15px;\n }\n\n .myio-goals-errors {\n margin: 0 24px;\n padding: 16px;\n background: #fff3cd;\n border: 1px solid #ffc107;\n border-radius: ${theme.borderRadius};\n margin-bottom: 16px;\n }\n\n .myio-goals-error-header {\n display: flex;\n align-items: center;\n gap: 8px;\n font-weight: 600;\n color: #856404;\n margin-bottom: 12px;\n }\n\n .myio-goals-error-list {\n margin: 0;\n padding-left: 24px;\n color: #856404;\n }\n\n .myio-goals-error-list li {\n margin-bottom: 4px;\n }\n\n .myio-goals-success-message {\n display: flex;\n align-items: center;\n gap: 8px;\n color: ${theme.successColor};\n font-weight: 500;\n }\n\n .myio-goals-modal-footer {\n padding: 16px 24px;\n background: #f8f9fa;\n border-top: 1px solid #dee2e6;\n display: flex;\n align-items: center;\n justify-content: space-between;\n flex-shrink: 0;\n }\n\n .myio-goals-meta-info {\n font-size: 12px;\n color: #6c757d;\n }\n\n .myio-goals-footer-actions {\n display: flex;\n gap: 12px;\n }\n\n .myio-goals-btn {\n padding: 10px 20px;\n border: none;\n border-radius: 6px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s;\n }\n\n .myio-goals-btn-primary {\n background: ${theme.primaryColor};\n color: white;\n }\n\n .myio-goals-btn-primary:hover:not(:disabled) {\n background: #5c1ba1;\n transform: translateY(-1px);\n box-shadow: 0 4px 12px rgba(74, 20, 140, 0.3);\n }\n\n .myio-goals-btn-primary:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n .myio-goals-btn-secondary {\n background: white;\n color: #495057;\n border: 1px solid #ced4da;\n }\n\n .myio-goals-btn-secondary:hover {\n background: #f8f9fa;\n }\n\n @media (max-width: 768px) {\n .myio-goals-modal-container {\n width: 95%;\n max-height: 95vh;\n }\n\n .myio-goals-monthly-grid {\n grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));\n gap: 12px;\n }\n\n .myio-goals-form-row {\n flex-direction: column;\n }\n }\n `;document.head.appendChild(style)}function getPortugueseStrings(){return{modalTitle:"Setup de Metas de Consumo",close:"Fechar",previousYear:"Ano anterior",nextYear:"Próximo ano",shoppingTab:"Shopping (Anual/Mensal)",assetsTab:"Por Asset",selectShopping:"Selecione o Shopping",annualGoal:"Meta Anual",unit:"Unidade",annualTotal:"Total Anual",monthlyDistribution:"Distribuição Mensal",autoFill:"Preencher Proporcionalmente",searchAssets:"Buscar assets...",addAsset:"Adicionar Asset",noAssets:'Nenhum asset configurado. Clique em "Adicionar Asset" para começar.',deleteAsset:"Remover asset",save:"Salvar",saving:"Salvando...",cancel:"Cancelar",lastUpdate:"Última atualização",jan:"Jan",feb:"Fev",mar:"Mar",apr:"Abr",may:"Mai",jun:"Jun",jul:"Jul",aug:"Ago",sep:"Set",oct:"Out",nov:"Nov",dec:"Dez",unsavedChanges:"Você tem alterações não salvas. Deseja sair mesmo assim?",confirmDeleteAsset:"Deseja realmente remover este asset?",enterAssetName:"Digite o nome do asset:",validationErrors:"Erros de validação",errorNegativeAnnual:"A meta anual não pode ser negativa",errorNegativeMonth:"O valor mensal não pode ser negativo para o mês",errorMonthlyExceedsAnnual:"A soma das metas mensais excede a meta anual",saveSuccess:"Metas salvas com sucesso!",saveError:"Erro ao salvar metas"}}function getEnglishStrings(){return{modalTitle:"Consumption Goals Setup",close:"Close",previousYear:"Previous year",nextYear:"Next year",shoppingTab:"Shopping (Annual/Monthly)",assetsTab:"By Asset",selectShopping:"Select Shopping Center",annualGoal:"Annual Goal",unit:"Unit",annualTotal:"Annual Total",monthlyDistribution:"Monthly Distribution",autoFill:"Auto Fill Proportionally",searchAssets:"Search assets...",addAsset:"Add Asset",noAssets:'No assets configured. Click "Add Asset" to get started.',deleteAsset:"Remove asset",save:"Save",saving:"Saving...",cancel:"Cancel",lastUpdate:"Last update",jan:"Jan",feb:"Feb",mar:"Mar",apr:"Apr",may:"May",jun:"Jun",jul:"Jul",aug:"Aug",sep:"Sep",oct:"Oct",nov:"Nov",dec:"Dec",unsavedChanges:"You have unsaved changes. Do you want to exit anyway?",confirmDeleteAsset:"Do you really want to remove this asset?",enterAssetName:"Enter asset name:",validationErrors:"Validation errors",errorNegativeAnnual:"Annual goal cannot be negative",errorNegativeMonth:"Monthly value cannot be negative for month",errorMonthlyExceedsAnnual:"Monthly sum exceeds annual goal",saveSuccess:"Goals saved successfully!",saveError:"Error saving goals"}}}var DAY_PERIODS=[{id:"madrugada",label:"Madrugada (00h-06h)",startHour:0,endHour:6},{id:"manha",label:"Manhã (06h-12h)",startHour:6,endHour:12},{id:"tarde",label:"Tarde (12h-18h)",startHour:12,endHour:18},{id:"noite",label:"Noite (18h-24h)",startHour:18,endHour:24}];var DEFAULT_CLAMP_RANGE={min:15,max:40};function getTodaySoFar(){const now=new Date;const startOfDay=new Date(now.getFullYear(),now.getMonth(),now.getDate(),0,0,0,0);return{startTs:startOfDay.getTime(),endTs:now.getTime()}}var CHART_COLORS=["#1976d2","#FF6B6B","#4CAF50","#FF9800","#9C27B0","#00BCD4","#E91E63","#795548"];async function fetchTemperatureData(token,deviceId,startTs,endTs){const url=`/api/plugins/telemetry/DEVICE/${deviceId}/values/timeseries?keys=temperature&startTs=${encodeURIComponent(startTs)}&endTs=${encodeURIComponent(endTs)}&limit=50000&agg=NONE`;const response=await fetch(url,{headers:{"X-Authorization":`Bearer ${token}`,"Content-Type":"application/json"}});if(!response.ok){throw new Error(`Failed to fetch temperature data: ${response.status}`)}const data=await response.json();return data?.temperature||[]}function clampTemperature(value,range=DEFAULT_CLAMP_RANGE){const num=Number(value||0);if(num<range.min)return range.min;if(num>range.max)return range.max;return num}function calculateStats(data,clampRange=DEFAULT_CLAMP_RANGE){if(data.length===0){return{avg:0,min:0,max:0,count:0}}const values=data.map(item=>clampTemperature(item.value,clampRange));const sum=values.reduce((acc,v)=>acc+v,0);return{avg:sum/values.length,min:Math.min(...values),max:Math.max(...values),count:values.length}}function interpolateTemperature(data,options){const{intervalMinutes:intervalMinutes,startTs:startTs,endTs:endTs,clampRange:clampRange=DEFAULT_CLAMP_RANGE}=options;const intervalMs=intervalMinutes*60*1e3;if(data.length===0){return[]}const sortedData=[...data].sort((a,b)=>a.ts-b.ts);const result=[];let lastKnownValue=clampTemperature(sortedData[0].value,clampRange);let dataIndex=0;for(let ts=startTs;ts<=endTs;ts+=intervalMs){while(dataIndex<sortedData.length-1&&sortedData[dataIndex+1].ts<=ts){dataIndex++}const currentData=sortedData[dataIndex];if(currentData&&Math.abs(currentData.ts-ts)<intervalMs){lastKnownValue=clampTemperature(currentData.value,clampRange)}result.push({ts:ts,value:lastKnownValue})}return result}function aggregateByDay(data,clampRange=DEFAULT_CLAMP_RANGE){if(data.length===0){return[]}const dayMap=new Map;data.forEach(item=>{const date=new Date(item.ts);const dateKey=date.toISOString().split("T")[0];if(!dayMap.has(dateKey)){dayMap.set(dateKey,[])}dayMap.get(dateKey).push(item)});const result=[];dayMap.forEach((dayData,dateKey)=>{const values=dayData.map(item=>clampTemperature(item.value,clampRange));const sum=values.reduce((acc,v)=>acc+v,0);result.push({date:dateKey,dateTs:new Date(dateKey).getTime(),avg:sum/values.length,min:Math.min(...values),max:Math.max(...values),count:values.length})});return result.sort((a,b)=>a.dateTs-b.dateTs)}function filterByDayPeriods(data,selectedPeriods){if(selectedPeriods.length===0||selectedPeriods.length===DAY_PERIODS.length){return data}return data.filter(item=>{const date=new Date(item.ts);const hour=date.getHours();return selectedPeriods.some(periodId=>{const period=DAY_PERIODS.find(p=>p.id===periodId);if(!period)return false;return hour>=period.startHour&&hour<period.endHour})})}function getSelectedPeriodsLabel(selectedPeriods){if(selectedPeriods.length===0||selectedPeriods.length===DAY_PERIODS.length){return"Todos os períodos"}if(selectedPeriods.length===1){const period=DAY_PERIODS.find(p=>p.id===selectedPeriods[0]);return period?.label||""}return`${selectedPeriods.length} períodos selecionados`}function formatTemperature2(value,decimals=1){return`${value.toFixed(decimals)}°C`}function exportTemperatureCSV(data,deviceLabel,stats,startDate,endDate){if(data.length===0){console.warn("No data to export");return}const BOM="\ufeff";let csvContent=BOM;csvContent+=`Relatório de Temperatura - ${deviceLabel}\n`;csvContent+=`Período: ${startDate} até ${endDate}\n`;csvContent+=`Média: ${formatTemperature2(stats.avg)}\n`;csvContent+=`Mínima: ${formatTemperature2(stats.min)}\n`;csvContent+=`Máxima: ${formatTemperature2(stats.max)}\n`;csvContent+=`Total de leituras: ${stats.count}\n`;csvContent+="\n";csvContent+="Data/Hora,Temperatura (°C)\n";data.forEach(item=>{const date=new Date(item.ts).toLocaleString("pt-BR");const temp=Number(item.value).toFixed(2);csvContent+=`"${date}",${temp}\n`});const blob=new Blob([csvContent],{type:"text/csv;charset=utf-8;"});const url=URL.createObjectURL(blob);const link=document.createElement("a");link.href=url;link.download=`temperatura_${deviceLabel.replace(/\s+/g,"_")}_${startDate}_${endDate}.csv`;document.body.appendChild(link);link.click();document.body.removeChild(link);URL.revokeObjectURL(url)}var DARK_THEME={background:"rgba(0, 0, 0, 0.85)",surface:"#1a1f28",text:"#ffffff",textMuted:"rgba(255, 255, 255, 0.7)",border:"rgba(255, 255, 255, 0.1)",primary:"#1976d2",success:"#4CAF50",warning:"#FF9800",danger:"#f44336",chartLine:"#1976d2",chartGrid:"rgba(255, 255, 255, 0.1)"};var LIGHT_THEME={background:"rgba(0, 0, 0, 0.6)",surface:"#ffffff",text:"#333333",textMuted:"#666666",border:"#e0e0e0",primary:"#1976d2",success:"#4CAF50",warning:"#FF9800",danger:"#f44336",chartLine:"#1976d2",chartGrid:"#e0e0e0"};function getThemeColors(theme){return theme==="dark"?DARK_THEME:LIGHT_THEME}async function openTemperatureModal(params){const modalId=`myio-temp-modal-${Date.now()}`;const defaultDateRange=getTodaySoFar();const startTs=params.startDate?new Date(params.startDate).getTime():defaultDateRange.startTs;const endTs=params.endDate?new Date(params.endDate).getTime():defaultDateRange.endTs;const state={token:params.token,deviceId:params.deviceId,label:params.label||"Sensor de Temperatura",currentTemperature:params.currentTemperature??null,temperatureMin:params.temperatureMin??null,temperatureMax:params.temperatureMax??null,temperatureStatus:params.temperatureStatus??null,startTs:startTs,endTs:endTs,granularity:params.granularity||"hour",theme:params.theme||"light",clampRange:params.clampRange||DEFAULT_CLAMP_RANGE,locale:params.locale||"pt-BR",data:[],stats:{avg:0,min:0,max:0,count:0},isLoading:true,dateRangePicker:null,selectedPeriods:["madrugada","manha","tarde","noite"]};const savedGranularity=localStorage.getItem("myio-temp-modal-granularity");const savedTheme=localStorage.getItem("myio-temp-modal-theme");if(savedGranularity)state.granularity=savedGranularity;if(savedTheme)state.theme=savedTheme;const modalContainer=document.createElement("div");modalContainer.id=modalId;document.body.appendChild(modalContainer);renderModal(modalContainer,state,modalId);try{state.data=await fetchTemperatureData(state.token,state.deviceId,state.startTs,state.endTs);state.stats=calculateStats(state.data,state.clampRange);state.isLoading=false;renderModal(modalContainer,state,modalId);drawChart(modalId,state)}catch(error){console.error("[TemperatureModal] Error fetching data:",error);state.isLoading=false;renderModal(modalContainer,state,modalId,error)}await setupEventListeners(modalContainer,state,modalId,params.onClose);return{destroy:()=>{modalContainer.remove();params.onClose?.()},updateData:async(startDate,endDate,granularity)=>{state.startTs=new Date(startDate).getTime();state.endTs=new Date(endDate).getTime();if(granularity)state.granularity=granularity;state.isLoading=true;renderModal(modalContainer,state,modalId);try{state.data=await fetchTemperatureData(state.token,state.deviceId,state.startTs,state.endTs);state.stats=calculateStats(state.data,state.clampRange);state.isLoading=false;renderModal(modalContainer,state,modalId);drawChart(modalId,state)}catch(error){console.error("[TemperatureModal] Error updating data:",error);state.isLoading=false;renderModal(modalContainer,state,modalId,error)}}}}function renderModal(container,state,modalId,error){const colors=getThemeColors(state.theme);const startDateStr=new Date(state.startTs).toLocaleDateString(state.locale);const endDateStr=new Date(state.endTs).toLocaleDateString(state.locale);const statusText=state.temperatureStatus==="ok"?"Dentro da faixa":state.temperatureStatus==="above"?"Acima do limite":state.temperatureStatus==="below"?"Abaixo do limite":"N/A";const statusColor=state.temperatureStatus==="ok"?colors.success:state.temperatureStatus==="above"?colors.danger:state.temperatureStatus==="below"?colors.primary:colors.textMuted;const rangeText=state.temperatureMin!==null&&state.temperatureMax!==null?`${state.temperatureMin}°C - ${state.temperatureMax}°C`:"Não definida";new Date(state.startTs).toISOString().slice(0,16);new Date(state.endTs).toISOString().slice(0,16);const isMaximized=container.__isMaximized||false;const contentMaxWidth=isMaximized?"100%":"900px";const contentMaxHeight=isMaximized?"100vh":"95vh";const contentBorderRadius=isMaximized?"0":"10px";container.innerHTML=`\n <div class="myio-temp-modal-overlay myio-modal-scope" style="\n position: fixed; top: 0; left: 0; width: 100%; height: 100%;\n background: rgba(0, 0, 0, 0.5); z-index: 9998;\n display: flex; justify-content: center; align-items: center;\n backdrop-filter: blur(2px);\n ">\n <div class="myio-temp-modal-content" style="\n background: ${colors.surface}; border-radius: ${contentBorderRadius};\n max-width: ${contentMaxWidth}; width: ${isMaximized?"100%":"95%"};\n max-height: ${contentMaxHeight}; height: ${isMaximized?"100%":"auto"};\n overflow: hidden; display: flex; flex-direction: column;\n box-shadow: 0 4px 12px rgba(0,0,0,0.15);\n font-family: 'Roboto', Arial, sans-serif;\n ">\n \x3c!-- Header - MyIO Premium Style --\x3e\n <div style="\n padding: 4px 8px; display: flex; align-items: center; justify-content: space-between;\n background: #3e1a7d; color: white; border-radius: ${isMaximized?"0":"10px 10px 0 0"};\n min-height: 20px;\n ">\n <h2 style="margin: 6px; font-size: 18px; font-weight: 600; color: white; line-height: 2;">\n 🌡️ ${state.label} - Histórico de Temperatura\n </h2>\n <div style="display: flex; gap: 4px; align-items: center;">\n \x3c!-- Theme Toggle --\x3e\n <button id="${modalId}-theme-toggle" title="Alternar tema" style="\n background: none; border: none; font-size: 16px; cursor: pointer;\n padding: 4px 8px; border-radius: 6px; color: rgba(255,255,255,0.8);\n transition: background-color 0.2s;\n ">${state.theme==="dark"?"☀️":"🌙"}</button>\n \x3c!-- Maximize Button --\x3e\n <button id="${modalId}-maximize" title="${isMaximized?"Restaurar":"Maximizar"}" style="\n background: none; border: none; font-size: 16px; cursor: pointer;\n padding: 4px 8px; border-radius: 6px; color: rgba(255,255,255,0.8);\n transition: background-color 0.2s;\n ">${isMaximized?"🗗":"🗖"}</button>\n \x3c!-- Close Button --\x3e\n <button id="${modalId}-close" title="Fechar" style="\n background: none; border: none; font-size: 20px; cursor: pointer;\n padding: 4px 8px; border-radius: 6px; color: rgba(255,255,255,0.8);\n transition: background-color 0.2s;\n ">×</button>\n </div>\n </div>\n\n \x3c!-- Body --\x3e\n <div style="flex: 1; overflow-y: auto; padding: 16px;">\n\n \x3c!-- Controls Row --\x3e\n <div style="\n display: flex; gap: 16px; flex-wrap: wrap; align-items: flex-end;\n margin-bottom: 16px; padding: 16px; background: ${state.theme==="dark"?"rgba(255,255,255,0.05)":"#f7f7f7"};\n border-radius: 6px; border: 1px solid ${colors.border};\n ">\n \x3c!-- Granularity Select --\x3e\n <div>\n <label style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500; display: block; margin-bottom: 4px;">\n Granularidade\n </label>\n <select id="${modalId}-granularity" style="\n padding: 8px 12px; border: 1px solid ${colors.border}; border-radius: 6px;\n font-size: 14px; color: ${colors.text}; background: ${colors.surface};\n cursor: pointer; min-width: 130px;\n ">\n <option value="hour" ${state.granularity==="hour"?"selected":""}>Hora (30 min)</option>\n <option value="day" ${state.granularity==="day"?"selected":""}>Dia (média)</option>\n </select>\n </div>\n \x3c!-- Day Period Filter (Multiselect) --\x3e\n <div style="position: relative;">\n <label style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500; display: block; margin-bottom: 4px;">\n Períodos do Dia\n </label>\n <button id="${modalId}-period-btn" type="button" style="\n padding: 8px 12px; border: 1px solid ${colors.border}; border-radius: 6px;\n font-size: 14px; color: ${colors.text}; background: ${colors.surface};\n cursor: pointer; min-width: 180px; text-align: left;\n display: flex; align-items: center; justify-content: space-between; gap: 8px;\n ">\n <span>${getSelectedPeriodsLabel(state.selectedPeriods)}</span>\n <span style="font-size: 10px;">▼</span>\n </button>\n <div id="${modalId}-period-dropdown" style="\n display: none; position: absolute; top: 100%; left: 0; z-index: 1000;\n background: ${colors.surface}; border: 1px solid ${colors.border};\n border-radius: 6px; box-shadow: 0 4px 12px rgba(0,0,0,0.15);\n min-width: 200px; margin-top: 4px; padding: 8px 0;\n ">\n ${DAY_PERIODS.map(period=>`\n <label style="\n display: flex; align-items: center; gap: 8px; padding: 8px 12px;\n cursor: pointer; font-size: 13px; color: ${colors.text};\n " onmouseover="this.style.background='${state.theme==="dark"?"rgba(255,255,255,0.1)":"#f0f0f0"}'"\n onmouseout="this.style.background='transparent'">\n <input type="checkbox"\n name="${modalId}-period"\n value="${period.id}"\n ${state.selectedPeriods.includes(period.id)?"checked":""}\n style="width: 16px; height: 16px; cursor: pointer; accent-color: #3e1a7d;">\n ${period.label}\n </label>\n `).join("")}\n <div style="border-top: 1px solid ${colors.border}; margin-top: 8px; padding-top: 8px;">\n <button id="${modalId}-period-select-all" type="button" style="\n width: calc(100% - 16px); margin: 0 8px 4px; padding: 6px;\n background: ${state.theme==="dark"?"rgba(255,255,255,0.1)":"#f0f0f0"};\n border: none; border-radius: 4px; cursor: pointer;\n font-size: 12px; color: ${colors.text};\n ">Selecionar Todos</button>\n <button id="${modalId}-period-clear" type="button" style="\n width: calc(100% - 16px); margin: 0 8px; padding: 6px;\n background: ${state.theme==="dark"?"rgba(255,255,255,0.1)":"#f0f0f0"};\n border: none; border-radius: 4px; cursor: pointer;\n font-size: 12px; color: ${colors.text};\n ">Limpar Seleção</button>\n </div>\n </div>\n </div>\n \x3c!-- Date Range Picker --\x3e\n <div style="flex: 1; min-width: 220px;">\n <label style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500; display: block; margin-bottom: 4px;">\n Período\n </label>\n <input type="text" id="${modalId}-date-range" readonly placeholder="Selecione o período..." style="\n padding: 8px 12px; border: 1px solid ${colors.border}; border-radius: 6px;\n font-size: 14px; color: ${colors.text}; background: ${colors.surface};\n width: 100%; cursor: pointer; box-sizing: border-box;\n "/>\n </div>\n \x3c!-- Query Button --\x3e\n <button id="${modalId}-query" style="\n background: #3e1a7d; color: white; border: none;\n padding: 8px 16px; border-radius: 6px; cursor: pointer;\n font-size: 14px; font-weight: 500; height: 38px;\n display: flex; align-items: center; gap: 8px;\n font-family: 'Roboto', Arial, sans-serif;\n " ${state.isLoading?"disabled":""}>\n ${state.isLoading?'<span style="animation: spin 1s linear infinite; display: inline-block;">↻</span> Carregando...':"Carregar"}\n </button>\n </div>\n\n \x3c!-- Stats Cards --\x3e\n <div style="\n display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));\n gap: 12px; margin-bottom: 16px;\n ">\n \x3c!-- Current Temperature --\x3e\n <div style="\n padding: 16px; background: ${state.theme==="dark"?"rgba(255,255,255,0.05)":"#fafafa"};\n border-radius: 12px; border: 1px solid ${colors.border};\n ">\n <span style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500;">Temperatura Atual</span>\n <div style="font-weight: 700; font-size: 24px; color: ${statusColor}; margin-top: 4px;">\n ${state.currentTemperature!==null?formatTemperature2(state.currentTemperature):"N/A"}\n </div>\n <div style="font-size: 11px; color: ${statusColor}; margin-top: 2px;">${statusText}</div>\n </div>\n \x3c!-- Average --\x3e\n <div style="\n padding: 16px; background: ${state.theme==="dark"?"rgba(255,255,255,0.05)":"#fafafa"};\n border-radius: 12px; border: 1px solid ${colors.border};\n ">\n <span style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500;">Média do Período</span>\n <div id="${modalId}-avg" style="font-weight: 600; font-size: 20px; color: ${colors.text}; margin-top: 4px;">\n ${state.stats.count>0?formatTemperature2(state.stats.avg):"N/A"}\n </div>\n <div style="font-size: 11px; color: ${colors.textMuted}; margin-top: 2px;">${startDateStr} - ${endDateStr}</div>\n </div>\n \x3c!-- Min/Max --\x3e\n <div style="\n padding: 16px; background: ${state.theme==="dark"?"rgba(255,255,255,0.05)":"#fafafa"};\n border-radius: 12px; border: 1px solid ${colors.border};\n ">\n <span style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500;">Min / Max</span>\n <div id="${modalId}-minmax" style="font-weight: 600; font-size: 20px; color: ${colors.text}; margin-top: 4px;">\n ${state.stats.count>0?`${formatTemperature2(state.stats.min)} / ${formatTemperature2(state.stats.max)}`:"N/A"}\n </div>\n <div style="font-size: 11px; color: ${colors.textMuted}; margin-top: 2px;">${state.stats.count} leituras</div>\n </div>\n \x3c!-- Ideal Range --\x3e\n <div style="\n padding: 16px; background: ${state.theme==="dark"?"rgba(255,255,255,0.05)":"#fafafa"};\n border-radius: 12px; border: 1px solid ${colors.border};\n ">\n <span style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500;">Faixa Ideal</span>\n <div style="font-weight: 600; font-size: 20px; color: ${colors.success}; margin-top: 4px;">\n ${rangeText}\n </div>\n </div>\n </div>\n\n \x3c!-- Chart Container --\x3e\n <div style="margin-bottom: 20px;">\n <h3 style="margin: 0 0 12px 0; font-size: 14px; color: ${colors.textMuted}; font-weight: 500;">\n Histórico de Temperatura\n </h3>\n <div id="${modalId}-chart" style="\n height: 320px; background: ${state.theme==="dark"?"rgba(255,255,255,0.03)":"#fafafa"};\n border-radius: 12px; display: flex; justify-content: center; align-items: center;\n border: 1px solid ${colors.border}; position: relative;\n ">\n ${state.isLoading?`<div style="text-align: center; color: ${colors.textMuted};">\n <div style="animation: spin 1s linear infinite; font-size: 32px; margin-bottom: 8px;">↻</div>\n <div>Carregando dados...</div>\n </div>`:error?`<div style="text-align: center; color: ${colors.danger};">\n <div style="font-size: 32px; margin-bottom: 8px;">⚠️</div>\n <div>Erro ao carregar dados</div>\n <div style="font-size: 12px; margin-top: 4px;">${error.message}</div>\n </div>`:state.data.length===0?`<div style="text-align: center; color: ${colors.textMuted};">\n <div style="font-size: 32px; margin-bottom: 8px;">📭</div>\n <div>Sem dados para o período selecionado</div>\n </div>`:`<canvas id="${modalId}-canvas" style="width: 100%; height: 100%;"></canvas>`}\n </div>\n </div>\n\n \x3c!-- Actions --\x3e\n <div style="display: flex; justify-content: flex-end; gap: 12px;">\n <button id="${modalId}-export" style="\n background: ${state.theme==="dark"?"rgba(255,255,255,0.1)":"#f7f7f7"};\n color: ${colors.text}; border: 1px solid ${colors.border};\n padding: 8px 16px; border-radius: 6px; cursor: pointer;\n font-size: 14px; display: flex; align-items: center; gap: 8px;\n font-family: 'Roboto', Arial, sans-serif;\n " ${state.data.length===0?"disabled":""}>\n 📥 Exportar CSV\n </button>\n <button id="${modalId}-close-btn" style="\n background: #3e1a7d; color: white; border: none;\n padding: 8px 16px; border-radius: 6px; cursor: pointer;\n font-size: 14px; font-weight: 500;\n font-family: 'Roboto', Arial, sans-serif;\n ">\n Fechar\n </button>\n </div>\n </div>\x3c!-- End Body --\x3e\n </div>\n </div>\n <style>\n @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }\n #${modalId} select:focus, #${modalId} input:focus {\n outline: 2px solid #3e1a7d;\n outline-offset: 2px;\n }\n #${modalId} button:hover:not(:disabled) {\n opacity: 0.9;\n }\n #${modalId} button:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n #${modalId} .myio-temp-modal-content > div:first-child button:hover {\n background: rgba(255, 255, 255, 0.1) !important;\n color: white !important;\n }\n\n /* DateRangePicker styles */\n ${CSS_TOKENS}\n ${DATERANGEPICKER_STYLES}\n\n /* Fix DateRangePicker buttons alignment */\n .myio-modal-scope .daterangepicker .drp-buttons {\n display: flex;\n justify-content: flex-end;\n align-items: center;\n gap: 8px;\n }\n .myio-modal-scope .daterangepicker .drp-buttons .btn {\n display: inline-block;\n margin-left: 0;\n }\n </style>\n `}function drawChart(modalId,state){const chartContainer=document.getElementById(`${modalId}-chart`);const canvas=document.getElementById(`${modalId}-canvas`);if(!chartContainer||!canvas||state.data.length===0)return;const ctx=canvas.getContext("2d");if(!ctx)return;const colors=getThemeColors(state.theme);const filteredData=filterByDayPeriods(state.data,state.selectedPeriods);if(filteredData.length===0){canvas.width=chartContainer.clientWidth;canvas.height=chartContainer.clientHeight;ctx.fillStyle=colors.textMuted;ctx.font="14px Roboto, Arial, sans-serif";ctx.textAlign="center";ctx.fillText("Nenhum dado para os períodos selecionados",canvas.width/2,canvas.height/2);return}let chartData;if(state.granularity==="hour"){const interpolated=interpolateTemperature(filteredData,{intervalMinutes:30,startTs:state.startTs,endTs:state.endTs,clampRange:state.clampRange});const filteredInterpolated=filterByDayPeriods(interpolated,state.selectedPeriods);chartData=filteredInterpolated.map(item=>({x:item.ts,y:Number(item.value),screenX:0,screenY:0}))}else{const daily=aggregateByDay(filteredData,state.clampRange);chartData=daily.map(item=>({x:item.dateTs,y:item.avg,screenX:0,screenY:0,label:item.date}))}if(chartData.length===0)return;const width=chartContainer.clientWidth-2;const height=320;canvas.width=width;canvas.height=height;const paddingLeft=60;const paddingRight=20;const paddingTop=20;const paddingBottom=55;const isPeriodsFiltered=state.selectedPeriods.length<4&&state.selectedPeriods.length>0;const values=chartData.map(d=>d.y);const dataMin=Math.min(...values);const dataMax=Math.max(...values);const thresholdMin=state.temperatureMin!==null?state.temperatureMin:dataMin;const thresholdMax=state.temperatureMax!==null?state.temperatureMax:dataMax;const minY=Math.min(dataMin,thresholdMin)-1;const maxY=Math.max(dataMax,thresholdMax)+1;const chartWidth=width-paddingLeft-paddingRight;const chartHeight=height-paddingTop-paddingBottom;const scaleY=chartHeight/(maxY-minY||1);if(isPeriodsFiltered){const pointSpacing=chartWidth/Math.max(1,chartData.length-1);chartData.forEach((point,index)=>{point.screenX=paddingLeft+index*pointSpacing;point.screenY=height-paddingBottom-(point.y-minY)*scaleY})}else{const minX=chartData[0].x;const maxX=chartData[chartData.length-1].x;const timeRange=maxX-minX||1;const scaleX=chartWidth/timeRange;chartData.forEach(point=>{point.screenX=paddingLeft+(point.x-minX)*scaleX;point.screenY=height-paddingBottom-(point.y-minY)*scaleY})}ctx.clearRect(0,0,width,height);ctx.strokeStyle=colors.chartGrid;ctx.lineWidth=1;for(let i=0;i<=4;i++){const y=paddingTop+chartHeight*i/4;ctx.beginPath();ctx.moveTo(paddingLeft,y);ctx.lineTo(width-paddingRight,y);ctx.stroke()}if(state.temperatureMin!==null&&state.temperatureMax!==null){const rangeMinY=height-paddingBottom-(state.temperatureMin-minY)*scaleY;const rangeMaxY=height-paddingBottom-(state.temperatureMax-minY)*scaleY;ctx.fillStyle="rgba(76, 175, 80, 0.1)";ctx.fillRect(paddingLeft,rangeMaxY,chartWidth,rangeMinY-rangeMaxY);ctx.strokeStyle=colors.success;ctx.setLineDash([5,5]);ctx.beginPath();ctx.moveTo(paddingLeft,rangeMinY);ctx.lineTo(width-paddingRight,rangeMinY);ctx.moveTo(paddingLeft,rangeMaxY);ctx.lineTo(width-paddingRight,rangeMaxY);ctx.stroke();ctx.setLineDash([])}ctx.strokeStyle=colors.chartLine;ctx.lineWidth=2;ctx.beginPath();chartData.forEach((point,i)=>{if(i===0)ctx.moveTo(point.screenX,point.screenY);else ctx.lineTo(point.screenX,point.screenY)});ctx.stroke();ctx.fillStyle=colors.chartLine;chartData.forEach(point=>{ctx.beginPath();ctx.arc(point.screenX,point.screenY,4,0,Math.PI*2);ctx.fill()});ctx.fillStyle=colors.textMuted;ctx.font="11px system-ui, sans-serif";ctx.textAlign="right";for(let i=0;i<=4;i++){const val=minY+(maxY-minY)*(4-i)/4;const y=paddingTop+chartHeight*i/4;ctx.fillText(val.toFixed(1)+"°C",paddingLeft-8,y+4)}ctx.textAlign="center";const numLabels=Math.min(8,chartData.length);const labelInterval=Math.max(1,Math.floor(chartData.length/numLabels));for(let i=0;i<chartData.length;i+=labelInterval){const point=chartData[i];const date=new Date(point.x);let label;if(state.granularity==="hour"){label=date.toLocaleTimeString(state.locale,{hour:"2-digit",minute:"2-digit"})}else{label=date.toLocaleDateString(state.locale,{day:"2-digit",month:"2-digit"})}ctx.strokeStyle=colors.chartGrid;ctx.lineWidth=1;ctx.beginPath();ctx.moveTo(point.screenX,paddingTop);ctx.lineTo(point.screenX,height-paddingBottom);ctx.stroke();ctx.fillStyle=colors.textMuted;ctx.fillText(label,point.screenX,height-paddingBottom+18)}ctx.strokeStyle=colors.border;ctx.lineWidth=1;ctx.beginPath();ctx.moveTo(paddingLeft,paddingTop);ctx.lineTo(paddingLeft,height-paddingBottom);ctx.lineTo(width-paddingRight,height-paddingBottom);ctx.stroke();setupChartTooltip(canvas,chartContainer,chartData,state,colors)}function setupChartTooltip(canvas,container,chartData,state,colors){const existingTooltip=container.querySelector(".myio-chart-tooltip");if(existingTooltip)existingTooltip.remove();const tooltip=document.createElement("div");tooltip.className="myio-chart-tooltip";tooltip.style.cssText=`\n position: absolute;\n background: ${state.theme==="dark"?"rgba(30, 30, 40, 0.95)":"rgba(255, 255, 255, 0.98)"};\n border: 1px solid ${colors.border};\n border-radius: 8px;\n padding: 10px 14px;\n font-size: 13px;\n color: ${colors.text};\n pointer-events: none;\n opacity: 0;\n transition: opacity 0.15s;\n z-index: 1000;\n box-shadow: 0 4px 12px rgba(0,0,0,0.15);\n min-width: 140px;\n `;container.appendChild(tooltip);const findNearestPoint=(mouseX,mouseY)=>{const threshold=20;let nearest=null;let minDist=Infinity;for(const point of chartData){const dist=Math.sqrt(Math.pow(mouseX-point.screenX,2)+Math.pow(mouseY-point.screenY,2));if(dist<minDist&&dist<threshold){minDist=dist;nearest=point}}return nearest};canvas.addEventListener("mousemove",e=>{const rect=canvas.getBoundingClientRect();const mouseX=e.clientX-rect.left;const mouseY=e.clientY-rect.top;const point=findNearestPoint(mouseX,mouseY);if(point){const date=new Date(point.x);let dateStr;if(state.granularity==="hour"){dateStr=date.toLocaleDateString(state.locale,{day:"2-digit",month:"2-digit",year:"numeric"})+" "+date.toLocaleTimeString(state.locale,{hour:"2-digit",minute:"2-digit"})}else{dateStr=date.toLocaleDateString(state.locale,{day:"2-digit",month:"2-digit",year:"numeric"})}tooltip.innerHTML=`\n <div style="font-weight: 600; margin-bottom: 6px; color: ${colors.primary};">\n ${formatTemperature2(point.y)}\n </div>\n <div style="font-size: 11px; color: ${colors.textMuted};">\n 📅 ${dateStr}\n </div>\n `;let tooltipX=point.screenX+15;let tooltipY=point.screenY-15;const tooltipRect=tooltip.getBoundingClientRect();const containerRect=container.getBoundingClientRect();if(tooltipX+tooltipRect.width>containerRect.width-10){tooltipX=point.screenX-tooltipRect.width-15}if(tooltipY<10){tooltipY=point.screenY+15}tooltip.style.left=`${tooltipX}px`;tooltip.style.top=`${tooltipY}px`;tooltip.style.opacity="1";canvas.style.cursor="pointer"}else{tooltip.style.opacity="0";canvas.style.cursor="default"}});canvas.addEventListener("mouseleave",()=>{tooltip.style.opacity="0";canvas.style.cursor="default"})}async function setupEventListeners(container,state,modalId,onClose){const closeModal=()=>{container.remove();onClose?.()};container.querySelector(".myio-temp-modal-overlay")?.addEventListener("click",e=>{if(e.target===e.currentTarget)closeModal()});document.getElementById(`${modalId}-close`)?.addEventListener("click",closeModal);document.getElementById(`${modalId}-close-btn`)?.addEventListener("click",closeModal);const dateRangeInput=document.getElementById(`${modalId}-date-range`);if(dateRangeInput&&!state.dateRangePicker){try{state.dateRangePicker=await createDateRangePicker2(dateRangeInput,{presetStart:new Date(state.startTs).toISOString(),presetEnd:new Date(state.endTs).toISOString(),includeTime:true,timePrecision:"minute",maxRangeDays:90,locale:state.locale,parentEl:container.querySelector(".myio-temp-modal-content"),onApply:result=>{state.startTs=new Date(result.startISO).getTime();state.endTs=new Date(result.endISO).getTime();console.log("[TemperatureModal] Date range applied:",result)}})}catch(error){console.warn("[TemperatureModal] DateRangePicker initialization failed:",error)}}document.getElementById(`${modalId}-theme-toggle`)?.addEventListener("click",async()=>{state.theme=state.theme==="dark"?"light":"dark";localStorage.setItem("myio-temp-modal-theme",state.theme);state.dateRangePicker=null;renderModal(container,state,modalId);if(state.data.length>0)drawChart(modalId,state);await setupEventListeners(container,state,modalId,onClose)});document.getElementById(`${modalId}-maximize`)?.addEventListener("click",async()=>{container.__isMaximized=!container.__isMaximized;state.dateRangePicker=null;renderModal(container,state,modalId);if(state.data.length>0)drawChart(modalId,state);await setupEventListeners(container,state,modalId,onClose)});const periodBtn=document.getElementById(`${modalId}-period-btn`);const periodDropdown=document.getElementById(`${modalId}-period-dropdown`);periodBtn?.addEventListener("click",e=>{e.stopPropagation();if(periodDropdown){periodDropdown.style.display=periodDropdown.style.display==="none"?"block":"none"}});document.addEventListener("click",e=>{if(periodDropdown&&!periodDropdown.contains(e.target)&&e.target!==periodBtn){periodDropdown.style.display="none"}});const periodCheckboxes=document.querySelectorAll(`input[name="${modalId}-period"]`);periodCheckboxes.forEach(checkbox=>{checkbox.addEventListener("change",()=>{const checked=Array.from(periodCheckboxes).filter(cb=>cb.checked).map(cb=>cb.value);state.selectedPeriods=checked;const btnLabel=periodBtn?.querySelector("span:first-child");if(btnLabel){btnLabel.textContent=getSelectedPeriodsLabel(state.selectedPeriods)}if(state.data.length>0)drawChart(modalId,state)})});document.getElementById(`${modalId}-period-select-all`)?.addEventListener("click",()=>{periodCheckboxes.forEach(cb=>{cb.checked=true});state.selectedPeriods=["madrugada","manha","tarde","noite"];const btnLabel=periodBtn?.querySelector("span:first-child");if(btnLabel){btnLabel.textContent=getSelectedPeriodsLabel(state.selectedPeriods)}if(state.data.length>0)drawChart(modalId,state)});document.getElementById(`${modalId}-period-clear`)?.addEventListener("click",()=>{periodCheckboxes.forEach(cb=>{cb.checked=false});state.selectedPeriods=[];const btnLabel=periodBtn?.querySelector("span:first-child");if(btnLabel){btnLabel.textContent=getSelectedPeriodsLabel(state.selectedPeriods)}if(state.data.length>0)drawChart(modalId,state)});document.getElementById(`${modalId}-granularity`)?.addEventListener("change",e=>{state.granularity=e.target.value;localStorage.setItem("myio-temp-modal-granularity",state.granularity);if(state.data.length>0)drawChart(modalId,state)});document.getElementById(`${modalId}-query`)?.addEventListener("click",async()=>{if(state.startTs>=state.endTs){alert("Por favor, selecione um período válido");return}state.isLoading=true;state.dateRangePicker=null;renderModal(container,state,modalId);try{state.data=await fetchTemperatureData(state.token,state.deviceId,state.startTs,state.endTs);state.stats=calculateStats(state.data,state.clampRange);state.isLoading=false;renderModal(container,state,modalId);drawChart(modalId,state);await setupEventListeners(container,state,modalId,onClose)}catch(error){console.error("[TemperatureModal] Error fetching data:",error);state.isLoading=false;renderModal(container,state,modalId,error);await setupEventListeners(container,state,modalId,onClose)}});document.getElementById(`${modalId}-export`)?.addEventListener("click",()=>{if(state.data.length===0)return;const startDateStr=new Date(state.startTs).toLocaleDateString(state.locale).replace(/\//g,"-");const endDateStr=new Date(state.endTs).toLocaleDateString(state.locale).replace(/\//g,"-");exportTemperatureCSV(state.data,state.label,state.stats,startDateStr,endDateStr)})}async function openTemperatureComparisonModal(params){const modalId=`myio-temp-comparison-modal-${Date.now()}`;const defaultDateRange=getTodaySoFar();const startTs=params.startDate?new Date(params.startDate).getTime():defaultDateRange.startTs;const endTs=params.endDate?new Date(params.endDate).getTime():defaultDateRange.endTs;const state={token:params.token,devices:params.devices,startTs:startTs,endTs:endTs,granularity:params.granularity||"hour",theme:params.theme||"dark",clampRange:params.clampRange||DEFAULT_CLAMP_RANGE,locale:params.locale||"pt-BR",deviceData:[],isLoading:true,dateRangePicker:null,selectedPeriods:["madrugada","manha","tarde","noite"],temperatureMin:params.temperatureMin??null,temperatureMax:params.temperatureMax??null};const savedGranularity=localStorage.getItem("myio-temp-comparison-granularity");const savedTheme=localStorage.getItem("myio-temp-comparison-theme");if(savedGranularity)state.granularity=savedGranularity;if(savedTheme)state.theme=savedTheme;const modalContainer=document.createElement("div");modalContainer.id=modalId;document.body.appendChild(modalContainer);renderModal2(modalContainer,state,modalId);await fetchAllDevicesData(state);renderModal2(modalContainer,state,modalId);drawComparisonChart(modalId,state);await setupEventListeners2(modalContainer,state,modalId,params.onClose);return{destroy:()=>{modalContainer.remove();params.onClose?.()},updateData:async(startDate,endDate,granularity)=>{state.startTs=new Date(startDate).getTime();state.endTs=new Date(endDate).getTime();if(granularity)state.granularity=granularity;state.isLoading=true;renderModal2(modalContainer,state,modalId);await fetchAllDevicesData(state);renderModal2(modalContainer,state,modalId);drawComparisonChart(modalId,state);setupEventListeners2(modalContainer,state,modalId,params.onClose)}}}async function fetchAllDevicesData(state){state.isLoading=true;state.deviceData=[];try{const results=await Promise.all(state.devices.map(async(device,index)=>{const deviceId=device.tbId||device.id;try{const data=await fetchTemperatureData(state.token,deviceId,state.startTs,state.endTs);const stats=calculateStats(data,state.clampRange);return{device:device,data:data,stats:stats,color:CHART_COLORS[index%CHART_COLORS.length]}}catch(error){console.error(`[TemperatureComparisonModal] Error fetching data for ${device.label}:`,error);return{device:device,data:[],stats:{avg:0,min:0,max:0,count:0},color:CHART_COLORS[index%CHART_COLORS.length]}}}));state.deviceData=results}catch(error){console.error("[TemperatureComparisonModal] Error fetching data:",error)}state.isLoading=false}function renderModal2(container,state,modalId){const colors=getThemeColors(state.theme);new Date(state.startTs).toLocaleDateString(state.locale);new Date(state.endTs).toLocaleDateString(state.locale);new Date(state.startTs).toISOString().slice(0,16);new Date(state.endTs).toISOString().slice(0,16);const legendHTML=state.deviceData.map(dd=>`\n <div style="display: flex; align-items: center; gap: 8px; padding: 8px 12px;\n background: ${state.theme==="dark"?"rgba(255,255,255,0.05)":"rgba(0,0,0,0.03)"};\n border-radius: 8px;">\n <span style="width: 12px; height: 12px; border-radius: 50%; background: ${dd.color};"></span>\n <span style="color: ${colors.text}; font-size: 13px;">${dd.device.label}</span>\n <span style="color: ${colors.textMuted}; font-size: 11px; margin-left: auto;">\n ${dd.stats.count>0?formatTemperature2(dd.stats.avg):"N/A"}\n </span>\n </div>\n `).join("");const statsHTML=state.deviceData.map(dd=>`\n <div style="\n padding: 12px; background: ${state.theme==="dark"?"rgba(255,255,255,0.05)":"#fafafa"};\n border-radius: 10px; border-left: 4px solid ${dd.color};\n min-width: 150px;\n ">\n <div style="font-weight: 600; color: ${colors.text}; font-size: 13px; margin-bottom: 8px;">\n ${dd.device.label}\n </div>\n <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 4px; font-size: 11px;">\n <span style="color: ${colors.textMuted};">Média:</span>\n <span style="color: ${colors.text}; font-weight: 500;">\n ${dd.stats.count>0?formatTemperature2(dd.stats.avg):"N/A"}\n </span>\n <span style="color: ${colors.textMuted};">Min:</span>\n <span style="color: ${colors.text};">\n ${dd.stats.count>0?formatTemperature2(dd.stats.min):"N/A"}\n </span>\n <span style="color: ${colors.textMuted};">Max:</span>\n <span style="color: ${colors.text};">\n ${dd.stats.count>0?formatTemperature2(dd.stats.max):"N/A"}\n </span>\n <span style="color: ${colors.textMuted};">Leituras:</span>\n <span style="color: ${colors.text};">${dd.stats.count}</span>\n </div>\n </div>\n `).join("");const isMaximized=container.__isMaximized||false;const contentMaxWidth=isMaximized?"100%":"1100px";const contentMaxHeight=isMaximized?"100vh":"95vh";const contentBorderRadius=isMaximized?"0":"10px";container.innerHTML=`\n <div class="myio-temp-comparison-overlay" style="\n position: fixed; top: 0; left: 0; width: 100%; height: 100%;\n background: rgba(0, 0, 0, 0.5); z-index: 9998;\n display: flex; justify-content: center; align-items: center;\n backdrop-filter: blur(2px);\n ">\n <div class="myio-temp-comparison-content" style="\n background: ${colors.surface}; border-radius: ${contentBorderRadius};\n max-width: ${contentMaxWidth}; width: ${isMaximized?"100%":"95%"};\n max-height: ${contentMaxHeight}; height: ${isMaximized?"100%":"auto"};\n overflow: hidden; display: flex; flex-direction: column;\n box-shadow: 0 4px 12px rgba(0,0,0,0.15);\n font-family: 'Roboto', Arial, sans-serif;\n ">\n \x3c!-- Header - MyIO Premium Style --\x3e\n <div style="\n padding: 4px 8px; display: flex; align-items: center; justify-content: space-between;\n background: #3e1a7d; color: white; border-radius: ${isMaximized?"0":"10px 10px 0 0"};\n min-height: 20px;\n ">\n <h2 style="margin: 6px; font-size: 18px; font-weight: 600; color: white; line-height: 2;">\n 🌡️ Comparação de Temperatura - ${state.devices.length} sensores\n </h2>\n <div style="display: flex; gap: 4px; align-items: center;">\n \x3c!-- Theme Toggle --\x3e\n <button id="${modalId}-theme-toggle" title="Alternar tema" style="\n background: none; border: none; font-size: 16px; cursor: pointer;\n padding: 4px 8px; border-radius: 6px; color: rgba(255,255,255,0.8);\n transition: background-color 0.2s;\n ">${state.theme==="dark"?"☀️":"🌙"}</button>\n \x3c!-- Maximize Button --\x3e\n <button id="${modalId}-maximize" title="${isMaximized?"Restaurar":"Maximizar"}" style="\n background: none; border: none; font-size: 16px; cursor: pointer;\n padding: 4px 8px; border-radius: 6px; color: rgba(255,255,255,0.8);\n transition: background-color 0.2s;\n ">${isMaximized?"🗗":"🗖"}</button>\n \x3c!-- Close Button --\x3e\n <button id="${modalId}-close" title="Fechar" style="\n background: none; border: none; font-size: 20px; cursor: pointer;\n padding: 4px 8px; border-radius: 6px; color: rgba(255,255,255,0.8);\n transition: background-color 0.2s;\n ">×</button>\n </div>\n </div>\n\n \x3c!-- Body --\x3e\n <div style="flex: 1; overflow-y: auto; padding: 16px;">\n\n \x3c!-- Controls Row --\x3e\n <div style="\n display: flex; gap: 16px; flex-wrap: wrap; align-items: flex-end;\n margin-bottom: 16px; padding: 16px;\n background: ${state.theme==="dark"?"rgba(255,255,255,0.05)":"#f7f7f7"};\n border-radius: 6px; border: 1px solid ${colors.border};\n ">\n \x3c!-- Granularity Select --\x3e\n <div>\n <label style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500; display: block; margin-bottom: 4px;">\n Granularidade\n </label>\n <select id="${modalId}-granularity" style="\n padding: 8px 12px; border: 1px solid ${colors.border}; border-radius: 6px;\n font-size: 14px; color: ${colors.text}; background: ${colors.surface};\n cursor: pointer; min-width: 130px;\n ">\n <option value="hour" ${state.granularity==="hour"?"selected":""}>Hora (30 min)</option>\n <option value="day" ${state.granularity==="day"?"selected":""}>Dia (média)</option>\n </select>\n </div>\n \x3c!-- Day Period Filter (Multiselect) --\x3e\n <div style="position: relative;">\n <label style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500; display: block; margin-bottom: 4px;">\n Períodos do Dia\n </label>\n <button id="${modalId}-period-btn" type="button" style="\n padding: 8px 12px; border: 1px solid ${colors.border}; border-radius: 6px;\n font-size: 14px; color: ${colors.text}; background: ${colors.surface};\n cursor: pointer; min-width: 180px; text-align: left;\n display: flex; align-items: center; justify-content: space-between; gap: 8px;\n ">\n <span>${getSelectedPeriodsLabel(state.selectedPeriods)}</span>\n <span style="font-size: 10px;">▼</span>\n </button>\n <div id="${modalId}-period-dropdown" style="\n display: none; position: absolute; top: 100%; left: 0; z-index: 1000;\n background: ${colors.surface}; border: 1px solid ${colors.border};\n border-radius: 6px; box-shadow: 0 4px 12px rgba(0,0,0,0.15);\n min-width: 200px; margin-top: 4px; padding: 8px 0;\n ">\n ${DAY_PERIODS.map(period=>`\n <label style="\n display: flex; align-items: center; gap: 8px; padding: 8px 12px;\n cursor: pointer; font-size: 13px; color: ${colors.text};\n " onmouseover="this.style.background='${state.theme==="dark"?"rgba(255,255,255,0.1)":"#f0f0f0"}'"\n onmouseout="this.style.background='transparent'">\n <input type="checkbox"\n name="${modalId}-period"\n value="${period.id}"\n ${state.selectedPeriods.includes(period.id)?"checked":""}\n style="width: 16px; height: 16px; cursor: pointer; accent-color: #3e1a7d;">\n ${period.label}\n </label>\n `).join("")}\n <div style="border-top: 1px solid ${colors.border}; margin-top: 8px; padding-top: 8px;">\n <button id="${modalId}-period-select-all" type="button" style="\n width: calc(100% - 16px); margin: 0 8px 4px; padding: 6px;\n background: ${state.theme==="dark"?"rgba(255,255,255,0.1)":"#f0f0f0"};\n border: none; border-radius: 4px; cursor: pointer;\n font-size: 12px; color: ${colors.text};\n ">Selecionar Todos</button>\n <button id="${modalId}-period-clear" type="button" style="\n width: calc(100% - 16px); margin: 0 8px; padding: 6px;\n background: ${state.theme==="dark"?"rgba(255,255,255,0.1)":"#f0f0f0"};\n border: none; border-radius: 4px; cursor: pointer;\n font-size: 12px; color: ${colors.text};\n ">Limpar Seleção</button>\n </div>\n </div>\n </div>\n \x3c!-- Date Range Picker --\x3e\n <div style="flex: 1; min-width: 220px;">\n <label style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500; display: block; margin-bottom: 4px;">\n Período\n </label>\n <input type="text" id="${modalId}-date-range" readonly placeholder="Selecione o período..." style="\n padding: 8px 12px; border: 1px solid ${colors.border}; border-radius: 6px;\n font-size: 14px; color: ${colors.text}; background: ${colors.surface};\n width: 100%; cursor: pointer; box-sizing: border-box;\n "/>\n </div>\n \x3c!-- Query Button --\x3e\n <button id="${modalId}-query" style="\n background: #3e1a7d; color: white; border: none;\n padding: 8px 16px; border-radius: 6px; cursor: pointer;\n font-size: 14px; font-weight: 500; height: 38px;\n display: flex; align-items: center; gap: 8px;\n font-family: 'Roboto', Arial, sans-serif;\n " ${state.isLoading?"disabled":""}>\n ${state.isLoading?'<span style="animation: spin 1s linear infinite; display: inline-block;">↻</span> Carregando...':"Carregar"}\n </button>\n </div>\n\n \x3c!-- Legend --\x3e\n <div style="\n display: flex; flex-wrap: wrap; gap: 10px;\n margin-bottom: 20px;\n ">\n ${legendHTML}\n </div>\n\n \x3c!-- Chart Container --\x3e\n <div style="margin-bottom: 24px;">\n <div id="${modalId}-chart" style="\n height: 380px;\n background: ${state.theme==="dark"?"rgba(255,255,255,0.03)":"#fafafa"};\n border-radius: 14px; display: flex; justify-content: center; align-items: center;\n border: 1px solid ${colors.border}; position: relative;\n ">\n ${state.isLoading?`<div style="text-align: center; color: ${colors.textMuted};">\n <div style="animation: spin 1s linear infinite; font-size: 36px; margin-bottom: 12px;">↻</div>\n <div style="font-size: 15px;">Carregando dados de ${state.devices.length} sensores...</div>\n </div>`:state.deviceData.every(dd=>dd.data.length===0)?`<div style="text-align: center; color: ${colors.textMuted};">\n <div style="font-size: 48px; margin-bottom: 12px;">📭</div>\n <div style="font-size: 16px;">Sem dados para o período selecionado</div>\n </div>`:`<canvas id="${modalId}-canvas" style="width: 100%; height: 100%;"></canvas>`}\n </div>\n </div>\n\n \x3c!-- Stats Cards --\x3e\n <div style="\n display: flex; flex-wrap: wrap; gap: 12px;\n margin-bottom: 24px;\n ">\n ${statsHTML}\n </div>\n\n \x3c!-- Actions --\x3e\n <div style="display: flex; justify-content: flex-end; gap: 12px;">\n <button id="${modalId}-export" style="\n background: ${state.theme==="dark"?"rgba(255,255,255,0.1)":"#f7f7f7"};\n color: ${colors.text}; border: 1px solid ${colors.border};\n padding: 8px 16px; border-radius: 6px; cursor: pointer;\n font-size: 14px; display: flex; align-items: center; gap: 8px;\n font-family: 'Roboto', Arial, sans-serif;\n " ${state.deviceData.every(dd=>dd.data.length===0)?"disabled":""}>\n 📥 Exportar CSV\n </button>\n <button id="${modalId}-close-btn" style="\n background: #3e1a7d; color: white; border: none;\n padding: 8px 16px; border-radius: 6px; cursor: pointer;\n font-size: 14px; font-weight: 500;\n font-family: 'Roboto', Arial, sans-serif;\n ">\n Fechar\n </button>\n </div>\n </div>\x3c!-- End Body --\x3e\n </div>\n </div>\n <style>\n @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }\n #${modalId} select:focus, #${modalId} input:focus {\n outline: 2px solid #3e1a7d;\n outline-offset: 2px;\n }\n #${modalId} button:hover:not(:disabled) {\n opacity: 0.9;\n }\n #${modalId} button:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n #${modalId} .myio-temp-comparison-content > div:first-child button:hover {\n background: rgba(255, 255, 255, 0.1) !important;\n color: white !important;\n }\n </style>\n `}function drawComparisonChart(modalId,state){const chartContainer=document.getElementById(`${modalId}-chart`);const canvas=document.getElementById(`${modalId}-canvas`);if(!chartContainer||!canvas)return;const hasData=state.deviceData.some(dd=>dd.data.length>0);if(!hasData)return;const ctx=canvas.getContext("2d");if(!ctx)return;const colors=getThemeColors(state.theme);const width=chartContainer.clientWidth-2;const height=380;canvas.width=width;canvas.height=height;const paddingLeft=65;const paddingRight=25;const paddingTop=25;const paddingBottom=55;ctx.clearRect(0,0,width,height);const processedData=[];state.deviceData.forEach(dd=>{if(dd.data.length===0)return;const filteredData=filterByDayPeriods(dd.data,state.selectedPeriods);if(filteredData.length===0)return;let points;if(state.granularity==="hour"){const interpolated=interpolateTemperature(filteredData,{intervalMinutes:30,startTs:state.startTs,endTs:state.endTs,clampRange:state.clampRange});const filteredInterpolated=filterByDayPeriods(interpolated,state.selectedPeriods);points=filteredInterpolated.map(item=>({x:item.ts,y:Number(item.value),screenX:0,screenY:0,deviceLabel:dd.device.label,deviceColor:dd.color}))}else{const daily=aggregateByDay(filteredData,state.clampRange);points=daily.map(item=>({x:item.dateTs,y:item.avg,screenX:0,screenY:0,deviceLabel:dd.device.label,deviceColor:dd.color}))}if(points.length>0){processedData.push({device:dd,points:points})}});if(processedData.length===0){ctx.fillStyle=colors.textMuted;ctx.font="14px Roboto, Arial, sans-serif";ctx.textAlign="center";ctx.fillText("Nenhum dado para os períodos selecionados",width/2,height/2);return}const isPeriodsFiltered=state.selectedPeriods.length<4&&state.selectedPeriods.length>0;let dataMinY=Infinity;let dataMaxY=-Infinity;processedData.forEach(({points:points})=>{points.forEach(point=>{if(point.y<dataMinY)dataMinY=point.y;if(point.y>dataMaxY)dataMaxY=point.y})});const rangeMap=new Map;state.deviceData.forEach((dd,index)=>{const device=dd.device;const min=device.temperatureMin;const max=device.temperatureMax;if(min!==void 0&&min!==null&&max!==void 0&&max!==null){const key=`${min}-${max}`;if(!rangeMap.has(key)){rangeMap.set(key,{min:min,max:max,customerName:device.customerName||"",color:CHART_COLORS[index%CHART_COLORS.length],deviceLabels:[device.label]})}else{rangeMap.get(key).deviceLabels.push(device.label)}}});if(rangeMap.size===0&&state.temperatureMin!==null&&state.temperatureMax!==null){rangeMap.set("global",{min:state.temperatureMin,max:state.temperatureMax,customerName:"Global",color:colors.success,deviceLabels:[]})}const temperatureRanges=Array.from(rangeMap.values());let thresholdMinY=dataMinY;let thresholdMaxY=dataMaxY;temperatureRanges.forEach(range=>{if(range.min<thresholdMinY)thresholdMinY=range.min;if(range.max>thresholdMaxY)thresholdMaxY=range.max});const globalMinY=Math.floor(Math.min(dataMinY,thresholdMinY))-1;const globalMaxY=Math.ceil(Math.max(dataMaxY,thresholdMaxY))+1;const chartWidth=width-paddingLeft-paddingRight;const chartHeight=height-paddingTop-paddingBottom;const scaleY=chartHeight/(globalMaxY-globalMinY||1);if(isPeriodsFiltered){const maxPoints=Math.max(...processedData.map(({points:points})=>points.length));const pointSpacing=chartWidth/Math.max(1,maxPoints-1);processedData.forEach(({points:points})=>{points.forEach((point,index)=>{point.screenX=paddingLeft+index*pointSpacing;point.screenY=height-paddingBottom-(point.y-globalMinY)*scaleY})})}else{let globalMinX=Infinity;let globalMaxX=-Infinity;processedData.forEach(({points:points})=>{points.forEach(point=>{if(point.x<globalMinX)globalMinX=point.x;if(point.x>globalMaxX)globalMaxX=point.x})});const timeRange=globalMaxX-globalMinX||1;const scaleX=chartWidth/timeRange;processedData.forEach(({points:points})=>{points.forEach(point=>{point.screenX=paddingLeft+(point.x-globalMinX)*scaleX;point.screenY=height-paddingBottom-(point.y-globalMinY)*scaleY})})}ctx.strokeStyle=colors.chartGrid;ctx.lineWidth=1;for(let i=0;i<=5;i++){const y=paddingTop+chartHeight*i/5;ctx.beginPath();ctx.moveTo(paddingLeft,y);ctx.lineTo(width-paddingRight,y);ctx.stroke()}const rangeColors=[{fill:"rgba(76, 175, 80, 0.12)",stroke:"#4CAF50"},{fill:"rgba(33, 150, 243, 0.12)",stroke:"#2196F3"},{fill:"rgba(255, 152, 0, 0.12)",stroke:"#FF9800"},{fill:"rgba(156, 39, 176, 0.12)",stroke:"#9C27B0"}];temperatureRanges.forEach((range,index)=>{const rangeMinY=height-paddingBottom-(range.min-globalMinY)*scaleY;const rangeMaxY=height-paddingBottom-(range.max-globalMinY)*scaleY;const colorSet=rangeColors[index%rangeColors.length];ctx.fillStyle=colorSet.fill;ctx.fillRect(paddingLeft,rangeMaxY,chartWidth,rangeMinY-rangeMaxY);ctx.strokeStyle=colorSet.stroke;ctx.lineWidth=1.5;ctx.setLineDash([6,4]);ctx.beginPath();ctx.moveTo(paddingLeft,rangeMinY);ctx.lineTo(width-paddingRight,rangeMinY);ctx.moveTo(paddingLeft,rangeMaxY);ctx.lineTo(width-paddingRight,rangeMaxY);ctx.stroke();ctx.setLineDash([]);if(temperatureRanges.length>1||range.customerName){ctx.fillStyle=colorSet.stroke;ctx.font="10px system-ui, sans-serif";ctx.textAlign="left";const labelY=(rangeMinY+rangeMaxY)/2;const labelText=range.customerName||`${range.min}°-${range.max}°C`;ctx.fillText(labelText,width-paddingRight+5,labelY+3)}});processedData.forEach(({device:device,points:points})=>{ctx.strokeStyle=device.color;ctx.lineWidth=2.5;ctx.beginPath();points.forEach((point,i)=>{if(i===0)ctx.moveTo(point.screenX,point.screenY);else ctx.lineTo(point.screenX,point.screenY)});ctx.stroke();ctx.fillStyle=device.color;points.forEach(point=>{ctx.beginPath();ctx.arc(point.screenX,point.screenY,4,0,Math.PI*2);ctx.fill()})});ctx.fillStyle=colors.textMuted;ctx.font="12px system-ui, sans-serif";ctx.textAlign="right";for(let i=0;i<=5;i++){const val=globalMinY+(globalMaxY-globalMinY)*(5-i)/5;const y=paddingTop+chartHeight*i/5;ctx.fillText(val.toFixed(1)+"°C",paddingLeft-10,y+4)}ctx.textAlign="center";const xAxisPoints=processedData[0]?.points||[];const numLabels=Math.min(8,xAxisPoints.length);const labelInterval=Math.max(1,Math.floor(xAxisPoints.length/numLabels));for(let i=0;i<xAxisPoints.length;i+=labelInterval){const point=xAxisPoints[i];const date=new Date(point.x);let label;if(state.granularity==="hour"){label=date.toLocaleTimeString(state.locale,{hour:"2-digit",minute:"2-digit"})}else{label=date.toLocaleDateString(state.locale,{day:"2-digit",month:"2-digit"})}ctx.strokeStyle=colors.chartGrid;ctx.lineWidth=1;ctx.beginPath();ctx.moveTo(point.screenX,paddingTop);ctx.lineTo(point.screenX,height-paddingBottom);ctx.stroke();ctx.fillStyle=colors.textMuted;ctx.fillText(label,point.screenX,height-paddingBottom+18)}ctx.strokeStyle=colors.border;ctx.lineWidth=1;ctx.beginPath();ctx.moveTo(paddingLeft,paddingTop);ctx.lineTo(paddingLeft,height-paddingBottom);ctx.lineTo(width-paddingRight,height-paddingBottom);ctx.stroke();const allChartPoints=processedData.flatMap(pd=>pd.points);setupComparisonChartTooltip(canvas,chartContainer,allChartPoints,state,colors)}function setupComparisonChartTooltip(canvas,container,chartData,state,colors){const existingTooltip=container.querySelector(".myio-chart-tooltip");if(existingTooltip)existingTooltip.remove();const tooltip=document.createElement("div");tooltip.className="myio-chart-tooltip";tooltip.style.cssText=`\n position: absolute;\n background: ${state.theme==="dark"?"rgba(30, 30, 40, 0.95)":"rgba(255, 255, 255, 0.98)"};\n border: 1px solid ${colors.border};\n border-radius: 8px;\n padding: 10px 14px;\n font-size: 13px;\n color: ${colors.text};\n pointer-events: none;\n opacity: 0;\n transition: opacity 0.15s;\n z-index: 1000;\n box-shadow: 0 4px 12px rgba(0,0,0,0.15);\n min-width: 160px;\n `;container.appendChild(tooltip);const findNearestPoint=(mouseX,mouseY)=>{const threshold=20;let nearest=null;let minDist=Infinity;for(const point of chartData){const dist=Math.sqrt(Math.pow(mouseX-point.screenX,2)+Math.pow(mouseY-point.screenY,2));if(dist<minDist&&dist<threshold){minDist=dist;nearest=point}}return nearest};canvas.addEventListener("mousemove",e=>{const rect=canvas.getBoundingClientRect();const mouseX=e.clientX-rect.left;const mouseY=e.clientY-rect.top;const point=findNearestPoint(mouseX,mouseY);if(point){const date=new Date(point.x);let dateStr;if(state.granularity==="hour"){dateStr=date.toLocaleDateString(state.locale,{day:"2-digit",month:"2-digit",year:"numeric"})+" "+date.toLocaleTimeString(state.locale,{hour:"2-digit",minute:"2-digit"})}else{dateStr=date.toLocaleDateString(state.locale,{day:"2-digit",month:"2-digit",year:"numeric"})}tooltip.innerHTML=`\n <div style="display: flex; align-items: center; gap: 8px; margin-bottom: 6px;">\n <span style="width: 10px; height: 10px; border-radius: 50%; background: ${point.deviceColor};"></span>\n <span style="font-weight: 600;">${point.deviceLabel}</span>\n </div>\n <div style="font-weight: 600; font-size: 16px; color: ${point.deviceColor}; margin-bottom: 4px;">\n ${formatTemperature2(point.y)}\n </div>\n <div style="font-size: 11px; color: ${colors.textMuted};">\n 📅 ${dateStr}\n </div>\n `;let tooltipX=point.screenX+15;let tooltipY=point.screenY-15;const tooltipRect=tooltip.getBoundingClientRect();const containerRect=container.getBoundingClientRect();if(tooltipX+tooltipRect.width>containerRect.width-10){tooltipX=point.screenX-tooltipRect.width-15}if(tooltipY<10){tooltipY=point.screenY+15}tooltip.style.left=`${tooltipX}px`;tooltip.style.top=`${tooltipY}px`;tooltip.style.opacity="1";canvas.style.cursor="pointer"}else{tooltip.style.opacity="0";canvas.style.cursor="default"}});canvas.addEventListener("mouseleave",()=>{tooltip.style.opacity="0";canvas.style.cursor="default"})}async function setupEventListeners2(container,state,modalId,onClose){const closeModal=()=>{container.remove();onClose?.()};container.querySelector(".myio-temp-comparison-overlay")?.addEventListener("click",e=>{if(e.target===e.currentTarget)closeModal()});document.getElementById(`${modalId}-close`)?.addEventListener("click",closeModal);document.getElementById(`${modalId}-close-btn`)?.addEventListener("click",closeModal);const dateRangeInput=document.getElementById(`${modalId}-date-range`);if(dateRangeInput&&!state.dateRangePicker){try{state.dateRangePicker=await createDateRangePicker2(dateRangeInput,{presetStart:new Date(state.startTs).toISOString(),presetEnd:new Date(state.endTs).toISOString(),includeTime:true,timePrecision:"minute",maxRangeDays:90,locale:state.locale,parentEl:container.querySelector(".myio-temp-comparison-content"),onApply:result=>{state.startTs=new Date(result.startISO).getTime();state.endTs=new Date(result.endISO).getTime();console.log("[TemperatureComparisonModal] Date range applied:",result)}})}catch(error){console.warn("[TemperatureComparisonModal] DateRangePicker initialization failed:",error)}}document.getElementById(`${modalId}-theme-toggle`)?.addEventListener("click",async()=>{state.theme=state.theme==="dark"?"light":"dark";localStorage.setItem("myio-temp-comparison-theme",state.theme);state.dateRangePicker=null;renderModal2(container,state,modalId);if(state.deviceData.some(dd=>dd.data.length>0)){drawComparisonChart(modalId,state)}await setupEventListeners2(container,state,modalId,onClose)});document.getElementById(`${modalId}-maximize`)?.addEventListener("click",async()=>{container.__isMaximized=!container.__isMaximized;state.dateRangePicker=null;renderModal2(container,state,modalId);if(state.deviceData.some(dd=>dd.data.length>0)){drawComparisonChart(modalId,state)}await setupEventListeners2(container,state,modalId,onClose)});const periodBtn=document.getElementById(`${modalId}-period-btn`);const periodDropdown=document.getElementById(`${modalId}-period-dropdown`);periodBtn?.addEventListener("click",e=>{e.stopPropagation();if(periodDropdown){periodDropdown.style.display=periodDropdown.style.display==="none"?"block":"none"}});document.addEventListener("click",e=>{if(periodDropdown&&!periodDropdown.contains(e.target)&&e.target!==periodBtn){periodDropdown.style.display="none"}});const periodCheckboxes=document.querySelectorAll(`input[name="${modalId}-period"]`);periodCheckboxes.forEach(checkbox=>{checkbox.addEventListener("change",()=>{const checked=Array.from(periodCheckboxes).filter(cb=>cb.checked).map(cb=>cb.value);state.selectedPeriods=checked;const btnLabel=periodBtn?.querySelector("span:first-child");if(btnLabel){btnLabel.textContent=getSelectedPeriodsLabel(state.selectedPeriods)}if(state.deviceData.some(dd=>dd.data.length>0)){drawComparisonChart(modalId,state)}})});document.getElementById(`${modalId}-period-select-all`)?.addEventListener("click",()=>{periodCheckboxes.forEach(cb=>{cb.checked=true});state.selectedPeriods=["madrugada","manha","tarde","noite"];const btnLabel=periodBtn?.querySelector("span:first-child");if(btnLabel){btnLabel.textContent=getSelectedPeriodsLabel(state.selectedPeriods)}if(state.deviceData.some(dd=>dd.data.length>0)){drawComparisonChart(modalId,state)}});document.getElementById(`${modalId}-period-clear`)?.addEventListener("click",()=>{periodCheckboxes.forEach(cb=>{cb.checked=false});state.selectedPeriods=[];const btnLabel=periodBtn?.querySelector("span:first-child");if(btnLabel){btnLabel.textContent=getSelectedPeriodsLabel(state.selectedPeriods)}if(state.deviceData.some(dd=>dd.data.length>0)){drawComparisonChart(modalId,state)}});document.getElementById(`${modalId}-granularity`)?.addEventListener("change",e=>{state.granularity=e.target.value;localStorage.setItem("myio-temp-comparison-granularity",state.granularity);if(state.deviceData.some(dd=>dd.data.length>0)){drawComparisonChart(modalId,state)}});document.getElementById(`${modalId}-query`)?.addEventListener("click",async()=>{if(state.startTs>=state.endTs){alert("Por favor, selecione um período válido");return}state.isLoading=true;state.dateRangePicker=null;renderModal2(container,state,modalId);await fetchAllDevicesData(state);renderModal2(container,state,modalId);drawComparisonChart(modalId,state);await setupEventListeners2(container,state,modalId,onClose)});document.getElementById(`${modalId}-export`)?.addEventListener("click",()=>{if(state.deviceData.every(dd=>dd.data.length===0))return;exportComparisonCSV(state)})}function exportComparisonCSV(state){const startDateStr=new Date(state.startTs).toLocaleDateString(state.locale).replace(/\//g,"-");const endDateStr=new Date(state.endTs).toLocaleDateString(state.locale).replace(/\//g,"-");const BOM="\ufeff";let csvContent=BOM;csvContent+=`Comparação de Temperatura\n`;csvContent+=`Período: ${startDateStr} até ${endDateStr}\n`;csvContent+=`Sensores: ${state.devices.map(d=>d.label).join(", ")}\n`;csvContent+="\n";csvContent+="Estatísticas por Sensor:\n";csvContent+="Sensor,Média (°C),Min (°C),Max (°C),Leituras\n";state.deviceData.forEach(dd=>{csvContent+=`"${dd.device.label}",${dd.stats.avg.toFixed(2)},${dd.stats.min.toFixed(2)},${dd.stats.max.toFixed(2)},${dd.stats.count}\n`});csvContent+="\n";csvContent+="Dados Detalhados:\n";csvContent+="Data/Hora,Sensor,Temperatura (°C)\n";state.deviceData.forEach(dd=>{dd.data.forEach(item=>{const date=new Date(item.ts).toLocaleString(state.locale);const temp=Number(item.value).toFixed(2);csvContent+=`"${date}","${dd.device.label}",${temp}\n`})});const blob=new Blob([csvContent],{type:"text/csv;charset=utf-8;"});const url=URL.createObjectURL(blob);const link=document.createElement("a");link.href=url;link.download=`comparacao_temperatura_${startDateStr}_${endDateStr}.csv`;document.body.appendChild(link);link.click();document.body.removeChild(link);URL.revokeObjectURL(url)}var DARK_THEME2={modalBg:"linear-gradient(180deg, #1e1e2e 0%, #151521 100%)",headerBg:"#3e1a7d",textPrimary:"#ffffff",textSecondary:"rgba(255, 255, 255, 0.7)",textMuted:"rgba(255, 255, 255, 0.5)",inputBg:"rgba(255, 255, 255, 0.08)",inputBorder:"rgba(255, 255, 255, 0.2)",inputText:"#ffffff",buttonPrimary:"#3e1a7d",buttonPrimaryHover:"#5a2da8",buttonSecondary:"rgba(255, 255, 255, 0.1)",success:"#4CAF50",error:"#f44336",overlay:"rgba(0, 0, 0, 0.85)"};var LIGHT_THEME2={modalBg:"#ffffff",headerBg:"#3e1a7d",textPrimary:"#1a1a2e",textSecondary:"rgba(0, 0, 0, 0.7)",textMuted:"rgba(0, 0, 0, 0.5)",inputBg:"#f5f5f5",inputBorder:"rgba(0, 0, 0, 0.2)",inputText:"#1a1a2e",buttonPrimary:"#3e1a7d",buttonPrimaryHover:"#5a2da8",buttonSecondary:"rgba(0, 0, 0, 0.05)",success:"#4CAF50",error:"#f44336",overlay:"rgba(0, 0, 0, 0.5)"};function getColors(theme){return theme==="dark"?DARK_THEME2:LIGHT_THEME2}async function fetchCustomerAttributes(customerId,token){const url=`/api/plugins/telemetry/CUSTOMER/${customerId}/values/attributes/SERVER_SCOPE`;const response=await fetch(url,{method:"GET",headers:{"Content-Type":"application/json","X-Authorization":`Bearer ${token}`}});if(!response.ok){if(response.status===404||response.status===400){return{minTemperature:null,maxTemperature:null}}throw new Error(`Failed to fetch attributes: ${response.status}`)}const attributes=await response.json();let minTemperature=null;let maxTemperature=null;if(Array.isArray(attributes)){for(const attr of attributes){if(attr.key==="minTemperature"){minTemperature=Number(attr.value)}else if(attr.key==="maxTemperature"){maxTemperature=Number(attr.value)}}}return{minTemperature:minTemperature,maxTemperature:maxTemperature}}async function saveCustomerAttributes(customerId,token,minTemperature,maxTemperature){const url=`/api/plugins/telemetry/CUSTOMER/${customerId}/SERVER_SCOPE`;const attributes={minTemperature:minTemperature,maxTemperature:maxTemperature};const response=await fetch(url,{method:"POST",headers:{"Content-Type":"application/json","X-Authorization":`Bearer ${token}`},body:JSON.stringify(attributes)});if(!response.ok){throw new Error(`Failed to save attributes: ${response.status}`)}}function renderModal3(container,state,modalId,onClose,onSave){const colors=getColors(state.theme);const minValue=state.minTemperature!==null?state.minTemperature:"";const maxValue=state.maxTemperature!==null?state.maxTemperature:"";container.innerHTML=`\n <style>\n @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }\n @keyframes slideIn { from { transform: translateY(-20px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }\n @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }\n\n #${modalId} {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background: ${colors.overlay};\n z-index: 10000;\n display: flex;\n align-items: center;\n justify-content: center;\n animation: fadeIn 0.2s ease-out;\n }\n\n #${modalId} .modal-content {\n background: ${colors.modalBg};\n border-radius: 16px;\n width: 90%;\n max-width: 480px;\n box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);\n border: 1px solid rgba(255, 255, 255, 0.1);\n animation: slideIn 0.3s ease-out;\n overflow: hidden;\n }\n\n #${modalId} .modal-header {\n background: ${colors.headerBg};\n padding: 20px 24px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n }\n\n #${modalId} .modal-title {\n margin: 0;\n font-size: 18px;\n font-weight: 600;\n color: #fff;\n font-family: 'Roboto', sans-serif;\n display: flex;\n align-items: center;\n gap: 10px;\n }\n\n #${modalId} .close-btn {\n width: 32px;\n height: 32px;\n background: rgba(255, 255, 255, 0.1);\n border: 1px solid rgba(255, 255, 255, 0.2);\n border-radius: 8px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.2s;\n color: #fff;\n font-size: 18px;\n }\n\n #${modalId} .close-btn:hover {\n background: rgba(255, 68, 68, 0.25);\n border-color: rgba(255, 68, 68, 0.5);\n }\n\n #${modalId} .modal-body {\n padding: 24px;\n }\n\n #${modalId} .customer-info {\n margin-bottom: 24px;\n padding: 12px 16px;\n background: ${colors.inputBg};\n border-radius: 8px;\n border: 1px solid ${colors.inputBorder};\n }\n\n #${modalId} .customer-label {\n font-size: 12px;\n color: ${colors.textMuted};\n margin-bottom: 4px;\n }\n\n #${modalId} .customer-name {\n font-size: 16px;\n font-weight: 500;\n color: ${colors.textPrimary};\n }\n\n #${modalId} .form-group {\n margin-bottom: 20px;\n }\n\n #${modalId} .form-label {\n display: block;\n font-size: 14px;\n font-weight: 500;\n color: ${colors.textSecondary};\n margin-bottom: 8px;\n }\n\n #${modalId} .form-input {\n width: 100%;\n padding: 12px 16px;\n font-size: 16px;\n background: ${colors.inputBg};\n border: 1px solid ${colors.inputBorder};\n border-radius: 8px;\n color: ${colors.inputText};\n outline: none;\n transition: border-color 0.2s;\n box-sizing: border-box;\n }\n\n #${modalId} .form-input:focus {\n border-color: ${colors.buttonPrimary};\n }\n\n #${modalId} .form-input::placeholder {\n color: ${colors.textMuted};\n }\n\n #${modalId} .form-hint {\n font-size: 12px;\n color: ${colors.textMuted};\n margin-top: 6px;\n }\n\n #${modalId} .temperature-range {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 16px;\n }\n\n #${modalId} .range-preview {\n margin-top: 20px;\n padding: 16px;\n background: rgba(76, 175, 80, 0.1);\n border: 1px dashed ${colors.success};\n border-radius: 8px;\n text-align: center;\n }\n\n #${modalId} .range-preview-label {\n font-size: 12px;\n color: ${colors.textMuted};\n margin-bottom: 8px;\n }\n\n #${modalId} .range-preview-value {\n font-size: 24px;\n font-weight: 600;\n color: ${colors.success};\n }\n\n #${modalId} .modal-footer {\n padding: 16px 24px;\n border-top: 1px solid ${colors.inputBorder};\n display: flex;\n justify-content: flex-end;\n gap: 12px;\n }\n\n #${modalId} .btn {\n padding: 10px 24px;\n font-size: 14px;\n font-weight: 500;\n border-radius: 8px;\n cursor: pointer;\n transition: all 0.2s;\n border: none;\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n #${modalId} .btn-secondary {\n background: ${colors.buttonSecondary};\n color: ${colors.textSecondary};\n border: 1px solid ${colors.inputBorder};\n }\n\n #${modalId} .btn-secondary:hover {\n background: ${colors.inputBg};\n }\n\n #${modalId} .btn-primary {\n background: ${colors.buttonPrimary};\n color: #fff;\n }\n\n #${modalId} .btn-primary:hover {\n background: ${colors.buttonPrimaryHover};\n }\n\n #${modalId} .btn-primary:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n #${modalId} .spinner {\n width: 16px;\n height: 16px;\n border: 2px solid rgba(255,255,255,0.3);\n border-top-color: #fff;\n border-radius: 50%;\n animation: spin 1s linear infinite;\n }\n\n #${modalId} .message {\n padding: 12px 16px;\n border-radius: 8px;\n margin-bottom: 16px;\n font-size: 14px;\n }\n\n #${modalId} .message-error {\n background: rgba(244, 67, 54, 0.1);\n border: 1px solid ${colors.error};\n color: ${colors.error};\n }\n\n #${modalId} .message-success {\n background: rgba(76, 175, 80, 0.1);\n border: 1px solid ${colors.success};\n color: ${colors.success};\n }\n\n #${modalId} .loading-overlay {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 48px;\n color: ${colors.textSecondary};\n }\n\n #${modalId} .loading-spinner {\n width: 40px;\n height: 40px;\n border: 3px solid ${colors.inputBorder};\n border-top-color: ${colors.buttonPrimary};\n border-radius: 50%;\n animation: spin 1s linear infinite;\n margin-bottom: 16px;\n }\n </style>\n\n <div id="${modalId}" class="modal-overlay">\n <div class="modal-content">\n <div class="modal-header">\n <h2 class="modal-title">\n <span>🌡️</span>\n Configurar Temperatura\n </h2>\n <button class="close-btn" id="${modalId}-close">×</button>\n </div>\n\n <div class="modal-body">\n ${state.isLoading?`\n <div class="loading-overlay">\n <div class="loading-spinner"></div>\n <div>Carregando configurações...</div>\n </div>\n `:`\n ${state.error?`\n <div class="message message-error">${state.error}</div>\n `:""}\n\n ${state.successMessage?`\n <div class="message message-success">${state.successMessage}</div>\n `:""}\n\n <div class="customer-info">\n <div class="customer-label">Shopping / Cliente</div>\n <div class="customer-name">${state.customerName||"Não identificado"}</div>\n </div>\n\n <div class="form-group">\n <label class="form-label">Faixa de Temperatura Ideal</label>\n <div class="temperature-range">\n <div>\n <input\n type="number"\n id="${modalId}-min"\n class="form-input"\n placeholder="Mínima"\n value="${minValue}"\n step="0.5"\n min="0"\n max="50"\n />\n <div class="form-hint">Temperatura mínima (°C)</div>\n </div>\n <div>\n <input\n type="number"\n id="${modalId}-max"\n class="form-input"\n placeholder="Máxima"\n value="${maxValue}"\n step="0.5"\n min="0"\n max="50"\n />\n <div class="form-hint">Temperatura máxima (°C)</div>\n </div>\n </div>\n </div>\n\n <div class="range-preview" id="${modalId}-preview">\n <div class="range-preview-label">Faixa configurada</div>\n <div class="range-preview-value" id="${modalId}-preview-value">\n ${minValue&&maxValue?`${minValue}°C - ${maxValue}°C`:"Não definida"}\n </div>\n </div>\n `}\n </div>\n\n ${!state.isLoading?`\n <div class="modal-footer">\n <button class="btn btn-secondary" id="${modalId}-cancel">Cancelar</button>\n <button class="btn btn-primary" id="${modalId}-save" ${state.isSaving?"disabled":""}>\n ${state.isSaving?'<div class="spinner"></div> Salvando...':"Salvar"}\n </button>\n </div>\n `:""}\n </div>\n </div>\n `;const closeBtn=document.getElementById(`${modalId}-close`);const cancelBtn=document.getElementById(`${modalId}-cancel`);const saveBtn=document.getElementById(`${modalId}-save`);const minInput=document.getElementById(`${modalId}-min`);const maxInput=document.getElementById(`${modalId}-max`);const previewValue=document.getElementById(`${modalId}-preview-value`);const overlay=document.getElementById(modalId);closeBtn?.addEventListener("click",onClose);cancelBtn?.addEventListener("click",onClose);overlay?.addEventListener("click",e=>{if(e.target===overlay)onClose()});const updatePreview=()=>{if(previewValue&&minInput&&maxInput){const min=minInput.value;const max=maxInput.value;if(min&&max){previewValue.textContent=`${min}°C - ${max}°C`}else{previewValue.textContent="Não definida"}}};minInput?.addEventListener("input",updatePreview);maxInput?.addEventListener("input",updatePreview);saveBtn?.addEventListener("click",async()=>{if(!minInput||!maxInput)return;const min=parseFloat(minInput.value);const max=parseFloat(maxInput.value);if(isNaN(min)||isNaN(max)){state.error="Por favor, preencha ambos os valores.";renderModal3(container,state,modalId,onClose,onSave);return}if(min>=max){state.error="A temperatura mínima deve ser menor que a máxima.";renderModal3(container,state,modalId,onClose,onSave);return}if(min<0||max>50){state.error="Os valores devem estar entre 0°C e 50°C.";renderModal3(container,state,modalId,onClose,onSave);return}await onSave(min,max)})}function openTemperatureSettingsModal(params){const modalId=`myio-temp-settings-${Date.now()}`;const state={customerId:params.customerId,customerName:params.customerName||"",token:params.token,theme:params.theme||"dark",minTemperature:null,maxTemperature:null,isLoading:true,isSaving:false,error:null,successMessage:null};const container=document.createElement("div");container.id=`${modalId}-container`;document.body.appendChild(container);const destroy=()=>{container.remove();params.onClose?.()};const handleSave=async(min,max)=>{state.isSaving=true;state.error=null;state.successMessage=null;renderModal3(container,state,modalId,destroy,handleSave);try{await saveCustomerAttributes(state.customerId,state.token,min,max);state.minTemperature=min;state.maxTemperature=max;state.isSaving=false;state.successMessage="Configurações salvas com sucesso!";renderModal3(container,state,modalId,destroy,handleSave);params.onSave?.({minTemperature:min,maxTemperature:max});setTimeout(()=>{destroy()},1500)}catch(error){state.isSaving=false;state.error=`Erro ao salvar: ${error.message}`;renderModal3(container,state,modalId,destroy,handleSave)}};renderModal3(container,state,modalId,destroy,handleSave);fetchCustomerAttributes(state.customerId,state.token).then(({minTemperature:minTemperature,maxTemperature:maxTemperature})=>{state.minTemperature=minTemperature;state.maxTemperature=maxTemperature;state.isLoading=false;renderModal3(container,state,modalId,destroy,handleSave)}).catch(error=>{state.isLoading=false;state.error=`Erro ao carregar: ${error.message}`;renderModal3(container,state,modalId,destroy,handleSave)});return{destroy:destroy}}var DEFAULT_BG_COLOR="#3e1a7d";var DEFAULT_TEXT_COLOR="white";var DEFAULT_BORDER_RADIUS="10px 10px 0 0";var EXPORT_FORMAT_LABELS={csv:"CSV",xls:"Excel (XLS)",pdf:"PDF"};var EXPORT_FORMAT_ICONS={csv:"📄",xls:"📊",pdf:"📑"};function createModalHeader(config){let currentTheme=config.theme||"light";let currentIsMaximized=config.isMaximized||false;let currentTitle=config.title;let themeBtn=null;let maximizeBtn=null;let closeBtn=null;let exportBtn=null;let exportDropdown=null;const cleanupHandlers=[];const handleThemeClick=()=>{currentTheme=currentTheme==="light"?"dark":"light";config.onThemeToggle?.(currentTheme);updateButtonIcons()};const handleMaximizeClick=()=>{currentIsMaximized=!currentIsMaximized;config.onMaximize?.(currentIsMaximized);updateButtonIcons()};const handleCloseClick=()=>{config.onClose?.()};const handleExportClick=format=>{config.onExport?.(format);if(exportDropdown){exportDropdown.style.display="none"}};function updateButtonIcons(){if(themeBtn){themeBtn.textContent=currentTheme==="dark"?"☀️":"🌙";themeBtn.title=currentTheme==="dark"?"Modo claro":"Modo escuro"}if(maximizeBtn){maximizeBtn.textContent=currentIsMaximized?"🗗":"🗖";maximizeBtn.title=currentIsMaximized?"Restaurar":"Maximizar"}}function getButtonStyle(){return`\n background: none;\n border: none;\n font-size: 16px;\n cursor: pointer;\n padding: 4px 8px;\n border-radius: 6px;\n color: rgba(255, 255, 255, 0.8);\n transition: background-color 0.2s, color 0.2s;\n `.replace(/\s+/g," ").trim()}function renderExportDropdown(){const formats=config.exportFormats||[];if(formats.length===0)return"";if(formats.length===1){return`\n <button id="${config.id}-export" title="Exportar ${EXPORT_FORMAT_LABELS[formats[0]]}" style="${getButtonStyle()}">\n 📥\n </button>\n `}return`\n <div style="position: relative; display: inline-block;">\n <button id="${config.id}-export-btn" title="Exportar" style="${getButtonStyle()}">\n 📥\n </button>\n <div id="${config.id}-export-dropdown" style="\n display: none;\n position: absolute;\n top: 100%;\n right: 0;\n background: white;\n border-radius: 6px;\n box-shadow: 0 4px 12px rgba(0,0,0,0.15);\n min-width: 140px;\n z-index: 10001;\n margin-top: 4px;\n overflow: hidden;\n ">\n ${formats.map(format=>`\n <button\n id="${config.id}-export-${format}"\n class="myio-export-option"\n data-format="${format}"\n style="\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n padding: 10px 14px;\n border: none;\n background: white;\n cursor: pointer;\n font-size: 13px;\n color: #333;\n text-align: left;\n transition: background-color 0.2s;\n "\n >\n ${EXPORT_FORMAT_ICONS[format]} ${EXPORT_FORMAT_LABELS[format]}\n </button>\n `).join("")}\n </div>\n </div>\n `}const instance={render(){const bgColor=config.backgroundColor||DEFAULT_BG_COLOR;const textColor=config.textColor||DEFAULT_TEXT_COLOR;const borderRadius=currentIsMaximized?"0":config.borderRadius||DEFAULT_BORDER_RADIUS;const showTheme=config.showThemeToggle!==false;const showMax=config.showMaximize!==false;const showClose=config.showClose!==false;const showExport=config.exportFormats&&config.exportFormats.length>0;const iconHtml=config.icon?`<span style="margin-right: 8px;">${config.icon}</span>`:"";const buttonStyle=getButtonStyle();return`\n <div class="myio-modal-header" style="\n padding: 4px 8px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n background: ${bgColor};\n color: ${textColor};\n border-radius: ${borderRadius};\n min-height: 20px;\n font-family: 'Roboto', Arial, sans-serif;\n ">\n <h2 id="${config.id}-header-title" style="\n margin: 6px;\n font-size: 18px;\n font-weight: 600;\n color: ${textColor};\n line-height: 2;\n display: flex;\n align-items: center;\n ">\n ${iconHtml}${currentTitle}\n </h2>\n <div style="display: flex; gap: 4px; align-items: center;">\n ${showExport?renderExportDropdown():""}\n ${showTheme?`\n <button id="${config.id}-theme-toggle" title="${currentTheme==="dark"?"Modo claro":"Modo escuro"}" style="${buttonStyle}">\n ${currentTheme==="dark"?"☀️":"🌙"}\n </button>\n `:""}\n ${showMax?`\n <button id="${config.id}-maximize" title="${currentIsMaximized?"Restaurar":"Maximizar"}" style="${buttonStyle}">\n ${currentIsMaximized?"🗗":"🗖"}\n </button>\n `:""}\n ${showClose?`\n <button id="${config.id}-close" title="Fechar" style="${buttonStyle}; font-size: 20px;">\n ×\n </button>\n `:""}\n </div>\n </div>\n `},attachListeners(){themeBtn=document.getElementById(`${config.id}-theme-toggle`);maximizeBtn=document.getElementById(`${config.id}-maximize`);closeBtn=document.getElementById(`${config.id}-close`);const singleExportBtn=document.getElementById(`${config.id}-export`);if(singleExportBtn&&config.exportFormats?.length===1){exportBtn=singleExportBtn;const format=config.exportFormats[0];const clickHandler=()=>handleExportClick(format);exportBtn.addEventListener("click",clickHandler);cleanupHandlers.push(()=>exportBtn?.removeEventListener("click",clickHandler));const enterHandler=()=>{exportBtn.style.backgroundColor="rgba(255, 255, 255, 0.2)"};const leaveHandler=()=>{exportBtn.style.backgroundColor="transparent"};exportBtn.addEventListener("mouseenter",enterHandler);exportBtn.addEventListener("mouseleave",leaveHandler);cleanupHandlers.push(()=>{exportBtn?.removeEventListener("mouseenter",enterHandler);exportBtn?.removeEventListener("mouseleave",leaveHandler)})}const exportDropdownBtn=document.getElementById(`${config.id}-export-btn`);exportDropdown=document.getElementById(`${config.id}-export-dropdown`);if(exportDropdownBtn&&exportDropdown){exportBtn=exportDropdownBtn;const toggleHandler=e=>{e.stopPropagation();if(exportDropdown){exportDropdown.style.display=exportDropdown.style.display==="none"?"block":"none"}};exportDropdownBtn.addEventListener("click",toggleHandler);cleanupHandlers.push(()=>exportDropdownBtn.removeEventListener("click",toggleHandler));const outsideClickHandler=e=>{if(exportDropdown&&!exportDropdown.contains(e.target)&&e.target!==exportDropdownBtn){exportDropdown.style.display="none"}};document.addEventListener("click",outsideClickHandler);cleanupHandlers.push(()=>document.removeEventListener("click",outsideClickHandler));config.exportFormats?.forEach(format=>{const btn=document.getElementById(`${config.id}-export-${format}`);if(btn){const clickHandler=()=>handleExportClick(format);btn.addEventListener("click",clickHandler);cleanupHandlers.push(()=>btn.removeEventListener("click",clickHandler));const enterHandler2=()=>{btn.style.backgroundColor="#f0f0f0"};const leaveHandler2=()=>{btn.style.backgroundColor="white"};btn.addEventListener("mouseenter",enterHandler2);btn.addEventListener("mouseleave",leaveHandler2);cleanupHandlers.push(()=>{btn.removeEventListener("mouseenter",enterHandler2);btn.removeEventListener("mouseleave",leaveHandler2)})}});const enterHandler=()=>{exportDropdownBtn.style.backgroundColor="rgba(255, 255, 255, 0.2)"};const leaveHandler=()=>{exportDropdownBtn.style.backgroundColor="transparent"};exportDropdownBtn.addEventListener("mouseenter",enterHandler);exportDropdownBtn.addEventListener("mouseleave",leaveHandler);cleanupHandlers.push(()=>{exportDropdownBtn.removeEventListener("mouseenter",enterHandler);exportDropdownBtn.removeEventListener("mouseleave",leaveHandler)})}if(themeBtn&&config.showThemeToggle!==false){themeBtn.addEventListener("click",handleThemeClick);cleanupHandlers.push(()=>themeBtn?.removeEventListener("click",handleThemeClick));const enterHandler=()=>{themeBtn.style.backgroundColor="rgba(255, 255, 255, 0.2)"};const leaveHandler=()=>{themeBtn.style.backgroundColor="transparent"};themeBtn.addEventListener("mouseenter",enterHandler);themeBtn.addEventListener("mouseleave",leaveHandler);cleanupHandlers.push(()=>{themeBtn?.removeEventListener("mouseenter",enterHandler);themeBtn?.removeEventListener("mouseleave",leaveHandler)})}if(maximizeBtn&&config.showMaximize!==false){maximizeBtn.addEventListener("click",handleMaximizeClick);cleanupHandlers.push(()=>maximizeBtn?.removeEventListener("click",handleMaximizeClick));const enterHandler=()=>{maximizeBtn.style.backgroundColor="rgba(255, 255, 255, 0.2)"};const leaveHandler=()=>{maximizeBtn.style.backgroundColor="transparent"};maximizeBtn.addEventListener("mouseenter",enterHandler);maximizeBtn.addEventListener("mouseleave",leaveHandler);cleanupHandlers.push(()=>{maximizeBtn?.removeEventListener("mouseenter",enterHandler);maximizeBtn?.removeEventListener("mouseleave",leaveHandler)})}if(closeBtn&&config.showClose!==false){closeBtn.addEventListener("click",handleCloseClick);cleanupHandlers.push(()=>closeBtn?.removeEventListener("click",handleCloseClick));const enterHandler=()=>{closeBtn.style.backgroundColor="rgba(255, 255, 255, 0.2)"};const leaveHandler=()=>{closeBtn.style.backgroundColor="transparent"};closeBtn.addEventListener("mouseenter",enterHandler);closeBtn.addEventListener("mouseleave",leaveHandler);cleanupHandlers.push(()=>{closeBtn?.removeEventListener("mouseenter",enterHandler);closeBtn?.removeEventListener("mouseleave",leaveHandler)})}},update(updates){if(updates.theme!==void 0){currentTheme=updates.theme;updateButtonIcons()}if(updates.isMaximized!==void 0){currentIsMaximized=updates.isMaximized;updateButtonIcons()}if(updates.title!==void 0){currentTitle=updates.title;const titleEl=document.getElementById(`${config.id}-header-title`);if(titleEl){const iconHtml=config.icon?`<span style="margin-right: 8px;">${config.icon}</span>`:"";titleEl.innerHTML=`${iconHtml}${currentTitle}`}}},getState(){return{theme:currentTheme,isMaximized:currentIsMaximized}},destroy(){cleanupHandlers.forEach(handler=>handler());cleanupHandlers.length=0;themeBtn=null;maximizeBtn=null;closeBtn=null;exportBtn=null;exportDropdown=null}};return instance}function getModalHeaderStyles(){return`\n .myio-modal-header button:hover {\n background-color: rgba(255, 255, 255, 0.2) !important;\n }\n .myio-modal-header button:active {\n background-color: rgba(255, 255, 255, 0.3) !important;\n }\n .myio-export-option:hover {\n background-color: #f0f0f0 !important;\n }\n `}var DEFAULT_COLORS={energy:{primary:"#2563eb",background:"rgba(37, 99, 235, 0.1)",gradient:["#f0fdf4","#dcfce7"],pointBackground:"#2563eb",pointBorder:"#ffffff"},water:{primary:"#0288d1",background:"rgba(2, 136, 209, 0.1)",gradient:["#f0f9ff","#bae6fd"],pointBackground:"#0288d1",pointBorder:"#ffffff"},gas:{primary:"#ea580c",background:"rgba(234, 88, 12, 0.1)",gradient:["#fff7ed","#fed7aa"],pointBackground:"#ea580c",pointBorder:"#ffffff"},temperature:{primary:"#dc2626",background:"rgba(220, 38, 38, 0.1)",gradient:["#fef2f2","#fecaca"],pointBackground:"#dc2626",pointBorder:"#ffffff"}};var THEME_COLORS={light:{chartBackground:"#ffffff",text:"#1f2937",textMuted:"#6b7280",grid:"rgba(0, 0, 0, 0.1)",border:"#e5e7eb",tooltipBackground:"#ffffff",tooltipText:"#1f2937"},dark:{chartBackground:"#1f2937",text:"#f9fafb",textMuted:"#9ca3af",grid:"rgba(255, 255, 255, 0.1)",border:"#374151",tooltipBackground:"#374151",tooltipText:"#f9fafb"}};var DEFAULT_CONFIG={defaultPeriod:7,defaultChartType:"line",defaultVizMode:"total",defaultTheme:"light",cacheTTL:3e5,decimalPlaces:1,lineTension:.4,pointRadius:4,borderWidth:2,fill:true,showLegend:false,enableExport:true};function createConsumption7DaysChart(config){let chartInstance=null;let cachedData=null;let currentPeriod=config.defaultPeriod??DEFAULT_CONFIG.defaultPeriod;let currentChartType=config.defaultChartType??DEFAULT_CONFIG.defaultChartType;let currentVizMode=config.defaultVizMode??DEFAULT_CONFIG.defaultVizMode;let currentTheme=config.theme??DEFAULT_CONFIG.defaultTheme;let currentIdealRange=config.idealRange??null;let isRendered=false;let autoRefreshTimer=null;const colors={...DEFAULT_COLORS[config.domain]??DEFAULT_COLORS.energy,...config.colors};function $id(id){if(config.$container&&config.$container[0]){return config.$container[0].querySelector(`#${id}`)}return document.getElementById(id)}function log(level,...args){const prefix=`[${config.domain.toUpperCase()}]`;console[level](prefix,...args)}function calculateYAxisMax(values){const maxValue=Math.max(...values,0);if(config.domain==="temperature"){const tempConfig=config.temperatureConfig;if(tempConfig?.clampRange){return tempConfig.clampRange.max}const maxWithThreshold=Math.max(maxValue,tempConfig?.maxThreshold?.value??0,tempConfig?.idealRange?.max??0);return Math.ceil(maxWithThreshold+5)}if(maxValue===0){return config.thresholdForLargeUnit?config.thresholdForLargeUnit/2:500}let roundTo;if(config.thresholdForLargeUnit&&maxValue>=config.thresholdForLargeUnit){roundTo=config.thresholdForLargeUnit/10}else if(maxValue>=1e3){roundTo=100}else if(maxValue>=100){roundTo=50}else if(maxValue>=10){roundTo=10}else{roundTo=5}return Math.ceil(maxValue*1.1/roundTo)*roundTo}function calculateYAxisMin(values){if(config.domain!=="temperature"){return 0}const tempConfig=config.temperatureConfig;if(tempConfig?.clampRange){return tempConfig.clampRange.min}const minValue=Math.min(...values);const minWithThreshold=Math.min(minValue,tempConfig?.minThreshold?.value??minValue,tempConfig?.idealRange?.min??minValue);return Math.floor(minWithThreshold-5)}function buildTemperatureAnnotations(){const tempConfig=config.temperatureConfig;if(!tempConfig||config.domain!=="temperature"){return{}}const annotations={};const createLineAnnotation=(line,id)=>{const borderDash=line.lineStyle==="dashed"?[6,6]:line.lineStyle==="dotted"?[2,2]:[];return{type:"line",yMin:line.value,yMax:line.value,borderColor:line.color,borderWidth:line.lineWidth??2,borderDash:borderDash,label:{display:true,content:line.label,position:"end",backgroundColor:line.color,color:"#fff",font:{size:10,weight:"bold"},padding:{x:4,y:2}}}};if(tempConfig.minThreshold){annotations["minThreshold"]=createLineAnnotation(tempConfig.minThreshold)}if(tempConfig.maxThreshold){annotations["maxThreshold"]=createLineAnnotation(tempConfig.maxThreshold)}if(tempConfig.idealRange){annotations["idealRange"]={type:"box",yMin:tempConfig.idealRange.min,yMax:tempConfig.idealRange.max,backgroundColor:tempConfig.idealRange.color,borderWidth:0,label:tempConfig.idealRange.label?{display:true,content:tempConfig.idealRange.label,position:{x:"start",y:"center"},color:"#666",font:{size:10}}:void 0}}return annotations}function buildIdealRangeAnnotation(){if(!currentIdealRange){return{}}const{min:min,max:max,enabled:enabled=true}=currentIdealRange;if(!enabled||min===0&&max===0||min>=max){return{}}const defaultColors={temperature:{bg:"rgba(34, 197, 94, 0.15)",border:"rgba(34, 197, 94, 0.4)"},energy:{bg:"rgba(37, 99, 235, 0.1)",border:"rgba(37, 99, 235, 0.3)"},water:{bg:"rgba(2, 136, 209, 0.1)",border:"rgba(2, 136, 209, 0.3)"},gas:{bg:"rgba(234, 88, 12, 0.1)",border:"rgba(234, 88, 12, 0.3)"}};const domainDefaults=defaultColors[config.domain]||defaultColors.energy;return{idealRangeBox:{type:"box",yMin:min,yMax:max,backgroundColor:currentIdealRange.color||domainDefaults.bg,borderColor:currentIdealRange.borderColor||domainDefaults.border,borderWidth:1,label:currentIdealRange.label?{display:true,content:currentIdealRange.label,position:{x:"start",y:"center"},color:"#666",font:{size:10,style:"italic"},backgroundColor:"rgba(255, 255, 255, 0.8)",padding:{x:4,y:2}}:void 0}}}function formatValue(value,includeUnit=true){const decimals=config.decimalPlaces??DEFAULT_CONFIG.decimalPlaces;if(config.unitLarge&&config.thresholdForLargeUnit&&value>=config.thresholdForLargeUnit){const converted=value/config.thresholdForLargeUnit;return includeUnit?`${converted.toFixed(decimals)} ${config.unitLarge}`:converted.toFixed(decimals)}return includeUnit?`${value.toFixed(decimals)} ${config.unit}`:value.toFixed(decimals)}function formatTickValue(value){if(config.unitLarge&&config.thresholdForLargeUnit&&value>=config.thresholdForLargeUnit){return`${(value/config.thresholdForLargeUnit).toFixed(1)}`}return value.toFixed(0)}function buildChartConfig(data){const yAxisMax=calculateYAxisMax(data.dailyTotals);const yAxisMin=calculateYAxisMin(data.dailyTotals);const tension=config.lineTension??DEFAULT_CONFIG.lineTension;const pointRadius=config.pointRadius??DEFAULT_CONFIG.pointRadius;const borderWidth=config.borderWidth??DEFAULT_CONFIG.borderWidth;const fill=config.fill??DEFAULT_CONFIG.fill;const showLegend=config.showLegend??DEFAULT_CONFIG.showLegend;const themeColors=THEME_COLORS[currentTheme];const isTemperature=config.domain==="temperature";let datasets;if(currentVizMode==="separate"&&data.shoppingData&&data.shoppingNames){const shoppingColors=colors.shoppingColors||["#2563eb","#16a34a","#ea580c","#dc2626","#8b5cf6","#0891b2","#65a30d","#d97706","#be185d","#0d9488"];datasets=Object.entries(data.shoppingData).map(([shoppingId,values],index)=>({label:data.shoppingNames?.[shoppingId]||shoppingId,data:values,borderColor:shoppingColors[index%shoppingColors.length],backgroundColor:currentChartType==="line"?`${shoppingColors[index%shoppingColors.length]}20`:shoppingColors[index%shoppingColors.length],fill:currentChartType==="line"&&fill,tension:tension,borderWidth:borderWidth,pointRadius:currentChartType==="line"?pointRadius:0,pointBackgroundColor:shoppingColors[index%shoppingColors.length],pointBorderColor:"#fff",pointBorderWidth:2}))}else{const datasetLabel=isTemperature?`Temperatura (${config.unit})`:`Consumo (${config.unit})`;datasets=[{label:datasetLabel,data:data.dailyTotals,borderColor:colors.primary,backgroundColor:currentChartType==="line"?colors.background:colors.primary,fill:currentChartType==="line"&&fill,tension:tension,borderWidth:borderWidth,pointRadius:currentChartType==="line"?pointRadius:0,pointBackgroundColor:colors.pointBackground||colors.primary,pointBorderColor:colors.pointBorder||"#fff",pointBorderWidth:2,borderRadius:currentChartType==="bar"?4:0}]}const temperatureAnnotations=buildTemperatureAnnotations();const idealRangeAnnotations=buildIdealRangeAnnotation();const allAnnotations={...temperatureAnnotations,...idealRangeAnnotations};const yAxisLabel=config.unitLarge&&config.thresholdForLargeUnit&&yAxisMax>=config.thresholdForLargeUnit?config.unitLarge:config.unit;return{type:currentChartType,data:{labels:data.labels,datasets:datasets},options:{responsive:true,maintainAspectRatio:false,animation:false,plugins:{legend:{display:showLegend||currentVizMode==="separate",position:"bottom",labels:{color:themeColors.text}},tooltip:{backgroundColor:themeColors.tooltipBackground,titleColor:themeColors.tooltipText,bodyColor:themeColors.tooltipText,borderColor:themeColors.border,borderWidth:1,callbacks:{label:function(context){const value=context.parsed.y||0;const label=context.dataset.label||"";return`${label}: ${formatValue(value)}`}}},annotation:Object.keys(allAnnotations).length>0?{annotations:allAnnotations}:void 0},scales:{y:{beginAtZero:!isTemperature,min:yAxisMin,max:yAxisMax,grid:{color:themeColors.grid},title:{display:true,text:yAxisLabel,font:{size:12},color:themeColors.text},ticks:{font:{size:11},color:themeColors.textMuted,callback:function(value){return formatTickValue(value)}}},x:{grid:{color:themeColors.grid},ticks:{font:{size:11},color:themeColors.textMuted}}}}}}function validateChartJs(){if(typeof Chart==="undefined"){log("error","Chart.js not loaded. Cannot initialize chart.");config.onError?.(new Error("Chart.js not loaded"));return false}return true}function validateCanvas(){const canvas=$id(config.containerId);if(!canvas){log("error",`Canvas #${config.containerId} not found`);config.onError?.(new Error(`Canvas #${config.containerId} not found`));return null}return canvas}function setupAutoRefresh(){if(config.autoRefreshInterval&&config.autoRefreshInterval>0){if(autoRefreshTimer){clearInterval(autoRefreshTimer)}autoRefreshTimer=setInterval(async()=>{log("log","Auto-refreshing data...");await instance.refresh(true)},config.autoRefreshInterval)}}function cleanupAutoRefresh(){if(autoRefreshTimer){clearInterval(autoRefreshTimer);autoRefreshTimer=null}}function setupButtonHandlers(){if(config.settingsButtonId&&config.onSettingsClick){const settingsBtn=$id(config.settingsButtonId);if(settingsBtn){settingsBtn.addEventListener("click",()=>{log("log","Settings button clicked");config.onSettingsClick?.()});log("log","Settings button handler attached")}}if(config.maximizeButtonId&&config.onMaximizeClick){const maximizeBtn=$id(config.maximizeButtonId);if(maximizeBtn){maximizeBtn.addEventListener("click",()=>{log("log","Maximize button clicked");config.onMaximizeClick?.()});log("log","Maximize button handler attached")}}const enableExport=config.enableExport??DEFAULT_CONFIG.enableExport;if(enableExport&&config.exportButtonId){const exportBtn=$id(config.exportButtonId);if(exportBtn){exportBtn.addEventListener("click",()=>{log("log","Export button clicked");if(config.onExportCSV&&cachedData){config.onExportCSV(cachedData)}else{instance.exportCSV()}});log("log","Export button handler attached")}}}function generateCSVContent(data){const rows=[];const decimals=config.decimalPlaces??DEFAULT_CONFIG.decimalPlaces;if(currentVizMode==="separate"&&data.shoppingData&&data.shoppingNames){const shoppingHeaders=Object.keys(data.shoppingData).map(id=>data.shoppingNames?.[id]||id);rows.push(["Data",...shoppingHeaders,"Total"].join(";"));data.labels.forEach((label,index)=>{const shoppingValues=Object.keys(data.shoppingData).map(id=>data.shoppingData[id][index].toFixed(decimals));rows.push([label,...shoppingValues,data.dailyTotals[index].toFixed(decimals)].join(";"))})}else{rows.push(["Data",`Consumo (${config.unit})`].join(";"));data.labels.forEach((label,index)=>{rows.push([label,data.dailyTotals[index].toFixed(decimals)].join(";"))})}const total=data.dailyTotals.reduce((sum,v)=>sum+v,0);const avg=total/data.dailyTotals.length;rows.push("");rows.push(["Total",total.toFixed(decimals)].join(";"));rows.push(["Média",avg.toFixed(decimals)].join(";"));return rows.join("\n")}function downloadCSV(content,filename){const BOM="\ufeff";const blob=new Blob([BOM+content],{type:"text/csv;charset=utf-8"});const url=URL.createObjectURL(blob);const link=document.createElement("a");link.href=url;link.download=`${filename}.csv`;document.body.appendChild(link);link.click();document.body.removeChild(link);URL.revokeObjectURL(url);log("log",`CSV exported: ${filename}.csv`)}function updateTitle(){if(config.titleElementId){const titleEl=$id(config.titleElementId);if(titleEl){if(currentPeriod===0){titleEl.textContent=`Consumo - Período Personalizado`}else{titleEl.textContent=`Consumo dos últimos ${currentPeriod} dias`}}}}const instance={async render(){log("log","Rendering chart...");if(!validateChartJs())return;const canvas=validateCanvas();if(!canvas)return;try{log("log",`Fetching ${currentPeriod} days of data...`);cachedData=await config.fetchData(currentPeriod);cachedData.fetchTimestamp=Date.now();if(config.onBeforeRender){cachedData=config.onBeforeRender(cachedData)}if(chartInstance){chartInstance.destroy();chartInstance=null}const ctx=canvas.getContext("2d");const chartConfig=buildChartConfig(cachedData);chartInstance=new Chart(ctx,chartConfig);isRendered=true;const yAxisMax=calculateYAxisMax(cachedData.dailyTotals);log("log",`Chart initialized with yAxisMax: ${yAxisMax}`);config.onDataLoaded?.(cachedData);config.onAfterRender?.(chartInstance);setupButtonHandlers();updateTitle();setupAutoRefresh()}catch(error){log("error","Failed to render chart:",error);config.onError?.(error instanceof Error?error:new Error(String(error)))}},async update(data){if(data){cachedData=data;cachedData.fetchTimestamp=Date.now()}if(!chartInstance||!cachedData){log("warn","Cannot update: chart not initialized or no data");return}let renderData=cachedData;if(config.onBeforeRender){renderData=config.onBeforeRender(cachedData)}const chartConfig=buildChartConfig(renderData);chartInstance.data=chartConfig.data;chartInstance.options=chartConfig.options;chartInstance.update("none");log("log","Chart updated")},setChartType(type){if(currentChartType===type)return;log("log",`Changing chart type to: ${type}`);currentChartType=type;if(cachedData&&chartInstance){const canvas=validateCanvas();if(canvas){chartInstance.destroy();const ctx=canvas.getContext("2d");chartInstance=new Chart(ctx,buildChartConfig(cachedData))}}},setVizMode(mode){if(currentVizMode===mode)return;log("log",`Changing viz mode to: ${mode}`);currentVizMode=mode;if(cachedData){this.update()}},async setPeriod(days){if(currentPeriod===days)return;log("log",`Changing period to: ${days} days`);currentPeriod=days;updateTitle();await this.refresh(true)},async refresh(forceRefresh=false){if(!forceRefresh&&cachedData?.fetchTimestamp){const age=Date.now()-cachedData.fetchTimestamp;const ttl=config.cacheTTL??DEFAULT_CONFIG.cacheTTL;if(age<ttl){log("log",`Using cached data (age: ${Math.round(age/1e3)}s)`);return}}log("log","Refreshing data...");await this.render()},destroy(){log("log","Destroying chart...");cleanupAutoRefresh();if(chartInstance){chartInstance.destroy();chartInstance=null}cachedData=null;isRendered=false},getChartInstance(){return chartInstance},getCachedData(){return cachedData},getState(){return{period:currentPeriod,chartType:currentChartType,vizMode:currentVizMode,theme:currentTheme,isRendered:isRendered}},exportCSV(filename){if(!cachedData){log("warn","Cannot export: no data available");return}const defaultFilename=config.exportFilename||`${config.domain}-consumo-${(new Date).toISOString().slice(0,10)}`;const csvContent=generateCSVContent(cachedData);downloadCSV(csvContent,filename||defaultFilename)},setTheme(theme){if(currentTheme===theme)return;log("log",`Changing theme to: ${theme}`);currentTheme=theme;if(cachedData&&chartInstance){const canvas=validateCanvas();if(canvas){chartInstance.destroy();const ctx=canvas.getContext("2d");chartInstance=new Chart(ctx,buildChartConfig(cachedData))}}},setIdealRange(range){const rangeChanged=JSON.stringify(currentIdealRange)!==JSON.stringify(range);if(!rangeChanged)return;if(range){log("log",`Setting ideal range: ${range.min} - ${range.max}`)}else{log("log","Clearing ideal range")}currentIdealRange=range;if(cachedData&&chartInstance){const canvas=validateCanvas();if(canvas){chartInstance.destroy();const ctx=canvas.getContext("2d");chartInstance=new Chart(ctx,buildChartConfig(cachedData))}}},getIdealRange(){return currentIdealRange}};return instance}var DOMAIN_CONFIG3={energy:{name:"Energia",icon:"⚡"},water:{name:"Água",icon:"💧"},gas:{name:"Gás",icon:"🔥"},temperature:{name:"Temperatura",icon:"🌡️"}};function createConsumptionModal(config){const modalId=`myio-consumption-modal-${Date.now()}`;let modalElement=null;let chartInstance=null;let headerInstance=null;let currentTheme=config.theme??"light";let currentChartType=config.defaultChartType??"line";let currentVizMode=config.defaultVizMode??"total";let isMaximized=false;const domainCfg=DOMAIN_CONFIG3[config.domain]||{name:config.domain,icon:"📊"};const title=config.title||`${domainCfg.name} - Histórico de Consumo`;function getThemeColors3(){return THEME_COLORS[currentTheme]}const consolidadoIcon=`<svg viewBox="0 0 16 16" fill="currentColor" style="width:14px;height:14px;pointer-events:none"><rect x="3" y="3" width="10" height="10" rx="2"/></svg>`;const porShoppingIcon=`<svg viewBox="0 0 16 16" fill="currentColor" style="width:14px;height:14px;pointer-events:none"><rect x="1" y="1" width="6" height="6" rx="1"/><rect x="9" y="1" width="6" height="6" rx="1"/><rect x="1" y="9" width="6" height="6" rx="1"/><rect x="9" y="9" width="6" height="6" rx="1"/></svg>`;const lineChartIcon=`<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:14px;height:14px;pointer-events:none"><polyline points="2,12 5,7 9,9 14,3"/></svg>`;const barChartIcon=`<svg viewBox="0 0 16 16" fill="currentColor" style="width:14px;height:14px;pointer-events:none"><rect x="1" y="9" width="3" height="6" rx="0.5"/><rect x="6" y="5" width="3" height="10" rx="0.5"/><rect x="11" y="7" width="3" height="8" rx="0.5"/></svg>`;const showSettingsButton=config.showSettingsButton??true;function renderModal4(){const colors=getThemeColors3();const exportFormats=config.exportFormats||["csv"];headerInstance=createModalHeader({id:modalId,title:title,icon:domainCfg.icon,theme:currentTheme,isMaximized:isMaximized,exportFormats:exportFormats,onExport:format=>{if(config.onExport){config.onExport(format)}else{if(format==="csv"){chartInstance?.exportCSV()}else{console.warn(`[ConsumptionModal] Export format "${format}" requires custom onExport handler`)}}},onThemeToggle:theme=>{currentTheme=theme;chartInstance?.setTheme(currentTheme);updateModal()},onMaximize:maximized=>{isMaximized=maximized;updateModal()},onClose:()=>{instance.close()}});const btnBaseStyle=`\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n padding: 6px 12px;\n border: none;\n border-radius: 6px;\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s;\n white-space: nowrap;\n `.replace(/\s+/g," ").trim();const tabBgColor=currentTheme==="dark"?"#4b5563":"#e5e7eb";const activeColor="#3e1a7d";const inactiveTextColor=colors.text;return`\n <style>\n .myio-modal-tab-btn {\n ${btnBaseStyle}\n }\n .myio-modal-tab-btn:hover {\n opacity: 0.85;\n }\n .myio-modal-tab-btn.active {\n background: ${activeColor} !important;\n color: white !important;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);\n }\n .myio-modal-tab-btn svg {\n pointer-events: none;\n }\n .myio-modal-settings-btn {\n background: transparent;\n border: 1px solid ${colors.border};\n font-size: 16px;\n cursor: pointer;\n padding: 6px 10px;\n border-radius: 6px;\n transition: all 0.2s;\n color: ${colors.text};\n }\n .myio-modal-settings-btn:hover {\n background: ${activeColor};\n border-color: ${activeColor};\n color: white;\n }\n </style>\n <div class="myio-consumption-modal-overlay" style="\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background: rgba(0, 0, 0, 0.5);\n backdrop-filter: blur(2px);\n z-index: 99998;\n display: flex;\n justify-content: center;\n align-items: center;\n ">\n <div class="myio-consumption-modal-content" style="\n background: ${colors.chartBackground};\n border-radius: ${isMaximized?"0":"10px"};\n width: ${isMaximized?"100%":"95%"};\n max-width: ${isMaximized?"100%":"1200px"};\n height: ${isMaximized?"100%":"85vh"};\n display: flex;\n flex-direction: column;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n overflow: hidden;\n ">\n \x3c!-- MyIO Premium Header (using ModalHeader component) --\x3e\n ${headerInstance.render()}\n\n \x3c!-- Controls Bar --\x3e\n <div class="myio-consumption-modal-controls" style="\n display: flex;\n gap: 12px;\n padding: 12px 16px;\n background: ${currentTheme==="dark"?"#374151":"#f7f7f7"};\n border-bottom: 1px solid ${colors.border};\n align-items: center;\n flex-wrap: wrap;\n ">\n \x3c!-- Settings Button --\x3e\n ${showSettingsButton?`\n <button id="${modalId}-settings-btn" class="myio-modal-settings-btn" title="Configurações">⚙️</button>\n `:""}\n\n \x3c!-- Viz Mode Tabs --\x3e\n <div style="display: flex; gap: 2px; background: ${tabBgColor}; border-radius: 8px; padding: 3px;">\n <button id="${modalId}-viz-total" class="myio-modal-tab-btn ${currentVizMode==="total"?"active":""}"\n data-viz="total" title="Consolidado"\n style="background: ${currentVizMode==="total"?activeColor:"transparent"}; color: ${currentVizMode==="total"?"white":inactiveTextColor};">\n ${consolidadoIcon}\n </button>\n <button id="${modalId}-viz-separate" class="myio-modal-tab-btn ${currentVizMode==="separate"?"active":""}"\n data-viz="separate" title="Por Shopping"\n style="background: ${currentVizMode==="separate"?activeColor:"transparent"}; color: ${currentVizMode==="separate"?"white":inactiveTextColor};">\n ${porShoppingIcon}\n </button>\n </div>\n\n \x3c!-- Chart Type Tabs --\x3e\n <div style="display: flex; gap: 2px; background: ${tabBgColor}; border-radius: 8px; padding: 3px;">\n <button id="${modalId}-type-line" class="myio-modal-tab-btn ${currentChartType==="line"?"active":""}"\n data-type="line" title="Gráfico de Linhas"\n style="background: ${currentChartType==="line"?activeColor:"transparent"}; color: ${currentChartType==="line"?"white":inactiveTextColor};">\n ${lineChartIcon}\n </button>\n <button id="${modalId}-type-bar" class="myio-modal-tab-btn ${currentChartType==="bar"?"active":""}"\n data-type="bar" title="Gráfico de Barras"\n style="background: ${currentChartType==="bar"?activeColor:"transparent"}; color: ${currentChartType==="bar"?"white":inactiveTextColor};">\n ${barChartIcon}\n </button>\n </div>\n </div>\n\n \x3c!-- Chart Container --\x3e\n <div style="\n flex: 1;\n padding: 16px;\n min-height: 0;\n position: relative;\n background: ${colors.chartBackground};\n ">\n <canvas id="${modalId}-chart" style="width: 100%; height: 100%;"></canvas>\n </div>\n </div>\n </div>\n `}function setupListeners(){if(!modalElement)return;headerInstance?.attachListeners();if(showSettingsButton){document.getElementById(`${modalId}-settings-btn`)?.addEventListener("click",()=>{config.onSettingsClick?.()})}document.getElementById(`${modalId}-viz-total`)?.addEventListener("click",()=>{currentVizMode="total";chartInstance?.setVizMode("total");updateControlStyles()});document.getElementById(`${modalId}-viz-separate`)?.addEventListener("click",()=>{currentVizMode="separate";chartInstance?.setVizMode("separate");updateControlStyles()});document.getElementById(`${modalId}-type-line`)?.addEventListener("click",()=>{currentChartType="line";chartInstance?.setChartType("line");updateControlStyles()});document.getElementById(`${modalId}-type-bar`)?.addEventListener("click",()=>{currentChartType="bar";chartInstance?.setChartType("bar");updateControlStyles()});modalElement.querySelector(".myio-consumption-modal-overlay")?.addEventListener("click",e=>{if(e.target.classList.contains("myio-consumption-modal-overlay")){instance.close()}});const handleKeydown=e=>{if(e.key==="Escape"){instance.close()}};document.addEventListener("keydown",handleKeydown);modalElement.__handleKeydown=handleKeydown}function updateControlStyles(){const colors=getThemeColors3();const activeColor="#3e1a7d";const vizTotalBtn=document.getElementById(`${modalId}-viz-total`);const vizSeparateBtn=document.getElementById(`${modalId}-viz-separate`);if(vizTotalBtn){vizTotalBtn.classList.toggle("active",currentVizMode==="total");vizTotalBtn.style.background=currentVizMode==="total"?activeColor:"transparent";vizTotalBtn.style.color=currentVizMode==="total"?"white":colors.text}if(vizSeparateBtn){vizSeparateBtn.classList.toggle("active",currentVizMode==="separate");vizSeparateBtn.style.background=currentVizMode==="separate"?activeColor:"transparent";vizSeparateBtn.style.color=currentVizMode==="separate"?"white":colors.text}const typeLineBtn=document.getElementById(`${modalId}-type-line`);const typeBarBtn=document.getElementById(`${modalId}-type-bar`);if(typeLineBtn){typeLineBtn.classList.toggle("active",currentChartType==="line");typeLineBtn.style.background=currentChartType==="line"?activeColor:"transparent";typeLineBtn.style.color=currentChartType==="line"?"white":colors.text}if(typeBarBtn){typeBarBtn.classList.toggle("active",currentChartType==="bar");typeBarBtn.style.background=currentChartType==="bar"?activeColor:"transparent";typeBarBtn.style.color=currentChartType==="bar"?"white":colors.text}}function updateModal(){if(!modalElement)return;const cachedData=chartInstance?.getCachedData();headerInstance?.destroy();chartInstance?.destroy();modalElement.innerHTML=renderModal4();setupListeners();if(cachedData){chartInstance=createConsumption7DaysChart({...config,containerId:`${modalId}-chart`,theme:currentTheme,defaultChartType:currentChartType,defaultVizMode:currentVizMode});chartInstance.update(cachedData)}}const instance={async open(){modalElement=document.createElement("div");modalElement.id=modalId;modalElement.innerHTML=renderModal4();const container=config.container||document.body;container.appendChild(modalElement);setupListeners();chartInstance=createConsumption7DaysChart({...config,containerId:`${modalId}-chart`,theme:currentTheme,defaultChartType:currentChartType,defaultVizMode:currentVizMode});await chartInstance.render()},close(){if(modalElement){const handleKeydown=modalElement.__handleKeydown;if(handleKeydown){document.removeEventListener("keydown",handleKeydown)}headerInstance?.destroy();headerInstance=null;chartInstance?.destroy();chartInstance=null;modalElement.remove();modalElement=null;config.onClose?.()}},getChart(){return chartInstance},destroy(){instance.close()}};return instance}var DOMAIN_CONFIG4={energy:{name:"Energia",icon:"⚡",color:"#6c2fbf",colors:["#2563eb","#16a34a","#8b5cf6","#ea580c","#dc2626"]},water:{name:"Água",icon:"💧",color:"#0288d1",colors:["#0288d1","#06b6d4","#0891b2","#22d3ee","#67e8f9"]},gas:{name:"Gás",icon:"🔥",color:"#ea580c",colors:["#ea580c","#f97316","#fb923c","#fdba74","#fed7aa"]},temperature:{name:"Temperatura",icon:"🌡️",color:"#e65100",colors:["#dc2626","#059669","#0ea5e9","#f59e0b","#8b5cf6"]}};function getWidgetStyles(theme,primaryColor){const colors=THEME_COLORS[theme];return`\n .myio-chart-widget {\n font-family: Inter, system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif;\n background: ${colors.chartBackground};\n border: 1px solid ${colors.border};\n border-radius: 16px;\n overflow: hidden;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);\n }\n\n .myio-chart-widget.dark {\n background: ${THEME_COLORS.dark.chartBackground};\n border-color: ${THEME_COLORS.dark.border};\n }\n\n .myio-chart-widget-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 16px 20px;\n border-bottom: 1px solid ${colors.border};\n flex-wrap: wrap;\n gap: 12px;\n }\n\n .myio-chart-widget-title-group {\n display: flex;\n align-items: center;\n gap: 10px;\n }\n\n .myio-chart-widget-title {\n margin: 0;\n font-size: 14px;\n font-weight: 600;\n color: ${colors.text};\n }\n\n .myio-chart-widget-tabs {\n display: flex;\n gap: 2px;\n background: ${theme==="dark"?"#374151":"#f3f4f6"};\n padding: 3px;\n border-radius: 8px;\n }\n\n .myio-chart-widget-tab {\n padding: 6px 14px;\n font-size: 12px;\n font-weight: 500;\n border: none;\n background: transparent;\n color: ${colors.textMuted};\n cursor: pointer;\n border-radius: 6px;\n transition: all 0.2s;\n white-space: nowrap;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 4px;\n }\n\n .myio-chart-widget-tab.icon-only {\n padding: 6px 10px;\n }\n\n .myio-chart-widget-tab svg {\n width: 16px;\n height: 16px;\n pointer-events: none;\n }\n\n .myio-chart-widget-tab:hover {\n color: ${colors.text};\n background: ${theme==="dark"?"rgba(255,255,255,0.1)":"rgba(0,0,0,0.05)"};\n }\n\n .myio-chart-widget-tab.active {\n background: ${primaryColor};\n color: white;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);\n }\n\n .myio-chart-widget-btn {\n background: transparent;\n border: 1px solid ${colors.border};\n font-size: 16px;\n cursor: pointer;\n padding: 6px 10px;\n border-radius: 6px;\n transition: all 0.2s;\n color: ${colors.text};\n }\n\n .myio-chart-widget-btn:hover {\n background: ${primaryColor};\n border-color: ${primaryColor};\n color: white;\n }\n\n .myio-chart-widget-body {\n position: relative;\n padding: 16px 20px;\n }\n\n .myio-chart-widget-canvas-container {\n position: relative;\n width: 100%;\n }\n\n .myio-chart-widget-loading {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(255, 255, 255, 0.9);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10;\n border-radius: 8px;\n }\n\n .myio-chart-widget.dark .myio-chart-widget-loading {\n background: rgba(31, 41, 55, 0.9);\n }\n\n .myio-chart-widget-spinner {\n width: 32px;\n height: 32px;\n border: 3px solid ${colors.border};\n border-top-color: ${primaryColor};\n border-radius: 50%;\n animation: myio-spin 1s linear infinite;\n }\n\n @keyframes myio-spin {\n to { transform: rotate(360deg); }\n }\n\n .myio-chart-widget-footer {\n display: flex;\n justify-content: space-around;\n padding: 16px 20px;\n border-top: 1px solid ${colors.border};\n gap: 16px;\n flex-wrap: wrap;\n }\n\n .myio-chart-widget-stat {\n display: flex;\n flex-direction: column;\n align-items: center;\n text-align: center;\n min-width: 100px;\n }\n\n .myio-chart-widget-stat-label {\n font-size: 11px;\n font-weight: 500;\n color: ${colors.textMuted};\n text-transform: uppercase;\n letter-spacing: 0.5px;\n margin-bottom: 4px;\n }\n\n .myio-chart-widget-stat-value {\n font-size: 20px;\n font-weight: 700;\n color: ${colors.text};\n }\n\n .myio-chart-widget-stat-value.primary {\n color: ${primaryColor};\n }\n\n .myio-chart-widget-stat-sub {\n font-size: 11px;\n color: ${colors.textMuted};\n margin-top: 2px;\n }\n\n /* Settings Modal Overlay */\n .myio-settings-overlay {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.6);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 99999;\n backdrop-filter: blur(4px);\n }\n\n .myio-settings-overlay.hidden {\n display: none;\n }\n\n .myio-settings-card {\n background: ${colors.chartBackground};\n border-radius: 10px;\n width: 90%;\n max-width: 860px;\n max-height: 90vh;\n display: flex;\n flex-direction: column;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);\n overflow: hidden;\n }\n\n .myio-settings-card .myio-modal-header {\n border-radius: 10px 10px 0 0;\n }\n\n .myio-settings-body {\n padding: 20px;\n overflow-y: auto;\n display: flex;\n flex-direction: column;\n gap: 12px;\n }\n\n .myio-settings-section {\n background: ${theme==="dark"?"rgba(255,255,255,0.05)":"#f8fafc"};\n border-radius: 10px;\n padding: 16px;\n border: 1px solid ${theme==="dark"?"rgba(255,255,255,0.1)":"#e2e8f0"};\n }\n\n .myio-settings-section-label {\n font-size: 13px;\n font-weight: 600;\n color: ${colors.text};\n margin-bottom: 12px;\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .myio-settings-row {\n display: flex;\n gap: 16px;\n flex-wrap: wrap;\n align-items: flex-end;\n }\n\n .myio-settings-field {\n display: flex;\n flex-direction: column;\n gap: 6px;\n flex: 1;\n min-width: 120px;\n }\n\n .myio-settings-field-label {\n font-size: 12px;\n font-weight: 500;\n color: ${colors.textMuted};\n }\n\n .myio-settings-input,\n .myio-settings-select {\n padding: 8px 12px;\n border: 1px solid ${colors.border};\n border-radius: 8px;\n font-size: 12px;\n background: ${colors.chartBackground};\n color: ${colors.text};\n width: 100%;\n }\n\n .myio-settings-input:focus,\n .myio-settings-select:focus {\n outline: 2px solid ${primaryColor};\n outline-offset: 1px;\n }\n\n .myio-settings-tabs {\n display: flex;\n gap: 2px;\n background: ${theme==="dark"?"#374151":"#e5e7eb"};\n padding: 3px;\n border-radius: 8px;\n }\n\n .myio-settings-tab {\n flex: 1;\n padding: 8px 12px;\n font-size: 12px;\n font-weight: 500;\n border: none;\n background: transparent;\n color: ${colors.textMuted};\n cursor: pointer;\n border-radius: 6px;\n transition: all 0.2s;\n white-space: nowrap;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n }\n\n .myio-settings-tab:hover {\n color: ${colors.text};\n background: ${theme==="dark"?"rgba(255,255,255,0.1)":"rgba(0,0,0,0.05)"};\n }\n\n .myio-settings-tab.active {\n background: ${primaryColor};\n color: white;\n }\n\n .myio-settings-footer {\n display: flex;\n gap: 12px;\n justify-content: flex-end;\n padding: 16px 20px;\n border-top: 1px solid ${colors.border};\n background: ${theme==="dark"?"rgba(0,0,0,0.2)":"#fafafa"};\n }\n\n .myio-settings-btn {\n padding: 10px 20px;\n border-radius: 8px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s;\n }\n\n .myio-settings-btn-secondary {\n background: transparent;\n border: 1px solid ${colors.border};\n color: ${colors.text};\n }\n\n .myio-settings-btn-secondary:hover {\n background: ${theme==="dark"?"rgba(255,255,255,0.1)":"#f3f4f6"};\n }\n\n .myio-settings-btn-primary {\n background: ${primaryColor};\n border: none;\n color: white;\n }\n\n .myio-settings-btn-primary:hover {\n filter: brightness(1.1);\n }\n\n .myio-settings-hint {\n font-size: 11px;\n color: ${colors.textMuted};\n font-weight: normal;\n }\n\n .myio-settings-context-group {\n margin-bottom: 12px;\n }\n\n .myio-settings-context-group:last-child {\n margin-bottom: 0;\n }\n\n .myio-settings-section + .myio-settings-section {\n margin-top: 12px;\n }\n\n /* Dropdown styles */\n .myio-settings-dropdown-container {\n position: relative;\n }\n\n .myio-settings-dropdown-btn {\n padding: 10px 14px;\n border: 1px solid ${colors.border};\n border-radius: 8px;\n font-size: 14px;\n background: ${colors.chartBackground};\n color: ${colors.text};\n cursor: pointer;\n min-width: 180px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n width: 100%;\n }\n\n .myio-settings-dropdown-btn:hover {\n border-color: ${primaryColor};\n }\n\n .myio-settings-dropdown-arrow {\n font-size: 10px;\n color: ${colors.textMuted};\n }\n\n .myio-settings-dropdown {\n position: absolute;\n top: calc(100% + 4px);\n left: 0;\n z-index: 100001;\n background: ${colors.chartBackground};\n border: 1px solid ${colors.border};\n border-radius: 8px;\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);\n min-width: 220px;\n padding: 8px 0;\n }\n\n .myio-settings-dropdown.hidden {\n display: none;\n }\n\n .myio-settings-dropdown-option {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 14px;\n cursor: pointer;\n font-size: 13px;\n color: ${colors.text};\n transition: background 0.15s;\n }\n\n .myio-settings-dropdown-option:hover {\n background: ${theme==="dark"?"rgba(255,255,255,0.1)":"#f3f4f6"};\n }\n\n .myio-settings-dropdown-option input {\n width: 16px;\n height: 16px;\n cursor: pointer;\n accent-color: ${primaryColor};\n }\n\n .myio-settings-dropdown-actions {\n border-top: 1px solid ${colors.border};\n margin-top: 8px;\n padding: 8px;\n display: flex;\n flex-direction: column;\n gap: 6px;\n }\n\n .myio-settings-dropdown-actions button {\n width: 100%;\n padding: 8px;\n background: ${theme==="dark"?"rgba(255,255,255,0.1)":"#f3f4f6"};\n border: none;\n border-radius: 6px;\n cursor: pointer;\n font-size: 12px;\n color: ${colors.text};\n transition: background 0.15s;\n }\n\n .myio-settings-dropdown-actions button:hover {\n background: ${theme==="dark"?"rgba(255,255,255,0.15)":"#e5e7eb"};\n }\n\n /* Suggestion icon styles */\n .myio-settings-section-label span[id$="-settings-suggestion"] {\n transition: opacity 0.2s, transform 0.2s;\n }\n\n .myio-settings-section-label span[id$="-settings-suggestion"]:hover {\n opacity: 1 !important;\n transform: scale(1.2);\n }\n\n /* DateRangePicker styles (CSS tokens + premium styling) */\n ${CSS_TOKENS}\n ${DATERANGEPICKER_STYLES}\n\n /* Fix DateRangePicker buttons alignment */\n .myio-modal-scope .daterangepicker .drp-buttons {\n display: flex;\n justify-content: flex-end;\n align-items: center;\n gap: 8px;\n }\n\n .myio-modal-scope .daterangepicker .drp-buttons .btn {\n display: inline-block;\n margin-left: 0;\n }\n `}function createConsumptionChartWidget(config){const widgetId=`myio-widget-${Date.now()}-${Math.random().toString(36).substr(2,9)}`;let containerElement=null;let chartInstance=null;let styleElement=null;let settingsModalElement=null;let settingsHeaderInstance=null;let dateRangePickerInstance=null;let currentTheme=config.theme??"light";let currentChartType=config.defaultChartType??"line";let currentVizMode=config.defaultVizMode??"total";let currentPeriod=config.defaultPeriod??7;let currentIdealRange=config.idealRange??null;let tempPeriod=currentPeriod;let tempChartType=currentChartType;let tempVizMode=currentVizMode;let tempTheme=currentTheme;let tempIdealRange=currentIdealRange;let tempStartDate=null;let tempEndDate=null;let currentSuggestion=null;const domainCfg=DOMAIN_CONFIG4[config.domain]||DOMAIN_CONFIG4.energy;const primaryColor=config.colors?.primary||domainCfg.color;const domainColors=config.colors?.shoppingColors||domainCfg.colors;const showSettingsButton=config.showSettingsButton??true;const showMaximizeButton=config.showMaximizeButton??true;const showVizModeTabs=config.showVizModeTabs??true;const showChartTypeTabs=config.showChartTypeTabs??true;const chartHeight=typeof config.chartHeight==="number"?`${config.chartHeight}px`:config.chartHeight??"300px";function getTitle(){if(config.title)return config.title;const domainName=config.domain==="temperature"?"Temperatura":"Consumo";return`${domainName} dos últimos ${currentPeriod} dias`}function renderHTML(){const consolidadoIcon=`<svg viewBox="0 0 16 16" fill="currentColor"><rect x="3" y="3" width="10" height="10" rx="2"/></svg>`;const porShoppingIcon=`<svg viewBox="0 0 16 16" fill="currentColor"><rect x="1" y="1" width="6" height="6" rx="1"/><rect x="9" y="1" width="6" height="6" rx="1"/><rect x="1" y="9" width="6" height="6" rx="1"/><rect x="9" y="9" width="6" height="6" rx="1"/></svg>`;const lineChartIcon=`<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="2,12 5,7 9,9 14,3"/></svg>`;const barChartIcon=`<svg viewBox="0 0 16 16" fill="currentColor"><rect x="1" y="9" width="3" height="6" rx="0.5"/><rect x="6" y="5" width="3" height="10" rx="0.5"/><rect x="11" y="7" width="3" height="8" rx="0.5"/></svg>`;return`\n <div id="${widgetId}" class="myio-chart-widget ${currentTheme==="dark"?"dark":""} ${config.className||""}">\n <div class="myio-chart-widget-header">\n <div class="myio-chart-widget-title-group">\n ${showSettingsButton?`\n <button id="${widgetId}-settings-btn" class="myio-chart-widget-btn" title="Configurações">⚙️</button>\n `:""}\n <h4 id="${widgetId}-title" class="myio-chart-widget-title">${getTitle()}</h4>\n ${showVizModeTabs?`\n <div class="myio-chart-widget-tabs" id="${widgetId}-viz-tabs">\n <button class="myio-chart-widget-tab icon-only ${currentVizMode==="total"?"active":""}" data-viz="total" title="Consolidado">${consolidadoIcon}</button>\n <button class="myio-chart-widget-tab icon-only ${currentVizMode==="separate"?"active":""}" data-viz="separate" title="Por Shopping">${porShoppingIcon}</button>\n </div>\n `:""}\n ${showChartTypeTabs?`\n <div class="myio-chart-widget-tabs" id="${widgetId}-type-tabs">\n <button class="myio-chart-widget-tab icon-only ${currentChartType==="line"?"active":""}" data-type="line" title="Gráfico de Linhas">${lineChartIcon}</button>\n <button class="myio-chart-widget-tab icon-only ${currentChartType==="bar"?"active":""}" data-type="bar" title="Gráfico de Barras">${barChartIcon}</button>\n </div>\n `:""}\n ${showMaximizeButton?`\n <button id="${widgetId}-maximize-btn" class="myio-chart-widget-btn" title="Maximizar">⛶</button>\n `:""}\n </div>\n </div>\n <div class="myio-chart-widget-body">\n <div id="${widgetId}-loading" class="myio-chart-widget-loading" style="display: none;">\n <div class="myio-chart-widget-spinner"></div>\n </div>\n <div class="myio-chart-widget-canvas-container" style="height: ${chartHeight};">\n <canvas id="${widgetId}-canvas"></canvas>\n </div>\n </div>\n <div class="myio-chart-widget-footer" id="${widgetId}-footer">\n <div class="myio-chart-widget-stat">\n <span class="myio-chart-widget-stat-label">Total Período</span>\n <span id="${widgetId}-stat-total" class="myio-chart-widget-stat-value primary">--</span>\n </div>\n <div class="myio-chart-widget-stat">\n <span class="myio-chart-widget-stat-label">Média Diária</span>\n <span id="${widgetId}-stat-avg" class="myio-chart-widget-stat-value">--</span>\n </div>\n <div class="myio-chart-widget-stat">\n <span class="myio-chart-widget-stat-label">Dia de Pico</span>\n <span id="${widgetId}-stat-peak" class="myio-chart-widget-stat-value">--</span>\n <span id="${widgetId}-stat-peak-date" class="myio-chart-widget-stat-sub"></span>\n </div>\n </div>\n </div>\n `}function renderSettingsModal(){const unit=config.unit??"";const isTemperature=config.domain==="temperature";const consolidadoIcon=`<svg viewBox="0 0 16 16" fill="currentColor" style="width:14px;height:14px;pointer-events:none"><rect x="3" y="3" width="10" height="10" rx="2"/></svg>`;const porShoppingIcon=`<svg viewBox="0 0 16 16" fill="currentColor" style="width:14px;height:14px;pointer-events:none"><rect x="1" y="1" width="6" height="6" rx="1"/><rect x="9" y="1" width="6" height="6" rx="1"/><rect x="1" y="9" width="6" height="6" rx="1"/><rect x="9" y="9" width="6" height="6" rx="1"/></svg>`;const lineChartIcon=`<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:14px;height:14px;pointer-events:none"><polyline points="2,12 5,7 9,9 14,3"/></svg>`;const barChartIcon=`<svg viewBox="0 0 16 16" fill="currentColor" style="width:14px;height:14px;pointer-events:none"><rect x="1" y="9" width="3" height="6" rx="0.5"/><rect x="6" y="5" width="3" height="10" rx="0.5"/><rect x="11" y="7" width="3" height="8" rx="0.5"/></svg>`;settingsHeaderInstance=createModalHeader({id:`${widgetId}-settings`,title:"Configurações",icon:"⚙️",theme:tempTheme,showThemeToggle:false,showMaximize:false,showClose:true,onClose:()=>closeSettingsModal()});return`\n <div id="${widgetId}-settings-overlay" class="myio-settings-overlay myio-modal-scope hidden">\n <div class="myio-settings-card">\n ${settingsHeaderInstance.render()}\n <div class="myio-settings-body">\n \x3c!-- CONTEXT 1: Período e Dados --\x3e\n <div class="myio-settings-context-group">\n \x3c!-- Período --\x3e\n <div class="myio-settings-section">\n <div class="myio-settings-section-label">📅 Período</div>\n <div class="myio-settings-row">\n <div class="myio-settings-field" style="flex: 1;">\n <input type="text" id="${widgetId}-settings-daterange" class="myio-settings-input"\n readonly placeholder="Selecione o período..." style="cursor: pointer;">\n </div>\n </div>\n </div>\n\n \x3c!-- Dados --\x3e\n <div class="myio-settings-section">\n <div class="myio-settings-section-label">📊 Dados</div>\n <div class="myio-settings-row">\n \x3c!-- Granularity Select --\x3e\n <div class="myio-settings-field">\n <label class="myio-settings-field-label">Granularidade</label>\n <select id="${widgetId}-settings-granularity" class="myio-settings-select">\n <option value="1d" selected>📆 Por Dia</option>\n <option value="1h">🕐 Por Hora</option>\n </select>\n </div>\n\n \x3c!-- Weekday Filter --\x3e\n <div class="myio-settings-field">\n <label class="myio-settings-field-label">Dias da Semana</label>\n <div class="myio-settings-dropdown-container">\n <button type="button" id="${widgetId}-settings-weekday-btn" class="myio-settings-dropdown-btn">\n <span id="${widgetId}-settings-weekday-label">Todos os dias</span>\n <span class="myio-settings-dropdown-arrow">▼</span>\n </button>\n <div id="${widgetId}-settings-weekday-dropdown" class="myio-settings-dropdown hidden">\n <label class="myio-settings-dropdown-option">\n <input type="checkbox" name="${widgetId}-weekday" value="dom" checked /> Domingo\n </label>\n <label class="myio-settings-dropdown-option">\n <input type="checkbox" name="${widgetId}-weekday" value="seg" checked /> Segunda-feira\n </label>\n <label class="myio-settings-dropdown-option">\n <input type="checkbox" name="${widgetId}-weekday" value="ter" checked /> Terça-feira\n </label>\n <label class="myio-settings-dropdown-option">\n <input type="checkbox" name="${widgetId}-weekday" value="qua" checked /> Quarta-feira\n </label>\n <label class="myio-settings-dropdown-option">\n <input type="checkbox" name="${widgetId}-weekday" value="qui" checked /> Quinta-feira\n </label>\n <label class="myio-settings-dropdown-option">\n <input type="checkbox" name="${widgetId}-weekday" value="sex" checked /> Sexta-feira\n </label>\n <label class="myio-settings-dropdown-option">\n <input type="checkbox" name="${widgetId}-weekday" value="sab" checked /> Sábado\n </label>\n <div class="myio-settings-dropdown-actions">\n <button type="button" id="${widgetId}-settings-weekday-all">Selecionar Todos</button>\n <button type="button" id="${widgetId}-settings-weekday-clear">Limpar</button>\n </div>\n </div>\n </div>\n </div>\n\n \x3c!-- Day Period Filter (only visible when hourly) --\x3e\n <div class="myio-settings-field" id="${widgetId}-settings-dayperiod-field" style="display: none;">\n <label class="myio-settings-field-label">Períodos do Dia</label>\n <div class="myio-settings-dropdown-container">\n <button type="button" id="${widgetId}-settings-dayperiod-btn" class="myio-settings-dropdown-btn">\n <span id="${widgetId}-settings-dayperiod-label">Todos os períodos</span>\n <span class="myio-settings-dropdown-arrow">▼</span>\n </button>\n <div id="${widgetId}-settings-dayperiod-dropdown" class="myio-settings-dropdown hidden">\n <label class="myio-settings-dropdown-option">\n <input type="checkbox" name="${widgetId}-dayperiod" value="madrugada" checked /> Madrugada (00h-06h)\n </label>\n <label class="myio-settings-dropdown-option">\n <input type="checkbox" name="${widgetId}-dayperiod" value="manha" checked /> Manhã (06h-12h)\n </label>\n <label class="myio-settings-dropdown-option">\n <input type="checkbox" name="${widgetId}-dayperiod" value="tarde" checked /> Tarde (12h-18h)\n </label>\n <label class="myio-settings-dropdown-option">\n <input type="checkbox" name="${widgetId}-dayperiod" value="noite" checked /> Noite (18h-24h)\n </label>\n <div class="myio-settings-dropdown-actions">\n <button type="button" id="${widgetId}-settings-dayperiod-all">Selecionar Todos</button>\n <button type="button" id="${widgetId}-settings-dayperiod-clear">Limpar</button>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n \x3c!-- CONTEXT 2: Faixa Ideal --\x3e\n <div class="myio-settings-context-group">\n <div class="myio-settings-section">\n <div class="myio-settings-section-label">\n 🎯 Faixa Ideal\n <span class="myio-settings-hint" id="${widgetId}-settings-range-hint">(opcional - deixe zerado para não exibir)</span>\n <span\n id="${widgetId}-settings-suggestion"\n title=""\n style="cursor: pointer; font-size: 16px; opacity: 0.7; transition: opacity 0.2s; margin-left: 4px;"\n >💡</span>\n </div>\n <div class="myio-settings-row">\n <div class="myio-settings-field" style="min-width: 100px;">\n <label class="myio-settings-field-label">Mínimo (${unit})</label>\n <input type="number" id="${widgetId}-settings-range-min" class="myio-settings-input"\n value="${tempIdealRange?.min??""}" placeholder="0" step="0.1">\n </div>\n <div class="myio-settings-field" style="min-width: 100px;">\n <label class="myio-settings-field-label">Máximo (${unit})</label>\n <input type="number" id="${widgetId}-settings-range-max" class="myio-settings-input"\n value="${tempIdealRange?.max??""}" placeholder="0" step="0.1">\n </div>\n <div class="myio-settings-field" style="flex: 1;">\n <label class="myio-settings-field-label">Rótulo</label>\n <input type="text" id="${widgetId}-settings-range-label" class="myio-settings-input"\n value="${tempIdealRange?.label??""}" placeholder="${isTemperature?"Faixa Ideal":"Meta de Consumo"}">\n </div>\n </div>\n </div>\n </div>\n\n \x3c!-- CONTEXT 3: Visualização --\x3e\n <div class="myio-settings-context-group">\n <div class="myio-settings-section">\n <div class="myio-settings-section-label">🎨 Visualização</div>\n <div class="myio-settings-row" style="gap: 20px; flex-wrap: wrap;">\n \x3c!-- Chart Type --\x3e\n <div class="myio-settings-field" style="flex: 1; min-width: 180px;">\n <label class="myio-settings-field-label">Tipo de Gráfico</label>\n <div class="myio-settings-tabs" id="${widgetId}-settings-chart-type">\n <button class="myio-settings-tab ${tempChartType==="line"?"active":""}" data-type="line">${lineChartIcon} Linhas</button>\n <button class="myio-settings-tab ${tempChartType==="bar"?"active":""}" data-type="bar">${barChartIcon} Barras</button>\n </div>\n </div>\n\n \x3c!-- Viz Mode --\x3e\n <div class="myio-settings-field" style="flex: 1; min-width: 200px;">\n <label class="myio-settings-field-label">Agrupamento</label>\n <div class="myio-settings-tabs" id="${widgetId}-settings-viz-mode">\n <button class="myio-settings-tab ${tempVizMode==="total"?"active":""}" data-viz="total">${consolidadoIcon} Consolidado</button>\n <button class="myio-settings-tab ${tempVizMode==="separate"?"active":""}" data-viz="separate">${porShoppingIcon} Por Shopping</button>\n </div>\n </div>\n\n \x3c!-- Theme --\x3e\n <div class="myio-settings-field" style="flex: 1; min-width: 160px;">\n <label class="myio-settings-field-label">Tema</label>\n <div class="myio-settings-tabs" id="${widgetId}-settings-theme">\n <button class="myio-settings-tab ${tempTheme==="light"?"active":""}" data-theme="light">☀️ Light</button>\n <button class="myio-settings-tab ${tempTheme==="dark"?"active":""}" data-theme="dark">🌙 Dark</button>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n <div class="myio-settings-footer">\n <button id="${widgetId}-settings-reset" class="myio-settings-btn myio-settings-btn-secondary">Resetar</button>\n <button id="${widgetId}-settings-apply" class="myio-settings-btn myio-settings-btn-primary">Carregar</button>\n </div>\n </div>\n </div>\n `}function injectStyles(){if(styleElement)return;styleElement=document.createElement("style");styleElement.id=`${widgetId}-styles`;styleElement.textContent=getWidgetStyles(currentTheme,primaryColor);document.head.appendChild(styleElement)}function updateStyles(){if(styleElement){styleElement.textContent=getWidgetStyles(currentTheme,primaryColor)}}function setupListeners(){if(showSettingsButton){document.getElementById(`${widgetId}-settings-btn`)?.addEventListener("click",()=>{openSettingsModal();config.onSettingsClick?.()})}if(showMaximizeButton&&config.onMaximizeClick){document.getElementById(`${widgetId}-maximize-btn`)?.addEventListener("click",()=>{config.onMaximizeClick?.()})}if(showVizModeTabs){document.getElementById(`${widgetId}-viz-tabs`)?.addEventListener("click",e=>{const target=e.target;if(target.classList.contains("myio-chart-widget-tab")){const mode=target.dataset.viz;if(mode){instance.setVizMode(mode)}}})}if(showChartTypeTabs){document.getElementById(`${widgetId}-type-tabs`)?.addEventListener("click",e=>{const target=e.target;if(target.classList.contains("myio-chart-widget-tab")){const type=target.dataset.type;if(type){instance.setChartType(type)}}})}}function updateTabStates(){document.querySelectorAll(`#${widgetId}-viz-tabs .myio-chart-widget-tab`).forEach(tab=>{const btn=tab;btn.classList.toggle("active",btn.dataset.viz===currentVizMode)});document.querySelectorAll(`#${widgetId}-type-tabs .myio-chart-widget-tab`).forEach(tab=>{const btn=tab;btn.classList.toggle("active",btn.dataset.type===currentChartType)})}function updateTitle(){const titleEl=document.getElementById(`${widgetId}-title`);if(titleEl){titleEl.textContent=getTitle()}}function setLoading(loading){const loadingEl=document.getElementById(`${widgetId}-loading`);if(loadingEl){loadingEl.style.display=loading?"flex":"none"}}function formatValue(value){const unit=config.unit??"";const unitLarge=config.unitLarge;const threshold=config.thresholdForLargeUnit??1e3;if(unitLarge&&Math.abs(value)>=threshold){return`${(value/threshold).toFixed(2)} ${unitLarge}`}return`${value.toFixed(2)} ${unit}`}function updateFooterStats(data){const totalEl=document.getElementById(`${widgetId}-stat-total`);const avgEl=document.getElementById(`${widgetId}-stat-avg`);const peakEl=document.getElementById(`${widgetId}-stat-peak`);const peakDateEl=document.getElementById(`${widgetId}-stat-peak-date`);if(!data.dailyTotals||data.dailyTotals.length===0){if(totalEl)totalEl.textContent="--";if(avgEl)avgEl.textContent="--";if(peakEl)peakEl.textContent="--";if(peakDateEl)peakDateEl.textContent="";return}const isTemperature=config.domain==="temperature";const totals=data.dailyTotals;const labels=data.labels??[];const total=totals.reduce((a,b)=>a+b,0);const avg=total/totals.length;const peakValue=Math.max(...totals);const peakIndex=totals.indexOf(peakValue);const peakDate=labels[peakIndex]??"";if(totalEl){if(isTemperature){totalEl.textContent=formatValue(avg);const labelEl=totalEl.previousElementSibling;if(labelEl)labelEl.textContent="Média Período"}else{totalEl.textContent=formatValue(total)}}if(avgEl){avgEl.textContent=formatValue(avg)}if(peakEl){peakEl.textContent=formatValue(peakValue)}if(peakDateEl){peakDateEl.textContent=peakDate}}async function openSettingsModal(){tempPeriod=currentPeriod;tempChartType=currentChartType;tempVizMode=currentVizMode;tempTheme=currentTheme;tempIdealRange=currentIdealRange?{...currentIdealRange}:null;dateRangePickerInstance=null;if(!settingsModalElement){settingsModalElement=document.createElement("div");settingsModalElement.innerHTML=renderSettingsModal();document.body.appendChild(settingsModalElement.firstElementChild);settingsModalElement=document.getElementById(`${widgetId}-settings-overlay`);await setupSettingsModalListeners()}else{await setupSettingsModalListeners()}updateSettingsModalValues();updateIdealRangeSuggestionTooltip();settingsModalElement?.classList.remove("hidden")}function closeSettingsModal(){settingsModalElement?.classList.add("hidden");if(dateRangePickerInstance){dateRangePickerInstance.destroy();dateRangePickerInstance=null}}function updateSettingsModalValues(){const periodSelect=document.getElementById(`${widgetId}-settings-period`);if(periodSelect)periodSelect.value=String(tempPeriod);const minInput=document.getElementById(`${widgetId}-settings-range-min`);const maxInput=document.getElementById(`${widgetId}-settings-range-max`);const labelInput=document.getElementById(`${widgetId}-settings-range-label`);if(minInput)minInput.value=tempIdealRange?.min?.toString()??"";if(maxInput)maxInput.value=tempIdealRange?.max?.toString()??"";if(labelInput)labelInput.value=tempIdealRange?.label??"";updateSettingsModalTabs()}function updateSettingsModalTabs(){document.querySelectorAll(`#${widgetId}-settings-chart-type .myio-settings-tab`).forEach(tab=>{const btn=tab;btn.classList.toggle("active",btn.dataset.type===tempChartType)});document.querySelectorAll(`#${widgetId}-settings-viz-mode .myio-settings-tab`).forEach(tab=>{const btn=tab;btn.classList.toggle("active",btn.dataset.viz===tempVizMode)});document.querySelectorAll(`#${widgetId}-settings-theme .myio-settings-tab`).forEach(tab=>{const btn=tab;btn.classList.toggle("active",btn.dataset.theme===tempTheme)})}function updateWeekdayLabel(){const checkboxes=document.querySelectorAll(`input[name="${widgetId}-weekday"]`);const checked=Array.from(checkboxes).filter(cb=>cb.checked);const label=document.getElementById(`${widgetId}-settings-weekday-label`);if(label){if(checked.length===0){label.textContent="Nenhum dia"}else if(checked.length===checkboxes.length){label.textContent="Todos os dias"}else{label.textContent=`${checked.length} dias selecionados`}}}function updateDayPeriodLabel(){const checkboxes=document.querySelectorAll(`input[name="${widgetId}-dayperiod"]`);const checked=Array.from(checkboxes).filter(cb=>cb.checked);const label=document.getElementById(`${widgetId}-settings-dayperiod-label`);if(label){if(checked.length===0){label.textContent="Nenhum período"}else if(checked.length===checkboxes.length){label.textContent="Todos os períodos"}else{label.textContent=`${checked.length} períodos selecionados`}}}function calculateIdealRangeSuggestion(){const data=chartInstance?.getCachedData();if(!data||!data.dailyTotals||data.dailyTotals.length===0){return{min:0,max:0,avg:0}}const total=data.dailyTotals.reduce((a,b)=>a+b,0);const avg=total/data.dailyTotals.length;const min=avg*.85;const max=avg*1.15;return{min:Math.round(min*10)/10,max:Math.round(max*10)/10,avg:Math.round(avg*10)/10}}function updateIdealRangeSuggestionTooltip(){const suggestion=calculateIdealRangeSuggestion();const suggestionEl=document.getElementById(`${widgetId}-settings-suggestion`);const hintEl=document.getElementById(`${widgetId}-settings-range-hint`);const unit=config.unit??"";const isTemperature=config.domain==="temperature";currentSuggestion=suggestion;if(hintEl){if(isTemperature){hintEl.textContent="(valores carregados do cliente)"}else{hintEl.textContent="(opcional - deixe zerado para não exibir)"}}if(suggestionEl){if(suggestion.avg>0){const tooltipText=`Sugestão: ${suggestion.min} - ${suggestion.max} ${unit} (média ±15%). Clique para aplicar.`;suggestionEl.title=tooltipText;suggestionEl.style.display="inline"}else{suggestionEl.style.display="none"}}}function applyIdealRangeSuggestion(){if(!currentSuggestion||currentSuggestion.min===0&¤tSuggestion.max===0){return}const minInput=document.getElementById(`${widgetId}-settings-range-min`);const maxInput=document.getElementById(`${widgetId}-settings-range-max`);const labelInput=document.getElementById(`${widgetId}-settings-range-label`);if(minInput)minInput.value=String(currentSuggestion.min);if(maxInput)maxInput.value=String(currentSuggestion.max);if(labelInput)labelInput.value="Faixa Sugerida";tempIdealRange={min:currentSuggestion.min,max:currentSuggestion.max,label:"Faixa Sugerida"}}async function setupSettingsModalListeners(){settingsHeaderInstance?.attachListeners();const dateRangeInput=document.getElementById(`${widgetId}-settings-daterange`);if(dateRangeInput&&!dateRangePickerInstance){try{const endDate=new Date;const startDate=new Date;startDate.setDate(endDate.getDate()-tempPeriod);const presetStart=tempStartDate||startDate.toISOString();const presetEnd=tempEndDate||endDate.toISOString();dateRangePickerInstance=await createDateRangePicker2(dateRangeInput,{presetStart:presetStart,presetEnd:presetEnd,includeTime:true,timePrecision:"hour",maxRangeDays:90,locale:"pt-BR",parentEl:document.getElementById(`${widgetId}-settings-overlay`),onApply:result=>{tempStartDate=result.startISO;tempEndDate=result.endISO;const start=new Date(result.startISO);const end=new Date(result.endISO);const diffTime=Math.abs(end.getTime()-start.getTime());const diffHours=diffTime/(1e3*60*60);const diffDays=Math.ceil(diffTime/(1e3*60*60*24));tempPeriod=diffDays||7;const granularitySelect=document.getElementById(`${widgetId}-settings-granularity`);if(granularitySelect&&diffHours<=48){granularitySelect.value="1h";const dayPeriodField=document.getElementById(`${widgetId}-settings-dayperiod-field`);if(dayPeriodField)dayPeriodField.style.display="block"}console.log("[ConsumptionChartWidget] Date range applied:",{start:result.startISO,end:result.endISO,hours:diffHours,days:tempPeriod})}})}catch(error){console.warn("[ConsumptionChartWidget] DateRangePicker initialization failed:",error)}}document.getElementById(`${widgetId}-settings-overlay`)?.addEventListener("click",e=>{if(e.target.classList.contains("myio-settings-overlay")){closeSettingsModal()}});document.getElementById(`${widgetId}-settings-granularity`)?.addEventListener("change",e=>{const select=e.target;const dayPeriodField=document.getElementById(`${widgetId}-settings-dayperiod-field`);if(dayPeriodField){dayPeriodField.style.display=select.value==="1h"?"block":"none"}});document.getElementById(`${widgetId}-settings-suggestion`)?.addEventListener("click",()=>{applyIdealRangeSuggestion()});document.getElementById(`${widgetId}-settings-weekday-btn`)?.addEventListener("click",e=>{e.stopPropagation();const dropdown=document.getElementById(`${widgetId}-settings-weekday-dropdown`);dropdown?.classList.toggle("hidden")});document.querySelectorAll(`input[name="${widgetId}-weekday"]`).forEach(cb=>{cb.addEventListener("change",updateWeekdayLabel)});document.getElementById(`${widgetId}-settings-weekday-all`)?.addEventListener("click",()=>{document.querySelectorAll(`input[name="${widgetId}-weekday"]`).forEach(cb=>{cb.checked=true});updateWeekdayLabel()});document.getElementById(`${widgetId}-settings-weekday-clear`)?.addEventListener("click",()=>{document.querySelectorAll(`input[name="${widgetId}-weekday"]`).forEach(cb=>{cb.checked=false});updateWeekdayLabel()});document.getElementById(`${widgetId}-settings-dayperiod-btn`)?.addEventListener("click",e=>{e.stopPropagation();const dropdown=document.getElementById(`${widgetId}-settings-dayperiod-dropdown`);dropdown?.classList.toggle("hidden")});document.querySelectorAll(`input[name="${widgetId}-dayperiod"]`).forEach(cb=>{cb.addEventListener("change",updateDayPeriodLabel)});document.getElementById(`${widgetId}-settings-dayperiod-all`)?.addEventListener("click",()=>{document.querySelectorAll(`input[name="${widgetId}-dayperiod"]`).forEach(cb=>{cb.checked=true});updateDayPeriodLabel()});document.getElementById(`${widgetId}-settings-dayperiod-clear`)?.addEventListener("click",()=>{document.querySelectorAll(`input[name="${widgetId}-dayperiod"]`).forEach(cb=>{cb.checked=false});updateDayPeriodLabel()});document.addEventListener("click",e=>{const target=e.target;const weekdayDropdown=document.getElementById(`${widgetId}-settings-weekday-dropdown`);const dayperiodDropdown=document.getElementById(`${widgetId}-settings-dayperiod-dropdown`);if(weekdayDropdown&&!target.closest(`#${widgetId}-settings-weekday-btn`)&&!target.closest(`#${widgetId}-settings-weekday-dropdown`)){weekdayDropdown.classList.add("hidden")}if(dayperiodDropdown&&!target.closest(`#${widgetId}-settings-dayperiod-btn`)&&!target.closest(`#${widgetId}-settings-dayperiod-dropdown`)){dayperiodDropdown.classList.add("hidden")}});document.getElementById(`${widgetId}-settings-chart-type`)?.addEventListener("click",e=>{const target=e.target;if(target.classList.contains("myio-settings-tab")){tempChartType=target.dataset.type;updateSettingsModalTabs()}});document.getElementById(`${widgetId}-settings-viz-mode`)?.addEventListener("click",e=>{const target=e.target;if(target.classList.contains("myio-settings-tab")){tempVizMode=target.dataset.viz;updateSettingsModalTabs()}});document.getElementById(`${widgetId}-settings-theme`)?.addEventListener("click",e=>{const target=e.target;if(target.classList.contains("myio-settings-tab")){tempTheme=target.dataset.theme;updateSettingsModalTabs()}});document.getElementById(`${widgetId}-settings-reset`)?.addEventListener("click",async()=>{tempPeriod=config.defaultPeriod??7;tempChartType=config.defaultChartType??"line";tempVizMode=config.defaultVizMode??"total";tempTheme=config.theme??"light";tempIdealRange=config.idealRange??null;tempStartDate=null;tempEndDate=null;if(dateRangePickerInstance){dateRangePickerInstance.destroy();dateRangePickerInstance=null}const dateRangeInput2=document.getElementById(`${widgetId}-settings-daterange`);if(dateRangeInput2){const endDate=new Date;const startDate=new Date;startDate.setDate(endDate.getDate()-tempPeriod);try{dateRangePickerInstance=await createDateRangePicker2(dateRangeInput2,{presetStart:startDate.toISOString(),presetEnd:endDate.toISOString(),includeTime:true,timePrecision:"hour",maxRangeDays:90,locale:"pt-BR",parentEl:document.getElementById(`${widgetId}-settings-overlay`),onApply:result=>{tempStartDate=result.startISO;tempEndDate=result.endISO;const start=new Date(result.startISO);const end=new Date(result.endISO);const diffTime=Math.abs(end.getTime()-start.getTime());const diffHours=diffTime/(1e3*60*60);const diffDays=Math.ceil(diffTime/(1e3*60*60*24));tempPeriod=diffDays||7;const granularitySelect2=document.getElementById(`${widgetId}-settings-granularity`);if(granularitySelect2&&diffHours<=48){granularitySelect2.value="1h";const dayPeriodField2=document.getElementById(`${widgetId}-settings-dayperiod-field`);if(dayPeriodField2)dayPeriodField2.style.display="block"}}})}catch(error){console.warn("[ConsumptionChartWidget] DateRangePicker reset failed:",error)}}const granularitySelect=document.getElementById(`${widgetId}-settings-granularity`);if(granularitySelect)granularitySelect.value="1d";const dayPeriodField=document.getElementById(`${widgetId}-settings-dayperiod-field`);if(dayPeriodField)dayPeriodField.style.display="none";document.querySelectorAll(`input[name="${widgetId}-weekday"]`).forEach(cb=>{cb.checked=true});updateWeekdayLabel();document.querySelectorAll(`input[name="${widgetId}-dayperiod"]`).forEach(cb=>{cb.checked=true});updateDayPeriodLabel();updateSettingsModalValues()});document.getElementById(`${widgetId}-settings-apply`)?.addEventListener("click",async()=>{const minInput=document.getElementById(`${widgetId}-settings-range-min`);const maxInput=document.getElementById(`${widgetId}-settings-range-max`);const labelInput=document.getElementById(`${widgetId}-settings-range-label`);const periodSelect=document.getElementById(`${widgetId}-settings-period`);const min=parseFloat(minInput?.value||"0");const max=parseFloat(maxInput?.value||"0");const label=labelInput?.value||"";tempPeriod=parseInt(periodSelect?.value||"7",10);if(min>0||max>0){tempIdealRange={min:min,max:max,label:label}}else{tempIdealRange=null}closeSettingsModal();if(tempTheme!==currentTheme){instance.setTheme(tempTheme)}if(tempChartType!==currentChartType){instance.setChartType(tempChartType)}if(tempVizMode!==currentVizMode){instance.setVizMode(tempVizMode)}if(JSON.stringify(tempIdealRange)!==JSON.stringify(currentIdealRange)){instance.setIdealRange(tempIdealRange)}if(tempPeriod!==currentPeriod){await instance.setPeriod(tempPeriod)}})}const instance={async render(){containerElement=document.getElementById(config.containerId);if(!containerElement){console.error(`[ConsumptionWidget] Container #${config.containerId} not found`);return}injectStyles();containerElement.innerHTML=renderHTML();setupListeners();setLoading(true);chartInstance=createConsumption7DaysChart({...config,containerId:`${widgetId}-canvas`,theme:currentTheme,defaultChartType:currentChartType,defaultVizMode:currentVizMode,defaultPeriod:currentPeriod,idealRange:currentIdealRange,colors:{primary:primaryColor,background:`${primaryColor}20`,shoppingColors:domainColors,...config.colors},onDataLoaded:data=>{setLoading(false);updateFooterStats(data);config.onDataLoaded?.(data)},onError:error=>{setLoading(false);config.onError?.(error)}});await chartInstance.render();setLoading(false)},async refresh(forceRefresh=false){if(!chartInstance)return;setLoading(true);await chartInstance.refresh(forceRefresh);setLoading(false)},setChartType(type){if(currentChartType===type)return;currentChartType=type;chartInstance?.setChartType(type);updateTabStates()},setVizMode(mode){if(currentVizMode===mode)return;currentVizMode=mode;chartInstance?.setVizMode(mode);updateTabStates()},setTheme(theme){if(currentTheme===theme)return;currentTheme=theme;chartInstance?.setTheme(theme);const widget=document.getElementById(widgetId);if(widget){widget.classList.toggle("dark",theme==="dark")}updateStyles()},async setPeriod(days){if(currentPeriod===days)return;currentPeriod=days;updateTitle();setLoading(true);await(chartInstance?.setPeriod(days));setLoading(false)},setIdealRange(range){currentIdealRange=range;chartInstance?.setIdealRange(range)},getChart(){return chartInstance},getChartInstance(){return chartInstance?.getChartInstance?.()??null},getCachedData(){return chartInstance?.getCachedData()??null},exportCSV(filename){chartInstance?.exportCSV(filename)},destroy(){chartInstance?.destroy();chartInstance=null;if(styleElement){styleElement.remove();styleElement=null}settingsHeaderInstance?.destroy();settingsHeaderInstance=null;if(dateRangePickerInstance){dateRangePickerInstance.destroy();dateRangePickerInstance=null}if(settingsModalElement){settingsModalElement.remove();settingsModalElement=null}if(containerElement){containerElement.innerHTML="";containerElement=null}}};return instance}var DEFAULT_COLORS4={primary:"#3e1a7d",secondary:"#6b4c9a",accent:"#00bcd4",background:"#ffffff",text:"#333333",chartColors:["#3e1a7d","#00bcd4","#4caf50","#ff9800","#e91e63","#9c27b0"]};var DOMAIN_ICONS={energy:"⚡",water:"💧",temperature:"🌡️"};var DOMAIN_LABELS={energy:"Energia",water:"Água",temperature:"Temperatura"};var DOMAIN_LABELS_EN={energy:"Energy",water:"Water",temperature:"Temperature"};var DOMAIN_UNITS={energy:"kWh",water:"m³",temperature:"°C"};var CSV_SEPARATORS={"pt-BR":";","en-US":",",default:";"};function formatDateForFilename(date){const pad=n=>n.toString().padStart(2,"0");return`${date.getFullYear()}-${pad(date.getMonth()+1)}-${pad(date.getDate())}-${pad(date.getHours())}-${pad(date.getMinutes())}-${pad(date.getSeconds())}`}function sanitizeFilename(str){return str.replace(/[<>:"/\\|?*]/g,"").replace(/\s+/g,"_").substring(0,50)}function generateFilename(data,config){const timestamp=formatDateForFilename(new Date);const domainLabel=DOMAIN_LABELS_EN[config.domain].toUpperCase();const ext=config.formatExport;let baseName="export";if("device"in data&&data.device){const device=data.device;const label=device.label||device.name||"device";const identifier=device.identifier?`-${device.identifier}`:"";baseName=`${sanitizeFilename(label)}${identifier}`}else if("customer"in data&&data.customer?.customerName){baseName=sanitizeFilename(data.customer.customerName)}else if("groupName"in data){baseName=sanitizeFilename(data.groupName)}return`${baseName}-${domainLabel}-${timestamp}.${ext}`}function normalizeTimestamp(ts){if(ts instanceof Date)return ts;if(typeof ts==="number")return new Date(ts);return new Date(ts)}function calculateStats2(dataPoints){if(dataPoints.length===0){return{min:0,max:0,average:0,sum:0,count:0}}const values=dataPoints.map(d=>d.value);const sum=values.reduce((a,b)=>a+b,0);return{min:Math.min(...values),max:Math.max(...values),average:sum/values.length,sum:sum,count:values.length}}function formatNumber2(value,locale,decimals=2){return new Intl.NumberFormat(locale,{minimumFractionDigits:decimals,maximumFractionDigits:decimals}).format(value)}function formatDate3(date,locale){return new Intl.DateTimeFormat(locale,{year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit"}).format(date)}function generateCSV(data,config){const sep=CSV_SEPARATORS[config.locale]||CSV_SEPARATORS["default"];const rows=[];const escapeCSV=val=>{const str=String(val??"");if(str.includes(sep)||str.includes('"')||str.includes("\n")){return`"${str.replace(/"/g,'""')}"`}return str};const formatNumCSV=val=>formatNumber2(val,config.locale);if("device"in data&&"data"in data&&Array.isArray(data.data)){const deviceData=data;rows.push(["Timestamp",config.domainLabel,`Unit (${config.domainUnit})`]);for(const point of deviceData.data){const ts=normalizeTimestamp(point.timestamp);rows.push([formatDate3(ts,config.locale),formatNumCSV(point.value),point.unit||config.domainUnit])}if(config.includeStats){const stats=calculateStats2(deviceData.data);rows.push([]);rows.push(["Statistics","",""]);rows.push(["Minimum",formatNumCSV(stats.min),config.domainUnit]);rows.push(["Maximum",formatNumCSV(stats.max),config.domainUnit]);rows.push(["Average",formatNumCSV(stats.average),config.domainUnit]);rows.push(["Total",formatNumCSV(stats.sum),config.domainUnit]);rows.push(["Count",String(stats.count),"points"])}}else if("devices"in data&&Array.isArray(data.devices)){const compData=data;const deviceHeaders=compData.devices.map(d=>d.device.label||d.device.name||"Device");rows.push(["Timestamp",...deviceHeaders]);const allTimestamps=new Set;compData.devices.forEach(d=>{d.data.forEach(point=>{allTimestamps.add(normalizeTimestamp(point.timestamp).getTime())})});const sortedTimestamps=Array.from(allTimestamps).sort((a,b)=>a-b);for(const ts of sortedTimestamps){const row=[formatDate3(new Date(ts),config.locale)];for(const device of compData.devices){const point=device.data.find(p=>normalizeTimestamp(p.timestamp).getTime()===ts);row.push(point?formatNumCSV(point.value):"")}rows.push(row)}}return rows.map(row=>row.map(escapeCSV).join(sep)).join("\r\n")}function generateXLSX(data,config){return generateCSV(data,config)}function generatePDFContent(data,config){const{colors:colors,domainIcon:domainIcon,domainLabel:domainLabel,domainUnit:domainUnit,locale:locale,includeStats:includeStats,includeChart:includeChart}=config;let deviceLabel="Export";let customerName="";let identifier="";let dataPoints=[];if("device"in data&&"data"in data){const deviceData=data;deviceLabel=deviceData.device.label||deviceData.device.name||"Device";identifier=deviceData.device.identifier||"";customerName=deviceData.customer?.customerName||"";dataPoints=deviceData.data}const stats=calculateStats2(dataPoints);const tableRows=dataPoints.slice(0,100).map(point=>{const ts=normalizeTimestamp(point.timestamp);return`\n <tr>\n <td style="padding: 8px; border-bottom: 1px solid #eee;">${formatDate3(ts,locale)}</td>\n <td style="padding: 8px; border-bottom: 1px solid #eee; text-align: right;">${formatNumber2(point.value,locale)}</td>\n <td style="padding: 8px; border-bottom: 1px solid #eee;">${point.unit||domainUnit}</td>\n </tr>\n `}).join("");const statsSection=includeStats?`\n <div style="margin-top: 24px; padding: 16px; background: #f5f5f5; border-radius: 8px;">\n <h3 style="margin: 0 0 12px 0; color: ${colors.primary};">Statistics</h3>\n <table style="width: 100%;">\n <tr>\n <td><strong>Minimum:</strong></td>\n <td>${formatNumber2(stats.min,locale)} ${domainUnit}</td>\n <td><strong>Maximum:</strong></td>\n <td>${formatNumber2(stats.max,locale)} ${domainUnit}</td>\n </tr>\n <tr>\n <td><strong>Average:</strong></td>\n <td>${formatNumber2(stats.average,locale)} ${domainUnit}</td>\n <td><strong>Total:</strong></td>\n <td>${formatNumber2(stats.sum,locale)} ${domainUnit}</td>\n </tr>\n </table>\n </div>\n `:"";return`\n <!DOCTYPE html>\n <html>\n <head>\n <meta charset="UTF-8">\n <title>${deviceLabel} - ${domainLabel} Report</title>\n <style>\n body {\n font-family: 'Roboto', Arial, sans-serif;\n margin: 0;\n padding: 24px;\n color: ${colors.text};\n background: ${colors.background};\n }\n .header {\n background: ${colors.primary};\n color: white;\n padding: 20px;\n border-radius: 8px;\n margin-bottom: 24px;\n }\n .header h1 {\n margin: 0;\n font-size: 24px;\n }\n .header .subtitle {\n opacity: 0.9;\n margin-top: 8px;\n }\n .device-info {\n display: flex;\n gap: 16px;\n margin-bottom: 16px;\n padding: 12px;\n background: #f5f5f5;\n border-radius: 8px;\n }\n .device-info span {\n padding: 4px 12px;\n background: ${colors.secondary};\n color: white;\n border-radius: 4px;\n font-size: 14px;\n }\n table {\n width: 100%;\n border-collapse: collapse;\n }\n th {\n background: ${colors.primary};\n color: white;\n padding: 12px 8px;\n text-align: left;\n }\n th:nth-child(2) {\n text-align: right;\n }\n .footer {\n margin-top: 24px;\n padding-top: 16px;\n border-top: 1px solid #eee;\n text-align: center;\n font-size: 12px;\n color: #999;\n }\n @media print {\n body { padding: 0; }\n .header { border-radius: 0; }\n }\n </style>\n </head>\n <body>\n <div class="header">\n <h1>${domainIcon} ${deviceLabel}</h1>\n <div class="subtitle">${domainLabel} Report - Generated ${formatDate3(new Date,locale)}</div>\n </div>\n\n ${customerName?`<div class="customer-name" style="margin-bottom: 16px; font-size: 18px;"><strong>Customer:</strong> ${customerName}</div>`:""}\n\n ${identifier?`\n <div class="device-info">\n <span>ID: ${identifier}</span>\n <span>Domain: ${domainLabel}</span>\n <span>Unit: ${domainUnit}</span>\n </div>\n `:""}\n\n <table>\n <thead>\n <tr>\n <th>Timestamp</th>\n <th>${domainLabel} (${domainUnit})</th>\n <th>Unit</th>\n </tr>\n </thead>\n <tbody>\n ${tableRows}\n ${dataPoints.length>100?`<tr><td colspan="3" style="text-align: center; padding: 16px; color: #999;">... and ${dataPoints.length-100} more rows</td></tr>`:""}\n </tbody>\n </table>\n\n ${statsSection}\n\n <div class="footer">\n <p>${config.footerText||"Generated by MyIO Platform"}</p>\n </div>\n </body>\n </html>\n `}function buildTemplateExport(params){const{domain:domain,formatExport:formatExport,typeExport:typeExport,colorsPallet:colorsPallet,locale:locale="pt-BR",includeChart:includeChart=formatExport==="pdf",includeStats:includeStats=true,headerText:headerText,footerText:footerText}=params;const colors={...DEFAULT_COLORS4,...colorsPallet,chartColors:colorsPallet?.chartColors||DEFAULT_COLORS4.chartColors};return{domain:domain,formatExport:formatExport,typeExport:typeExport,colors:colors,locale:locale,includeChart:includeChart,includeStats:includeStats,headerText:headerText||`${DOMAIN_LABELS[domain]} Report`,footerText:footerText||"Generated by MyIO Platform",domainIcon:DOMAIN_ICONS[domain],domainLabel:DOMAIN_LABELS[domain],domainUnit:DOMAIN_UNITS[domain]}}function myioExportData(data,config,options){const filename=generateFilename(data,config);let allDataPoints=[];if("data"in data&&Array.isArray(data.data)){allDataPoints=data.data}else if("devices"in data&&Array.isArray(data.devices)){allDataPoints=data.devices.flatMap(d=>d.data)}const stats=calculateStats2(allDataPoints);const instance={async export(){try{options?.onProgress?.(10,"Generating content...");let content;let mimeType;let finalFilename=filename;switch(config.formatExport){case"csv":content=generateCSV(data,config);mimeType="text/csv;charset=utf-8;";break;case"xlsx":content=generateXLSX(data,config);mimeType="text/csv;charset=utf-8;";finalFilename=filename.replace(".xlsx",".csv");break;case"pdf":content=generatePDFContent(data,config);mimeType="text/html;charset=utf-8;";finalFilename=filename.replace(".pdf",".html");break;default:throw new Error(`Unsupported format: ${config.formatExport}`)}options?.onProgress?.(80,"Creating file...");const bom=config.formatExport==="csv"?"\ufeff":"";const blob=new Blob([bom+content],{type:mimeType});options?.onProgress?.(100,"Export complete");return{success:true,filename:finalFilename,blob:blob,dataUrl:URL.createObjectURL(blob)}}catch(error){return{success:false,filename:filename,error:error instanceof Error?error.message:"Unknown error"}}},async download(){const result=await this.export();if(!result.success||!result.blob){console.error("Export failed:",result.error);return}const link=document.createElement("a");link.href=URL.createObjectURL(result.blob);link.download=result.filename;link.style.display="none";document.body.appendChild(link);link.click();document.body.removeChild(link);URL.revokeObjectURL(link.href)},async preview(){if(config.formatExport!=="pdf"){return null}const result=await this.export();return result.dataUrl||null},getStats(){return stats},getFilename(){return filename}};if(options?.autoDownload){instance.download()}return instance}var EXPORT_DEFAULT_COLORS=DEFAULT_COLORS4;var EXPORT_DOMAIN_ICONS=DOMAIN_ICONS;var EXPORT_DOMAIN_LABELS=DOMAIN_LABELS;var EXPORT_DOMAIN_UNITS=DOMAIN_UNITS;var DEFAULT_SHOPPING_COLORS=["#3b82f6","#8b5cf6","#f59e0b","#ef4444","#10b981","#06b6d4","#ec4899","#14b8a6","#f97316","#a855f7"];var DEFAULT_ENERGY_GROUP_COLORS={Elevadores:"#3b82f6","Escadas Rolantes":"#8b5cf6","Climatização":"#f59e0b",Climatizacao:"#f59e0b","Outros Equipamentos":"#ef4444",Lojas:"#10b981"};var DEFAULT_WATER_GROUP_COLORS={Lojas:"#10b981","Área Comum":"#0288d1","Area Comum":"#0288d1"};var DEFAULT_GAS_GROUP_COLORS={Cozinha:"#f59e0b",Aquecimento:"#ef4444",Outros:"#94a3b8"};function getDefaultGroupColors(domain){switch(domain.toLowerCase()){case"energy":return DEFAULT_ENERGY_GROUP_COLORS;case"water":return DEFAULT_WATER_GROUP_COLORS;case"gas":return DEFAULT_GAS_GROUP_COLORS;default:return DEFAULT_ENERGY_GROUP_COLORS}}function assignShoppingColors(shoppingIds){const colors={};shoppingIds.forEach((id,index)=>{colors[id]=DEFAULT_SHOPPING_COLORS[index%DEFAULT_SHOPPING_COLORS.length]});return colors}function getShoppingColor(shoppingId,shoppingColors,fallbackIndex=0){if(shoppingColors&&shoppingColors[shoppingId]){return shoppingColors[shoppingId]}if(shoppingColors){const normalizedId=shoppingId.toLowerCase();for(const[key,color]of Object.entries(shoppingColors)){if(key.toLowerCase()===normalizedId||key.toLowerCase().includes(normalizedId)){return color}}}return DEFAULT_SHOPPING_COLORS[Math.abs(fallbackIndex)%DEFAULT_SHOPPING_COLORS.length]}function getGroupColor(groupName,groupColors,domain="energy",fallbackIndex=0){if(groupColors&&groupColors[groupName]){return groupColors[groupName]}const defaultColors=getDefaultGroupColors(domain);if(defaultColors[groupName]){return defaultColors[groupName]}return DEFAULT_SHOPPING_COLORS[Math.abs(fallbackIndex)%DEFAULT_SHOPPING_COLORS.length]}function getThemeColors2(theme){if(theme==="dark"){return{text:"#f3f4f6",secondaryText:"#9ca3af",background:"#111827",cardBackground:"#1f2937",border:"#374151",grid:"#374151"}}return{text:"#1f2937",secondaryText:"#6b7280",background:"#f5f5f5",cardBackground:"#ffffff",border:"#e5e7eb",grid:"#e5e7eb"}}function getHashColor(str){let hash=0;for(let i=0;i<str.length;i++){const char=str.charCodeAt(i);hash=(hash<<5)-hash+char;hash=hash&hash}return DEFAULT_SHOPPING_COLORS[Math.abs(hash)%DEFAULT_SHOPPING_COLORS.length]}var settingsIcon=`<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:16px;height:16px;pointer-events:none"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>`;var maximizeIcon=`<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:16px;height:16px;pointer-events:none"><polyline points="15 3 21 3 21 9"/><polyline points="9 21 3 21 3 15"/><line x1="21" y1="3" x2="14" y2="10"/><line x1="3" y1="21" x2="10" y2="14"/></svg>`;function createDistributionChartWidget(config){const widgetId=`distribution-${config.domain}-${Date.now()}-${Math.random().toString(36).substr(2,9)}`;let chartInstance=null;let currentMode=config.defaultMode||"groups";let currentTheme=config.theme||"light";let currentData=null;let containerElement=null;const title=config.title||getDomainTitle(config.domain);const chartHeight=config.chartHeight||300;const showHeader=config.showHeader!==false;const showModeSelector=config.showModeSelector!==false;const showSettingsButton=config.showSettingsButton??false;const showMaximizeButton=config.showMaximizeButton??false;const decimalPlaces=config.decimalPlaces??2;const modes=config.modes||getDefaultModes(config.domain);const groupColors=config.groupColors||getDefaultGroupColors(config.domain);function getDomainTitle(domain){const titles={energy:"Distribuição de Energia",water:"Distribuição de Água",gas:"Distribuição de Gás",temperature:"Distribuição de Temperatura"};return titles[domain.toLowerCase()]||`Distribuição de ${domain}`}function getDefaultModes(domain){if(domain==="water"){return[{value:"groups",label:"Lojas vs Área Comum"},{value:"stores",label:"Lojas por Shopping"},{value:"common",label:"Área Comum por Shopping"}]}return[{value:"groups",label:"Por Grupos de Equipamentos"},{value:"elevators",label:"Elevadores por Shopping"},{value:"escalators",label:"Escadas Rolantes por Shopping"},{value:"hvac",label:"Climatização por Shopping"},{value:"others",label:"Outros Equipamentos por Shopping"},{value:"stores",label:"Lojas por Shopping"}]}function $id(id){if(config.$container){return config.$container[0].querySelector(`#${id}`)}return document.getElementById(id)}function formatValue(value){if(config.unitLarge&&config.thresholdForLargeUnit&&value>=config.thresholdForLargeUnit){return`${(value/config.thresholdForLargeUnit).toFixed(decimalPlaces)} ${config.unitLarge}`}return`${value.toFixed(decimalPlaces)} ${config.unit}`}function getColor(key,index,isGroupMode2){if(isGroupMode2){return getGroupColor(key,groupColors,config.domain,index)}const shoppingColors=config.getShoppingColors?.();return getShoppingColor(key,shoppingColors,index)}function getColors2(){return getThemeColors2(currentTheme)}function isGroupMode(){return currentMode==="groups"}function renderHTML(){const colors=getColors2();const modeOptions=modes.map(m=>`<option value="${m.value}" ${m.value===currentMode?"selected":""}>${m.label}</option>`).join("");const headerButtons=[];if(showSettingsButton){headerButtons.push(`\n <button id="${widgetId}-settings-btn" class="myio-dist-btn" title="Configurações">\n ${settingsIcon}\n </button>\n `)}if(showMaximizeButton){headerButtons.push(`\n <button id="${widgetId}-maximize-btn" class="myio-dist-btn" title="Expandir">\n ${maximizeIcon}\n </button>\n `)}return`\n <style>\n #${widgetId} {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n }\n #${widgetId} .myio-dist-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 16px;\n flex-wrap: wrap;\n gap: 12px;\n }\n #${widgetId} .myio-dist-title {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n color: ${colors.text};\n }\n #${widgetId} .myio-dist-controls {\n display: flex;\n align-items: center;\n gap: 8px;\n }\n #${widgetId} .myio-dist-label {\n font-size: 12px;\n color: ${colors.secondaryText};\n }\n #${widgetId} .myio-dist-select {\n padding: 6px 10px;\n border-radius: 6px;\n border: 1px solid ${colors.border};\n background: ${colors.cardBackground};\n color: ${colors.text};\n font-size: 12px;\n cursor: pointer;\n min-width: 180px;\n }\n #${widgetId} .myio-dist-select:focus {\n outline: none;\n border-color: #3e1a7d;\n }\n #${widgetId} .myio-dist-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 6px 8px;\n border: 1px solid ${colors.border};\n border-radius: 6px;\n background: transparent;\n color: ${colors.text};\n cursor: pointer;\n transition: all 0.2s;\n }\n #${widgetId} .myio-dist-btn:hover {\n background: #3e1a7d;\n border-color: #3e1a7d;\n color: white;\n }\n #${widgetId} .myio-dist-chart-container {\n position: relative;\n height: ${chartHeight}px;\n }\n #${widgetId} .myio-dist-loading {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: ${colors.cardBackground}ee;\n z-index: 10;\n }\n #${widgetId} .myio-dist-spinner {\n width: 32px;\n height: 32px;\n border: 3px solid ${colors.border};\n border-top-color: #3e1a7d;\n border-radius: 50%;\n animation: myio-dist-spin 0.8s linear infinite;\n }\n @keyframes myio-dist-spin {\n to { transform: rotate(360deg); }\n }\n #${widgetId} .myio-dist-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n color: ${colors.secondaryText};\n font-size: 14px;\n }\n #${widgetId} .myio-dist-empty-icon {\n font-size: 48px;\n margin-bottom: 12px;\n opacity: 0.5;\n }\n </style>\n\n <div id="${widgetId}" class="myio-distribution-widget" style="\n background: ${colors.cardBackground};\n border-radius: 12px;\n padding: 16px;\n height: 100%;\n display: flex;\n flex-direction: column;\n ">\n ${showHeader?`\n <div class="myio-dist-header">\n <h4 class="myio-dist-title">${title}</h4>\n <div class="myio-dist-controls">\n ${showModeSelector&&modes.length>1?`\n <label for="${widgetId}-mode" class="myio-dist-label">Visualizar:</label>\n <select id="${widgetId}-mode" class="myio-dist-select">\n ${modeOptions}\n </select>\n `:""}\n ${headerButtons.join("")}\n </div>\n </div>\n `:""}\n\n <div class="myio-dist-chart-container">\n <canvas id="${widgetId}-canvas"></canvas>\n <div id="${widgetId}-loading" class="myio-dist-loading" style="display: none;">\n <div class="myio-dist-spinner"></div>\n </div>\n </div>\n </div>\n `}function setLoading(loading){const loadingEl=$id(`${widgetId}-loading`);if(loadingEl){loadingEl.style.display=loading?"flex":"none"}}function buildChartData(distribution){const labels=[];const data=[];const backgroundColors=[];const total=Object.values(distribution).reduce((sum,val)=>sum+val,0);const isGroup=isGroupMode();const entries=Object.entries(distribution).filter(([_,value])=>value>0).sort((a,b)=>b[1]-a[1]);entries.forEach(([key,value],index)=>{const percentage=total>0?(value/total*100).toFixed(1):"0";labels.push(`${key} (${formatValue(value)} - ${percentage}%)`);data.push(value);backgroundColors.push(getColor(key,index,isGroup))});return{labels:labels,data:data,backgroundColors:backgroundColors,total:total}}function renderEmptyState(){const container=$id(`${widgetId}-canvas`)?.parentElement;if(container){const emptyEl=document.createElement("div");emptyEl.className="myio-dist-empty";emptyEl.innerHTML=`\n <div class="myio-dist-empty-icon">📊</div>\n <div>Sem dados disponíveis</div>\n `;const canvas=$id(`${widgetId}-canvas`);if(canvas)canvas.style.display="none";const existingEmpty=container.querySelector(".myio-dist-empty");if(!existingEmpty){container.appendChild(emptyEl)}}}function removeEmptyState(){const container=$id(`${widgetId}-canvas`)?.parentElement;if(container){const emptyEl=container.querySelector(".myio-dist-empty");if(emptyEl)emptyEl.remove();const canvas=$id(`${widgetId}-canvas`);if(canvas)canvas.style.display="block"}}async function updateChart(){const canvas=$id(`${widgetId}-canvas`);if(!canvas){console.error(`[${config.domain.toUpperCase()}] Distribution canvas not found`);return}setLoading(true);try{const distribution=await config.fetchDistribution(currentMode);if(!distribution||Object.keys(distribution).length===0){console.warn(`[${config.domain.toUpperCase()}] No distribution data for mode: ${currentMode}`);currentData=null;setLoading(false);renderEmptyState();return}removeEmptyState();currentData=distribution;const{labels:labels,data:data,backgroundColors:backgroundColors,total:total}=buildChartData(distribution);const colors=getColors2();const Chart2=window.Chart;if(!Chart2){throw new Error("Chart.js not loaded")}if(chartInstance){chartInstance.destroy();chartInstance=null}const ctx=canvas.getContext("2d");if(!ctx){throw new Error("Could not get canvas context")}chartInstance=new Chart2(ctx,{type:"bar",data:{labels:labels,datasets:[{label:`Consumo (${config.unit})`,data:data,backgroundColor:backgroundColors}]},options:{responsive:true,maintainAspectRatio:false,indexAxis:"y",animation:false,plugins:{legend:{display:false},tooltip:{callbacks:{label:context=>{const value=context.parsed.x||0;const percentage=total>0?(value/total*100).toFixed(1):"0";return`${formatValue(value)} (${percentage}%)`}}}},scales:{x:{beginAtZero:true,ticks:{callback:value=>formatValue(Number(value)),color:colors.secondaryText,font:{size:11}},grid:{color:colors.grid}},y:{ticks:{font:{size:11},color:colors.text},grid:{display:false}}}}});config.onDataLoaded?.(distribution);console.log(`[${config.domain.toUpperCase()}] Distribution chart updated for mode: ${currentMode}`)}catch(error){console.error(`[${config.domain.toUpperCase()}] Error updating distribution chart:`,error);config.onError?.(error);renderEmptyState()}finally{setLoading(false)}}function setupListeners(){const modeSelect=$id(`${widgetId}-mode`);if(modeSelect){modeSelect.addEventListener("change",async e=>{currentMode=e.target.value;config.onModeChange?.(currentMode);await updateChart()})}if(showSettingsButton){const settingsBtn=$id(`${widgetId}-settings-btn`);if(settingsBtn){settingsBtn.addEventListener("click",()=>{config.onSettingsClick?.()})}}if(showMaximizeButton){const maximizeBtn=$id(`${widgetId}-maximize-btn`);if(maximizeBtn){maximizeBtn.addEventListener("click",()=>{config.onMaximizeClick?.()})}}}function updateThemeStyles(){const colors=getColors2();const widget=$id(widgetId);if(widget){widget.style.background=colors.cardBackground;const title2=widget.querySelector(".myio-dist-title");if(title2)title2.style.color=colors.text;const labels=widget.querySelectorAll(".myio-dist-label");labels.forEach(l=>l.style.color=colors.secondaryText);const select=widget.querySelector(".myio-dist-select");if(select){select.style.background=colors.cardBackground;select.style.color=colors.text;select.style.borderColor=colors.border}const buttons=widget.querySelectorAll(".myio-dist-btn");buttons.forEach(b=>{b.style.color=colors.text;b.style.borderColor=colors.border})}}const instance={async render(){containerElement=$id(config.containerId);if(!containerElement){throw new Error(`Container #${config.containerId} not found`)}containerElement.innerHTML=renderHTML();setupListeners();await updateChart()},async setMode(mode){currentMode=mode;const modeSelect=$id(`${widgetId}-mode`);if(modeSelect){modeSelect.value=mode}config.onModeChange?.(mode);await updateChart()},async refresh(){await updateChart()},setTheme(theme){currentTheme=theme;updateThemeStyles();if(currentData){updateChart()}},destroy(){if(chartInstance){chartInstance.destroy();chartInstance=null}if(containerElement){containerElement.innerHTML=""}currentData=null},getChartInstance:()=>chartInstance,getCurrentMode:()=>currentMode,getCurrentData:()=>currentData};return instance}exports.CHART_COLORS=CHART_COLORS;exports.CONSUMPTION_CHART_COLORS=DEFAULT_COLORS;exports.CONSUMPTION_CHART_DEFAULTS=DEFAULT_CONFIG;exports.CONSUMPTION_THEME_COLORS=THEME_COLORS;exports.ConnectionStatusType=ConnectionStatusType;exports.DEFAULT_CLAMP_RANGE=DEFAULT_CLAMP_RANGE;exports.DEFAULT_ENERGY_GROUP_COLORS=DEFAULT_ENERGY_GROUP_COLORS;exports.DEFAULT_GAS_GROUP_COLORS=DEFAULT_GAS_GROUP_COLORS;exports.DEFAULT_SHOPPING_COLORS=DEFAULT_SHOPPING_COLORS;exports.DEFAULT_WATER_GROUP_COLORS=DEFAULT_WATER_GROUP_COLORS;exports.DeviceStatusType=DeviceStatusType;exports.EXPORT_DEFAULT_COLORS=EXPORT_DEFAULT_COLORS;exports.EXPORT_DOMAIN_ICONS=EXPORT_DOMAIN_ICONS;exports.EXPORT_DOMAIN_LABELS=EXPORT_DOMAIN_LABELS;exports.EXPORT_DOMAIN_UNITS=EXPORT_DOMAIN_UNITS;exports.MyIOChartModal=MyIOChartModal;exports.MyIODraggableCard=MyIODraggableCard;exports.MyIOSelectionStoreClass=MyIOSelectionStoreClass;exports.MyIOToast=MyIOToast;exports.addDetectionContext=addDetectionContext;exports.addNamespace=addNamespace;exports.aggregateByDay=aggregateByDay;exports.assignShoppingColors=assignShoppingColors;exports.averageByDay=averageByDay;exports.buildListItemsThingsboardByUniqueDatasource=buildListItemsThingsboardByUniqueDatasource;exports.buildMyioIngestionAuth=buildMyioIngestionAuth;exports.buildTemplateExport=buildTemplateExport;exports.buildWaterReportCSV=buildWaterReportCSV;exports.buildWaterStoresCSV=buildWaterStoresCSV;exports.calcDeltaPercent=calcDeltaPercent;exports.calculateDeviceStatus=calculateDeviceStatus;exports.calculateDeviceStatusWithRanges=calculateDeviceStatusWithRanges;exports.calculateExportStats=calculateStats2;exports.calculateStats=calculateStats;exports.clampTemperature=clampTemperature;exports.classify=classify;exports.classifyWaterLabel=classifyWaterLabel;exports.classifyWaterLabels=classifyWaterLabels;exports.clearAllAuthCaches=clearAllAuthCaches;exports.connectionStatusIcons=connectionStatusIcons;exports.createConsumption7DaysChart=createConsumption7DaysChart;exports.createConsumptionChartWidget=createConsumptionChartWidget;exports.createConsumptionModal=createConsumptionModal;exports.createDateRangePicker=createDateRangePicker2;exports.createDistributionChartWidget=createDistributionChartWidget;exports.createInputDateRangePickerInsideDIV=createInputDateRangePickerInsideDIV;exports.createModalHeader=createModalHeader;exports.decodePayload=decodePayload;exports.decodePayloadBase64Xor=decodePayloadBase64Xor;exports.detectDeviceType=detectDeviceType;exports.determineInterval=determineInterval;exports.deviceStatusIcons=deviceStatusIcons;exports.exportTemperatureCSV=exportTemperatureCSV;exports.exportToCSV=exportToCSV;exports.exportToCSVAll=exportToCSVAll;exports.extractMyIOCredentials=extractMyIOCredentials;exports.fetchTemperatureData=fetchTemperatureData;exports.fetchThingsboardCustomerAttrsFromStorage=fetchThingsboardCustomerAttrsFromStorage;exports.fetchThingsboardCustomerServerScopeAttrs=fetchThingsboardCustomerServerScopeAttrs;exports.findValue=findValue;exports.findValueWithDefault=findValueWithDefault;exports.fmtPerc=fmtPerc;exports.fmtPercLegacy=fmtPerc2;exports.formatAllInSameUnit=formatAllInSameUnit;exports.formatAllInSameWaterUnit=formatAllInSameWaterUnit;exports.formatDateForInput=formatDateForInput;exports.formatDateToYMD=formatDateToYMD;exports.formatDateWithTimezoneOffset=formatDateWithTimezoneOffset;exports.formatDuration=formatDuration;exports.formatEnergy=formatEnergy;exports.formatNumberReadable=formatNumberReadable;exports.formatRelativeTime=formatRelativeTime;exports.formatTankHeadFromCm=formatTankHeadFromCm;exports.formatTemperature=formatTemperature2;exports.formatWater=formatWater;exports.formatWaterByGroup=formatWaterByGroup;exports.formatWaterVolumeM3=formatWaterVolumeM3;exports.formatarDuracao=formatarDuracao;exports.generateExportFilename=generateFilename;exports.getAuthCacheStats=getAuthCacheStats;exports.getAvailableContexts=getAvailableContexts;exports.getConnectionStatusIcon=getConnectionStatusIcon;exports.getDateRangeArray=getDateRangeArray;exports.getDefaultGroupColors=getDefaultGroupColors;exports.getDeviceStatusIcon=getDeviceStatusIcon;exports.getDeviceStatusInfo=getDeviceStatusInfo;exports.getDistributionThemeColors=getThemeColors2;exports.getGroupColor=getGroupColor;exports.getHashColor=getHashColor;exports.getModalHeaderStyles=getModalHeaderStyles;exports.getSaoPauloISOString=getSaoPauloISOString;exports.getSaoPauloISOStringFixed=getSaoPauloISOStringFixed;exports.getShoppingColor=getShoppingColor;exports.getValueByDatakey=getValueByDatakey;exports.getValueByDatakeyLegacy=getValueByDatakeyLegacy;exports.getWaterCategories=getWaterCategories;exports.groupByDay=groupByDay;exports.interpolateTemperature=interpolateTemperature;exports.isDeviceOffline=isDeviceOffline;exports.isValidConnectionStatus=isValidConnectionStatus;exports.isValidDeviceStatus=isValidDeviceStatus;exports.isWaterCategory=isWaterCategory;exports.mapConnectionStatus=mapConnectionStatus;exports.mapDeviceStatusToCardStatus=mapDeviceStatusToCardStatus;exports.mapDeviceToConnectionStatus=mapDeviceToConnectionStatus;exports.myioExportData=myioExportData;exports.normalizeRecipients=normalizeRecipients;exports.numbers=numbers_exports;exports.openDashboardPopup=openDashboardPopup;exports.openDashboardPopupAllReport=openDashboardPopupAllReport;exports.openDashboardPopupEnergy=openDashboardPopupEnergy;exports.openDashboardPopupReport=openDashboardPopupReport;exports.openDashboardPopupSettings=openDashboardPopupSettings;exports.openDashboardPopupWaterTank=openDashboardPopupWaterTank;exports.openDemandModal=openDemandModal;exports.openGoalsPanel=openGoalsPanel;exports.openRealTimeTelemetryModal=openRealTimeTelemetryModal;exports.openTemperatureComparisonModal=openTemperatureComparisonModal;exports.openTemperatureModal=openTemperatureModal;exports.openTemperatureSettingsModal=openTemperatureSettingsModal;exports.parseInputDateToDate=parseInputDateToDate;exports.renderCardComponent=renderCardComponent;exports.renderCardComponentEnhanced=renderCardComponent2;exports.renderCardComponentHeadOffice=renderCardComponentHeadOffice;exports.renderCardComponentLegacy=renderCardComponentLegacy;exports.renderCardComponentV2=renderCardComponentV2;exports.renderCardComponentV5=renderCardComponent3;exports.renderCardV5=renderCardComponentV5;exports.shouldFlashIcon=shouldFlashIcon;exports.strings=strings_exports;exports.timeWindowFromInputYMD=timeWindowFromInputYMD;exports.toCSV=toCSV;exports.toFixedSafe=toFixedSafe;exports.waterDeviceStatusIcons=waterDeviceStatusIcons});
|
|
1
|
+
(function(global,factory){typeof exports==="object"&&typeof module!=="undefined"?factory(exports):typeof define==="function"&&define.amd?define(["exports"],factory):(global=typeof globalThis!=="undefined"?globalThis:global||self,factory(global.MyIOLibrary={}))})(this,function(exports){"use strict";var __defProp=Object.defineProperty;var __getOwnPropDesc=Object.getOwnPropertyDescriptor;var __getOwnPropNames=Object.getOwnPropertyNames;var __hasOwnProp=Object.prototype.hasOwnProperty;var __esm=(fn,res)=>function __init(){return fn&&(res=(0,fn[__getOwnPropNames(fn)[0]])(fn=0)),res};var __export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:true})};var __copyProps=(to,from,except,desc)=>{if(from&&typeof from==="object"||typeof from==="function"){for(let key of __getOwnPropNames(from))if(!__hasOwnProp.call(to,key)&&key!==except)__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable})}return to};var __toCommonJS=mod=>__copyProps(__defProp({},"__esModule",{value:true}),mod);var template_card_exports={};__export(template_card_exports,{renderCardComponent:()=>renderCardComponent});function renderCardComponent({entityObject:entityObject,handleActionDashboard:handleActionDashboard,handleActionReport:handleActionReport,handleActionSettings:handleActionSettings,handleSelect:handleSelect,handInfo:handInfo,handleClickCard:handleClickCard}){const{entityId:entityId,labelOrName:labelOrName,deviceIdentifier:deviceIdentifier,entityType:entityType,deviceType:deviceType,slaveId:slaveId,ingestionId:ingestionId,val:val,centralId:centralId,updatedIdentifiers:updatedIdentifiers={},perc:perc=0,connectionStatus:connectionStatus,centralName:centralName,connectionStatusTime:connectionStatusTime,timaVal:timaVal,valType:valType}=entityObject;const DeviceStatus={CONNECTED:"connected",OFFLINE:"offline",POWER_ON:"power_on",STANDBY:"standby",POWER_OFF:"power_off",WARNING:"warning",DANGER:"danger",MAINTENANCE:"maintenance"};const statusIcons={[DeviceStatus.CONNECTED]:"✅",[DeviceStatus.OFFLINE]:"🔌",[DeviceStatus.POWER_ON]:"⚡",[DeviceStatus.STANDBY]:"⏸️",[DeviceStatus.POWER_OFF]:"⏹️",[DeviceStatus.WARNING]:"⚠️",[DeviceStatus.DANGER]:"🚨",[DeviceStatus.MAINTENANCE]:"🛠️"};const isOfflineOrDanger=connectionStatus===DeviceStatus.OFFLINE||connectionStatus===DeviceStatus.DANGER;const shouldFlashIcon2=connectionStatus===DeviceStatus.OFFLINE||connectionStatus===DeviceStatus.WARNING||connectionStatus===DeviceStatus.DANGER||connectionStatus===DeviceStatus.MAINTENANCE;const icon=statusIcons[connectionStatus]||statusIcons[DeviceStatus.POWER_ON];const MyIO=typeof MyIOLibrary!=="undefined"&&MyIOLibrary||typeof window!=="undefined"&&window.MyIOLibrary||{formatEnergy:(v,g)=>`${v} kWh`,formatNumberReadable:n=>Number(n??0).toFixed(1)};let valFormatted=MyIO.formatEnergy(val);if(valType==="ENERGY"){valFormatted=MyIO.formatEnergy(val)}else if(valType==="WATER"){valFormatted=`${val} m³`}else if(valType==="TANK"){valFormatted=`${val} m.c.a`}else{valFormatted=val}const percFormatted=MyIO.formatNumberReadable(perc);if(!document.getElementById("myio-card-styles")){const style=document.createElement("style");style.id="myio-card-styles";style.textContent=` \n.device-card-centered,\n.clickable {\n width: 100%;\n border-radius: 10px;\n padding: 8px 12px;\n background: #fff;\n box-shadow: 0 4px 10px rgba(0, 0, 0, .05);\n /* display: flex; REMOVIDO */\n /* align-items: center; REMOVIDO */\n /* justify-content: center; REMOVIDO */\n cursor: pointer;\n transition: transform .2s;\n /* min-height: 140px; REMOVIDO (movido para .device-card-inner) */\n box-sizing: border-box;\n overflow: hidden;\n}\n\n.device-card-centered:hover,\n.clickable:hover {\n transform: scale(1.05);\n}\n\n.device-title-row {\n width: 100%;\n display: flex;\n justify-content: center;\n align-items: center;\n margin-bottom: 4px;\n padding: 0 4px;\n min-height: 22px;\n}\n\n.device-title {\n font-weight: 700;\n font-size: .85rem;\n text-align: center;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n max-width: 90%;\n line-height: 1.1;\n}\n\n.device-subtitle {\n font-size: 0.7rem; \n font-weight: 500;\n color: #888; /* Cinza */\n text-align: center;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n max-width: 90%;\n line-height: 1.1;\n margin-top: 2px;\n}\n\n.device-image {\n max-height: 44px;\n width: auto;\n margin: 4px 0;\n display: block;\n}\n\n.device-data-row {\n display: flex;\n justify-content: center;\n align-items: center;\n margin-top: auto;\n margin-bottom: 6px;\n gap: 6px;\n width: 100%;\n}\n\n.consumption-main {\n font-size: .9rem;\n font-weight: 700;\n color: #28a745;\n display: flex;\n align-items: center;\n gap: 6px;\n justify-content: center;\n white-space: nowrap;\n}\n\n.device-title-percent {\n font-size: .75rem;\n color: rgba(0, 0, 0, .45);\n font-weight: 500;\n}\n\n.flash {\n animation: flash 1s infinite;\n color: #ff9800;\n}\n\n@keyframes flash {\n 0% { opacity: 1; }\n 50% { opacity: .2; }\n 100% { opacity: 1; }\n}\n\n.card-actions {\n flex-shrink: 0;\n height: 100%;\n box-shadow: 1px 0 2px rgba(0, 0, 0, .1);\n display: flex;\n flex-direction: column;\n padding: 0 4px;\n justify-content: space-around;\n align-items: center;\n}\n\n.card-action img {\n width: 24px;\n height: 24px;\n transition: transform .2s ease;\n cursor: pointer;\n}\n\n.card-action img:hover {\n transform: scale(1.15);\n}\n\n.device-card-centered.offline {\n border: 2px solid #ff4d4f;\n animation: border-blink 1s infinite;\n}\n\n.device-card-centered.flipped .device-card-inner {\n transform: rotateY(180deg);\n}\n\n@keyframes border-blink {\n 0%, 100% { box-shadow: 0 0 8px rgba(255, 77, 79, 0.9); }\n 50% { box-shadow: 0 0 16px rgba(255, 0, 0, 0.6); }\n}\n\n.device-card-centered.offline .flash-icon {\n color: #ff4d4f !important;\n font-size: 1.2rem;\n} \n.device-card-centered {\n perspective: 1000px;\n}\n\n.device-card-inner {\n position: relative;\n width: 100%;\n height: 100%;\n transform-style: preserve-3d;\n min-height: 140px; /* ATUALIZADO: A altura mínima agora é controlada aqui */\n}\n\n.device-card-front {\n display: flex;\n flex-direction: row;\n justify-content: flex-start;\n align-items: flex-start; /* Agora isso vai funcionar */\n position: absolute;\n width: 100%;\n height: 100%;\n top: 0;\n left: 0;\n backface-visibility: hidden;\n gap: 4px;\n}\n\n.device-card-back {\n transform: rotateY(180deg);\n}\n\n.device-card-front .device-info,\n.device-card-back .device-info {\n position: absolute;\n top: 8px;\n right: 12px;\n margin: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 4px;\n border-radius: 50%;\n cursor: pointer;\n z-index: 10;\n background: none;\n border: none;\n transition: background 0.2s;\n}\n.device-card-front .device-info:hover,\n.device-card-back .device-info:hover {\n background: rgba(0, 0, 0, 0.05);\n}\n\n\n.device-card-back {\n display: flex;\n flex-direction: column;\n justify-content: space-around; /* Mantido para preencher o verso */\n align-items: stretch;\n padding: 10px;\n gap: 5px;\n height: 100%;\n width: 100%;\n box-sizing: border-box;\n \n /* Posições para o flip */\n position: absolute;\n top: 0;\n left: 0;\n backface-visibility: hidden;\n transform: rotateY(180deg);\n}\n\n.device-card-back #status-bar {\n position: relative;\n width: 100%;\n padding-top: 10px;\n}\n\n.device-card-back .value-container {\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: 5px;\n color: #c19efc;\n align-self: center;\n}\n\n.device-card-back #lastconsumptionTime {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 2px;\n font-size: 11px;\n font-weight: bold;\n color: black;\n width: 100%;\n padding-bottom: 10px;\n}\n\n\n.device-card-centered.flipped .device-card-inner {\n transform: rotateY(180deg);\n}\n\n.flash-icon.flash {\n animation: icon-blink 1s infinite;\n}\n\n@keyframes icon-blink {\n 0%, 100% { opacity: 1; transform: scale(1); }\n 50% { opacity: 0.2; transform: scale(1.2); }\n}\n\n.device-card-centered.offline .flash-icon {\n color: #ff4d4f !important;\n}\n\n.device-card-centered.online .flash-icon {\n color: #28a745 !important;\n}\n `;document.head.appendChild(style)}let formattedDateVal;let formattedDate;let Infcolor="#5cb85c";if(connectionStatusTime){const date=new Date(connectionStatusTime);const day=String(date.getDate()).padStart(2,"0");const month=String(date.getMonth()+1).padStart(2,"0");const year=date.getFullYear();const hours=String(date.getHours()).padStart(2,"0");const minutes=String(date.getMinutes()).padStart(2,"0");formattedDate=`Online: (${day}/${month}/${year} - ${hours}:${minutes})`;const datVal=new Date(timaVal);const dayVal=String(datVal.getDate()).padStart(2,"0");const monthVal=String(datVal.getMonth()+1).padStart(2,"0");const yearVal=date.getFullYear();const hoursVal=String(date.getHours()).padStart(2,"0");const minutesVal=String(date.getMinutes()).padStart(2,"0");const now=new Date;const diffMs=now-datVal;const diffMinutes=Math.floor(diffMs/(1e3*60));const diffHours=Math.floor(diffMs/(1e3*60*60));const diffDays=Math.floor(diffMs/(1e3*60*60*24));let diffText="";if(diffMinutes<60){diffText=`${diffMinutes} minuto${diffMinutes!==1?"s":""}`}else if(diffHours<24){diffText=`${diffHours} hora${diffHours!==1?"s":""}`}else{diffText=`${diffDays} dia${diffDays!==1?"s":""} `}formattedDateVal=`${dayVal}/${monthVal}/${yearVal} - ${hoursVal}:${minutesVal} (${diffText})`;if(diffHours>=24){Infcolor="#cc2900"}else if(diffMinutes>=30){Infcolor="#e89105"}}else{Infcolor="#d6dcdd"}function normalizeString(str){if(typeof str!=="string"){str=""}return str.normalize("NFD").replace(/[\u0300-\u036f]/g,"").toUpperCase()}const deviceImages={MOTOR:"https://dashboard.myio-bas.com/api/images/public/8Ezn8qVBJ3jXD0iDfnEAZ0MZhAP1b5Ts","3F_MEDIDOR":"https://dashboard.myio-bas.com/api/images/public/f9Ce4meybsdaAhAkUlAfy5ei3I4kcN4k",RELOGIO:"https://dashboard.myio-bas.com/api/images/public/ljHZostWg0G5AfKiyM8oZixWRIIGRASB",HIDROMETRO:"https://dashboard.myio-bas.com/api/images/public/aMQYFJbGHs9gQbQkMn6XseAlUZHanBR4",ENTRADA:"https://dashboard.myio-bas.com/api/images/public/TQHPFqiejMW6lOSVsb8Pi85WtC0QKOLU",CAIXA_DAGUA:"https://dashboard.myio-bas.com/api/images/public/3t6WVhMQJFsrKA8bSZmrngDsNPkZV7fq"};const defaultImage="https://cdn-icons-png.flaticon.com/512/1178/1178428.png";const nameType=normalizeString(deviceType);const img=deviceImages[nameType]||defaultImage;const html=`\n <div class="device-card-centered clickable ${isOfflineOrDanger?"offline":""}"\n data-entity-id="${entityId}"\n data-entity-label="${labelOrName}"\n data-entity-type="${entityType}"\n data-entity-slaveid="${slaveId}"\n data-entity-ingestionid="${ingestionId}"\n data-entity-consumption="${val}"\n data-entity-centralid="${centralId}"\n data-entity-updated-identifiers='${JSON.stringify(updatedIdentifiers)}'>\n <div class="device-card-inner" style="width:100%; height:100%; transform-style: preserve-3d; transition: transform 0.6s;">\n <div class="device-card-front">\n\n <div class="card-actions" >\n ${typeof handleActionDashboard==="function"?`\n <div class="card-action action-dashboard" data-action="dashboard" title="Dashboard">\n <img src="https://dashboard.myio-bas.com/api/images/public/TAVXE0sTbCZylwGsMF9lIWdllBB3iFtS"/>\n </div>`:``}\n ${typeof handleActionReport==="function"?`\n <div class="card-action action-report" data-action="report" title="Relatório">\n <img src="https://dashboard.myio-bas.com/api/images/public/d9XuQwMYQCG2otvtNSlqUHGavGaSSpz4"/>\n </div>`:``}\n ${typeof handleActionSettings==="function"?`\n <div class="card-action action-settings" data-action="settings" title="Configurações">\n <img src="https://dashboard.myio-bas.com/api/images/public/5n9tze6vED2uwIs5VvJxGzNNZ9eV4yoz"/>\n </div>`:``}\n ${typeof handleSelect==="function"?`\n <input class="card-action action-checker" data-action="checker" title="Selecionar" type="checkbox">`:``}\n </div>\n\n <div style="display:flex;flex-direction:column;justify-content:center;align-items:center;height:100%; flex-grow: 1; min-width: 0; padding: 0 12px;">\n \n <div class="device-title-row" style="flex-direction: column; min-height: 38px;">\n <span class="device-title" title="${labelOrName}">\n ${String(labelOrName??"").length>15?String(labelOrName).slice(0,15)+"…":String(labelOrName??"")}\n </span>\n ${deviceIdentifier?`\n <span class="device-subtitle" title="${deviceIdentifier}">\n ${deviceIdentifier}\n </span>\n `:""}\n </div>\n\n <img class="device-image" src="${img}" />\n <div class="device-data-row">\n <div class="consumption-main">\n \n <span class="flash-icon ${shouldFlashIcon2?"flash":""}">\n ${icon}\n </span>\n\n <span class="consumption-value" data-entity-consumption="${val}">${valFormatted}</span>\n <span class="device-title-percent">(${percFormatted}%)</span>\n </div>\n </div>\n </div>\n \n ${handInfo?`\n <button id="infoButtom-front" class="device-info info-button">\n <svg xmlns="http://www.w3.org/2000/svg" height="17px" viewBox="0 -960 960 960" width="17px" fill=${Infcolor}>\n <path d="M440-280h80v-240h-80v240Zm40-320q17 0 28.5-11.5T520-640q0-17-11.5-28.5T480-680q-17 0-28.5 11.5T440-640q0 17 11.5 28.5T480-600Zm0 520q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/>\n </svg>\n </button>`:``}\n </div>\n <div class="device-card-back" >\n <div id="status-bar"> \n <div id="status-information">\n <div style="font-size: 0.85rem; font-weight: bold; line-height: 1;"><span>Central: ${centralName}</span></div>\n <div style="display: flex; flex-direction: row; gap: 4px; font-weight: bold; align-items: center;">\n <div style="font-size: 12px; font-weight: bold; line-height: 1;"><span> ${formattedDate}</span></div>\n <div style="font-size: 10px; line-height: 1;"><span></span></div> \n </div>\n </div>\n <button id="infoButtom-back" class="device-info" style="background:none; border:none; cursor:pointer;">\n <svg xmlns="http://www.w3.org/2000/svg" height="17px" viewBox="0 -960 960 960" width="17px" fill=${Infcolor}>\n <path d="M440-280h80v-240h-80v240Zm40-320q17 0 28.5-11.5T520-640q0-17-11.5-28.5T480-680q-17 0-28.5 11.5T440-640q0 17 11.5 28.5T480-600Zm0 520q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/>\n </svg>\n </button>\n </div>\n\n <div class="value-container">\n <svg xmlns="http://www.w3.org/2000/svg" height="30px" viewBox="0 -960 960 960" width="30px" fill="#dea404">\n <path d="m456-200 174-340H510v-220L330-420h126v220Zm24 120q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/>\n </svg>\n <div><span>${MyIO.formatEnergy(val/1e3)||"-"}</span></div>\n </div>\n\n <div id="lastconsumptionTime">\n <div style="font-size: 12px; font-weight: bold; color: black; line-height: 1;">Ultima Telemetria:</div>\n <div style="font-size: 11px; font-weight: bold; color: black; line-height: 1;">${formattedDateVal}</div>\n </div>\n </div>\n </div>\n </div> \n </div>\n \n `;const $card=$(html);if(typeof handleActionDashboard==="function"){$card.find(".action-dashboard").on("click",e=>{e.stopPropagation();handleActionDashboard(entityObject)})}if(typeof handleActionReport==="function"){$card.find(".action-report").on("click",e=>{e.stopPropagation();handleActionReport(entityObject)})}if(typeof handleActionSettings==="function"){$card.find(".action-settings").on("click",e=>{e.stopPropagation();handleActionSettings(entityObject)})}if(typeof handleSelect==="function"){$card.find(".action-checker").on("click",e=>{e.stopPropagation();handleSelect(entityObject)})}if(typeof handleClickCard==="function"){$card.find(".action-checker").on("click",e=>{e.stopPropagation();handleClickCard(entityObject)})}$card.on("click",e=>{if(!$(e.target).closest(".card-action").length){if(typeof handleClickCard==="function"){handleClickCard(entityObject)}else if(typeof handleActionDashboard==="function"){handleActionDashboard(entityObject)}}});$card.find("#infoButtom-front").on("click",function(e){e.stopPropagation();$(this).closest(".device-card-centered").addClass("flipped")});$card.find("#infoButtom-back").on("click",function(e){e.stopPropagation();$(this).closest(".device-card-centered").removeClass("flipped")});return $card}var init_template_card=__esm({"src/thingsboard/main-dashboard-shopping/v-4.0.0/card/template-card.js"(){}});function formatEnergy(value,unit){if(value===null||value===void 0||isNaN(value)){return"-"}let adjustedValue=value;let adjustedUnit=unit;if(!adjustedUnit){if(value>=1e6){adjustedValue=value/1e6;adjustedUnit="GWh"}else if(value>=1e3){adjustedValue=value/1e3;adjustedUnit="MWh"}else{adjustedUnit="kWh"}}const formattedValue=adjustedValue.toLocaleString("pt-BR",{minimumFractionDigits:2,maximumFractionDigits:2});return`${formattedValue} ${adjustedUnit}`}function formatAllInSameUnit(values,targetUnit,sourceUnit="kWh"){const unitMultipliers={kWh:1,MWh:1e3,GWh:1e6};const targetMultiplier=unitMultipliers[targetUnit]||1;if(typeof values[0]==="number"){const numberValues=values;const sourceMultiplier=unitMultipliers[sourceUnit]||1;return numberValues.map(value=>{if(value===null||value===void 0||isNaN(value)){return"-"}const convertedValue=value*sourceMultiplier/targetMultiplier;return formatEnergy(convertedValue,targetUnit)})}const objectValues=values;return objectValues.map(item=>{if(item.value===null||item.value===void 0||isNaN(item.value)){return"-"}const sourceMultiplier=unitMultipliers[item.unit]||1;const convertedValue=item.value*sourceMultiplier/targetMultiplier;return formatEnergy(convertedValue,targetUnit)})}function fmtPerc(value){if(value===null||value===void 0||isNaN(value)||!isFinite(value)){return"-"}const percentage=value*100;return percentage.toLocaleString("pt-BR",{minimumFractionDigits:2,maximumFractionDigits:2})+"%"}function formatNumberReadable(value,locale="pt-BR",minimumFractionDigits=2,maximumFractionDigits=2){const n=typeof value==="string"?Number(value.replace(",",".")):Number(value);if(!Number.isFinite(n)){return"-"}const safe=Object.is(n,-0)?0:n;return safe.toLocaleString(locale,{minimumFractionDigits:minimumFractionDigits,maximumFractionDigits:maximumFractionDigits})}function formatWater(value){const num=Number(value)||0;return`${num.toFixed(2)} m³`}function formatWaterVolumeM3(value,locale="pt-BR"){if(value===null||value===void 0||isNaN(value)){return"-"}const formattedValue=value.toLocaleString(locale,{minimumFractionDigits:2,maximumFractionDigits:2});return`${formattedValue} M³`}function formatTankHeadFromCm(valueCm,locale="pt-BR"){if(valueCm===null||valueCm===void 0||isNaN(valueCm)){return"-"}const valueMeters=valueCm/100;const formattedValue=valueMeters.toLocaleString(locale,{minimumFractionDigits:2,maximumFractionDigits:2});return`${formattedValue} m.c.a.`}function calcDeltaPercent(prev,current){if(prev===null||prev===void 0||isNaN(prev)||current===null||current===void 0||isNaN(current)){return{value:0,type:"neutral"}}if(prev===0&¤t===0){return{value:0,type:"neutral"}}if(prev===0&¤t>0){return{value:100,type:"increase"}}if(prev===0&¤t<0){return{value:100,type:"decrease"}}const percentChange=(current-prev)/prev*100;if(percentChange>0){return{value:percentChange,type:"increase"}}else if(percentChange<0){return{value:Math.abs(percentChange),type:"decrease"}}else{return{value:0,type:"neutral"}}}function formatWaterByGroup(value,group){if(value===null||value===void 0||isNaN(value)){return"-"}if(group==="Caixas D'Água"){return formatTankHeadFromCm(value)}if(value>=1e3){return formatWaterVolumeM3(value/1e3)+" x 10³ "}return formatWaterVolumeM3(value)}function formatAllInSameWaterUnit(values){const max=Math.max(...values.filter(v=>!isNaN(v)&&v!==null&&v!==void 0));let divisor=1;let unit="M³";if(max>=1e6){divisor=1e6;unit="M³"}else if(max>=1e3){divisor=1e3;unit="M³"}return{format:val=>{if(val===null||val===void 0||isNaN(val)){return"-"}return(val/divisor).toFixed(2)+" "+unit},unit:unit}}function formatRelativeTime(timestamp){if(!timestamp||timestamp<=0){return"—"}const now=Date.now();const diffSeconds=Math.round((now-timestamp)/1e3);if(diffSeconds<10){return"agora"}if(diffSeconds<60){return`há ${diffSeconds}s`}const diffMinutes=Math.round(diffSeconds/60);if(diffMinutes===1){return"há 1 min"}if(diffMinutes<60){return`há ${diffMinutes} mins`}const diffHours=Math.round(diffMinutes/60);if(diffHours===1){return"há 1 hora"}if(diffHours<24){return`há ${diffHours} horas`}const diffDays=Math.round(diffHours/24);if(diffDays===1){return"ontem"}if(diffDays<=30){return`há ${diffDays} dias`}return new Date(timestamp).toLocaleDateString("pt-BR")}function formatarDuracao(ms){if(typeof ms!=="number"||ms<0||!isFinite(ms)){return"0s"}if(ms===0){return"0s"}const segundos=Math.floor(ms/1e3%60);const minutos=Math.floor(ms/(1e3*60)%60);const horas=Math.floor(ms/(1e3*60*60)%24);const dias=Math.floor(ms/(1e3*60*60*24));const parts=[];if(dias>0){parts.push(`${dias}d`);if(horas>0){parts.push(`${horas}h`)}}else if(horas>0){parts.push(`${horas}h`);if(minutos>0){parts.push(`${minutos}m`)}}else if(minutos>0){parts.push(`${minutos}m`);if(segundos>0){parts.push(`${segundos}s`)}}else{parts.push(`${segundos}s`)}return parts.length>0?parts.join(" "):"0s"}var formatDuration=formatarDuracao;function formatDateToYMD(date){if(!date){return""}const dateObj=new Date(date);if(isNaN(dateObj.getTime())){return""}const year=dateObj.getFullYear();const month=String(dateObj.getMonth()+1).padStart(2,"0");const day=String(dateObj.getDate()).padStart(2,"0");return`${year}-${month}-${day}`}function determineInterval(startDate,endDate){const start=new Date(startDate);const end=new Date(endDate);if(isNaN(start.getTime())||isNaN(end.getTime())){return"day"}const diffMs=end.getTime()-start.getTime();const diffDays=diffMs/(1e3*60*60*24);if(diffDays<=1){return"hour"}else if(diffDays<=7){return"day"}else if(diffDays<=31){return"week"}else if(diffDays<=365){return"month"}else{return"year"}}function getSaoPauloISOString(date,edge="start"){const dateObj=new Date(date);if(isNaN(dateObj.getTime())){return""}const saoPauloOffset=-3;const saoPauloDate=new Date(dateObj.getTime()+saoPauloOffset*60*60*1e3);if(edge==="start"){saoPauloDate.setHours(0,0,0,0)}else{saoPauloDate.setHours(23,59,59,999)}const utcDate=new Date(saoPauloDate.getTime()-saoPauloOffset*60*60*1e3);return utcDate.toISOString()}function getDateRangeArray(startDate,endDate,interval="day"){const start=new Date(startDate);const end=new Date(endDate);const dates=[];if(isNaN(start.getTime())||isNaN(end.getTime())||start>end){return dates}const current=new Date(start);while(current<=end){dates.push(new Date(current));switch(interval){case"day":current.setDate(current.getDate()+1);break;case"week":current.setDate(current.getDate()+7);break;case"month":current.setMonth(current.getMonth()+1);break;case"year":current.setFullYear(current.getFullYear()+1);break;default:current.setDate(current.getDate()+1)}}return dates}function formatDateForInput(date){if(!date||isNaN(date.getTime())){return""}const year=date.getFullYear();const month=String(date.getMonth()+1).padStart(2,"0");const day=String(date.getDate()).padStart(2,"0");return`${year}-${month}-${day}`}function parseInputDateToDate(inputDateStr){if(!inputDateStr){return null}const parts=inputDateStr.split("-");if(parts.length!==3){return null}const year=parseInt(parts[0],10);const month=parseInt(parts[1],10)-1;const day=parseInt(parts[2],10);if(isNaN(year)||isNaN(month)||isNaN(day)){return null}return new Date(year,month,day,0,0,0,0)}function timeWindowFromInputYMD(startYmd,endYmd,tzOffset="-03:00"){if(!startYmd||!endYmd){return{startTs:0,endTs:0}}const startParts=startYmd.split("-");const endParts=endYmd.split("-");if(startParts.length!==3||endParts.length!==3){return{startTs:0,endTs:0}}const startDate=new Date(parseInt(startParts[0],10),parseInt(startParts[1],10)-1,parseInt(startParts[2],10),0,0,0,0);const endDate=new Date(parseInt(endParts[0],10),parseInt(endParts[1],10)-1,parseInt(endParts[2],10),23,59,59,999);return{startTs:startDate.getTime(),endTs:endDate.getTime()}}function formatDateWithTimezoneOffset(date,endOfDay=false,tzOffset="-03:00"){if(!date||isNaN(date.getTime())){return""}const year=date.getFullYear();const month=String(date.getMonth()+1).padStart(2,"0");const day=String(date.getDate()).padStart(2,"0");let hours,minutes,seconds,milliseconds;if(endOfDay){hours="23";minutes="59";seconds="59";milliseconds="999"}else{hours=String(date.getHours()).padStart(2,"0");minutes=String(date.getMinutes()).padStart(2,"0");seconds=String(date.getSeconds()).padStart(2,"0");milliseconds=String(date.getMilliseconds()).padStart(3,"0")}return`${year}-${month}-${day}T${hours}:${minutes}:${seconds}.${milliseconds}${tzOffset}`}function getSaoPauloISOStringFixed(dateStr,endOfDay=false){if(!dateStr)return"";if(endOfDay){return`${dateStr}T23:59:59.999-03:00`}else{return`${dateStr}T00:00:00.000-03:00`}}function averageByDay(data){if(!data||data.length===0){return[]}const grouped={};data.forEach(item=>{if(!item||item.value===null||item.value===void 0||isNaN(item.value)){return}const date=new Date(item.ts);if(isNaN(date.getTime())){return}const day=date.toISOString().split("T")[0];if(!grouped[day]){grouped[day]=[]}grouped[day].push(Number(item.value))});const result=Object.entries(grouped).map(([day,values])=>{const sum=values.reduce((acc,val)=>acc+val,0);const average=sum/values.length;return{day:day,average:average}});result.sort((a,b)=>a.day.localeCompare(b.day));return result}function groupByDay(data){if(!data||data.length===0){return{}}const grouped={};data.forEach(item=>{if(!item||item.value===null||item.value===void 0||isNaN(item.value)){return}const date=new Date(item.ts);if(isNaN(date.getTime())){return}const day=date.toISOString().split("T")[0];if(!grouped[day]){grouped[day]=[]}grouped[day].push(Number(item.value))});return grouped}function exportToCSV(data,headers,filename){if(!data||data.length===0){return""}const csvHeaders=headers.join(",");const csvRows=data.map(row=>headers.map(header=>{const value=row[header];if(value===null||value===void 0){return""}const stringValue=String(value);if(stringValue.includes(",")||stringValue.includes('"')||stringValue.includes("\n")){return`"${stringValue.replace(/"/g,'""')}"`}return stringValue}).join(","));const csvContent=[csvHeaders,...csvRows].join("\n");return csvContent}function exportToCSVAll(storesData,headers,filename){if(!storesData||Object.keys(storesData).length===0){return""}const csvRows=[];const csvHeaders=["Store",...headers].join(",");csvRows.push(csvHeaders);Object.entries(storesData).forEach(([storeName,storeData])=>{if(!storeData||storeData.length===0){return}storeData.forEach(row=>{let formattedStoreName=storeName;if(storeName.includes(",")||storeName.includes('"')||storeName.includes("\n")){formattedStoreName=`"${storeName.replace(/"/g,'""')}"`}const csvRow=[formattedStoreName,...headers.map(header=>{const value=row[header];if(value===null||value===void 0){return""}const stringValue=String(value);if(stringValue.includes(",")||stringValue.includes('"')||stringValue.includes("\n")){return`"${stringValue.replace(/"/g,'""')}"`}return stringValue})].join(",");csvRows.push(csvRow)})});return csvRows.join("\n")}function buildWaterReportCSV(rows,meta){if(!rows||rows.length===0){return""}const csvRows=[];let totalConsumption=0;rows.forEach(row=>{const consumptionStr=String(row.totalConsumption).replace(",",".");const consumption=Number(consumptionStr)||0;totalConsumption+=consumption});const finalTotal=meta.total!==void 0?meta.total:totalConsumption;csvRows.push(["DATA EMISSÃO",meta.issueDate]);csvRows.push(["Total",finalTotal.toFixed(2)]);if(meta.name&&meta.identifier){csvRows.push(["Loja:",meta.name,meta.identifier])}csvRows.push(["Data","Dia da Semana","Consumo Médio (m³)","Consumo Mínimo (m³)","Consumo Máximo (m³)","Consumo (m³)"]);rows.forEach(row=>{csvRows.push([row.formattedDate,row.day,String(row.avgConsumption),String(row.minDemand),String(row.maxDemand),String(row.totalConsumption)])});return csvRows.map(row=>row.join(";")).join("\n")}function buildWaterStoresCSV(rows,meta){if(!rows||rows.length===0){return""}const csvRows=[];let totalConsumption=0;rows.forEach(row=>{const consumption=row.consumptionM3!==void 0?row.consumptionM3:row.consumptionKwh||0;totalConsumption+=consumption});const finalTotal=meta.total!==void 0?meta.total:totalConsumption;csvRows.push(["DATA EMISSÃO",meta.issueDate]);csvRows.push(["Total",finalTotal.toFixed(2)]);csvRows.push(["Loja","Identificador","Consumo"]);rows.forEach(row=>{const label=row.entityLabel||row.deviceName||"-";const deviceId=row.deviceId||"-";const consumption=row.consumptionM3!==void 0?row.consumptionM3:row.consumptionKwh||0;const formattedConsumption=consumption!==null&&consumption!==void 0?formatNumberReadable(consumption):"0,00";csvRows.push([label,deviceId,formattedConsumption])});return csvRows.map(row=>row.join(";")).join("\n")}function toCSV(rows,delimiter=";"){if(!rows||rows.length===0){return""}return rows.map(row=>row.map(cell=>{const value=String(cell);if(value.includes(delimiter)||value.includes('"')||value.includes("\n")){return`"${value.replace(/"/g,'""')}"`}return value}).join(delimiter)).join("\n")}function classify(entity,criteria){if(!entity||!criteria){return{category:"unknown",confidence:0}}let category="unknown";let subcategory;let confidence=0;if(entity.type){switch(entity.type.toLowerCase()){case"consumption":category="energy_consumption";confidence=.9;break;case"generation":category="energy_generation";confidence=.9;break;case"storage":category="energy_storage";confidence=.9;break;case"distribution":category="energy_distribution";confidence=.8;break;default:category="energy_other";confidence=.5}}if(entity.powerRating){const power=parseFloat(entity.powerRating);if(!isNaN(power)){if(power<1e3){subcategory="small_scale"}else if(power<1e4){subcategory="medium_scale"}else{subcategory="large_scale"}confidence=Math.min(confidence+.1,1)}}return{category:category,subcategory:subcategory,confidence:confidence}}function classifyWaterLabel(label){if(!label){console.warn('classifyWaterLabel: empty label, defaulting to "Lojas"');return"Lojas"}const normalizedLabel=label.toLowerCase().trim();if(/rel[óo]gio|caixa|superior|inferior|nível_terraço/.test(normalizedLabel)){return"Caixas D'Água"}if(/administra|bomba|chiller|adm/.test(normalizedLabel)){return"Área Comum"}return"Lojas"}function classifyWaterLabels(labels){const counts={"Caixas D'Água":0,Lojas:0,"Área Comum":0,total:0};labels.forEach(label=>{const category=classifyWaterLabel(label);counts[category]++;counts.total++});return counts}function getWaterCategories(){return["Caixas D'Água","Lojas","Área Comum"]}function isWaterCategory(label,category){return classifyWaterLabel(label)===category}function getValueByDatakey(data,datakey){if(!data||!datakey){return void 0}if(Array.isArray(data)){for(const item of data){const value=getValueByDatakey(item,datakey);if(value!==void 0){return value}}return void 0}if(typeof data==="object"&&data!==null){const keys=datakey.split(".");let current=data;for(const key of keys){if(current===null||current===void 0){return void 0}if(key.includes("[")&&key.includes("]")){const arrayKey=key.substring(0,key.indexOf("["));const indexMatch=key.match(/\[(\d+)\]/);if(indexMatch){const index=parseInt(indexMatch[1],10);current=current[arrayKey];if(Array.isArray(current)&&index>=0&&index<current.length){current=current[index]}else{return void 0}}else{return void 0}}else{current=current[key]}}return current}return void 0}function getValueByDatakeyLegacy(dataList,dataSourceNameTarget,dataKeyTarget){if(!Array.isArray(dataList)||!dataSourceNameTarget||!dataKeyTarget){return void 0}for(const item of dataList){if(item&&item.dataSourceName===dataSourceNameTarget&&item.dataKey===dataKeyTarget){return item.value}}return void 0}function findValue(data,keyOrPath,legacyDataKey){if(legacyDataKey!==void 0){return getValueByDatakeyLegacy(data,keyOrPath,legacyDataKey)}return getValueByDatakey(data,keyOrPath)}function findValueWithDefault(values,key,defaultValue=null){if(!Array.isArray(values))return defaultValue;const found=values.find(v=>v.key===key||v.dataType===key);return found?found.value:defaultValue}var DeviceStatusType={POWER_ON:"power_on",STANDBY:"standby",POWER_OFF:"power_off",WARNING:"warning",FAILURE:"failure",MAINTENANCE:"maintenance",NO_INFO:"no_info",NOT_INSTALLED:"not_installed"};var ConnectionStatusType={CONNECTED:"connected",OFFLINE:"offline"};var deviceStatusIcons={[DeviceStatusType.POWER_ON]:"⚡",[DeviceStatusType.STANDBY]:"🔌",[DeviceStatusType.POWER_OFF]:"🔴",[DeviceStatusType.WARNING]:"⚠️",[DeviceStatusType.FAILURE]:"🚨",[DeviceStatusType.MAINTENANCE]:"🛠️",[DeviceStatusType.NO_INFO]:"❓️",[DeviceStatusType.NOT_INSTALLED]:"📦"};var waterDeviceStatusIcons={[DeviceStatusType.POWER_ON]:"💧",[DeviceStatusType.STANDBY]:"🚰",[DeviceStatusType.POWER_OFF]:"🔴",[DeviceStatusType.WARNING]:"⚠️",[DeviceStatusType.FAILURE]:"🚨",[DeviceStatusType.MAINTENANCE]:"🛠️",[DeviceStatusType.NO_INFO]:"❓️",[DeviceStatusType.NOT_INSTALLED]:"📦"};var temperatureDeviceStatusIcons={[DeviceStatusType.POWER_ON]:"🌡️",[DeviceStatusType.STANDBY]:"🌡️",[DeviceStatusType.POWER_OFF]:"🔴",[DeviceStatusType.WARNING]:"⚠️",[DeviceStatusType.FAILURE]:"🚨",[DeviceStatusType.MAINTENANCE]:"🛠️",[DeviceStatusType.NO_INFO]:"❓️",[DeviceStatusType.NOT_INSTALLED]:"📦"};var connectionStatusIcons={[ConnectionStatusType.CONNECTED]:"🟢",[ConnectionStatusType.OFFLINE]:"🚫"};function mapDeviceToConnectionStatus(deviceStatus){if(deviceStatus===DeviceStatusType.NO_INFO){return ConnectionStatusType.OFFLINE}return ConnectionStatusType.CONNECTED}function mapConnectionStatus(rawStatus){const statusLower=String(rawStatus||"").toLowerCase().trim();if(statusLower==="online"||statusLower==="ok"||statusLower==="running"){return"online"}if(statusLower==="waiting"||statusLower==="connecting"||statusLower==="pending"){return"waiting"}return"offline"}function mapDeviceStatusToCardStatus(deviceStatus){const statusMap={[DeviceStatusType.POWER_ON]:"ok",[DeviceStatusType.STANDBY]:"alert",[DeviceStatusType.POWER_OFF]:"fail",[DeviceStatusType.WARNING]:"alert",[DeviceStatusType.FAILURE]:"fail",[DeviceStatusType.MAINTENANCE]:"alert",[DeviceStatusType.NO_INFO]:"unknown",[DeviceStatusType.NOT_INSTALLED]:"not_installed"};return statusMap[deviceStatus]||"unknown"}function shouldFlashIcon(deviceStatus){return deviceStatus===DeviceStatusType.POWER_OFF||deviceStatus===DeviceStatusType.WARNING||deviceStatus===DeviceStatusType.FAILURE||deviceStatus===DeviceStatusType.MAINTENANCE}function isDeviceOffline(deviceStatus){return deviceStatus===DeviceStatusType.NO_INFO}function getDeviceStatusIcon(deviceStatus,deviceType=null){const normalizedType=deviceType?.toUpperCase()||"";const isWaterDevice=normalizedType==="TANK"||normalizedType==="CAIXA_DAGUA";const isTemperatureDevice=normalizedType==="TERMOSTATO";let iconMap;if(isWaterDevice){iconMap=waterDeviceStatusIcons}else if(isTemperatureDevice){iconMap=temperatureDeviceStatusIcons}else{iconMap=deviceStatusIcons}return iconMap[deviceStatus]||iconMap[DeviceStatusType.POWER_ON]}function getConnectionStatusIcon(connectionStatus){return connectionStatusIcons[connectionStatus]||connectionStatusIcons[ConnectionStatusType.OFFLINE]}function isValidDeviceStatus(status){return Object.values(DeviceStatusType).includes(status)}function isValidConnectionStatus(status){return Object.values(ConnectionStatusType).includes(status)}function getDeviceStatusInfo(deviceStatus){const connectionStatus=mapDeviceToConnectionStatus(deviceStatus);return{deviceStatus:deviceStatus,connectionStatus:connectionStatus,cardStatus:mapDeviceStatusToCardStatus(deviceStatus),deviceIcon:getDeviceStatusIcon(deviceStatus),connectionIcon:getConnectionStatusIcon(connectionStatus),shouldFlash:shouldFlashIcon(deviceStatus),isOffline:isDeviceOffline(deviceStatus),isValid:isValidDeviceStatus(deviceStatus)}}function calculateDeviceStatus({connectionStatus:connectionStatus,lastConsumptionValue:lastConsumptionValue,limitOfPowerOnStandByWatts:limitOfPowerOnStandByWatts,limitOfPowerOnAlertWatts:limitOfPowerOnAlertWatts,limitOfPowerOnFailureWatts:limitOfPowerOnFailureWatts}){const validConnectionStatuses=["waiting","offline","online"];if(!validConnectionStatuses.includes(connectionStatus)){return DeviceStatusType.MAINTENANCE}if(connectionStatus==="waiting"){return DeviceStatusType.NOT_INSTALLED}if(connectionStatus==="offline"){return DeviceStatusType.NO_INFO}if(connectionStatus==="online"&&(lastConsumptionValue===null||lastConsumptionValue===void 0)){return DeviceStatusType.POWER_ON}if(connectionStatus==="online"&&lastConsumptionValue!==null&&lastConsumptionValue!==void 0){const consumption=Number(lastConsumptionValue);if(isNaN(consumption)){return DeviceStatusType.MAINTENANCE}if(consumption>=0&&consumption<=limitOfPowerOnStandByWatts){return DeviceStatusType.STANDBY}if(consumption>limitOfPowerOnStandByWatts&&consumption<=limitOfPowerOnAlertWatts){return DeviceStatusType.POWER_ON}if(consumption>limitOfPowerOnAlertWatts&&consumption<=limitOfPowerOnFailureWatts){return DeviceStatusType.WARNING}if(consumption>limitOfPowerOnFailureWatts){return DeviceStatusType.FAILURE}}return DeviceStatusType.MAINTENANCE}function calculateDeviceStatusWithRanges({connectionStatus:connectionStatus,lastConsumptionValue:lastConsumptionValue,ranges:ranges}){const validConnectionStatuses=["waiting","offline","online"];if(!validConnectionStatuses.includes(connectionStatus)){return DeviceStatusType.MAINTENANCE}if(connectionStatus==="waiting"){return DeviceStatusType.NOT_INSTALLED}if(connectionStatus==="offline"){return DeviceStatusType.NO_INFO}if(connectionStatus==="online"&&(lastConsumptionValue===null||lastConsumptionValue===void 0)){return DeviceStatusType.POWER_ON}if(!ranges||!ranges.standbyRange||!ranges.normalRange||!ranges.alertRange||!ranges.failureRange){console.error("[RFC-0077] Invalid ranges object:",ranges);return DeviceStatusType.MAINTENANCE}if(connectionStatus==="online"&&lastConsumptionValue!==null&&lastConsumptionValue!==void 0){const consumption=Number(lastConsumptionValue);if(isNaN(consumption)){return DeviceStatusType.MAINTENANCE}const{standbyRange:standbyRange,normalRange:normalRange,alertRange:alertRange,failureRange:failureRange}=ranges;if(consumption>=standbyRange.down&&consumption<=standbyRange.up){return DeviceStatusType.STANDBY}if(consumption>=normalRange.down&&consumption<=normalRange.up){return DeviceStatusType.POWER_ON}if(consumption>=alertRange.down&&consumption<=alertRange.up){return DeviceStatusType.WARNING}if(consumption>=failureRange.down&&consumption<=failureRange.up){return DeviceStatusType.FAILURE}if(consumption>failureRange.up){return DeviceStatusType.FAILURE}if(consumption<standbyRange.down){return DeviceStatusType.MAINTENANCE}}return DeviceStatusType.MAINTENANCE}function normalizeAttrKey(raw){const k=String(raw||"").trim();const lower=k.toLowerCase();if(lower==="ingestionid")return"ingestionId";if(lower==="identifier")return"identifier";if(lower==="label")return"label";if(lower==="id")return"id";return k}function buildEntityMapFromDatasource(datasources){const dsArray=Array.isArray(datasources)?datasources:[];const map=new Map;dsArray.forEach(ds=>{const entityId=ds?.entityId;if(!entityId)return;if(!map.has(entityId)){const entity=ds?.entity;const draftLabel=entity?.label||entity?.name||ds?.name||null;map.set(entityId,{id:entityId,identifier:null,label:draftLabel,expectedKeys:new Set,attrs:{}})}const keys=Array.isArray(ds?.dataKeys)?ds.dataKeys:[];const rec=map.get(entityId);keys.forEach(k=>{if(k?.name){rec.expectedKeys.add(String(k.name).toLowerCase())}})});return map}function hydrateEntityMapWithCtxData(data,map){const rows=Array.isArray(data)?data:[];rows.forEach(row=>{const entityId=row?.datasource?.entityId||null;if(!entityId||!map.has(entityId))return;const rawKey=row?.dataKey?.name||"";if(!rawKey)return;const val=Array.isArray(row?.data)&&Array.isArray(row.data[0])?row.data[0][1]:null;if(val==null)return;const rec=map.get(entityId);const attrKey=normalizeAttrKey(rawKey);if(attrKey==="identifier"){rec.identifier=val}else if(attrKey==="label"){rec.label=val}else if(attrKey==="ingestionId"){rec.id=val}else{rec.attrs[attrKey]=val}})}function buildListItemsThingsboardByUniqueDatasource(datasources,data){const map=buildEntityMapFromDatasource(datasources);hydrateEntityMapWithCtxData(data,map);const items=Array.from(map.values()).map(rec=>({id:rec.id,identifier:rec.identifier??rec.label??"",label:rec.label??rec.identifier??""}));items.sort((a,b)=>String(a.label).localeCompare(String(b.label),"pt-BR"));return items}var globalCache=new Map;function generateCacheKey(config){return`${config.dataApiHost}:${config.clientId}:${config.clientSecret}`}function buildMyioIngestionAuth(config){const{dataApiHost:dataApiHost,clientId:clientId,clientSecret:clientSecret,renewSkewSeconds:renewSkewSeconds=60,retryBaseMs:retryBaseMs=500,retryMaxAttempts:retryMaxAttempts=3}=config;if(!dataApiHost||!clientId||!clientSecret){throw new Error("dataApiHost, clientId, and clientSecret are required")}const authUrl=new URL(`${dataApiHost}/api/v1/auth`);const cacheKey=generateCacheKey(config);if(!globalCache.has(cacheKey)){globalCache.set(cacheKey,{token:null,expiresAt:0,inFlight:null})}const cache=globalCache.get(cacheKey);function now(){return Date.now()}function aboutToExpire(){if(!cache.token)return true;const skewMs=renewSkewSeconds*1e3;return now()>=cache.expiresAt-skewMs}async function sleep(ms){return new Promise(resolve=>setTimeout(resolve,ms))}async function requestNewToken(){const body={client_id:clientId,client_secret:clientSecret};let attempt=0;while(true){try{const response=await fetch(authUrl,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(body)});if(!response.ok){const text=await response.text().catch(()=>"");throw new Error(`Auth failed: HTTP ${response.status} ${response.statusText} ${text}`)}const json=await response.json();if(!json||!json.access_token||!json.expires_in){throw new Error("Auth response missing required fields (access_token, expires_in)")}cache.token=json.access_token;cache.expiresAt=now()+Number(json.expires_in)*1e3;console.log(`[MyIOAuth] New token obtained for ${clientId}. Expires in ~${Math.round(Number(json.expires_in)/60)} min`);return cache.token}catch(error){attempt++;console.warn(`[MyIOAuth] Error obtaining token (attempt ${attempt}/${retryMaxAttempts}):`,error instanceof Error?error.message:error);if(attempt>=retryMaxAttempts){throw error}const backoff=retryBaseMs*Math.pow(2,attempt-1);await sleep(backoff)}}}async function getToken(){if(cache.inFlight){return cache.inFlight}if(aboutToExpire()){cache.inFlight=requestNewToken().finally(()=>{cache.inFlight=null});return cache.inFlight}return cache.token}function getExpiryInfo(){return{expiresAt:cache.expiresAt,expiresInSeconds:Math.max(0,Math.floor((cache.expiresAt-now())/1e3))}}function clearCache(){cache.token=null;cache.expiresAt=0;cache.inFlight=null}function isTokenValid(){if(!globalCache.has(cacheKey)){return false}return!aboutToExpire()}return{getToken:getToken,getExpiryInfo:getExpiryInfo,clearCache:clearCache,isTokenValid:isTokenValid}}function clearAllAuthCaches(){globalCache.clear()}function getAuthCacheStats(){return{totalCaches:globalCache.size,cacheKeys:Array.from(globalCache.keys())}}async function fetchThingsboardCustomerServerScopeAttrs(config){const{customerId:customerId,tbToken:tbToken,baseUrl:baseUrl=""}=config;if(!customerId||!tbToken){throw new Error("customerId and tbToken are required")}const url=`${baseUrl}/api/plugins/telemetry/CUSTOMER/${customerId}/values/attributes/SERVER_SCOPE`;try{const response=await fetch(url,{method:"GET",headers:{"Content-Type":"application/json","X-Authorization":`Bearer ${tbToken}`}});if(!response.ok){console.warn(`[ThingsBoard Customer Attrs] HTTP ${response.status} ${response.statusText}`);if(response.status===404||response.status===403){return{}}throw new Error(`ThingsBoard API error: HTTP ${response.status} ${response.statusText}`)}const payload=await response.json();return processAttributesResponse(payload)}catch(error){console.error("[ThingsBoard Customer Attrs] Error fetching attributes:",error);if(error instanceof Error){throw new Error(`Failed to fetch customer attributes: ${error.message}`)}throw new Error("Failed to fetch customer attributes: Unknown error")}}function processAttributesResponse(payload){const attributesMap={};if(!payload){return attributesMap}if(Array.isArray(payload)){for(const item of payload){if(item&&typeof item==="object"&&item.key!==void 0){attributesMap[item.key]=item.value}}return attributesMap}if(typeof payload==="object"){for(const key of Object.keys(payload)){const value=payload[key];if(Array.isArray(value)&&value.length>0){const firstItem=value[0];attributesMap[key]=firstItem?.value??firstItem}else{attributesMap[key]=value}}return attributesMap}console.warn("[ThingsBoard Customer Attrs] Unexpected payload format:",typeof payload);return attributesMap}async function fetchThingsboardCustomerAttrsFromStorage(customerId,tbToken,baseUrl){return fetchThingsboardCustomerServerScopeAttrs({customerId:customerId,tbToken:tbToken,baseUrl:baseUrl})}function extractMyIOCredentials(attributes){if(!attributes||typeof attributes!=="object"){return{clientId:"",clientSecret:"",ingestionId:""}}return{clientId:attributes.client_id||attributes.clientId||"",clientSecret:attributes.client_secret||attributes.clientSecret||"",ingestionId:attributes.ingestionId||attributes.ingestion_id||""}}function normalize(str){if(typeof str!=="string")return"";let normalized=str.toUpperCase().trim();normalized=normalized.replace(/[ÀÁÂÃÄÅ]/g,"A").replace(/[ÈÉÊË]/g,"E").replace(/[ÌÍÎÏ]/g,"I").replace(/[ÒÓÔÕÖ]/g,"O").replace(/[ÙÚÛÜ]/g,"U").replace(/[Ç]/g,"C").replace(/[Ñ]/g,"N");normalized=normalized.replace(/\s+/g," ");return normalized}function matchCaixaDAgua(normalizedStr){if(normalizedStr.includes("SCD"))return true;const caixaVariants=["CAIXA D'AGUA","CAIXA D AGUA","CAIXA_D_AGUA","CAIXA DAGUA"];return caixaVariants.some(variant=>normalizedStr.includes(variant))}var contexts={building:name=>{const normalized=normalize(name);const rules=[{test:s=>s.includes("COMPRESSOR"),type:"COMPRESSOR"},{test:s=>s.includes("VENT"),type:"VENTILADOR"},{test:s=>s.includes("AUTOMATICO"),type:"SELETOR_AUTO_MANUAL"},{test:s=>s.includes("ESRL"),type:"ESCADA_ROLANTE"},{test:s=>s.includes("ESCADA"),type:"ESCADA_ROLANTE"},{test:s=>s.includes("ELEV"),type:"ELEVADOR"},{test:s=>s.includes("MOTR")||s.includes("RECALQUE"),type:"MOTOR"},{test:s=>s.includes("TERMOSTATO"),type:"TERMOSTATO"},{test:s=>s.includes("TERMO")||s.includes("TEMP"),type:"TERMOSTATO"},{test:s=>s.includes("3F"),type:"3F_MEDIDOR"},{test:s=>s.includes("HIDR"),type:"HIDROMETRO"},{test:s=>s.includes("ABRE"),type:"SOLENOIDE"},{test:s=>matchCaixaDAgua(s),type:"CAIXA_D_AGUA"},{test:s=>s.includes("AUTOMACAO")||s.includes("GW_AUTO"),type:"GLOBAL_AUTOMACAO"},{test:s=>s.includes("AC"),type:"CONTROLE REMOTO"}];for(const rule of rules){if(rule.test(normalized)){return rule.type}}return"default"},mall:name=>{const normalized=normalize(name);const rules=[{test:s=>s.includes("CHILLER"),type:"CHILLER"},{test:s=>s.includes("ESCADA"),type:"ESCADA_ROLANTE"},{test:s=>s.includes("LOJA"),type:"LOJA_SENSOR"},{test:s=>s.includes("ILUMINACAO"),type:"ILUMINACAO"}];for(const rule of rules){if(rule.test(normalized)){return rule.type}}return"default"}};var warnedContexts=new Set;function detectDeviceType(name,context="building"){if(typeof name!=="string"){throw new Error("Device name must be a string.")}const detectFunction=contexts[context];if(!detectFunction){if(!warnedContexts.has(context)){console.warn(`[myio-js-library] Context "${context}" not found. Using default fallback.`);warnedContexts.add(context)}return contexts.building(name)}return detectFunction(name)}function getAvailableContexts(){return Object.keys(contexts)}function addDetectionContext(contextName,detectFunction){if(typeof contextName!=="string"){throw new Error("Context name must be a string.")}if(typeof detectFunction!=="function"){throw new Error("Detection function must be a function.")}contexts[contextName]=detectFunction}function addNamespace(payload,namespace=""){if(!payload||typeof payload!=="object"||Array.isArray(payload)){throw new Error("Payload must be an object.")}const keys=Object.keys(payload);const suffix=namespace.trim()?` (${namespace.trim()})`:"";return keys.reduce((acc,key)=>{acc[`${key}${suffix}`]=payload[key];return acc},{})}var numbers_exports={};__export(numbers_exports,{fmtPerc:()=>fmtPerc2,toFixedSafe:()=>toFixedSafe});function fmtPerc2(x,digits=2){if(!Number.isFinite(x))return"—";return(x*100).toFixed(digits)+"%"}function toFixedSafe(x,digits=2){if(!Number.isFinite(x))return"—";return x.toFixed(digits)}var strings_exports={};__export(strings_exports,{normalizeRecipients:()=>normalizeRecipients});function normalizeRecipients(val){if(val===null||val===void 0||val==="")return"";if(Object.prototype.toString.call(val)==="[object Array]"){return val.filter(Boolean).join(",")}let s=String(val).trim();if(/^\s*\[/.test(s)){try{const arr=JSON.parse(s);if(Object.prototype.toString.call(arr)==="[object Array]"){return arr.filter(Boolean).join(",")}}catch{}}s=s.replace(/[;\s]+/g,",");s=s.replace(/,+/g,",").replace(/^,|,$/g,"");return s}function decodePayload(encoded,key){const bytes=base64ToBytesStrict(encoded);if(bytes.length===0)return"";if(key===""||key===void 0||key===null){return(new TextDecoder).decode(bytes)}if(typeof key==="number"&&Number.isFinite(key)){const k=key&255;for(let i=0;i<bytes.length;i++)bytes[i]^=k;return(new TextDecoder).decode(bytes)}const keyStr=String(key);const keyBytes=(new TextEncoder).encode(keyStr);if(keyBytes.length===0){return(new TextDecoder).decode(bytes)}for(let i=0;i<bytes.length;i++){bytes[i]^=keyBytes[i%keyBytes.length]}return(new TextDecoder).decode(bytes)}function decodePayloadBase64Xor(encoded,xorKey=73){const bytes=base64ToBytesStrict(encoded);for(let i=0;i<bytes.length;i++)bytes[i]^=xorKey&255;return(new TextDecoder).decode(bytes)}function base64ToBytesStrict(b64){if(b64===""||b64===void 0||b64===null)return new Uint8Array;const s=String(b64).replace(/\s+/g,"");const re=/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;if(!re.test(s))throw new Error("Invalid base64");if(typeof Buffer!=="undefined"&&Buffer.from){return Uint8Array.from(Buffer.from(s,"base64"))}const bin=atob(s);const out=new Uint8Array(bin.length);for(let i=0;i<bin.length;i++)out[i]=bin.charCodeAt(i);return out}init_template_card();var MyIOSelectionStoreClass=class _MyIOSelectionStoreClass{static GlobalDebug=false;static setGlobalDebug(enabled){_MyIOSelectionStoreClass.GlobalDebug=!!enabled;console.log(`[SelectionStore] 🔧 Global debug ${enabled?"ENABLED":"DISABLED"}`)}_log(level,...args){if(!_MyIOSelectionStoreClass.GlobalDebug)return;const logMethod=console[level]||console.log;logMethod.apply(console,args)}constructor(){this._log("log","[SelectionStore] 🔍 Constructor called - checking for existing instance...");this._log("log","[SelectionStore] typeof document:",typeof document);if(typeof window!=="undefined"){this._log("log","[SelectionStore] window.top === window:",window.top===window);this._log("log","[SelectionStore] document location:",window.location.href);this._log("log","[SelectionStore] Is in iframe:",window!==window.top)}this._log("log","[SelectionStore] document.__MyIOSelectionStore_INSTANCE__:",!!document?.__MyIOSelectionStore_INSTANCE__);if(typeof document!=="undefined"){const myioProps=Object.getOwnPropertyNames(document).filter(key=>key.startsWith("__MyIO"));this._log("log","[SelectionStore] All __MyIO* properties on document:",myioProps)}let existingInstance=null;try{const targetWindow=typeof window!=="undefined"&&window.top?window.top:window;existingInstance=targetWindow?.__MyIOSelectionStore_INSTANCE__;this._log("log","[SelectionStore] Checking window.top.__MyIOSelectionStore_INSTANCE__:",!!existingInstance)}catch(e){this._log("warn","[SelectionStore] Cannot access window.top:",e.message)}if(!existingInstance){existingInstance=typeof document!=="undefined"&&document.__MyIOSelectionStore_INSTANCE__||typeof window!=="undefined"&&window.__MyIOSelectionStore_INSTANCE__}if(existingInstance){this._log("warn","[SelectionStore] ⚠️ Constructor called but instance already exists! Returning existing instance.");this._log("log","[SelectionStore] Existing instance has listeners:",existingInstance.eventListeners.get("selection:change")?.length||0);return existingInstance}this._log("log","[SelectionStore] 🏗️ NEW INSTANCE CREATED at:",(new Date).toISOString());if(_MyIOSelectionStoreClass.GlobalDebug){console.trace("[SelectionStore] Constructor called from:")}this.MAX_SELECTION=6;this.state={selectedDevice:null};this.selectedIds=new Set;this.entities=new Map;this.eventListeners=new Map;this.analytics=null;this.timeSeriesCache=new Map;this.cacheExpiry=5*60*1e3;this.eventListeners.set("selection:change",[]);this.eventListeners.set("selection:totals",[]);this.eventListeners.set("selection:limit-reached",[]);this.eventListeners.set("comparison:open",[]);this.eventListeners.set("comparison:too_many",[]);try{const targetWindow=typeof window!=="undefined"&&window.top?window.top:window;if(targetWindow){this._log("log","[SelectionStore] 💾 Storing instance in window.top.__MyIOSelectionStore_INSTANCE__");targetWindow.__MyIOSelectionStore_INSTANCE__=this;this._log("log","[SelectionStore] ✅ Stored in top window! Verify:",!!targetWindow.__MyIOSelectionStore_INSTANCE__)}}catch(e){this._log("warn","[SelectionStore] ⚠️ Cannot access window.top (cross-origin iframe):",e.message);if(typeof document!=="undefined"){this._log("log","[SelectionStore] 💾 Storing instance in document.__MyIOSelectionStore_INSTANCE__ (fallback)");document.__MyIOSelectionStore_INSTANCE__=this;this._log("log","[SelectionStore] ✅ Stored! Verify:",!!document.__MyIOSelectionStore_INSTANCE__)}}}add(id){this._log("log","[MyIOSelectionStoreClass] Entrou na LIB",id);const wasSelected=this.selectedIds.has(id);if(wasSelected){this._log("log","[MyIOSelectionStoreClass] Item já está selecionado:",id);return}if(this.selectedIds.size>=this.MAX_SELECTION){this._log("warn",`[MyIOSelectionStoreClass] Limite de seleção atingido (${this.MAX_SELECTION})`);this._emit("selection:limit-reached",{maxAllowed:this.MAX_SELECTION,currentCount:this.selectedIds.size,attemptedId:id});this._trackEvent("selection.limit_reached",{entityId:id,limit:this.MAX_SELECTION});return}this.selectedIds.add(id);this._emitSelectionChange("add",id);this._trackEvent("footer_dock.drop_add",{entityId:id})}remove(id){this._log("log","[MyIOSelectionStoreClass] ITEM PARA REMOÇÃO ID",id);if(!this.selectedIds.has(id))return;this._log("log","[MyIOSelectionStoreClass] DELETE ID",id);this.selectedIds.delete(id);this._emitSelectionChange("remove",id);this._trackEvent("footer_dock.remove_chip",{entityId:id})}toggle(id){if(this.isSelected(id)){this.remove(id)}else{this.add(id)}}clear(){if(this.selectedIds.size===0)return;this.selectedIds.clear();this._emitSelectionChange("clear");this._trackEvent("footer_dock.clear_all",{count:0})}syncFromCheckbox(id,checked){if(typeof id!=="string"||typeof checked!=="boolean")return;if(checked&&!this.isSelected(id)){this.add(id)}else if(!checked&&this.isSelected(id)){this.remove(id)}this._trackEvent("card.checkbox_toggle",{entityId:id,checked:checked})}registerEntity(entity){if(!entity||typeof entity!=="object"||!entity.id){throw new Error("Entity must be an object with an id property")}const normalizedEntity={id:entity.id,name:entity.name||"",icon:entity.icon||"generic",group:entity.group||"",lastValue:Number(entity.lastValue)||0,unit:entity.unit||"",status:entity.status||"unknown",ingestionId:entity.ingestionId||entity.id,customerName:entity.customerName||""};this.entities.set(entity.id,normalizedEntity)}unregisterEntity(id){if(typeof id!=="string")return;this.entities.delete(id);this.remove(id)}getSelectedIds(){return Array.from(this.selectedIds)}getSelectedEntities(){this._log("log","[MyIOSelectionStoreClass] biblioteca:",this.getSelectedIds());return this.getSelectedIds().map(id=>this.entities.get(id)).filter(entity=>entity!==void 0)}getTotals(){const selectedEntities=this.getSelectedEntities();const totals={energyKwh:0,waterM3:0,tempC:0,percentage:0,count:selectedEntities.length,unitBreakdown:{}};selectedEntities.forEach(entity=>{const value=entity.lastValue||0;const unit=entity.unit||"";if(!totals.unitBreakdown[unit]){totals.unitBreakdown[unit]=0}totals.unitBreakdown[unit]+=value;switch(unit.toLowerCase()){case"kwh":case"mwh":case"gwh":totals.energyKwh+=this._convertToKwh(value,unit);break;case"m³":case"m3":totals.waterM3+=value;break;case"°c":case"celsius":totals.tempC+=value;break;case"%":case"percent":totals.percentage+=value;break}});return totals}isSelected(id){return this.selectedIds.has(id)}getSelectionCount(){return this.selectedIds.size}getMultiUnitTotalDisplay(){const totals=this.getTotals();const parts=[];if(totals.energyKwh>0){parts.push(`Energy: ${this._formatNumber(totals.energyKwh)} kWh`)}if(totals.waterM3>0){parts.push(`Water: ${this._formatNumber(totals.waterM3)} m³`)}if(totals.tempC>0){parts.push(`Temp: ${this._formatNumber(totals.tempC)} °C`)}if(totals.percentage>0){parts.push(`${this._formatNumber(totals.percentage)}%`)}return parts.join(" | ")||"No selection"}on(event,callback){this._log("log",`[SelectionStore] 📝 Registering listener for event: ${event}`);if(typeof event!=="string"||typeof callback!=="function"){this._log("error",`[SelectionStore] ❌ Invalid registration: event=${typeof event}, callback=${typeof callback}`);return}if(!this.eventListeners.has(event)){this._log("log",`[SelectionStore] 🆕 Creating new listener array for: ${event}`);this.eventListeners.set(event,[])}this.eventListeners.get(event).push(callback);this._log("log",`[SelectionStore] ✅ Listener registered! Total for ${event}: ${this.eventListeners.get(event).length}`)}off(event,callback){if(!this.eventListeners.has(event))return;const listeners=this.eventListeners.get(event);const index=listeners.indexOf(callback);if(index>-1){listeners.splice(index,1)}}async getTimeSeriesData(entityIds,startDate,endDate){if(!Array.isArray(entityIds)||entityIds.length===0){return{}}const cacheKey=`${entityIds.join(",")}_${startDate.getTime()}_${endDate.getTime()}`;const cached=this.timeSeriesCache.get(cacheKey);if(cached&&Date.now()-cached.timestamp<this.cacheExpiry){return cached.data}const mockData={};entityIds.forEach(id=>{mockData[id]=this._generateMockTimeSeriesData(startDate,endDate)});this.timeSeriesCache.set(cacheKey,{data:mockData,timestamp:Date.now()});return mockData}invalidateCache(reason="manual"){this.timeSeriesCache.clear();this._trackEvent("cache.invalidated",{reason:reason})}setAnalytics(analyticsInstance){if(!analyticsInstance||typeof analyticsInstance.track!=="function"){throw new Error("Analytics instance must have a track method")}this.analytics=analyticsInstance}trackEvent(eventName,payload={}){this._trackEvent(eventName,payload)}openComparison(){const count=this.getSelectionCount();if(count===0){this.announceToScreenReader("No items selected for comparison");return false}if(count>20){this._emit("comparison:too_many",{count:count,maxAllowed:20,selectedIds:this.getSelectedIds()});this._trackEvent("chart_modal.too_many_entities",{count:count});return false}const data={entities:this.getSelectedEntities(),totals:this.getTotals(),count:count};this._emit("comparison:open",data);this._trackEvent("chart_modal.open",{entityCount:count});return true}startDrag(id){if(typeof id!=="string")return;this._trackEvent("drag.start",{entityId:id})}announceToScreenReader(message){if(typeof message!=="string"||typeof document==="undefined")return;let announcer=document.getElementById("myio-sr-announcer");if(!announcer){announcer=document.createElement("div");announcer.id="myio-sr-announcer";announcer.setAttribute("aria-live","polite");announcer.setAttribute("aria-atomic","true");announcer.style.position="absolute";announcer.style.left="-10000px";announcer.style.width="1px";announcer.style.height="1px";announcer.style.overflow="hidden";document.body.appendChild(announcer)}announcer.textContent=message}_emitSelectionChange(action,id=null){const data={action:action,id:id,selectedIds:this.getSelectedIds(),totals:this.getTotals()};this._emit("selection:change",data);this._emit("selection:totals",data.totals);this._trackEvent("footer_dock.total_update",{action:action,count:data.selectedIds.length,totals:data.totals})}_emit(event,data){this._log("log",`[SelectionStore] 🔔 Emitting event: ${event}, listeners: ${this.eventListeners.get(event)?.length||0}`);this._log("log",`[SelectionStore] 📦 Event data:`,data);if(!this.eventListeners.has(event)){this._log("warn",`[SelectionStore] ⚠️ No listener map for event: ${event}`);return}const listeners=this.eventListeners.get(event);if(listeners.length===0){this._log("warn",`[SelectionStore] ⚠️ No listeners registered for event: ${event}`)}listeners.forEach((callback,index)=>{this._log("log",`[SelectionStore] 🎯 Calling listener #${index} for ${event}`);try{callback(data)}catch(error){this._log("error",`[SelectionStore] ❌ Error in ${event} listener #${index}:`,error)}})}_trackEvent(eventName,payload={}){if(!this.analytics)return;try{this.analytics.track(eventName,{timestamp:Date.now(),...payload})}catch(error){this._log("error","Analytics tracking error:",error)}}_convertToKwh(value,unit){switch(unit.toLowerCase()){case"mwh":return value*1e3;case"gwh":return value*1e6;case"kwh":default:return value}}_formatNumber(value){if(typeof value!=="number"||isNaN(value))return"0";return new Intl.NumberFormat("pt-BR",{minimumFractionDigits:0,maximumFractionDigits:2}).format(value)}_generateMockTimeSeriesData(startDate,endDate){const data=[];const current=new Date(startDate);const end=new Date(endDate);while(current<=end){data.push({timestamp:current.getTime(),value:Math.random()*100+50,unit:"kWh"});current.setHours(current.getHours()+1)}return data}};function _moduleLog(level,...args){if(!MyIOSelectionStoreClass.GlobalDebug)return;const logMethod=console[level]||console.log;logMethod.apply(console,args)}exports.MyIOSelectionStore=void 0;var _singletonInstance=null;if(typeof globalThis!=="undefined"&&typeof globalThis.window!=="undefined"){_moduleLog("log","[SelectionStore] 🔧 Module initialization - checking for existing instance...");if(!globalThis.window.__MyIOLibrary_PROTECTED__){_moduleLog("log","[SelectionStore] 🛡️ Protecting window.MyIOLibrary object from UMD overwrites...");const existingLib=globalThis.window.MyIOLibrary;Object.defineProperty(globalThis.window,"MyIOLibrary",{get:function(){if(!globalThis.window.__MyIOLibrary_INSTANCE__){_moduleLog("log","[SelectionStore] 📦 Creating protected MyIOLibrary container object");globalThis.window.__MyIOLibrary_INSTANCE__=existingLib||{}}return globalThis.window.__MyIOLibrary_INSTANCE__},set:function(value){_moduleLog("log","[SelectionStore] 🔄 UMD tried to overwrite MyIOLibrary - merging properties instead");if(value&&typeof value==="object"){const currentLib=globalThis.window.__MyIOLibrary_INSTANCE__||{};Object.keys(value).forEach(key=>{if(key==="MyIOSelectionStore"&¤tLib.MyIOSelectionStore){_moduleLog("log","[SelectionStore] ⏭️ Skipping MyIOSelectionStore - already set");return}currentLib[key]=value[key]});globalThis.window.__MyIOLibrary_INSTANCE__=currentLib}},configurable:false,enumerable:true});globalThis.window.__MyIOLibrary_PROTECTED__=true;_moduleLog("log","[SelectionStore] ✅ window.MyIOLibrary protected!")}let existingInstance=null;try{const targetWindow=globalThis.window.top?globalThis.window.top:globalThis.window;existingInstance=targetWindow.__MyIOSelectionStore_INSTANCE__;_moduleLog("log","[SelectionStore] window.top.__MyIOSelectionStore_INSTANCE__:",!!existingInstance)}catch(e){_moduleLog("warn","[SelectionStore] Cannot access window.top during module init:",e.message)}if(!existingInstance){existingInstance=typeof document!=="undefined"&&document.__MyIOSelectionStore_INSTANCE__||globalThis.window.__MyIOSelectionStore_INSTANCE__;_moduleLog("log","[SelectionStore] document.__MyIOSelectionStore_INSTANCE__:",!!(typeof document!=="undefined"&&document.__MyIOSelectionStore_INSTANCE__));_moduleLog("log","[SelectionStore] window.__MyIOSelectionStore_INSTANCE__:",!!globalThis.window.__MyIOSelectionStore_INSTANCE__)}if(existingInstance){_moduleLog("log","[SelectionStore] 🔄 REUSING constructor-created instance from __MyIOSelectionStore_INSTANCE__");_singletonInstance=existingInstance;exports.MyIOSelectionStore=_singletonInstance;if(!Object.getOwnPropertyDescriptor(globalThis.window,"MyIOSelectionStore")?.get){_moduleLog("log","[SelectionStore] 🔗 Defining window.MyIOSelectionStore getter to point to singleton");Object.defineProperty(globalThis.window,"MyIOSelectionStore",{get:function(){return _singletonInstance},set:function(value){_moduleLog("warn","[SelectionStore] ⚠️ Attempted to overwrite singleton - ignoring")},configurable:false,enumerable:true})}if(globalThis.window.MyIOLibrary&&typeof globalThis.window.MyIOLibrary==="object"){_moduleLog("log","[SelectionStore] 🔗 Updating window.MyIOLibrary.MyIOSelectionStore to point to singleton");globalThis.window.MyIOLibrary.MyIOSelectionStore=_singletonInstance}}else if(Object.getOwnPropertyDescriptor(globalThis.window,"MyIOSelectionStore")?.get){_moduleLog("log","[SelectionStore] 🔄 REUSING protected global instance via getter");exports.MyIOSelectionStore=globalThis.window.MyIOSelectionStore;_singletonInstance=exports.MyIOSelectionStore;if(globalThis.window.MyIOLibrary&&typeof globalThis.window.MyIOLibrary==="object"){_moduleLog("log","[SelectionStore] 🔗 Updating window.MyIOLibrary.MyIOSelectionStore to point to singleton");globalThis.window.MyIOLibrary.MyIOSelectionStore=_singletonInstance}}else if(globalThis.window.MyIOSelectionStore&&typeof globalThis.window.MyIOSelectionStore==="object"){_moduleLog("log","[SelectionStore] 🔒 UPGRADING existing instance to protected");_singletonInstance=globalThis.window.MyIOSelectionStore;exports.MyIOSelectionStore=_singletonInstance;Object.defineProperty(globalThis.window,"MyIOSelectionStore",{get:function(){return _singletonInstance},set:function(value){_moduleLog("warn","[SelectionStore] ⚠️ Attempted to overwrite singleton - ignoring")},configurable:false,enumerable:true});if(globalThis.window.MyIOLibrary&&typeof globalThis.window.MyIOLibrary==="object"){_moduleLog("log","[SelectionStore] 🔗 Updating window.MyIOLibrary.MyIOSelectionStore to point to singleton");globalThis.window.MyIOLibrary.MyIOSelectionStore=_singletonInstance}}else{_moduleLog("log","[SelectionStore] 🆕 Creating new protected singleton instance");_singletonInstance=new MyIOSelectionStoreClass;exports.MyIOSelectionStore=_singletonInstance;Object.defineProperty(globalThis.window,"MyIOSelectionStore",{get:function(){return _singletonInstance},set:function(value){_moduleLog("warn","[SelectionStore] ⚠️ Attempted to overwrite singleton - ignoring")},configurable:false,enumerable:true});if(globalThis.window.MyIOLibrary&&typeof globalThis.window.MyIOLibrary==="object"){_moduleLog("log","[SelectionStore] 🔗 Setting window.MyIOLibrary.MyIOSelectionStore to singleton");globalThis.window.MyIOLibrary.MyIOSelectionStore=_singletonInstance}_moduleLog("log","[SelectionStore] 🔒 Instance protected and ready")}if(!globalThis.window.MyIOSelectionStoreClass){globalThis.window.MyIOSelectionStoreClass=MyIOSelectionStoreClass}}else{exports.MyIOSelectionStore=new MyIOSelectionStoreClass}if(typeof module!=="undefined"&&module.exports){module.exports={MyIOSelectionStore:exports.MyIOSelectionStore,MyIOSelectionStoreClass:MyIOSelectionStoreClass}}var MyIODraggableCard=class{constructor(container,entity,options={}){if(!container||!entity||!entity.id){throw new Error("Container and entity with id are required")}this.container=container;this.entity=entity;this.options={showCheckbox:true,draggable:true,className:"",...options};this.cardElement=null;this.checkbox=null;this.isDragging=false;this.touchStartTime=0;this.touchTimer=null;this.destroyed=false;this._init()}destroy(){if(this.destroyed)return;this._removeEventListeners();if(this.cardElement&&this.cardElement.parentNode){this.cardElement.parentNode.removeChild(this.cardElement)}this.destroyed=true}updateEntity(newEntity){if(this.destroyed)return;this.entity={...this.entity,...newEntity};this._updateCardContent()}setSelected(selected){if(this.destroyed)return;if(this.checkbox){this.checkbox.checked=selected}this._updateSelectionState(selected)}_init(){this._createCardElement();this._attachEventListeners();this._updateSelectionState(this._getSelectionStore()?.isSelected(this.entity.id)||false)}_createCardElement(){if(typeof document==="undefined")return;const card=document.createElement("div");card.className=`myio-draggable-card ${this.options.className}`.trim();card.setAttribute("data-entity-id",this.entity.id);card.setAttribute("role","article");card.setAttribute("tabindex","0");if(this.options.draggable){card.draggable=true;card.setAttribute("aria-grabbed","false")}card.innerHTML=this._generateCardHTML();this.cardElement=card;this.checkbox=card.querySelector(".card-checkbox");this.container.appendChild(card)}_generateCardHTML(){const{id:id,name:name,icon:icon,group:group,lastValue:lastValue,unit:unit,status:status}=this.entity;const checkboxHtml=this.options.showCheckbox?`<input type="checkbox" class="card-checkbox" aria-label="Select ${name}">`:"";return`\n <div class="card-header">\n ${checkboxHtml}\n <div class="card-icon card-icon-${icon}">\n ${this._getIconSvg(icon)}\n </div>\n <div class="card-status card-status-${status}"></div>\n </div>\n <div class="card-body">\n <h3 class="card-title">${this._escapeHtml(name)}</h3>\n <p class="card-group">${this._escapeHtml(group)}</p>\n <div class="card-value">\n <span class="value">${this._formatValue(lastValue)}</span>\n <span class="unit">${this._escapeHtml(unit)}</span>\n </div>\n </div>\n <div class="card-footer">\n <span class="card-id">${this._escapeHtml(id)}</span>\n </div>\n `}_attachEventListeners(){if(!this.cardElement)return;this.cardElement.addEventListener("dragstart",this._handleDragStart.bind(this));this.cardElement.addEventListener("dragend",this._handleDragEnd.bind(this));this.cardElement.addEventListener("click",this._handleClick.bind(this));this.cardElement.addEventListener("touchstart",this._handleTouchStart.bind(this),{passive:false});this.cardElement.addEventListener("touchmove",this._handleTouchMove.bind(this),{passive:false});this.cardElement.addEventListener("touchend",this._handleTouchEnd.bind(this));this.cardElement.addEventListener("keydown",this._handleKeyDown.bind(this));if(this.checkbox){this.checkbox.addEventListener("change",this._handleCheckboxChange.bind(this));this.checkbox.addEventListener("click",this._handleCheckboxClick.bind(this))}const store=this._getSelectionStore();if(store){this._selectionChangeHandler=data=>{const isSelected=data.selectedIds.includes(this.entity.id);this._updateSelectionState(isSelected)};store.on("selection:change",this._selectionChangeHandler)}}_removeEventListeners(){if(!this.cardElement)return;const store=this._getSelectionStore();if(store&&this._selectionChangeHandler){store.off("selection:change",this._selectionChangeHandler)}this._selectionChangeHandler=null}_handleDragStart(event){if(!this.options.draggable){event.preventDefault();return}this.isDragging=true;this.cardElement.setAttribute("aria-grabbed","true");this.cardElement.classList.add("dragging");event.dataTransfer.setData("text/myio-id",this.entity.id);event.dataTransfer.setData("text/plain",this.entity.id);event.dataTransfer.effectAllowed="copy";const store=this._getSelectionStore();if(store){store.startDrag(this.entity.id)}this._announceToScreenReader(`Started dragging ${this.entity.name}`)}_handleDragEnd(event){this.isDragging=false;this.cardElement.setAttribute("aria-grabbed","false");this.cardElement.classList.remove("dragging");this._announceToScreenReader(`Finished dragging ${this.entity.name}`)}_handleTouchStart(event){if(!this.options.draggable)return;this.touchStartTime=Date.now();this.touchTimer=setTimeout(()=>{this._startTouchDrag(event)},500)}_handleTouchMove(event){if(this.touchTimer){clearTimeout(this.touchTimer);this.touchTimer=null}if(this.isDragging){event.preventDefault()}}_handleTouchEnd(event){if(this.touchTimer){clearTimeout(this.touchTimer);this.touchTimer=null}if(this.isDragging){this._endTouchDrag(event)}}_startTouchDrag(event){if(!this.options.draggable)return;this.isDragging=true;this.cardElement.classList.add("touch-dragging");if(navigator.vibrate){navigator.vibrate(50)}this._announceToScreenReader(`Touch drag started for ${this.entity.name}`)}_endTouchDrag(event){this.isDragging=false;this.cardElement.classList.remove("touch-dragging");this._announceToScreenReader(`Touch drag ended for ${this.entity.name}`)}_handleClick(event){if(event.target===this.checkbox)return;if(this.isDragging)return;const store=this._getSelectionStore();if(store){store.toggle(this.entity.id)}}_handleKeyDown(event){switch(event.key){case"Enter":case" ":event.preventDefault();if(event.shiftKey){const store=this._getSelectionStore();if(store){store.openComparison()}}else{const store=this._getSelectionStore();if(store){store.toggle(this.entity.id)}}break;case"Delete":case"Backspace":{event.preventDefault();const store=this._getSelectionStore();if(store){store.remove(this.entity.id)}break}}}_handleCheckboxChange(event){event.stopPropagation();const store=this._getSelectionStore();if(store){store.syncFromCheckbox(this.entity.id,event.target.checked)}}_handleCheckboxClick(event){event.stopPropagation()}_updateSelectionState(isSelected){if(this.destroyed)return;if(this.checkbox){this.checkbox.checked=isSelected}this.cardElement.classList.toggle("selected",isSelected);this.cardElement.setAttribute("aria-selected",isSelected.toString());const action=isSelected?"selected":"not selected";this.cardElement.setAttribute("aria-label",`${this.entity.name}, ${this.entity.group}, ${action}`)}_updateCardContent(){if(this.destroyed)return;const titleElement=this.cardElement.querySelector(".card-title");const groupElement=this.cardElement.querySelector(".card-group");const valueElement=this.cardElement.querySelector(".value");const unitElement=this.cardElement.querySelector(".unit");const idElement=this.cardElement.querySelector(".card-id");if(titleElement)titleElement.textContent=this.entity.name||"";if(groupElement)groupElement.textContent=this.entity.group||"";if(valueElement)valueElement.textContent=this._formatValue(this.entity.lastValue);if(unitElement)unitElement.textContent=this.entity.unit||"";if(idElement)idElement.textContent=this.entity.id||"";const iconElement=this.cardElement.querySelector(".card-icon");const statusElement=this.cardElement.querySelector(".card-status");if(iconElement){iconElement.className=`card-icon card-icon-${this.entity.icon||"generic"}`;iconElement.innerHTML=this._getIconSvg(this.entity.icon||"generic")}if(statusElement){statusElement.className=`card-status card-status-${this.entity.status||"unknown"}`}}_getSelectionStore(){if(typeof globalThis!=="undefined"&&globalThis.window?.MyIOSelectionStore){return globalThis.window.MyIOSelectionStore}if(typeof globalThis!=="undefined"&&globalThis.window&&globalThis.window.MyIOSelectionStore){return globalThis.window.MyIOSelectionStore}return null}_formatValue(value){if(value===null||value===void 0||isNaN(value)){return"-"}return new Intl.NumberFormat("pt-BR",{minimumFractionDigits:0,maximumFractionDigits:2}).format(Number(value))}_escapeHtml(text){if(typeof text!=="string")return"";if(typeof document==="undefined")return text;const div=document.createElement("div");div.textContent=text;return div.innerHTML}_getIconSvg(iconType){const icons={energy:'<svg viewBox="0 0 24 24"><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/></svg>',water:'<svg viewBox="0 0 24 24"><path d="M12 2c-5.33 4.55-8 8.48-8 11.8 0 4.98 3.8 8.2 8 8.2s8-3.22 8-8.2c0-3.32-2.67-7.25-8-11.8z"/></svg>',temp:'<svg viewBox="0 0 24 24"><path d="M15 13V5c0-1.66-1.34-3-3-3S9 3.34 9 5v8c-1.21.91-2 2.37-2 4 0 2.76 2.24 5 5 5s5-2.24 5-5c0-1.63-.79-3.09-2-4z"/></svg>',net:'<svg viewBox="0 0 24 24"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM9 17H7v-7h2v7zm4 0h-2V7h2v10zm4 0h-2v-4h2v4z"/></svg>',alert:'<svg viewBox="0 0 24 24"><path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z"/></svg>',generic:'<svg viewBox="0 0 24 24"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/></svg>'};return icons[iconType]||icons.generic}_announceToScreenReader(message){const store=this._getSelectionStore();if(store&&store.announceToScreenReader){store.announceToScreenReader(message)}}};if(typeof globalThis!=="undefined"&&typeof globalThis.window!=="undefined"){globalThis.window.MyIODraggableCard=MyIODraggableCard}if(typeof module!=="undefined"&&module.exports){module.exports={MyIODraggableCard:MyIODraggableCard}}function renderCardComponentV2({entityObject:entityObject,handleActionDashboard:handleActionDashboard,handleActionReport:handleActionReport,handleActionSettings:handleActionSettings,handleSelect:handleSelect,handInfo:handInfo,handleClickCard:handleClickCard,useNewComponents:useNewComponents=true,enableSelection:enableSelection=true,enableDragDrop:enableDragDrop=true}){const{entityId:entityId,labelOrName:labelOrName,deviceIdentifier:deviceIdentifier,entityType:entityType,deviceType:deviceType,slaveId:slaveId,ingestionId:ingestionId,val:val,centralId:centralId,updatedIdentifiers:updatedIdentifiers={},perc:perc=0,deviceStatus:deviceStatus,centralName:centralName,connectionStatusTime:connectionStatusTime,timeVal:timeVal}=entityObject;const MyIOToast2=function(){let toastContainer=null;let toastTimeout=null;const TOAST_CSS=`\n #myio-global-toast-container {\n position: fixed;\n top: 25px;\n right: 25px;\n z-index: 99999;\n width: 320px;\n padding: 16px;\n border-radius: 8px;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;\n font-size: 15px;\n color: #fff;\n transform: translateX(120%);\n transition: transform 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);\n box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);\n border-left: 5px solid transparent;\n display: flex;\n align-items: center;\n }\n #myio-global-toast-container.show {\n transform: translateX(0);\n }\n #myio-global-toast-container.warning {\n background-color: #ff9800; /* Laranja para alerta */\n border-color: #f57c00;\n }\n #myio-global-toast-container.error {\n background-color: #d32f2f; /* Vermelho para erro */\n border-color: #b71c1c;\n }\n #myio-global-toast-container::before {\n content: '⚠️'; /* Ícone de alerta */\n margin-right: 12px;\n font-size: 20px;\n }\n #myio-global-toast-container.error::before {\n content: '🚫'; /* Ícone de erro */\n }\n `;function createToastElement(){if(document.getElementById("myio-global-toast-container")){toastContainer=document.getElementById("myio-global-toast-container");return}const style=document.createElement("style");style.id="myio-global-toast-styles";style.textContent=TOAST_CSS;document.head.appendChild(style);toastContainer=document.createElement("div");toastContainer.id="myio-global-toast-container";document.body.appendChild(toastContainer)}function show(message,type="warning",duration=3500){if(!toastContainer){createToastElement()}clearTimeout(toastTimeout);toastContainer.textContent=message;toastContainer.className="";toastContainer.classList.add(type);setTimeout(()=>{toastContainer.classList.add("show")},10);toastTimeout=setTimeout(()=>{toastContainer.classList.remove("show")},duration)}if(document.readyState==="loading"){document.addEventListener("DOMContentLoaded",createToastElement)}else{createToastElement()}return{show:show}}();if(!useNewComponents){return renderCardComponentLegacy({entityObject:entityObject,handleActionDashboard:handleActionDashboard,handleActionReport:handleActionReport,handleActionSettings:handleActionSettings,handleSelect:handleSelect,handInfo:handInfo,handleClickCard:handleClickCard})}const connectionStatus=mapDeviceToConnectionStatus(deviceStatus);isDeviceOffline(deviceStatus);const shouldFlashIcon2=shouldFlashIcon(deviceStatus);const icon=getDeviceStatusIcon(deviceStatus);getConnectionStatusIcon(connectionStatus);const mapDeviceTypeToIcon=deviceType2=>{const typeMap={COMPRESSOR:"energy",VENTILADOR:"energy",ESCADA_ROLANTE:"energy",ELEVADOR:"energy",MOTOR:"energy","3F_MEDIDOR":"energy",RELOGIO:"energy",ENTRADA:"energy",SUBESTACAO:"energy",HIDROMETRO:"water",CAIXA_DAGUA:"water",TANK:"water"};const normalizedType=deviceType2?.toUpperCase()||"";return typeMap[normalizedType]||"generic"};const getValueTypeFromDeviceType=deviceType2=>{const typeMap={COMPRESSOR:"ENERGY",VENTILADOR:"ENERGY",ESCADA_ROLANTE:"ENERGY",ELEVADOR:"ENERGY",MOTOR:"ENERGY","3F_MEDIDOR":"ENERGY",RELOGIO:"ENERGY",ENTRADA:"ENERGY",SUBESTACAO:"ENERGY",HIDROMETRO:"WATER",CAIXA_DAGUA:"WATER",TANK:"TANK"};const normalizedType=deviceType2?.toUpperCase()||"";return typeMap[normalizedType]||"ENERGY"};const isEnergyDevice=deviceType2=>{const energyDeviceTypes=["COMPRESSOR","VENTILADOR","ESCADA_ROLANTE","ELEVADOR","MOTOR","3F_MEDIDOR","RELOGIO","ENTRADA","SUBESTACAO"];const normalizedType=deviceType2?.toUpperCase()||"";return energyDeviceTypes.includes(normalizedType)};const formatCardValue=(value,deviceType2)=>{const numValue=Number(value)||0;if(isEnergyDevice(deviceType2)){return formatEnergy(numValue)}else{const unit=determineUnit(deviceType2);const formattedValue=numValue.toLocaleString("pt-BR",{minimumFractionDigits:0,maximumFractionDigits:2});return`${formattedValue} ${unit}`}};const determineUnit=deviceType2=>{const valueType=getValueTypeFromDeviceType(deviceType2);switch(valueType){case"ENERGY":return"";case"WATER":return"m³";case"TANK":return"m.c.a";default:return""}};const cardEntity={id:entityId,name:labelOrName||"Dispositivo",icon:mapDeviceTypeToIcon(deviceType),group:deviceIdentifier||entityType||"Dispositivo",lastValue:Number(val)||0,unit:determineUnit(deviceType),status:mapDeviceStatusToCardStatus(deviceStatus),ingestionId:ingestionId||entityId};if(enableSelection&&exports.MyIOSelectionStore){exports.MyIOSelectionStore.registerEntity(cardEntity)}const container=document.createElement("div");container.className="myio-enhanced-card-container";if(!document.getElementById("myio-enhanced-card-styles")){const style=document.createElement("style");style.id="myio-enhanced-card-styles";style.textContent=`\n .myio-enhanced-card-container {\n position: relative;\n width: 100%;\n height: 100%;\n }\n\n .myio-enhanced-card-container .myio-draggable-card {\n width: 100%;\n border-radius: 10px;\n padding: 8px 12px; \n background: #fff;\n box-shadow: 0 4px 10px rgba(0, 0, 0, .05);\n cursor: pointer;\n \n transition: transform .2s;\n box-sizing: border-box;\n overflow: hidden;\n min-height: 140px;\n display: flex;\n flex-direction: row;\n align-items: stretch;\n }\n \n .myio-enhanced-card-container .myio-draggable-card:hover {\n transform: scale(1.05);\n }\n \n .myio-enhanced-card-container .myio-draggable-card.selected {\n border: 2px solid #00e09e;\n box-shadow: 0 4px 12px rgba(0,224,158,0.2);\n background: linear-gradient(135deg, #f0fdf9, #ecfdf5);\n }\n \n .myio-enhanced-card-container .myio-draggable-card.offline {\n border: 2px solid #ff4d4f;\n animation: border-blink 1s infinite;\n }\n \n @keyframes border-blink {\n 0%, 100% { box-shadow: 0 0 8px rgba(255, 77, 79, 0.9); }\n 50% { box-shadow: 0 0 16px rgba(255, 0, 0, 0.6); }\n }\n \n .myio-enhanced-card-container .card-actions {\n flex-shrink: 0;\n height: 100%;\n box-shadow: 1px 0 2px rgba(0, 0, 0, .1);\n display: flex;\n flex-direction: column;\n padding: 0 4px;\n justify-content: space-around;\n align-items: center;\n gap: 8px;\n }\n \n .myio-enhanced-card-container .card-action {\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 6px;\n transition: all 0.2s ease;\n cursor: pointer;\n border: none;\n background: rgba(0, 0, 0, 0.05);\n }\n \n .myio-enhanced-card-container .card-action:hover {\n background: rgba(0, 224, 158, 0.1);\n transform: scale(1.1);\n }\n \n .myio-enhanced-card-container .card-action img {\n width: 20px;\n height: 20px;\n }\n \n .myio-enhanced-card-container .card-checkbox {\n width: 16px;\n height: 16px;\n cursor: pointer;\n }\n \n .myio-enhanced-card-container .card-body {\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: center;\n align-items: center;\n padding: 0 12px;\n text-align: center;\n }\n \n .myio-enhanced-card-container .card-title {\n font-weight: 700;\n font-size: 0.85rem;\n margin-bottom: 4px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n max-width: 100%;\n }\n \n .myio-enhanced-card-container .card-group {\n font-size: 0.7rem;\n color: #888;\n margin-bottom: 8px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n max-width: 100%;\n }\n \n .myio-enhanced-card-container .card-value {\n display: flex;\n align-items: center;\n gap: 4px;\n font-size: 0.9rem;\n font-weight: 700;\n color: #28a745;\n }\n \n .myio-enhanced-card-container .card-unit {\n font-size: 0.75rem;\n color: rgba(0, 0, 0, 0.6);\n }\n \n .myio-enhanced-card-container .card-percentage {\n font-size: 0.75rem;\n color: rgba(0, 0, 0, 0.45);\n margin-left: 4px;\n }\n \n .myio-enhanced-card-container .card-icon {\n width: 24px;\n height: 24px;\n margin-bottom: 8px;\n }\n \n .myio-enhanced-card-container .card-icon svg {\n width: 100%;\n height: 100%;\n fill: currentColor;\n }\n \n .myio-enhanced-card-container .card-status-indicator {\n position: absolute;\n top: 8px;\n right: 8px;\n width: 12px;\n height: 12px;\n border-radius: 50%;\n z-index: 10;\n }\n \n .myio-enhanced-card-container .card-status-ok {\n background: #28a745;\n }\n \n .myio-enhanced-card-container .card-status-alert {\n background: #ffc107;\n }\n \n .myio-enhanced-card-container .card-status-fail,\n .myio-enhanced-card-container .card-status-offline {\n background: #dc3545;\n }\n \n .myio-enhanced-card-container .card-status-unknown {\n background: #6c757d;\n }\n \n .myio-enhanced-card-container .info-panel {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(255, 255, 255, 0.95);\n backdrop-filter: blur(4px);\n display: none;\n flex-direction: column;\n justify-content: center;\n align-items: center;\n padding: 16px;\n border-radius: 10px;\n z-index: 20;\n }\n \n .myio-enhanced-card-container .info-panel.active {\n display: flex;\n }\n \n .myio-enhanced-card-container .info-close {\n position: absolute;\n top: 8px;\n right: 8px;\n background: none;\n border: none;\n font-size: 18px;\n cursor: pointer;\n color: #666;\n }\n \n .myio-enhanced-card-container .info-content {\n text-align: center;\n font-size: 0.8rem;\n line-height: 1.4;\n }\n \n .myio-enhanced-card-container .info-content strong {\n display: block;\n margin-bottom: 8px;\n color: #333;\n }\n `;document.head.appendChild(style)}const getDeviceImageUrl=deviceType2=>{function normalizeString(str){if(typeof str!=="string"){str=""}return str.normalize("NFD").replace(/[\u0300-\u036f]/g,"").toUpperCase()}const deviceImages={MOTOR:"https://dashboard.myio-bas.com/api/images/public/Rge8Q3t0CP5PW8XyTn9bBK9aVP6uzSTT","3F_MEDIDOR":"https://dashboard.myio-bas.com/api/images/public/f9Ce4meybsdaAhAkUlAfy5ei3I4kcN4k",RELOGIO:"https://dashboard.myio-bas.com/api/images/public/ljHZostWg0G5AfKiyM8oZixWRIIGRASB",HIDROMETRO:"https://dashboard.myio-bas.com/api/images/public/aMQYFJbGHs9gQbQkMn6XseAlUZHanBR4",ENTRADA:"https://dashboard.myio-bas.com/api/images/public/TQHPFqiejMW6lOSVsb8Pi85WtC0QKOLU",CAIXA_DAGUA:"https://dashboard.myio-bas.com/api/images/public/3t6WVhMQJFsrKA8bSZmrngDsNPkZV7fq",ELEVADOR:"https://dashboard.myio-bas.com/api/images/public/rAjOvdsYJLGah6w6BABPJSD9znIyrkJX",ESCADA_ROLANTE:"https://dashboard.myio-bas.com/api/images/public/EJ997iB2HD1AYYUHwIloyQOOszeqb2jp"};const defaultImage="https://cdn-icons-png.flaticon.com/512/1178/1178428.png";const nameType=normalizeString(deviceType2);return deviceImages[nameType]||defaultImage};const deviceImageUrl=getDeviceImageUrl(deviceType);const cardHTML=`\n <div class="device-card-centered clickable ${cardEntity.status==="offline"?"offline":""}" \n data-entity-id="${entityId}"\n draggable="${enableDragDrop}"\n tabindex="0"\n role="article"\n aria-label="${cardEntity.name}, ${cardEntity.group}">\n \n <div class="device-card-inner">\n <div class="device-card-front">\n ${enableSelection&&typeof handleSelect==="function"?`<input type="checkbox" class="card-checkbox action-checker" aria-label="Select ${cardEntity.name}" style="position: absolute; top: 8px; right: 8px; z-index: 10;">`:""}\n \n <div style="display:flex;flex-direction:column;justify-content:center;align-items:center;height:100%; flex-grow: 1; min-width: 0; padding: 0 12px 0 20px; margin-left: 16px;">\n \n <div class="device-title-row" style="flex-direction: column; min-height: 38px; text-align: center; width: 100%;">\n <span class="device-title" title="${cardEntity.name}">\n ${cardEntity.name.length>15?cardEntity.name.slice(0,15)+"…":cardEntity.name}\n </span>\n ${deviceIdentifier?`\n <span class="device-subtitle" title="${deviceIdentifier}">\n ${deviceIdentifier}\n </span>\n `:""}\n </div>\n\n <img class="device-image" src="${deviceImageUrl}" alt="${deviceType}" />\n \n <div class="device-data-row">\n <div class="consumption-main">\n <span class="flash-icon ${shouldFlashIcon2?"flash":""}">\n ${icon}\n </span>\n <span class="consumption-value">${formatCardValue(cardEntity.lastValue,deviceType)}</span>\n <span class="device-title-percent">(${perc.toFixed(1)}%)</span>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <div class="connection-status-icon" data-conn="${connectionStatus}" data-state="${deviceStatus}" aria-label="${connectionStatus}"></div>\n \n </div>\n `;container.innerHTML=cardHTML;const enhancedCardElement=container.querySelector(".device-card-centered");if(!document.getElementById("myio-enhanced-card-layout-styles")){const layoutStyle=document.createElement("style");layoutStyle.id="myio-enhanced-card-layout-styles";layoutStyle.textContent=`\n /* ===== MYIO Card v2 — Clean Piano Keys & Flat Status ===== */\n\n /* Card shell (kept) */\n .device-card-centered.clickable {\n width: 90% !important;\n max-width: 280px !important;\n border-radius: 14px !important;\n padding: 18px !important;\n background: linear-gradient(145deg, #ffffff 0%, #f8fafc 100%) !important;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.08), 0 2px 8px rgba(0, 0, 0, 0.04) !important;\n cursor: pointer;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;\n border: 1px solid rgba(226, 232, 240, 0.8) !important;\n position: relative;\n overflow: hidden;\n backdrop-filter: blur(10px);\n min-height: 126px !important;\n margin: 0 auto;\n }\n \n .device-card-centered.clickable::before {\n content: '';\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n height: 3px;\n background: linear-gradient(90deg, #00e09e 0%, #00b4d8 50%, #7209b7 100%);\n opacity: 0;\n transition: opacity 0.3s ease;\n }\n \n .device-card-centered.clickable:hover {\n transform: translateY(-4px) scale(1.02) !important;\n box-shadow: 0 16px 48px rgba(0, 0, 0, 0.12), 0 8px 16px rgba(0, 0, 0, 0.08) !important;\n border-color: rgba(0, 224, 158, 0.3) !important;\n }\n \n .device-card-centered.clickable:hover::before {\n opacity: 1;\n }\n\n /* Selected / Offline (kept) */\n .device-card-centered.selected {\n border: 2px solid #00e09e !important;\n box-shadow: 0 12px 40px rgba(0, 224, 158, 0.25), 0 4px 12px rgba(0, 224, 158, 0.15) !important;\n background: linear-gradient(145deg, #f0fdf9 0%, #ecfdf5 50%, #f0fdf9 100%) !important;\n transform: translateY(-2px) !important;\n }\n \n .device-card-centered.selected::before {\n opacity: 1;\n background: linear-gradient(90deg, #00e09e 0%, #00d4aa 100%);\n }\n \n .device-card-centered.offline {\n border: 2px solid #ef4444 !important;\n background: linear-gradient(145deg, #fef2f2 0%, #fee2e2 50%, #fef2f2 100%) !important;\n animation: premium-offline-pulse 2s infinite !important;\n }\n \n .device-card-centered.offline::before {\n opacity: 1;\n background: linear-gradient(90deg, #ef4444 0%, #dc2626 100%);\n }\n \n @keyframes premium-offline-pulse {\n 0%, 100% { \n box-shadow: 0 8px 32px rgba(239, 68, 68, 0.15), 0 2px 8px rgba(239, 68, 68, 0.1);\n }\n 50% { \n box-shadow: 0 12px 40px rgba(239, 68, 68, 0.25), 0 4px 12px rgba(239, 68, 68, 0.2);\n }\n }\n\n /* Device image & titles (kept) */\n .device-card-centered .device-image {\n max-height: 47px !important;\n width: auto;\n margin: 10px 0 !important;\n display: block;\n filter: drop-shadow(0 3px 6px rgba(0, 0, 0, 0.1));\n transition: all 0.3s ease;\n border-radius: 7px;\n }\n \n .device-card-centered:hover .device-image {\n filter: drop-shadow(0 5px 10px rgba(0, 0, 0, 0.15));\n transform: scale(1.05);\n }\n \n .device-card-centered .device-title-row {\n display: flex !important;\n flex-direction: column !important;\n align-items: center !important;\n justify-content: center !important;\n text-align: center !important;\n width: 100% !important;\n min-height: 38px !important;\n margin-bottom: 8px !important;\n }\n \n .device-card-centered .device-title {\n font-weight: 700 !important;\n font-size: 0.85rem !important;\n color: #1e293b !important;\n margin: 0 0 4px 0 !important;\n text-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n letter-spacing: -0.025em;\n display: block !important;\n width: 100% !important;\n text-align: center !important;\n }\n \n .device-card-centered .device-subtitle {\n font-size: 0.67rem !important;\n color: #64748b !important;\n font-weight: 500 !important;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n opacity: 0.8;\n display: block !important;\n width: 100% !important;\n text-align: center !important;\n margin: 0 !important;\n padding: 0 !important;\n }\n\n /* Value pill (kept) */\n .device-card-centered .consumption-main {\n background: linear-gradient(135deg, rgba(0, 224, 158, 0.1) 0%, rgba(0, 180, 216, 0.1) 100%);\n border-radius: 10px;\n padding: 7px 10px;\n margin-top: 7px;\n border: 1px solid rgba(0, 224, 158, 0.2);\n backdrop-filter: blur(10px);\n }\n \n .device-card-centered .consumption-value {\n font-weight: 700 !important;\n font-size: 0.9rem !important;\n color: #059669 !important;\n text-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n }\n \n .device-card-centered .device-title-percent {\n font-size: 0.72rem !important;\n color: #6b7280 !important;\n font-weight: 600 !important;\n margin-left: 5px;\n }\n \n .device-card-centered .flash-icon {\n font-size: 1rem !important;\n margin-right: 7px;\n transition: all 0.3s ease;\n }\n \n .device-card-centered:hover .flash-icon {\n transform: scale(1.1);\n }\n \n .device-card-centered .flash-icon.flash {\n animation: premium-flash 1.5s infinite;\n }\n \n @keyframes premium-flash {\n 0%, 100% { \n opacity: 1; \n transform: scale(1);\n filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.1));\n }\n 50% { \n opacity: 0.3; \n transform: scale(1.15);\n filter: drop-shadow(0 4px 8px rgba(239, 68, 68, 0.3));\n }\n }\n\n /* Checkbox (kept) */\n .device-card-centered .card-checkbox {\n width: 16px !important;\n height: 16px !important;\n cursor: pointer;\n background: rgba(255, 255, 255, 0.9) !important;\n border: 2px solid #e2e8f0 !important;\n border-radius: 5px !important;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n backdrop-filter: blur(10px);\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);\n position: relative;\n }\n \n .device-card-centered .card-checkbox:hover {\n border-color: #00e09e !important;\n box-shadow: 0 3px 6px rgba(0, 224, 158, 0.15);\n transform: scale(1.05);\n }\n \n .device-card-centered .card-checkbox:checked {\n background: linear-gradient(135deg, #00e09e 0%, #00d4aa 100%) !important;\n border-color: #00e09e !important;\n box-shadow: 0 3px 10px rgba(0, 224, 158, 0.3);\n }\n \n .device-card-centered .card-checkbox:checked::after {\n content: '✓';\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n color: white;\n font-size: 10px;\n font-weight: bold;\n text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);\n }\n\n /* ——— NEW: Piano-Key Actions (flat by default, full height) ——— */\n .device-card-centered .card-actions {\n position: absolute;\n left: 12px;\n top: 12px;\n bottom: 12px;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 0;\n border: 1px solid rgba(226, 232, 240, 0.9);\n border-radius: 8px;\n background: #fff;\n overflow: visible;\n box-shadow: none !important;\n z-index: 10;\n }\n \n .device-card-centered .card-action {\n width: 36px !important;\n height: 36px !important;\n border: 0;\n border-bottom: 1px solid rgba(226, 232, 240, 0.9);\n background: #fff !important;\n box-shadow: none !important;\n backdrop-filter: none !important;\n transform: translateZ(0);\n transition: transform 0.18s ease, box-shadow 0.18s ease;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n margin: 0;\n border-radius: 0;\n }\n \n .device-card-centered .card-action:first-child {\n border-top-left-radius: 7px;\n border-top-right-radius: 7px;\n }\n \n .device-card-centered .card-action:last-child {\n border-bottom: 0;\n border-bottom-left-radius: 7px;\n border-bottom-right-radius: 7px;\n }\n \n .device-card-centered .card-action img {\n width: 16px !important;\n height: 16px !important;\n filter: grayscale(0.2) brightness(0.85);\n transition: transform 0.18s ease, filter 0.18s ease;\n }\n\n /* Lift on interaction only */\n .device-card-centered .card-action:hover,\n .device-card-centered .card-action:focus-visible {\n transform: translateY(-2px) scale(1.05);\n box-shadow: 0 6px 14px rgba(16, 24, 40, 0.12), 0 2px 6px rgba(16, 24, 40, 0.08);\n outline: none;\n }\n \n .device-card-centered .card-action:hover img,\n .device-card-centered .card-action:focus-visible img {\n filter: grayscale(0) brightness(1);\n transform: scale(1.08);\n }\n\n /* ——— NEW: Flat Status (CSS dot, never clipped) ——— */\n .device-card-centered .connection-status-icon {\n position: absolute;\n bottom: 18px;\n right: 18px;\n width: 14px;\n height: 14px;\n border-radius: 50%;\n background: #22c55e;\n border: 1px solid rgba(0, 0, 0, 0.06);\n box-shadow: 0 3px 6px rgba(0, 0, 0, 0.15),\n 0 1px 3px rgba(0, 0, 0, 0.1),\n inset 0 -2px 4px rgba(0, 0, 0, 0.1),\n inset 0 2px 3px rgba(255, 255, 255, 0.4) !important;\n backdrop-filter: none !important;\n z-index: 5;\n transform: translateZ(0);\n transition: transform 0.2s ease, box-shadow 0.2s ease;\n }\n\n .device-card-centered .connection-status-icon:hover {\n transform: translateZ(2px) scale(1.05);\n box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2),\n 0 2px 4px rgba(0, 0, 0, 0.12),\n inset 0 -2px 5px rgba(0, 0, 0, 0.15),\n inset 0 2px 4px rgba(255, 255, 255, 0.5) !important;\n }\n\n /* Map colors by connection or device state */\n .device-card-centered .connection-status-icon[data-conn="offline"] {\n background: #94a3b8;\n }\n .device-card-centered .connection-status-icon[data-state="warning"] {\n background: #f59e0b;\n }\n .device-card-centered .connection-status-icon[data-state="danger"] {\n background: #ef4444;\n }\n .device-card-centered .connection-status-icon[data-state="no_info"] {\n background: #94a3b8;\n }\n .device-card-centered .connection-status-icon[data-state="maintenance"] {\n background: #0ea5e9;\n }\n \n .myio-enhanced-card-container .info-close:hover {\n background: rgba(239, 68, 68, 0.2) !important;\n transform: scale(1.1);\n }\n\n /* Responsive / Dark mode (kept) */\n @media (max-width: 768px) {\n .device-card-centered.clickable {\n padding: 16px !important;\n border-radius: 12px !important;\n }\n \n .device-card-centered .device-image {\n max-height: 44px !important;\n }\n \n .device-card-centered .card-action {\n width: 36px !important;\n height: 36px !important;\n }\n }\n \n @media (prefers-color-scheme: dark) {\n .device-card-centered.clickable {\n background: linear-gradient(145deg, #1e293b 0%, #334155 100%) !important;\n border-color: rgba(71, 85, 105, 0.8) !important;\n color: #f1f5f9 !important;\n }\n \n .device-card-centered .device-title {\n color: #f1f5f9 !important;\n }\n \n .device-card-centered .device-subtitle {\n color: #94a3b8 !important;\n }\n \n .device-card-centered .card-actions {\n border-color: rgba(71, 85, 105, 0.8);\n background: #1e293b;\n }\n \n .device-card-centered .card-action {\n background: #1e293b !important;\n border-bottom: 1px solid rgba(71, 85, 105, 0.8);\n }\n }\n `;document.head.appendChild(layoutStyle)}const actionsContainer=document.createElement("div");actionsContainer.className="card-actions";if(typeof handleActionDashboard==="function"){const dashboardBtn=document.createElement("button");dashboardBtn.className="card-action action-dashboard";dashboardBtn.title="Dashboard";dashboardBtn.innerHTML='<img src="https://dashboard.myio-bas.com/api/images/public/TAVXE0sTbCZylwGsMF9lIWdllBB3iFtS"/>';dashboardBtn.addEventListener("click",e=>{e.stopPropagation();handleActionDashboard(entityObject)});actionsContainer.appendChild(dashboardBtn)}if(typeof handleActionReport==="function"){const reportBtn=document.createElement("button");reportBtn.className="card-action action-report";reportBtn.title="Relatório";reportBtn.innerHTML='<img src="https://dashboard.myio-bas.com/api/images/public/d9XuQwMYQCG2otvtNSlqUHGavGaSSpz4"/>';reportBtn.addEventListener("click",e=>{e.stopPropagation();handleActionReport(entityObject)});actionsContainer.appendChild(reportBtn)}if(typeof handleActionSettings==="function"){const settingsBtn=document.createElement("button");settingsBtn.className="card-action action-settings";settingsBtn.title="Configurações";settingsBtn.innerHTML='<img src="https://dashboard.myio-bas.com/api/images/public/5n9tze6vED2uwIs5VvJxGzNNZ9eV4yoz"/>';settingsBtn.addEventListener("click",e=>{e.stopPropagation();handleActionSettings(entityObject)});actionsContainer.appendChild(settingsBtn)}if(handInfo){const infoPanel=document.createElement("div");infoPanel.className="info-panel";const closeBtn=document.createElement("button");closeBtn.className="info-close";closeBtn.innerHTML="×";closeBtn.addEventListener("click",()=>{infoPanel.classList.remove("active")});const infoContent=document.createElement("div");infoContent.className="info-content";let connectionInfo="";if(connectionStatusTime){const date=new Date(connectionStatusTime);connectionInfo=`<strong>Central:</strong> ${centralName||"N/A"}<br>`;connectionInfo+=`<strong>Última Conexão:</strong> ${date.toLocaleString("pt-BR")}<br>`}if(timeVal){const telemetryDate=new Date(timeVal);const now=new Date;const diffMs=now-telemetryDate;const diffHours=Math.floor(diffMs/(1e3*60*60));connectionInfo+=`<strong>Última Telemetria:</strong> ${telemetryDate.toLocaleString("pt-BR")}`;if(diffHours>0){connectionInfo+=` (${diffHours}h atrás)`}}infoContent.innerHTML=connectionInfo||"<strong>Informações não disponíveis</strong>";infoPanel.appendChild(closeBtn);infoPanel.appendChild(infoContent);container.appendChild(infoPanel);const infoBtn=document.createElement("button");infoBtn.className="card-action action-info";infoBtn.title="Informações";infoBtn.innerHTML="ℹ️";infoBtn.addEventListener("click",e=>{e.stopPropagation();infoPanel.classList.toggle("active")});actionsContainer.appendChild(infoBtn)}if(enhancedCardElement&&actionsContainer.children.length>0){enhancedCardElement.insertBefore(actionsContainer,enhancedCardElement.firstChild)}if(enableSelection&&exports.MyIOSelectionStore){const checkbox=enhancedCardElement.querySelector(".card-checkbox");if(checkbox){checkbox.addEventListener("change",e=>{e.stopPropagation();if(e.target.checked){const currentCount=exports.MyIOSelectionStore.getSelectedEntities().length;const selectedEntityes=exports.MyIOSelectionStore.getSelectedEntities();console.log("selectedEntityes",selectedEntityes);const isTryingToAdd=e.target.checked;if(isTryingToAdd&¤tCount>=6){e.preventDefault();e.target.checked=false;MyIOToast2.show("Não é possível selecionar mais de 6 itens.","warning");return}exports.MyIOSelectionStore.add(entityId)}else{exports.MyIOSelectionStore.remove(entityId)}})}const handleSelectionChange=data=>{const isSelected=data.selectedIds.includes(entityId);if(checkbox){checkbox.checked=isSelected}enhancedCardElement.classList.toggle("selected",isSelected)};exports.MyIOSelectionStore.on("selection:change",handleSelectionChange);const isInitiallySelected=exports.MyIOSelectionStore.isSelected(entityId);if(checkbox){checkbox.checked=isInitiallySelected}enhancedCardElement.classList.toggle("selected",isInitiallySelected);container._cleanup=()=>{exports.MyIOSelectionStore.off("selection:change",handleSelectionChange)}}if(enableDragDrop){enhancedCardElement.addEventListener("dragstart",e=>{e.dataTransfer.setData("text/myio-id",entityId);e.dataTransfer.setData("application/json",JSON.stringify(entityObject));e.dataTransfer.setData("text/myio-name",entityObject.labelOrName);e.dataTransfer.effectAllowed="copy";if(exports.MyIOSelectionStore){exports.MyIOSelectionStore.startDrag(entityId)}});enhancedCardElement.addEventListener("dragend",()=>{})}if(typeof handleClickCard==="function"){enhancedCardElement.addEventListener("click",e=>{if(!e.target.closest(".card-action")&&!e.target.closest(".card-checkbox")){handleClickCard(entityObject)}})}const jQueryLikeObject={get:index=>index===0?container:void 0,0:container,length:1,find:selector=>{const found=container.querySelector(selector);return{get:index=>index===0?found:void 0,0:found,length:found?1:0,on:(event,handler)=>{if(found)found.addEventListener(event,handler);return this}}},on:(event,handler)=>{container.addEventListener(event,handler);return this},addClass:className=>{container.classList.add(className);return this},removeClass:className=>{container.classList.remove(className);return this},destroy:()=>{if(container._cleanup){container._cleanup()}}};return jQueryLikeObject}function renderCardComponentLegacy(options){const{renderCardComponent:renderCardComponent4}=(init_template_card(),__toCommonJS(template_card_exports));return renderCardComponent4(options)}function renderCardComponent2(options){const useNewComponents=options.useNewComponents!==false&&typeof exports.MyIOSelectionStore!=="undefined"&&typeof MyIODraggableCard!=="undefined";if(useNewComponents){return renderCardComponentV2(options)}else{return renderCardComponentLegacy(options)}}var CSS_STRING=`\n/* CSS Variables for theming */\n:root {\n --myio-card-radius: 16px;\n --myio-card-shadow: 0 2px 8px rgba(10, 31, 68, .06);\n --myio-card-bg: #fff;\n --myio-card-border: #e9eef5;\n\n /* Status colors - Normal/Power On (blue) */\n --myio-chip-ok-bg: #dbeafe;\n --myio-chip-ok-fg: #1d4ed8;\n --myio-border-ok: rgba(59, 130, 246, 0.4);\n\n /* Status colors - Standby (green) */\n --myio-chip-standby-bg: #dcfce7;\n --myio-chip-standby-fg: #15803d;\n --myio-border-standby: rgba(34, 197, 94, 0.4);\n\n /* Status colors - Alert/Warning (yellow) */\n --myio-chip-alert-bg: #fef3c7;\n --myio-chip-alert-fg: #b45309;\n --myio-border-alert: rgba(245, 158, 11, 0.5);\n\n /* Status colors - Failure (red) */\n --myio-chip-failure-bg: #fee2e2;\n --myio-chip-failure-fg: #b91c1c;\n --myio-border-failure: rgba(239, 68, 68, 0.5);\n\n /* Status colors - Offline (gray) */\n --myio-chip-offline-bg: #f1f5f9;\n --myio-chip-offline-fg: #64748b;\n --myio-border-offline: rgba(100, 116, 139, 0.4);\n\n --myio-text-1: #0f172a;\n --myio-text-2: #4b5563;\n --myio-muted: #94a3b8;\n\n --myio-eff-bar-bg: #e6edf5;\n --myio-eff-bar-a: #1e90ff;\n --myio-eff-bar-b: #a3d1ff;\n\n --myio-badge-border: rgba(255, 153, 0, .35);\n --myio-badge-border-failure: rgba(244, 67, 54, .45);\n}\n\n/* Main card container */\n.myio-ho-card {\n background: var(--myio-card-bg);\n border: 1px solid var(--myio-card-border);\n border-radius: var(--myio-card-radius);\n box-shadow: var(--myio-card-shadow);\n padding: 14px;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n line-height: 1.4;\n color: var(--myio-text-1);\n position: relative;\n cursor: pointer;\n transition: all 0.2s ease;\n min-width: 252px;\n max-width: 288px;\n overflow: visible;\n}\n\n.myio-ho-card:hover {\n box-shadow: 0 4px 12px rgba(10, 31, 68, .12);\n transform: translateY(-1px);\n}\n\n.myio-ho-card:focus-within {\n outline: 2px solid #007ecc;\n outline-offset: 2px;\n}\n\n/* Selected state with light green background */\n.myio-ho-card.is-selected {\n border: 2px solid #00e09e;\n box-shadow: 0 12px 40px rgba(0, 224, 158, 0.25), 0 4px 12px rgba(0, 224, 158, 0.15);\n background: linear-gradient(145deg, #f0fdf9 0%, #ecfdf5 50%, #f0fdf9 100%);\n transform: translateY(-2px);\n}\n\n.myio-ho-card.is-selected:hover {\n box-shadow: 0 16px 48px rgba(0, 224, 158, 0.3), 0 8px 16px rgba(0, 224, 158, 0.2);\n}\n\n/* Card border states based on device status */\n.myio-ho-card.is-ok {\n border-color: var(--myio-border-ok);\n box-shadow: 0 0 0 2px var(--myio-border-ok), var(--myio-card-shadow);\n}\n\n.myio-ho-card.is-standby {\n border-color: var(--myio-border-standby);\n box-shadow: 0 0 0 2px var(--myio-border-standby), var(--myio-card-shadow);\n}\n\n.myio-ho-card.is-alert {\n border-color: var(--myio-border-alert);\n box-shadow: 0 0 0 2px var(--myio-border-alert), var(--myio-card-shadow);\n}\n\n.myio-ho-card.is-failure {\n border-color: var(--myio-border-failure);\n box-shadow: 0 0 0 2px var(--myio-border-failure), var(--myio-card-shadow);\n}\n\n/* Header section */\n.myio-ho-card__header {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n margin-bottom: 10px;\n overflow: visible;\n}\n\n.myio-ho-card__icon {\n width: 28px;\n height: 28px;\n color: #4b6bfb;\n flex-shrink: 0;\n margin-top: 2px;\n}\n\n.myio-ho-card.is-alert .myio-ho-card__icon {\n color: var(--myio-chip-alert-fg);\n}\n\n.myio-ho-card.is-failure .myio-ho-card__icon {\n color: var(--myio-chip-failure-fg);\n}\n\n.myio-ho-card__title {\n flex: 1;\n min-width: 0;\n}\n\n/* Adicione estas duas novas regras ao seu CSS_STRING */\n\n/* Estado Offline - borda cinza */\n.myio-ho-card.is-offline {\n border-color: var(--myio-border-offline);\n box-shadow: 0 0 0 2px var(--myio-border-offline), var(--myio-card-shadow);\n}\n\n.myio-ho-card__name {\n font-weight: 600;\n font-size: 13px;\n color: var(--myio-text-1);\n margin-bottom: 2px;\n word-wrap: break-word;\n line-height: 1.3;\n}\n\n.myio-ho-card__code {\n font-size: 12px;\n color: var(--myio-muted);\n font-weight: 500;\n}\n\n.myio-ho-card__actions {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-shrink: 0;\n position: relative;\n z-index: 10;\n}\n\n.myio-ho-card__kebab {\n background: none;\n border: none;\n padding: 4px;\n cursor: pointer;\n border-radius: 4px;\n color: var(--myio-muted);\n transition: all 0.15s ease;\n position: relative;\n overflow: visible;\n}\n\n.myio-ho-card__kebab:hover {\n background: #f1f5f9;\n color: var(--myio-text-2);\n}\n\n.myio-ho-card__kebab:focus {\n outline: 2px solid #007ecc;\n outline-offset: 1px;\n}\n\n.myio-ho-card__menu {\n position: absolute;\n top: 100%;\n right: 0;\n background: white;\n border: 1px solid var(--myio-card-border);\n border-radius: 8px;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n z-index: 10000;\n min-width: 160px;\n padding: 4px 0;\n margin-top: 4px;\n}\n\n/* Estilos para o Modal */\n.myio-modal-overlay {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background-color: rgba(0, 0, 0, 0.6);\n display: flex;\n justify-content: center;\n align-items: center;\n z-index: 1000; /* Garante que o modal fique na frente de tudo */\n opacity: 0;\n visibility: hidden;\n transition: opacity 0.3s ease, visibility 0.3s ease;\n}\n\n.myio-modal-overlay.visible {\n opacity: 1;\n visibility: visible;\n}\n\n.myio-modal-content {\n background-color: #fff;\n padding: 25px;\n border-radius: 8px;\n box-shadow: 0 4px 15px rgba(0,0,0,0.2);\n position: relative;\n min-width: 300px;\n text-align: left;\n font-family: sans-serif; /* Use a fonte que preferir */\n line-height: 1.6;\n}\n\n.myio-modal-close {\n position: absolute;\n top: 10px;\n right: 15px;\n border: none;\n background: none;\n font-size: 24px;\n cursor: pointer;\n color: #888;\n}\n\n.myio-modal-close:hover {\n color: #000;\n}\n\n.myio-modal-content p {\n margin: 0;\n color: #333;\n}\n\n.myio-modal-content strong {\n color: #000;\n}\n\n.myio-modal-title {\n margin-top: 0;\n margin-bottom: 15px;\n padding-bottom: 10px;\n border-bottom: 1px solid #eee;\n font-size: 1.25rem; /* 20px */\n color: #333;\n font-weight: 600;\n}\n\n.myio-ho-card__menu[hidden] {\n display: none;\n}\n\n.myio-ho-card__menu button {\n width: 100%;\n background: none;\n border: none;\n padding: 8px 12px;\n text-align: left;\n cursor: pointer;\n font-size: 13px;\n color: var(--myio-text-1);\n transition: background-color 0.15s ease;\n}\n\n.myio-ho-card__menu button:hover {\n background: #f8fafc;\n}\n\n.myio-ho-card__menu button:focus {\n background: #e2e8f0;\n outline: none;\n}\n\n.myio-ho-card__select {\n display: flex;\n align-items: center;\n cursor: pointer;\n}\n\n.myio-ho-card__select[hidden] {\n display: none;\n}\n\n.myio-ho-card__select input[type="checkbox"] {\n width: 16px;\n height: 16px;\n margin: 0;\n cursor: pointer;\n}\n\n/* ======================================================== */\n/* === BLOCO AJUSTADO PARA ALINHAMENTO DE CHIPS === */\n/* ======================================================== */\n\n/* ====== CONTAINER DOS CHIPS DE SHOPPING E STATUS ====== */\n.myio-ho-card__chips-row {\n display: flex; /* Usa Flexbox para alinhamento horizontal */\n justify-content: space-between; /* Empurra os itens para as extremidades (esquerda e direita) */\n align-items: center; /* Centraliza verticalmente os chips */\n \n /* Usa o mesmo padding lateral do card (14px) */\n padding: 0 0px; \n \n /* Espaçamento acima (para separar do header) e abaixo (para separar do valor) */\n margin-top: 10px; \n margin-bottom: 12px;\n}\n\n/* ====== ESTILO DO CHIP DE SHOPPING (estilo "Mont Serrat") ====== */\n.myio-ho-card__shopping-chip {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n\n /* Cores baseadas na imagem "Mont Serrat" */\n background-color: #EBF4FF; /* Fundo azul bem claro */\n border: 1px solid #BEE3F8; /* Borda azul clara */\n color: #2C5282; /* Texto azul escuro */\n\n border-radius: 8px;\n padding: 4px 10px;\n font-size: 11px;\n font-weight: 500;\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n white-space: nowrap;\n}\n\n.myio-ho-card__shopping-chip .chip-icon {\n /* Ajusta o ícone SVG */\n width: 14px;\n height: 14px;\n opacity: 0.7;\n stroke: currentColor; /* Faz o SVG usar a cor do texto */\n}\n\n/* ====== CONTAINER ADICIONAL PARA O CHIP DE STATUS ====== */\n.myio-ho-card__status-chip-container {\n /* Este seletor é um container para o chip de status, \n permitindo que o flexbox o alinhe à direita. */\n /* O alinhamento é feito pelo justify-content: space-between no pai */\n}\n\n/* =============================================== */\n/* === ESTILOS PARA O MODAL DE INFORMAÇÕES === */\n/* =============================================== */\n\n/* Fundo escurecido que cobre a tela */\n.myio-modal-overlay {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background: rgba(0, 0, 0, 0.6);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1000;\n opacity: 0;\n visibility: hidden;\n transition: opacity 0.3s ease, visibility 0.3s ease;\n}\n\n/* Torna o modal visível */\n.myio-modal-overlay.visible {\n opacity: 1;\n visibility: visible;\n}\n\n/* Caixa de conteúdo do modal */\n.myio-modal-content {\n background: #fff;\n padding: 24px;\n border-radius: 12px;\n box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);\n width: 90%;\n max-width: 450px;\n position: relative;\n transform: translateY(-20px);\n transition: transform 0.3s ease;\n}\n\n.myio-modal-overlay.visible .myio-modal-content {\n transform: translateY(0);\n}\n\n.myio-ho-card__menu button {\n display: flex;\n align-items: center;\n gap: 8px; /* Espaço entre o ícone e o texto */\n text-align: left;\n width: 100%;\n}\n\n.myio-ho-card__menu button img {\n flex-shrink: 0; /* Impede que o ícone seja espremido */\n}\n\n/* Título do modal */\n.myio-modal-title {\n margin-top: 0;\n margin-bottom: 16px;\n font-size: 1.25rem;\n font-weight: 600;\n color: #333;\n border-bottom: 1px solid #eee;\n padding-bottom: 12px;\n}\n\n/* Botão de fechar (X) */\n.myio-modal-close {\n position: absolute;\n top: 10px;\n right: 14px;\n background: none;\n border: none;\n font-size: 2rem;\n cursor: pointer;\n color: #aaa;\n line-height: 1;\n padding: 0;\n}\n.myio-modal-close:hover {\n color: #333;\n}\n\n/* Linha de informação (ícone + label + valor) */\n.info-row {\n display: flex;\n align-items: center;\n margin-bottom: 12px;\n font-size: 0.95rem;\n}\n\n/* Estilo do ícone */\n.info-icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n margin-right: 12px;\n color: #555;\n}\n.info-icon svg {\n width: 20px;\n height: 20px;\n}\n\n/* Rótulo (Ex: "Central:") */\n.info-label {\n color: #666;\n font-weight: 500;\n}\n\n/* Valor da informação */\n.info-value {\n margin-left: auto;\n font-weight: 600;\n color: #333;\n text-align: right;\n}\n\n/* Divisor entre seções */\n.info-divider {\n border: none;\n border-top: 1px solid #eee;\n margin: 16px 0;\n}\n\n/* Status chip */\n.myio-ho-card__status {\n /* margin-bottom: 7px; <-- Esta linha foi removida */\n /* O espaçamento agora é controlado por .myio-ho-card__chips-row */\n}\n\n.chip {\n display: inline-flex;\n align-items: center;\n padding: 4px 8px;\n border-radius: 12px;\n font-size: 12px;\n font-weight: 500;\n gap: 4px;\n}\n\n.chip--ok {\n background: var(--myio-chip-ok-bg);\n color: var(--myio-chip-ok-fg);\n}\n\n.chip--standby {\n background: var(--myio-chip-standby-bg);\n color: var(--myio-chip-standby-fg);\n}\n\n.chip--alert {\n background: var(--myio-chip-alert-bg);\n color: var(--myio-chip-alert-fg);\n}\n\n.chip--failure {\n background: var(--myio-chip-failure-bg);\n color: var(--myio-chip-failure-fg);\n}\n\n.chip--offline {\n background: var(--myio-chip-offline-bg);\n color: var(--myio-chip-offline-fg);\n}\n\n/* Status indicator dot for power metric */\n.status-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n flex-shrink: 0;\n background: var(--myio-chip-offline-fg);\n}\n\n.status-dot.dot--ok {\n background: var(--myio-chip-ok-fg);\n}\n\n.status-dot.dot--standby {\n background: var(--myio-chip-standby-fg);\n}\n\n.status-dot.dot--alert {\n background: var(--myio-chip-alert-fg);\n}\n\n.status-dot.dot--failure {\n background: var(--myio-chip-failure-fg);\n}\n\n.status-dot.dot--offline {\n background: var(--myio-chip-offline-fg);\n}\n\n.status-dot.dot--neutral {\n background: var(--myio-muted);\n}\n\n/* Primary metric */\n.myio-ho-card__primary {\n margin-bottom: 14px;\n padding: 7px 0;\n border-radius: 8px;\n transition: background-color 0.15s ease;\n}\n\n.myio-ho-card__primary:hover {\n background: #f8fafc;\n}\n\n.myio-ho-card__primary:focus {\n outline: 2px solid #007ecc;\n outline-offset: 2px;\n}\n\n.myio-ho-card__value {\n display: flex;\n align-items: baseline;\n gap: 4px;\n flex-wrap: wrap;\n justify-content: center;\n}\n\n.myio-ho-card__value .num {\n font-size: 24px;\n font-weight: 700;\n color: var(--myio-text-1);\n line-height: 1;\n}\n\n.myio-ho-card__value .unit {\n font-size: 16px;\n font-weight: 600;\n color: var(--myio-text-2);\n line-height: 1;\n}\n\n.myio-ho-card__value .suffix {\n font-size: 12px;\n color: var(--myio-muted);\n margin-left: 4px;\n}\n\n/* Efficiency bar */\n.myio-ho-card__eff {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 14px;\n}\n\n.myio-ho-card__eff .label {\n font-size: 12px;\n color: var(--myio-text-2);\n font-weight: 500;\n min-width: 60px;\n}\n\n.myio-ho-card__eff .bar {\n flex: 1;\n height: 6px;\n background: var(--myio-eff-bar-bg);\n border-radius: 3px;\n position: relative;\n overflow: hidden;\n}\n\n.myio-ho-card__eff .bar__fill {\n height: 100%;\n background: linear-gradient(90deg, var(--myio-eff-bar-a) 0%, var(--myio-eff-bar-b) 100%);\n border-radius: 3px;\n transition: width 0.3s ease;\n min-width: 2px;\n}\n\n.myio-ho-card__eff .perc {\n font-size: 12px;\n font-weight: 600;\n color: var(--myio-text-1);\n min-width: 32px;\n text-align: right;\n}\n\n/* Footer metrics - Now with 2 columns (removed temperature) */\n.myio-ho-card__footer {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 16px;\n padding: 0 8px;\n}\n\n.myio-ho-card__footer .metric {\n display: flex;\n flex-direction: column;\n align-items: center;\n text-align: center;\n gap: 2px;\n}\n\n.myio-ho-card__footer .metric .status-dot {\n margin-bottom: 2px;\n}\n\n.myio-ho-card__footer .metric .ico {\n width: 18px;\n height: 18px;\n color: var(--myio-muted);\n flex-shrink: 0;\n}\n\n.myio-ho-card__footer .metric .label {\n font-size: 10.5px;\n color: var(--myio-muted);\n font-weight: 500;\n line-height: 1.2;\n}\n\n.myio-ho-card__footer .metric .val {\n font-size: 11px;\n font-weight: 600;\n color: var(--myio-text-1);\n line-height: 1.3;\n word-break: break-word;\n max-width: 100%;\n}\n\n/* Drag and drop states */\n.myio-ho-card[draggable="true"] {\n cursor: grab;\n}\n\n.myio-ho-card[draggable="true"]:active {\n cursor: grabbing;\n}\n\n.myio-ho-card.is-dragging {\n opacity: 0.5;\n transform: rotate(2deg);\n}\n\n/* Responsive adjustments */\n@media (max-width: 320px) {\n .myio-ho-card {\n min-width: 234px;\n padding: 12px;\n }\n\n .myio-ho-card__value .num {\n font-size: 20px;\n }\n\n .myio-ho-card__footer {\n gap: 7px;\n }\n}\n\n/* High contrast mode support */\n@media (prefers-contrast: high) {\n .myio-ho-card {\n border-width: 2px;\n }\n \n .chip {\n border: 1px solid currentColor;\n }\n \n .myio-ho-card__eff .bar {\n border: 1px solid var(--myio-text-2);\n }\n}\n\n/* Reduced motion support */\n@media (prefers-reduced-motion: reduce) {\n .myio-ho-card,\n .myio-ho-card__primary,\n .myio-ho-card__kebab,\n .myio-ho-card__eff .bar__fill {\n transition: none;\n }\n}\n`;var Icons={gear:`\n<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"\n viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" focusable="false">\n <path d="M19.14 12.94c.04-.31.06-.63.06-.94s-.02-.63-.06-.94l2.03-1.58a.5.5 0 0 0 .12-.66l-1.92-3.32a.5.5 0 0 0-.62-.22l-2.39.96a7.36 7.36 0 0 0-1.63-.94l-.36-2.55A.5.5 0 0 0 13.89 1h-3.78a.5.5 0 0 0-.49.41l-.36 2.55c-.58.23-1.12.53-1.63.94l-2.39-.96a.5.5 0 0 0-.62.22L2.7 7.48a.5.5 0 0 0 .12.66l2.03 1.58c-.04.31-.06.63-.06.94s.02.63.06.94L2.82 13.18a.5.5 0 0 0-.12.66l1.92 3.32c.13.23.4.32.62.22l2.39-.96c.5.4 1.05.71 1.63.94l.36 2.55c.05.23.26.41.49.41h3.78c.23 0 .44-.18.49-.41l.36-2.55c.58-.23 1.12-.53 1.63-.94l2.39.96c.22.1.49 0 .62-.22l1.92-3.32a.5.5 0 0 0-.12-.66l-2.03-1.58ZM12 15.5A3.5 3.5 0 1 1 12 8.5a3.5 3.5 0 0 1 0 7Z"/>\n</svg>`,elevator:`\n<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"\n viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" focusable="false">\n <path d="M6 3h12a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2Zm0 2v14h12V5H6Z"/>\n <path d="M11 17h2v-5h-2v5Zm-2.5-7.5 2-2-2-2v4Zm7 0v-4l-2 2 2 2Z"/>\n</svg>`,escalator:`\n<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"\n viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" focusable="false">\n <path d="M3 16a3 3 0 0 1 3-3h3.6l4.4-6H20a2 2 0 1 1 0 4h-3.6l-4.4 6H6a3 3 0 0 1-3-3Z"/>\n <circle cx="8.5" cy="6" r="1.5"/>\n</svg>`,chiller:`\n<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"\n viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6"\n stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false">\n <path d="M12 2v20M4 7l16 10M4 17L20 7"/>\n <path d="M8 4l4 3 4-3M8 20l4-3 4 3M3 12h18"/>\n</svg>`,pump:`\n<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"\n viewBox="0 0 24 24" aria-hidden="true" focusable="false">\n <path fill="currentColor" d="M12 3s-5 5.6-5 9a5 5 0 0 0 10 0c0-3.4-5-9-5-9Zm0 12a3 3 0 1 1 0-6 3 3 0 0 1 0 6Z"/>\n <path fill="currentColor" d="M12 9.3c.9 0 1.7.8 1.7 1.7S12.9 12.7 12 12.7 10.3 12 10.3 11 11.1 9.3 12 9.3Z"/>\n</svg>`,fan:`\n<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"\n viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" focusable="false">\n <circle cx="12" cy="12" r="2.2"/>\n <path d="M12 4c2.5 0 4 1.5 4 3.3 0 1.3-.7 2.4-1.7 3.2-1.3 1-3 .7-3-.9V4Z"/>\n <path d="M20 12c0 2.5-1.5 4-3.3 4-1.3 0-2.4-.7-3.2-1.7-1-1.3-.7-3 .9-3H20Z"/>\n <path d="M12 20c-2.5 0-4-1.5-4-3.3 0-1.3.7-2.4 1.7-3.2 1.3-1 3-.7 3 .9V20Z"/>\n <path d="M4 12c0-2.5 1.5-4 3.3-4 1.3 0 2.4.7 3.2 1.7 1 1.3.7 3-.9 3H4Z"/>\n</svg>`,motor:`\n<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"\n viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" focusable="false">\n <rect x="3" y="7" width="14" height="10" rx="3"/>\n <rect x="17" y="10" width="4" height="4" rx="1"/>\n <circle cx="10" cy="12" r="2.5" fill="none" stroke="currentColor" stroke-width="1.6"/>\n</svg>`,thermometer:`\n<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"\n viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" focusable="false">\n <path d="M11 5a3 3 0 0 1 6 0v6.1a5 5 0 1 1-6 0V5Z"/>\n <rect x="13" y="4" width="2" height="9" rx="1"/>\n</svg>`,switch:`\n<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"\n viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" focusable="false">\n <rect x="2" y="7" width="20" height="10" rx="5"/>\n <circle cx="9" cy="12" r="3.5" fill="#fff"/>\n</svg>`,energyMeter:`\n<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"\n viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" focusable="false">\n <rect x="3" y="4" width="18" height="14" rx="2"/>\n <path d="M6 16h12v2H6z"/>\n <path d="M12 8a5 5 0 0 0-5 5h2a3 3 0 0 1 6 0h2a5 5 0 0 0-5-5Z"/>\n</svg>`,waterTank:`\n<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"\n viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" focusable="false">\n <rect x="5" y="4" width="14" height="16" rx="2"/>\n <path d="M7 12c1.5 1 3 .9 5-.3s3.5-1.3 5 .3v4H7v-4Z" fill="#fff" fill-opacity=".25"/>\n</svg>`,waterDrop:`\n<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">\n <g id="water" transform="translate(-4 -2)">\n <path id="secondary" fill="#2ca9bc" d="M19,14A7,7,0,1,1,5,14C5,8,12,3,12,3S19,8,19,14Z"/>\n <path id="primary" d="M19,14A7,7,0,1,1,5,14C5,8,12,3,12,3S19,8,19,14Z" fill="none" stroke="#000000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/>\n </g>\n</svg>`,kebab:`\n<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"\n viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" focusable="false">\n <circle cx="12" cy="5" r="2"/><circle cx="12" cy="12" r="2"/><circle cx="12" cy="19" r="2"/>\n</svg>`};var ICON_MAP={ESCADA_ROLANTE:Icons.escalator,ELEVADOR:Icons.elevator,ELEVADOR_SERVICO:Icons.elevator,CHILLER:Icons.chiller,PUMP:Icons.pump,COMPRESSOR:Icons.motor,VENTILADOR:Icons.fan,MOTOR:Icons.motor,TERMOSTATO:Icons.thermometer,SELETOR_AUTO_MANUAL:Icons.switch,"3F_MEDIDOR":Icons.energyMeter,CAIXA_D_AGUA:Icons.waterTank,DEFAULT:Icons.gear};var DEFAULT_I18N={in_operation:"⚡ Normal",in_operation_water:"💧 Normal",standby:"💤 Em standby",alert:"⚠️ Alerta",failure:"🚨 Falha",maintenance:"🔧 Manutenção",not_installed:"📦 Não instalado",offline:"📡 Offline",efficiency:"Eficiência",temperature:"Temperatura",operation_time:"Tempo em operação",last_telemetry:"Últ. Telemetria",instantaneous_power:"Potência",current_suffix:"Atual",menu_dashboard:"Dashboard",menu_report:"Relatório",menu_settings:"Configurações"};var CSS_TAG="head-office-card-v1";function ensureCss(){if(!document.querySelector(`style[data-myio-css="${CSS_TAG}"]`)){const style=document.createElement("style");style.setAttribute("data-myio-css",CSS_TAG);style.textContent=CSS_STRING;document.head.appendChild(style)}}function normalizeParams(params){if(!params||!params.entityObject){throw new Error("renderCardCompenteHeadOffice: entityObject is required")}const entityObject=params.entityObject;if(!entityObject.entityId){console.warn("renderCardCompenteHeadOffice: entityId is missing, generating temporary ID");entityObject.entityId=`temp-${Date.now()}-${Math.random().toString(36).substr(2,9)}`}return{entityObject:entityObject,i18n:{...DEFAULT_I18N,...params.i18n||{}},enableSelection:Boolean(params.enableSelection),enableDragDrop:Boolean(params.enableDragDrop),useNewComponents:Boolean(params.useNewComponents),delayTimeConnectionInMins:params.delayTimeConnectionInMins??15,callbacks:{handleActionDashboard:params.handleActionDashboard,handleActionReport:params.handleActionReport,handleActionSettings:params.handleActionSettings,handleSelect:params.handleSelect,handInfo:params.handInfo,handleClickCard:params.handleClickCard}}}function getIconSvg(deviceType,domain){if(domain==="water"){return Icons.waterDrop}if(domain==="temperature"){return Icons.thermometer}return ICON_MAP[deviceType]||ICON_MAP.DEFAULT}function formatPower(valueInWatts){if(valueInWatts===null||valueInWatts===void 0||isNaN(valueInWatts)){return{num:"-",unit:""}}const val=Number(valueInWatts);if(val>=1e3){const kw=Math.ceil(val/1e3*100)/100;return{num:kw.toFixed(2),unit:"kW"}}else{const w=Math.ceil(val);return{num:w.toString(),unit:"W"}}}function formatValueByDomain(value,domain){if(domain==="water"){return formatWaterVolumeM3(value)}if(domain==="temperature"){return formatTemperature(value)}return formatEnergy(value)}function formatTemperature(temp){if(temp===null||temp===void 0||isNaN(temp)){return"—"}return`${temp.toFixed(0)}°C`}function calculateConsumptionPercentage(target,consumption){const numericTarget=Number(target);const numericConsumption=Number(consumption);if(isNaN(numericTarget)||isNaN(numericConsumption)||numericTarget<=0){return 0}const percentage=numericConsumption/numericTarget*100;return percentage}function getStatusInfo(deviceStatus,i18n,domain){switch(deviceStatus){case"normal":return{chipClass:"chip--ok",label:"Normal"};case"cold":return{chipClass:"chip--standby",label:"Frio"};case"hot":return{chipClass:"chip--alert",label:"Quente"};case DeviceStatusType.POWER_ON:if(domain==="water"){return{chipClass:"chip--ok",label:i18n.in_operation_water}}return{chipClass:"chip--ok",label:i18n.in_operation};case DeviceStatusType.STANDBY:return{chipClass:"chip--standby",label:i18n.standby};case DeviceStatusType.WARNING:return{chipClass:"chip--alert",label:i18n.alert};case DeviceStatusType.FAILURE:case DeviceStatusType.POWER_OFF:return{chipClass:"chip--failure",label:i18n.failure};case DeviceStatusType.MAINTENANCE:return{chipClass:"chip--alert",label:i18n.maintenance};case DeviceStatusType.NOT_INSTALLED:return{chipClass:"chip--offline",label:i18n.not_installed};case DeviceStatusType.NO_INFO:default:return{chipClass:"chip--offline",label:i18n.offline}}}function getCardStateClass(deviceStatus){switch(deviceStatus){case DeviceStatusType.POWER_ON:return"is-ok";case DeviceStatusType.STANDBY:return"is-standby";case DeviceStatusType.WARNING:case DeviceStatusType.MAINTENANCE:return"is-alert";case DeviceStatusType.FAILURE:case DeviceStatusType.POWER_OFF:return"is-failure";case DeviceStatusType.NO_INFO:case DeviceStatusType.NOT_INSTALLED:return"is-offline";default:return""}}function getStatusDotClass(deviceStatus){switch(deviceStatus){case"normal":return"dot--ok";case"cold":return"dot--standby";case"hot":return"dot--alert";case DeviceStatusType.POWER_ON:return"dot--ok";case DeviceStatusType.STANDBY:return"dot--standby";case DeviceStatusType.WARNING:case DeviceStatusType.MAINTENANCE:return"dot--alert";case DeviceStatusType.FAILURE:case DeviceStatusType.POWER_OFF:return"dot--failure";default:return"dot--offline"}}function buildDOM(state){const{entityObject:entityObject,i18n:i18n,enableSelection:enableSelection,enableDragDrop:enableDragDrop}=state;const root=document.createElement("div");root.className="myio-ho-card";root.setAttribute("role","group");root.setAttribute("data-entity-id",entityObject.entityId);if(enableDragDrop){root.setAttribute("draggable","true")}const header=document.createElement("div");header.className="myio-ho-card__header";const iconContainer=document.createElement("div");iconContainer.className="myio-ho-card__icon";iconContainer.innerHTML=getIconSvg(entityObject.deviceType,entityObject.domain);header.appendChild(iconContainer);const titleSection=document.createElement("div");titleSection.className="myio-ho-card__title";const nameEl=document.createElement("div");nameEl.className="myio-ho-card__name";nameEl.textContent=entityObject.labelOrName||"Unknown Device";titleSection.appendChild(nameEl);if(entityObject.deviceIdentifier){const codeEl=document.createElement("div");codeEl.className="myio-ho-card__code";codeEl.textContent=entityObject.deviceIdentifier;titleSection.appendChild(codeEl)}header.appendChild(titleSection);const actionsSection=document.createElement("div");actionsSection.className="myio-ho-card__actions";const kebabBtn=document.createElement("button");kebabBtn.className="myio-ho-card__kebab";kebabBtn.setAttribute("aria-label","Open actions");kebabBtn.setAttribute("aria-haspopup","menu");kebabBtn.innerHTML=Icons.kebab;actionsSection.appendChild(kebabBtn);const menu=document.createElement("div");menu.className="myio-ho-card__menu";menu.setAttribute("role","menu");menu.setAttribute("hidden","");const dashboardBtn=document.createElement("button");dashboardBtn.setAttribute("role","menuitem");dashboardBtn.setAttribute("data-action","dashboard");dashboardBtn.innerHTML=`<img src="https://dashboard.myio-bas.com/api/images/public/TAVXE0sTbCZylwGsMF9lIWdllBB3iFtS" width="16" height="16"/> <span>Dashboard</span>`;menu.appendChild(dashboardBtn);const reportBtn=document.createElement("button");reportBtn.setAttribute("role","menuitem");reportBtn.setAttribute("data-action","report");reportBtn.innerHTML=`<img src="https://dashboard.myio-bas.com/api/images/public/d9XuQwMYQCG2otvtNSlqUHGavGaSSpz4" width="16" height="16"/> <span>${i18n.menu_report}</span>`;menu.appendChild(reportBtn);const settingsBtn=document.createElement("button");settingsBtn.setAttribute("role","menuitem");settingsBtn.setAttribute("data-action","settings");settingsBtn.innerHTML=`<img src="https://dashboard.myio-bas.com/api/images/public/5n9tze6vED2uwIs5VvJxGzNNZ9eV4yoz" width="16" height="16"/> <span>${i18n.menu_settings}</span>`;menu.appendChild(settingsBtn);actionsSection.appendChild(menu);if(enableSelection){const selectLabel=document.createElement("label");selectLabel.className="myio-ho-card__select";const checkbox=document.createElement("input");checkbox.type="checkbox";selectLabel.appendChild(checkbox);actionsSection.appendChild(selectLabel)}header.appendChild(actionsSection);root.appendChild(header);const chipsRow=document.createElement("div");chipsRow.className="myio-ho-card__chips-row";const statusChipContainer=document.createElement("div");statusChipContainer.className="myio-ho-card__status-chip-container";const chip=document.createElement("span");chip.className="chip";statusChipContainer.appendChild(chip);chipsRow.appendChild(statusChipContainer);const chipShopping=document.createElement("span");chipShopping.className="myio-ho-card__shopping-chip";const chipIcon=`<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="chip-icon"><path d="M4 22h16"/><path d="M7 22V4a2 2 0 0 1 2-2h6a2 2 0 0 1 2 2v18"/><path d="M9 18h6"/><path d="M9 14h6"/><path d="M9 10h6"/><path d="M9 6h6"/></svg>`;chipShopping.innerHTML=`${chipIcon}<span>${entityObject.customerName}</span>`;chipsRow.appendChild(chipShopping);root.appendChild(chipsRow);const primarySection=document.createElement("div");primarySection.className="myio-ho-card__primary";primarySection.setAttribute("role","button");primarySection.setAttribute("tabindex","0");const valueContainer=document.createElement("div");valueContainer.className="myio-ho-card__value";const numSpan=document.createElement("span");numSpan.className="num";valueContainer.appendChild(numSpan);const unitSpan=document.createElement("span");unitSpan.className="unit";valueContainer.appendChild(unitSpan);primarySection.appendChild(valueContainer);root.appendChild(primarySection);const effSection=document.createElement("div");effSection.className="myio-ho-card__eff";const effLabel=document.createElement("div");effLabel.className="label";effLabel.textContent=i18n.efficiency;effSection.appendChild(effLabel);const barContainer=document.createElement("div");barContainer.className="bar";barContainer.setAttribute("role","progressbar");barContainer.setAttribute("aria-valuemin","0");barContainer.setAttribute("aria-valuemax","100");const barFill=document.createElement("div");barFill.className="bar__fill";barContainer.appendChild(barFill);effSection.appendChild(barContainer);const percSpan=document.createElement("div");percSpan.className="perc";effSection.appendChild(percSpan);root.appendChild(effSection);const footer=document.createElement("div");footer.className="myio-ho-card__footer";const opTimeMetric=document.createElement("div");opTimeMetric.className="metric";const opTimeDot=document.createElement("span");opTimeDot.className="status-dot dot--neutral";opTimeMetric.appendChild(opTimeDot);const opTimeLabel=document.createElement("div");opTimeLabel.className="label";opTimeLabel.textContent=i18n.operation_time;opTimeMetric.appendChild(opTimeLabel);const opTimeVal=document.createElement("div");opTimeVal.className="val";opTimeMetric.appendChild(opTimeVal);footer.appendChild(opTimeMetric);const powerMetric=document.createElement("div");powerMetric.className="metric";const statusDot=document.createElement("span");statusDot.className="status-dot";powerMetric.appendChild(statusDot);const powerLabel=document.createElement("div");powerLabel.className="label";if(entityObject.domain==="water"){powerLabel.textContent="Leitura"}else{powerLabel.textContent=i18n.instantaneous_power||"Potência"}powerMetric.appendChild(powerLabel);const powerVal=document.createElement("div");powerVal.className="val";powerMetric.appendChild(powerVal);footer.appendChild(powerMetric);root.appendChild(footer);return root}function verifyOfflineStatus(entityObject,delayTimeInMins=15){const lastConnectionTime=new Date(entityObject.lastConnectTime||0);const lastDisconnectTime=new Date(entityObject.lastDisconnectTime||0);const now=new Date;const delayTimeInMs=delayTimeInMins*60*1e3;const timeSinceConnection=now.getTime()-lastConnectionTime.getTime();if(lastDisconnectTime.getTime()>lastConnectionTime.getTime()){return false}if(timeSinceConnection<delayTimeInMs){return false}return true}function paint(root,state){const{entityObject:entityObject,i18n:i18n,delayTimeConnectionInMins:delayTimeConnectionInMins,isSelected:isSelected}=state;if(entityObject.connectionStatus){if(entityObject.connectionStatus==="offline"){entityObject.deviceStatus=DeviceStatusType.NO_INFO}}else{if(verifyOfflineStatus(entityObject,delayTimeConnectionInMins)===false){entityObject.deviceStatus=DeviceStatusType.NO_INFO}}const stateClass=getCardStateClass(entityObject.deviceStatus);root.className=`myio-ho-card ${stateClass}`;const statusInfo=getStatusInfo(entityObject.deviceStatus,i18n,entityObject.domain);const chip=root.querySelector(".chip");chip.className=`chip ${statusInfo.chipClass}`;chip.innerHTML=statusInfo.label;const primaryValue=formatValueByDomain(entityObject.val,entityObject.domain);const numSpan=root.querySelector(".myio-ho-card__value .num");root.querySelector(".myio-ho-card__value .unit");numSpan.textContent=primaryValue;const barContainer=root.querySelector(".bar");const effContainer=root.querySelector(".myio-ho-card__eff");if(state.enableSelection){const checkbox=root.querySelector('.myio-ho-card__select input[type="checkbox"]');if(checkbox){checkbox.checked=!!isSelected}root.classList.toggle("is-selected",!!isSelected)}const targetValue=entityObject.consumptionTargetValue;if(targetValue){barContainer.style.display="";effContainer.style.display="";const barFill=root.querySelector(".bar__fill");const percSpan=root.querySelector(".myio-ho-card__eff .perc");const perc=calculateConsumptionPercentage(targetValue,entityObject.val);barFill.style.width=`${Math.max(0,Math.min(100,perc))}%`;percSpan.textContent=`${Math.round(perc)}%`;barContainer.setAttribute("aria-valuenow",Math.round(perc).toString());barContainer.setAttribute("aria-label",`${i18n.efficiency} ${Math.round(perc)}%`)}else{barContainer.style.display="none";effContainer.style.display="none"}const opTimeVal=root.querySelector(".myio-ho-card__footer .metric:nth-child(1) .val");if(opTimeVal){opTimeVal.textContent=entityObject.operationHours??"-"}const powerVal=root.querySelector(".myio-ho-card__footer .metric:nth-child(2) .val");if(powerVal){if(entityObject.domain==="water"){const pulses=entityObject.pulses??0;powerVal.textContent=`${pulses} L`}else{const instantPower=entityObject.instantaneousPower??entityObject.consumption_power??null;const powerFormatted=formatPower(instantPower);powerVal.textContent=instantPower!==null?`${powerFormatted.num} ${powerFormatted.unit}`:"-"}}const statusDot=root.querySelector(".myio-ho-card__footer .metric:nth-child(2) .status-dot");if(statusDot){const dotClass=getStatusDotClass(entityObject.deviceStatus);statusDot.className=`status-dot ${dotClass}`}}function bindEvents(root,state,callbacks){const{entityObject:entityObject}=state;const kebabBtn=root.querySelector(".myio-ho-card__kebab");const menu=root.querySelector(".myio-ho-card__menu");function toggleMenu(){const isHidden=menu.hasAttribute("hidden");if(isHidden){menu.removeAttribute("hidden");kebabBtn.setAttribute("aria-expanded","true")}else{menu.setAttribute("hidden","");kebabBtn.setAttribute("aria-expanded","false")}}function closeMenu(){menu.setAttribute("hidden","");kebabBtn.setAttribute("aria-expanded","false")}kebabBtn.addEventListener("click",e=>{e.stopPropagation();toggleMenu()});document.addEventListener("click",closeMenu);document.addEventListener("keydown",e=>{if(e.key==="Escape"){closeMenu()}});const dashboardBtn=menu.querySelector('[data-action="dashboard"]');const reportBtn=menu.querySelector('[data-action="report"]');const settingsBtn=menu.querySelector('[data-action="settings"]');if(callbacks.handleActionDashboard){dashboardBtn.addEventListener("click",e=>{e.stopPropagation();closeMenu();callbacks.handleActionDashboard(e,entityObject)})}if(callbacks.handleActionReport){reportBtn.addEventListener("click",e=>{e.stopPropagation();closeMenu();callbacks.handleActionReport(e,entityObject)})}if(callbacks.handleActionSettings){settingsBtn.addEventListener("click",e=>{e.stopPropagation();closeMenu();callbacks.handleActionSettings(e,entityObject)})}const MyIOSelectionStore2=window.MyIOLibrary?.MyIOSelectionStore||window.MyIOSelectionStore;if(MyIOSelectionStore2){const onSelectionChange=()=>{const selectedIds=MyIOSelectionStore2.getSelectedIds();const isSelected=selectedIds.includes(entityObject.entityId);if(state.isSelected!==isSelected){state.isSelected=isSelected;paint(root,state)}};MyIOSelectionStore2.on("selection:change",onSelectionChange);root._selectionListener=onSelectionChange}root._cleanup=()=>{document.removeEventListener("click",closeMenu);document.removeEventListener("keydown",closeMenu);if(MyIOSelectionStore2&&root._selectionListener){MyIOSelectionStore2.off("selection:change",root._selectionListener)}};const checkbox=root.querySelector('.myio-ho-card__select input[type="checkbox"]');if(checkbox&&callbacks.handleSelect){checkbox.addEventListener("change",e=>{e.stopPropagation();const isSelected=checkbox.checked;root.classList.toggle("is-selected",isSelected);callbacks.handleSelect(isSelected,entityObject)})}const primarySection=root.querySelector(".myio-ho-card__primary");if(callbacks.handleClickCard){let handleCardClick=function(e){if(e.target.closest(".myio-ho-card__actions"))return;callbacks.handleClickCard(e,entityObject)};root.addEventListener("click",handleCardClick);primarySection.addEventListener("keydown",e=>{if(e.key==="Enter"||e.key===" "){e.preventDefault();callbacks.handleClickCard(e,entityObject)}})}if(state.enableDragDrop){root.addEventListener("dragstart",e=>{root.classList.add("is-dragging");e.dataTransfer.setData("text/plain",entityObject.entityId);const customEvent=new CustomEvent("myio:dragstart",{detail:{entityObject:entityObject},bubbles:true});root.dispatchEvent(customEvent)});root.addEventListener("dragend",()=>{root.classList.remove("is-dragging")});root.addEventListener("drop",e=>{e.preventDefault();const draggedId=e.dataTransfer.getData("text/plain");const customEvent=new CustomEvent("myio:drop",{detail:{draggedId:draggedId,targetEntity:entityObject},bubbles:true});root.dispatchEvent(customEvent)});root.addEventListener("dragover",e=>{e.preventDefault()})}root._cleanup=()=>{document.removeEventListener("click",closeMenu);document.removeEventListener("keydown",closeMenu)}}function unbindEvents(root){if(root._cleanup){root._cleanup();delete root._cleanup}}function renderCardComponentHeadOffice(containerEl,params){if(!containerEl){throw new Error("renderCardComponentHeadOffice: containerEl is required")}ensureCss();const state=normalizeParams(params);const root=buildDOM(state);state.isSelected=params.isSelected||false;containerEl.appendChild(root);bindEvents(root,state,state.callbacks);paint(root,state);return{update(next){if(next){Object.assign(state.entityObject,next);paint(root,state)}},destroy(){unbindEvents(root);if(root.parentNode){root.parentNode.removeChild(root)}},getRoot(){return root}}}function renderCardComponentV5({entityObject:entityObject,handleActionDashboard:handleActionDashboard,handleActionReport:handleActionReport,handleActionSettings:handleActionSettings,handleSelect:handleSelect,handInfo:handInfo,handleClickCard:handleClickCard,useNewComponents:useNewComponents=true,enableSelection:enableSelection=true,enableDragDrop:enableDragDrop=true}){const{entityId:entityId,labelOrName:labelOrName,deviceIdentifier:deviceIdentifier,entityType:entityType,deviceType:deviceType,slaveId:slaveId,ingestionId:ingestionId,val:val,centralId:centralId,updatedIdentifiers:updatedIdentifiers={},perc:perc=0,deviceStatus:deviceStatus,centralName:centralName,connectionStatusTime:connectionStatusTime,timeVal:timeVal,customerName:customerName,waterLevel:waterLevel,waterPercentage:waterPercentage,temperature:temperature,temperatureMin:temperatureMin,temperatureMax:temperatureMax,temperatureStatus:temperatureStatus}=entityObject;const MyIOToast2=function(){let toastContainer=null;let toastTimeout=null;const TOAST_CSS=`\n #myio-global-toast-container {\n position: fixed;\n top: 25px;\n right: 25px;\n z-index: 99999;\n width: 320px;\n padding: 16px;\n border-radius: 8px;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;\n font-size: 15px;\n color: #fff;\n transform: translateX(120%);\n transition: transform 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);\n box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);\n border-left: 5px solid transparent;\n display: flex;\n align-items: center;\n }\n #myio-global-toast-container.show {\n transform: translateX(0);\n }\n #myio-global-toast-container.warning {\n background-color: #ff9800; /* Laranja para alerta */\n border-color: #f57c00;\n }\n #myio-global-toast-container.error {\n background-color: #d32f2f; /* Vermelho para erro */\n border-color: #b71c1c;\n }\n #myio-global-toast-container::before {\n content: '⚠️'; /* Ícone de alerta */\n margin-right: 12px;\n font-size: 20px;\n }\n #myio-global-toast-container.error::before {\n content: '🚫'; /* Ícone de erro */\n }\n `;function createToastElement(){if(document.getElementById("myio-global-toast-container")){toastContainer=document.getElementById("myio-global-toast-container");return}const style=document.createElement("style");style.id="myio-global-toast-styles";style.textContent=TOAST_CSS;document.head.appendChild(style);toastContainer=document.createElement("div");toastContainer.id="myio-global-toast-container";document.body.appendChild(toastContainer)}function show(message,type="warning",duration=3500){if(!toastContainer){createToastElement()}clearTimeout(toastTimeout);toastContainer.textContent=message;toastContainer.className="";toastContainer.classList.add(type);setTimeout(()=>{toastContainer.classList.add("show")},10);toastTimeout=setTimeout(()=>{toastContainer.classList.remove("show")},duration)}if(document.readyState==="loading"){document.addEventListener("DOMContentLoaded",createToastElement)}else{createToastElement()}return{show:show}}();if(handInfo){console.warn("[template-card-v5] handInfo parameter is deprecated. Info functionality has been moved to settings modal.")}if(!useNewComponents){console.warn("[template-card-v5] useNewComponents=false is not recommended. Consider using template-card-v2 directly.")}const connectionStatus=mapDeviceToConnectionStatus(deviceStatus);const isOffline=isDeviceOffline(deviceStatus);const shouldFlashIcon2=shouldFlashIcon(deviceStatus);const icon=getDeviceStatusIcon(deviceStatus,deviceType);getConnectionStatusIcon(connectionStatus);const mapDeviceTypeToIcon=deviceType2=>{const typeMap={COMPRESSOR:"energy",VENTILADOR:"energy",ESCADA_ROLANTE:"energy",ELEVADOR:"energy",MOTOR:"energy","3F_MEDIDOR":"energy",RELOGIO:"energy",ENTRADA:"energy",SUBESTACAO:"energy",HIDROMETRO:"water",CAIXA_DAGUA:"water",TANK:"water",TERMOSTATO:"temperature"};const normalizedType=deviceType2?.toUpperCase()||"";return typeMap[normalizedType]||"generic"};const getValueTypeFromDeviceType=deviceType2=>{const typeMap={COMPRESSOR:"ENERGY",VENTILADOR:"ENERGY",ESCADA_ROLANTE:"ENERGY",ELEVADOR:"ENERGY",MOTOR:"ENERGY","3F_MEDIDOR":"ENERGY",RELOGIO:"ENERGY",ENTRADA:"ENERGY",SUBESTACAO:"ENERGY",HIDROMETRO:"WATER",CAIXA_DAGUA:"WATER",TANK:"TANK",TERMOSTATO:"TEMPERATURE"};const normalizedType=deviceType2?.toUpperCase()||"";return typeMap[normalizedType]||"ENERGY"};const isEnergyDevice=deviceType2=>{const energyDeviceTypes=["COMPRESSOR","VENTILADOR","ESCADA_ROLANTE","ELEVADOR","MOTOR","3F_MEDIDOR","RELOGIO","ENTRADA","SUBESTACAO"];const normalizedType=deviceType2?.toUpperCase()||"";return energyDeviceTypes.includes(normalizedType)};const isTemperatureDevice=deviceType2=>{const temperatureDeviceTypes=["TERMOSTATO"];const normalizedType=deviceType2?.toUpperCase()||"";return temperatureDeviceTypes.includes(normalizedType)};const formatCardValue=(value,deviceType2)=>{const numValue=Number(value)||0;if(isEnergyDevice(deviceType2)){return formatEnergy(numValue)}else if(isTemperatureDevice(deviceType2)){const formattedTemp=numValue.toLocaleString("pt-BR",{minimumFractionDigits:1,maximumFractionDigits:1});return`${formattedTemp} °C`}else{const unit=determineUnit(deviceType2);const formattedValue=numValue.toLocaleString("pt-BR",{minimumFractionDigits:0,maximumFractionDigits:2});return`${formattedValue} ${unit}`}};const determineUnit=deviceType2=>{const valueType=getValueTypeFromDeviceType(deviceType2);switch(valueType){case"ENERGY":return"";case"WATER":return"m³";case"TANK":return"m.c.a";case"TEMPERATURE":return"°C";default:return""}};const cardEntity={id:entityId,name:labelOrName||"Dispositivo",icon:mapDeviceTypeToIcon(deviceType),group:deviceIdentifier||entityType||"Dispositivo",lastValue:Number(val)||0,unit:determineUnit(deviceType),status:mapDeviceStatusToCardStatus(deviceStatus),ingestionId:ingestionId||entityId};if(enableSelection&&exports.MyIOSelectionStore){exports.MyIOSelectionStore.registerEntity(cardEntity)}const container=document.createElement("div");container.className="myio-enhanced-card-container-v5";if(!document.getElementById("myio-enhanced-card-styles-v5")){const style=document.createElement("style");style.id="myio-enhanced-card-styles-v5";style.textContent=`\n .myio-enhanced-card-container-v5 {\n position: relative;\n width: 100%;\n height: 100%;\n }\n\n .myio-enhanced-card-container-v5 .myio-draggable-card {\n width: 100%;\n border-radius: 10px;\n padding: 8px 12px;\n background: #fff;\n box-shadow: 0 4px 10px rgba(0, 0, 0, .05);\n cursor: pointer;\n\n transition: transform .2s;\n box-sizing: border-box;\n overflow: hidden;\n min-height: 140px;\n display: flex;\n flex-direction: row;\n align-items: stretch;\n }\n\n .myio-enhanced-card-container-v5 .myio-draggable-card:hover {\n transform: scale(1.05);\n }\n\n .myio-enhanced-card-container-v5 .myio-draggable-card.selected {\n border: 2px solid #00e09e;\n box-shadow: 0 4px 12px rgba(0,224,158,0.2);\n background: linear-gradient(135deg, #f0fdf9, #ecfdf5);\n }\n\n .myio-enhanced-card-container-v5 .myio-draggable-card.offline {\n border: 2px solid #ff4d4f;\n animation: border-blink 1s infinite;\n }\n\n @keyframes border-blink {\n 0%, 100% { box-shadow: 0 0 8px rgba(255, 77, 79, 0.9); }\n 50% { box-shadow: 0 0 16px rgba(255, 0, 0, 0.6); }\n }\n\n .myio-enhanced-card-container-v5 .card-actions {\n flex-shrink: 0;\n height: 100%;\n box-shadow: 1px 0 2px rgba(0, 0, 0, .1);\n display: flex;\n flex-direction: column;\n padding: 0 4px;\n justify-content: space-evenly;\n align-items: center;\n gap: 8px;\n }\n\n .myio-enhanced-card-container-v5 .card-action {\n width: 32px;\n flex: 1;\n min-height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 6px;\n transition: all 0.2s ease;\n cursor: pointer;\n border: none;\n background: rgba(0, 0, 0, 0.05);\n }\n\n .myio-enhanced-card-container-v5 .card-action:hover {\n background: rgba(0, 224, 158, 0.1);\n transform: scale(1.1);\n }\n\n .myio-enhanced-card-container-v5 .card-action img {\n width: 20px;\n height: 20px;\n }\n\n .myio-enhanced-card-container-v5 .card-checkbox {\n width: 16px;\n height: 16px;\n cursor: pointer;\n }\n\n .myio-enhanced-card-container-v5 .card-body {\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: center;\n align-items: center;\n padding: 0 12px;\n text-align: center;\n }\n\n .myio-enhanced-card-container-v5 .card-title {\n font-weight: 700;\n font-size: 0.85rem;\n margin-bottom: 4px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n max-width: 100%;\n }\n\n .myio-enhanced-card-container-v5 .card-group {\n font-size: 0.7rem;\n color: #888;\n margin-bottom: 8px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n max-width: 100%;\n }\n\n .myio-enhanced-card-container-v5 .card-value {\n display: flex;\n align-items: center;\n gap: 4px;\n font-size: 0.9rem;\n font-weight: 700;\n color: #28a745;\n }\n\n .myio-enhanced-card-container-v5 .card-unit {\n font-size: 0.75rem;\n color: rgba(0, 0, 0, 0.6);\n }\n\n .myio-enhanced-card-container-v5 .card-percentage {\n font-size: 0.75rem;\n color: rgba(0, 0, 0, 0.45);\n margin-left: 4px;\n }\n\n .myio-enhanced-card-container-v5 .card-icon {\n width: 24px;\n height: 24px;\n margin-bottom: 8px;\n }\n\n .myio-enhanced-card-container-v5 .card-icon svg {\n width: 100%;\n height: 100%;\n fill: currentColor;\n }\n\n .myio-enhanced-card-container-v5 .card-status-indicator {\n position: absolute;\n top: 8px;\n right: 8px;\n width: 12px;\n height: 12px;\n border-radius: 50%;\n z-index: 10;\n }\n\n .myio-enhanced-card-container-v5 .card-status-ok {\n background: #28a745;\n }\n\n .myio-enhanced-card-container-v5 .card-status-alert {\n background: #ffc107;\n }\n\n .myio-enhanced-card-container-v5 .card-status-fail,\n .myio-enhanced-card-container-v5 .card-status-offline {\n background: #dc3545;\n }\n\n .myio-enhanced-card-container-v5 .card-status-unknown {\n background: #6c757d;\n }\n `;document.head.appendChild(style)}const getDeviceImageUrl=(deviceType2,percentage=0,options={})=>{const{tempStatus:tempStatus2,isOffline:isOffline2}=options;function normalizeString(str){if(typeof str!=="string"){str=""}return str.normalize("NFD").replace(/[\u0300-\u036f]/g,"").toUpperCase()}const nameType=normalizeString(deviceType2);if(nameType==="TERMOSTATO"){if(isOffline2){return"https://dashboard.myio-bas.com/api/images/public/Q4bE6zWz4pL3u5M3rjmMt2uSis6Xe52F"}if(tempStatus2==="above"){return"https://dashboard.myio-bas.com/api/images/public/S3IvpZRJvskqFrhoypKBCKKsLaKiqzJI"}else if(tempStatus2==="below"){return"https://dashboard.myio-bas.com/api/images/public/ctfORoxVGP2bB7VKeprJfIvNgmNjpaO4"}else{return"https://dashboard.myio-bas.com/api/images/public/rtCcq6kZZVCD7wgJywxEurRZwR8LA7Q7"}}if(nameType==="TANK"){if(percentage>=70){return"https://dashboard.myio-bas.com/api/images/public/3t6WVhMQJFsrKA8bSZmrngDsNPkZV7fq"}else if(percentage>=40){return"https://dashboard.myio-bas.com/api/images/public/4UBbShfXCVWR9wcw6IzVMNran4x1EW5n"}else if(percentage>=20){return"https://dashboard.myio-bas.com/api/images/public/aB9nX28F54fBBQs1Ht8jKUdYAMcq9QSm"}else{return"https://dashboard.myio-bas.com/api/images/public/qLdwhV4qw295poSCa7HinpnmXoN7dAPO"}}const deviceImages={MOTOR:"https://dashboard.myio-bas.com/api/images/public/Rge8Q3t0CP5PW8XyTn9bBK9aVP6uzSTT","3F_MEDIDOR":"https://dashboard.myio-bas.com/api/images/public/f9Ce4meybsdaAhAkUlAfy5ei3I4kcN4k",RELOGIO:"https://dashboard.myio-bas.com/api/images/public/ljHZostWg0G5AfKiyM8oZixWRIIGRASB",HIDROMETRO:"https://dashboard.myio-bas.com/api/images/public/aMQYFJbGHs9gQbQkMn6XseAlUZHanBR4",ENTRADA:"https://dashboard.myio-bas.com/api/images/public/TQHPFqiejMW6lOSVsb8Pi85WtC0QKOLU",CAIXA_DAGUA:"https://dashboard.myio-bas.com/api/images/public/3t6WVhMQJFsrKA8bSZmrngDsNPkZV7fq",ELEVADOR:"https://dashboard.myio-bas.com/api/images/public/rAjOvdsYJLGah6w6BABPJSD9znIyrkJX",ESCADA_ROLANTE:"https://dashboard.myio-bas.com/api/images/public/EJ997iB2HD1AYYUHwIloyQOOszeqb2jp"};const defaultImage="https://cdn-icons-png.flaticon.com/512/1178/1178428.png";return deviceImages[nameType]||defaultImage};const isTankDevice=deviceType==="TANK"||deviceType==="CAIXA_DAGUA";const isTermostatoDevice=deviceType?.toUpperCase()==="TERMOSTATO";const percentageForDisplay=isTankDevice?(waterPercentage||0)*100:perc;const calculateTempStatus=()=>{if(temperatureStatus)return temperatureStatus;const currentTemp=Number(val)||0;if(temperatureMin!==void 0&&temperatureMax!==void 0){if(currentTemp>temperatureMax)return"above";if(currentTemp<temperatureMin)return"below";return"ok"}return"ok"};const tempStatus=isTermostatoDevice?calculateTempStatus():null;const deviceImageUrl=getDeviceImageUrl(deviceType,percentageForDisplay,{tempStatus:tempStatus,isOffline:isOffline});const getTemperatureTooltip=()=>{if(!isTermostatoDevice)return"";const currentTemp=Number(val)||0;const hasRange=temperatureMin!==void 0&&temperatureMax!==void 0&&temperatureMin!==null&&temperatureMax!==null;if(isOffline){return"Dispositivo offline"}if(!hasRange){return`Temperatura atual: ${currentTemp.toFixed(1)}°C\nFaixa não configurada`}const rangeText=`Faixa ideal: ${temperatureMin}°C a ${temperatureMax}°C`;if(tempStatus==="above"){return`ACIMA da faixa ideal\nTemperatura atual: ${currentTemp.toFixed(1)}°C\n${rangeText}`}else if(tempStatus==="below"){return`ABAIXO da faixa ideal\nTemperatura atual: ${currentTemp.toFixed(1)}°C\n${rangeText}`}else{return`DENTRO da faixa ideal\nTemperatura atual: ${currentTemp.toFixed(1)}°C\n${rangeText}`}};const temperatureTooltip=getTemperatureTooltip();const cardHTML=`\n <div class="device-card-centered clickable ${cardEntity.status==="offline"?"offline":""}"\n data-entity-id="${entityId}"\n draggable="${enableDragDrop}"\n tabindex="0"\n role="article"\n aria-label="${cardEntity.name}, ${cardEntity.group}">\n\n <div class="device-card-inner">\n <div class="device-card-front">\n ${enableSelection&&typeof handleSelect==="function"?`<input type="checkbox" class="card-checkbox action-checker" aria-label="Select ${cardEntity.name}" style="position: absolute; top: 8px; right: 8px; z-index: 10;">`:""}\n\n <div style="display:flex;flex-direction:column;justify-content:center;align-items:center;height:100%; flex-grow: 1; min-width: 0; padding: 0 12px 0 20px; margin-left: 16px;">\n\n <div class="device-title-row" style="flex-direction: column; min-height: 38px; text-align: center; width: 100%;">\n <span class="device-title" title="${cardEntity.name}">\n ${cardEntity.name.length>18?cardEntity.name.slice(0,18)+"…":cardEntity.name}\n </span>\n ${deviceIdentifier?`\n <span class="device-subtitle" title="${deviceIdentifier}">\n ${deviceIdentifier}\n </span>\n `:""}\n </div>\n\n <img class="device-image" src="${deviceImageUrl}" alt="${deviceType}" ${isTermostatoDevice&&temperatureTooltip?`title="${temperatureTooltip}"`:""} style="${isTermostatoDevice?"cursor: help;":""}" />\n\n\n ${customerName&&String(customerName).trim()!==""?`\n <div class="myio-v5-shopping-badge-row">\n <span class="myio-v5-shopping-badge" title="${customerName}">\n <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="chip-icon"><path d="M4 22h16"/><path d="M7 22V4a2 2 0 0 1 2-2h6a2 2 0 0 1 2 2v18"/></svg>\n <span style="font-size: 0.65rem;">${customerName}</span>\n </span>\n </div>\n `:""}\n\n <div class="device-data-row">\n <div class="consumption-main">\n <span class="flash-icon ${shouldFlashIcon2?"flash":""}">\n ${icon}\n </span>\n <span class="consumption-value">${formatCardValue(cardEntity.lastValue,deviceType)}</span>\n ${!isTermostatoDevice?`<span class="device-title-percent">(${percentageForDisplay.toFixed(1)}%)</span>`:""}\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <div style="display: none;" class="connection-status-icon" data-conn="${connectionStatus}" data-state="${deviceStatus}" aria-label="${connectionStatus}"></div>\n\n </div>\n `;container.innerHTML=cardHTML;const enhancedCardElement=container.querySelector(".device-card-centered");if(!document.getElementById("myio-enhanced-card-layout-styles-v5")){const layoutStyle=document.createElement("style");layoutStyle.id="myio-enhanced-card-layout-styles-v5";layoutStyle.textContent=`\n /* ===== MYIO Card v5 — Optimized Spacing & Clean Piano Keys ===== */\n\n /* Card shell - UPDATED min-height: 126px → 114px */\n .device-card-centered.clickable {\n width: 90% !important;\n max-width: 280px !important;\n border-radius: 14px !important;\n padding: 18px !important;\n background: linear-gradient(145deg, #ffffff 0%, #f8fafc 100%) !important;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.08), 0 2px 8px rgba(0, 0, 0, 0.04) !important;\n cursor: pointer;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;\n border: 1px solid rgba(226, 232, 240, 0.8) !important;\n position: relative;\n overflow: hidden;\n backdrop-filter: blur(10px);\n min-height: 114px !important;\n margin: 0 auto;\n }\n\n .device-card-centered.clickable::before {\n content: '';\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n height: 3px;\n background: linear-gradient(90deg, #00e09e 0%, #00b4d8 50%, #7209b7 100%);\n opacity: 0;\n transition: opacity 0.3s ease;\n }\n\n .device-card-centered.clickable:hover {\n transform: translateY(-4px) scale(1.02) !important;\n box-shadow: 0 16px 48px rgba(0, 0, 0, 0.12), 0 8px 16px rgba(0, 0, 0, 0.08) !important;\n border-color: rgba(0, 224, 158, 0.3) !important;\n }\n\n .device-card-centered.clickable:hover::before {\n opacity: 1;\n }\n\n /* Selected / Offline */\n .device-card-centered.selected {\n border: 2px solid #00e09e !important;\n box-shadow: 0 12px 40px rgba(0, 224, 158, 0.25), 0 4px 12px rgba(0, 224, 158, 0.15) !important;\n background: linear-gradient(145deg, #f0fdf9 0%, #ecfdf5 50%, #f0fdf9 100%) !important;\n transform: translateY(-2px) !important;\n }\n\n .device-card-centered.selected::before {\n opacity: 1;\n background: linear-gradient(90deg, #00e09e 0%, #00d4aa 100%);\n }\n\n .device-card-centered.offline {\n border: 2px solid #ef4444 !important;\n background: linear-gradient(145deg, #fef2f2 0%, #fee2e2 50%, #fef2f2 100%) !important;\n animation: premium-offline-pulse 2s infinite !important;\n }\n\n .device-card-centered.offline::before {\n opacity: 1;\n background: linear-gradient(90deg, #ef4444 0%, #dc2626 100%);\n }\n\n @keyframes premium-offline-pulse {\n 0%, 100% {\n box-shadow: 0 8px 32px rgba(239, 68, 68, 0.15), 0 2px 8px rgba(239, 68, 68, 0.1);\n }\n 50% {\n box-shadow: 0 12px 40px rgba(239, 68, 68, 0.25), 0 4px 12px rgba(239, 68, 68, 0.2);\n }\n }\n\n /* Device image - UPDATED margin: 10px → 4px */\n .device-card-centered .device-image {\n max-height: 47px !important;\n width: auto;\n margin: 4px 0 !important;\n display: block;\n filter: drop-shadow(0 3px 6px rgba(0, 0, 0, 0.1));\n transition: all 0.3s ease;\n border-radius: 7px;\n }\n\n .device-card-centered:hover .device-image {\n filter: drop-shadow(0 5px 10px rgba(0, 0, 0, 0.15));\n transform: scale(1.05);\n }\n\n .device-card-centered .device-title-row {\n display: flex !important;\n flex-direction: column !important;\n align-items: center !important;\n justify-content: center !important;\n text-align: center !important;\n width: 100% !important;\n min-height: 38px !important;\n margin-bottom: 8px !important;\n }\n\n .device-card-centered .device-title {\n font-weight: 700 !important;\n font-size: 0.80rem !important;\n color: #1e293b !important;\n margin: 0 0 4px 0 !important;\n text-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n letter-spacing: -0.025em;\n display: block !important;\n width: 100% !important;\n text-align: center !important;\n }\n\n .device-card-centered .device-subtitle {\n font-size: 0.67rem !important;\n color: #64748b !important;\n font-weight: 500 !important;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n opacity: 0.8;\n display: block !important;\n width: 100% !important;\n text-align: center !important;\n margin: 0 !important;\n padding: 0 !important;\n }\n\n /* Value pill - COMPACT */\n .device-card-centered .consumption-main {\n background: linear-gradient(135deg, rgba(0, 224, 158, 0.1) 0%, rgba(0, 180, 216, 0.1) 100%);\n border-radius: 8px;\n padding: 4px 8px;\n margin-top: 5px;\n border: 1px solid rgba(0, 224, 158, 0.2);\n backdrop-filter: blur(10px);\n }\n\n .device-card-centered .consumption-value {\n font-weight: 700 !important;\n font-size: 0.75rem !important;\n color: #059669 !important;\n text-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n }\n\n .device-card-centered .device-title-percent {\n font-size: 0.65rem !important;\n color: #6b7280 !important;\n font-weight: 600 !important;\n margin-left: 4px;\n }\n\n .device-card-centered .flash-icon {\n font-size: 0.85rem !important;\n margin-right: 5px;\n transition: all 0.3s ease;\n }\n\n .device-card-centered:hover .flash-icon {\n transform: scale(1.1);\n }\n\n .device-card-centered .flash-icon.flash {\n animation: premium-flash 1.5s infinite;\n }\n\n @keyframes premium-flash {\n 0%, 100% {\n opacity: 1;\n transform: scale(1);\n filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.1));\n }\n 50% {\n opacity: 0.3;\n transform: scale(1.15);\n filter: drop-shadow(0 4px 8px rgba(239, 68, 68, 0.3));\n }\n }\n\n /* Checkbox */\n .device-card-centered .card-checkbox {\n width: 16px !important;\n height: 16px !important;\n cursor: pointer;\n background: rgba(255, 255, 255, 0.9) !important;\n border: 2px solid #e2e8f0 !important;\n border-radius: 5px !important;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n backdrop-filter: blur(10px);\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);\n position: relative;\n }\n\n .device-card-centered .card-checkbox:hover {\n border-color: #00e09e !important;\n box-shadow: 0 3px 6px rgba(0, 224, 158, 0.15);\n transform: scale(1.05);\n }\n\n .device-card-centered .card-checkbox:checked {\n background: linear-gradient(135deg, #00e09e 0%, #00d4aa 100%) !important;\n border-color: #00e09e !important;\n box-shadow: 0 3px 10px rgba(0, 224, 158, 0.3);\n }\n\n .device-card-centered .card-checkbox:checked::after {\n content: '✓';\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n color: white;\n font-size: 10px;\n font-weight: bold;\n text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);\n }\n\n /* Piano-Key Actions (3 BUTTONS - EVENLY DISTRIBUTED) */\n .device-card-centered .card-actions {\n position: absolute;\n left: 12px;\n top: 12px;\n bottom: 12px;\n padding: 0;\n display: flex;\n flex-direction: column;\n justify-content: space-evenly;\n gap: 0;\n border: 1px solid rgba(226, 232, 240, 0.9);\n border-radius: 8px;\n background: #fff;\n overflow: visible;\n box-shadow: none !important;\n z-index: 10;\n }\n\n .device-card-centered .card-action {\n width: 36px !important;\n flex: 1;\n min-height: 36px !important;\n border: 0;\n border-bottom: 1px solid rgba(226, 232, 240, 0.9);\n background: #fff !important;\n box-shadow: none !important;\n backdrop-filter: none !important;\n transform: translateZ(0);\n transition: transform 0.18s ease, box-shadow 0.18s ease;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n margin: 0;\n border-radius: 0;\n }\n\n .device-card-centered .card-action:first-child {\n border-top-left-radius: 7px;\n border-top-right-radius: 7px;\n }\n\n .device-card-centered .card-action:last-child {\n border-bottom: 0;\n border-bottom-left-radius: 7px;\n border-bottom-right-radius: 7px;\n }\n\n .device-card-centered .card-action img {\n width: 16px !important;\n height: 16px !important;\n filter: grayscale(0.2) brightness(0.85);\n transition: transform 0.18s ease, filter 0.18s ease;\n }\n\n /* Lift on interaction only */\n .device-card-centered .card-action:hover,\n .device-card-centered .card-action:focus-visible {\n transform: translateY(-2px) scale(1.05);\n box-shadow: 0 6px 14px rgba(16, 24, 40, 0.12), 0 2px 6px rgba(16, 24, 40, 0.08);\n outline: none;\n }\n\n .device-card-centered .card-action:hover img,\n .device-card-centered .card-action:focus-visible img {\n filter: grayscale(0) brightness(1);\n transform: scale(1.08);\n }\n\n /* Flat Status Indicator */\n .device-card-centered .connection-status-icon {\n position: absolute;\n bottom: 18px;\n right: 18px;\n width: 14px;\n height: 14px;\n border-radius: 50%;\n background: #22c55e;\n border: 1px solid rgba(0, 0, 0, 0.06);\n box-shadow: 0 3px 6px rgba(0, 0, 0, 0.15),\n 0 1px 3px rgba(0, 0, 0, 0.1),\n inset 0 -2px 4px rgba(0, 0, 0, 0.1),\n inset 0 2px 3px rgba(255, 255, 255, 0.4) !important;\n backdrop-filter: none !important;\n z-index: 5;\n transform: translateZ(0);\n transition: transform 0.2s ease, box-shadow 0.2s ease;\n }\n\n .device-card-centered .connection-status-icon:hover {\n transform: translateZ(2px) scale(1.05);\n box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2),\n 0 2px 4px rgba(0, 0, 0, 0.12),\n inset 0 -2px 5px rgba(0, 0, 0, 0.15),\n inset 0 2px 4px rgba(255, 255, 255, 0.5) !important;\n }\n\n /* Map colors by connection or device state */\n .device-card-centered .connection-status-icon[data-conn="offline"] {\n background: #94a3b8;\n }\n .device-card-centered .connection-status-icon[data-state="warning"] {\n background: #f59e0b;\n }\n .device-card-centered .connection-status-icon[data-state="danger"] {\n background: #ef4444;\n }\n .device-card-centered .connection-status-icon[data-state="no_info"] {\n background: #94a3b8;\n }\n .device-card-centered .connection-status-icon[data-state="maintenance"] {\n background: #0ea5e9;\n }\n\n .myio-v5-shopping-badge-row {\n width: 100%;\n text-align: center;\n margin-bottom: 8px; /* Espaço ANTES da imagem */\n }\n \n /* Este é o estilo da badge, copiado do v1 */\n .myio-v5-shopping-badge {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n \n /* Estilo do v1 (head-office) para replicar o visual */\n background-color: #EBF4FF; /* Fundo azul bem claro */\n border: 1px solid #BEE3F8; /* Borda azul clara */\n color: #2C5282; /* Texto azul escuro */\n \n border-radius: 8px;\n padding: 4px 10px;\n font-size: 11px; /* Um pouco menor para caber no v5 */\n font-weight: 500;\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n\n /* Garantir que não quebre o layout */\n max-width: 90%;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n \n .myio-v5-shopping-badge .chip-icon {\n width: 12px;\n height: 12px;\n stroke: currentColor;\n opacity: 0.7;\n flex-shrink: 0;\n }\n\n /* Responsive / Dark mode */\n @media (max-width: 768px) {\n .device-card-centered.clickable {\n padding: 16px !important;\n border-radius: 12px !important;\n min-height: 110px !important;\n }\n\n .device-card-centered .device-image {\n max-height: 44px !important;\n margin: 3px 0 !important;\n }\n\n .device-card-centered .card-action {\n width: 36px !important;\n height: 36px !important;\n }\n }\n\n @media (prefers-color-scheme: dark) {\n .device-card-centered.clickable {\n background: linear-gradient(145deg, #1e293b 0%, #334155 100%) !important;\n border-color: rgba(71, 85, 105, 0.8) !important;\n color: #f1f5f9 !important;\n }\n\n .myio-v5-shopping-badge {\n background-color: #334155; /* Fundo mais escuro */\n border-color: #475569;\n color: #cbd5e1; /* Texto mais claro */\n }\n\n .device-card-centered .device-title {\n color: #f1f5f9 !important;\n }\n\n .device-card-centered .device-subtitle {\n color: #94a3b8 !important;\n }\n\n .device-card-centered .card-actions {\n border-color: rgba(71, 85, 105, 0.8);\n background: #1e293b;\n }\n\n .device-card-centered .card-action {\n background: #1e293b !important;\n border-bottom: 1px solid rgba(71, 85, 105, 0.8);\n }\n }\n `;document.head.appendChild(layoutStyle)}const actionsContainer=document.createElement("div");actionsContainer.className="card-actions";if(typeof handleActionDashboard==="function"){const dashboardBtn=document.createElement("button");dashboardBtn.className="card-action action-dashboard";dashboardBtn.title="Dashboard";dashboardBtn.innerHTML='<img src="https://dashboard.myio-bas.com/api/images/public/TAVXE0sTbCZylwGsMF9lIWdllBB3iFtS"/>';dashboardBtn.addEventListener("click",e=>{e.stopPropagation();handleActionDashboard(entityObject)});actionsContainer.appendChild(dashboardBtn)}if(typeof handleActionReport==="function"){const reportBtn=document.createElement("button");reportBtn.className="card-action action-report";reportBtn.title="Relatório";reportBtn.innerHTML='<img src="https://dashboard.myio-bas.com/api/images/public/d9XuQwMYQCG2otvtNSlqUHGavGaSSpz4"/>';reportBtn.addEventListener("click",e=>{e.stopPropagation();handleActionReport(entityObject)});actionsContainer.appendChild(reportBtn)}if(typeof handleActionSettings==="function"){const settingsBtn=document.createElement("button");settingsBtn.className="card-action action-settings";settingsBtn.title="Configurações";settingsBtn.innerHTML='<img src="https://dashboard.myio-bas.com/api/images/public/5n9tze6vED2uwIs5VvJxGzNNZ9eV4yoz"/>';settingsBtn.addEventListener("click",e=>{e.stopPropagation();handleActionSettings(entityObject,{includeInfo:true,connectionData:{centralName:centralName,connectionStatusTime:connectionStatusTime,timeVal:timeVal,deviceStatus:deviceStatus}})});actionsContainer.appendChild(settingsBtn)}if(enhancedCardElement&&actionsContainer.children.length>0){enhancedCardElement.insertBefore(actionsContainer,enhancedCardElement.firstChild)}if(enableSelection&&exports.MyIOSelectionStore){const checkbox=enhancedCardElement.querySelector(".card-checkbox");if(checkbox){checkbox.addEventListener("change",e=>{e.stopPropagation();if(e.target.checked){const currentCount=exports.MyIOSelectionStore.getSelectedEntities().length;const selectedEntities=exports.MyIOSelectionStore.getSelectedEntities();console.log("selectedEntities",selectedEntities);const isTryingToAdd=e.target.checked;if(isTryingToAdd&¤tCount>=6){e.preventDefault();e.target.checked=false;MyIOToast2.show("Não é possível selecionar mais de 6 itens.","warning");return}exports.MyIOSelectionStore.add(entityId)}else{exports.MyIOSelectionStore.remove(entityId)}})}const handleSelectionChange=data=>{const isSelected=data.selectedIds.includes(entityId);if(checkbox){checkbox.checked=isSelected}enhancedCardElement.classList.toggle("selected",isSelected)};exports.MyIOSelectionStore.on("selection:change",handleSelectionChange);const isInitiallySelected=exports.MyIOSelectionStore.isSelected(entityId);if(checkbox){checkbox.checked=isInitiallySelected}enhancedCardElement.classList.toggle("selected",isInitiallySelected);container._cleanup=()=>{exports.MyIOSelectionStore.off("selection:change",handleSelectionChange)}}if(enableDragDrop){enhancedCardElement.addEventListener("dragstart",e=>{e.dataTransfer.setData("text/myio-id",entityId);e.dataTransfer.setData("application/json",JSON.stringify(entityObject));e.dataTransfer.setData("text/myio-name",entityObject.labelOrName);e.dataTransfer.effectAllowed="copy";if(exports.MyIOSelectionStore){exports.MyIOSelectionStore.startDrag(entityId)}});enhancedCardElement.addEventListener("dragend",()=>{})}if(typeof handleClickCard==="function"){enhancedCardElement.addEventListener("click",e=>{if(!e.target.closest(".card-action")&&!e.target.closest(".card-checkbox")){handleClickCard(entityObject)}})}const jQueryLikeObject={get:index=>index===0?container:void 0,0:container,length:1,find:selector=>{const found=container.querySelector(selector);return{get:index=>index===0?found:void 0,0:found,length:found?1:0,on:(event,handler)=>{if(found)found.addEventListener(event,handler);return this}}},on:(event,handler)=>{container.addEventListener(event,handler);return this},addClass:className=>{container.classList.add(className);return this},removeClass:className=>{container.classList.remove(className);return this},destroy:()=>{if(container._cleanup){container._cleanup()}}};return jQueryLikeObject}function renderCardComponent3(options){return renderCardComponentV5(options)}var MyIOChartModalClass=class{constructor(){this.isOpen=false;this.modalElement=null;this.chartInstance=null;this.currentData=null;this.chartConfig={type:"line",timeRange:7,maxEntities:20};this._init()}async open(data){if(!data||!data.entities||data.entities.length===0){console.warn("ChartModal: No data provided for comparison");return}if(data.count>this.chartConfig.maxEntities){this._showTooManyEntitiesWarning(data);return}this.currentData=data;this.isOpen=true;await this._createModal();await this._loadChartJS();await this._renderChart();this._trackEvent("chart_modal.open",{entityCount:data.count,chartType:this.chartConfig.type,timeRange:this.chartConfig.timeRange})}close(){if(!this.isOpen)return;this.isOpen=false;if(this.chartInstance){this.chartInstance.destroy();this.chartInstance=null}if(this.modalElement){this.modalElement.remove();this.modalElement=null}if(typeof document!=="undefined"){document.body.focus()}this._trackEvent("chart_modal.close",{entityCount:this.currentData?.count||0})}exportCsv(){if(!this.currentData)return;const timestamp=(new Date).toISOString().slice(0,19).replace(/:/g,"-");const filename=`comparativo_${timestamp}.csv`;const csvData=this._generateCsvData();this._downloadFile(csvData,filename,"text/csv");this._trackEvent("chart_modal.export",{format:"csv",entityCount:this.currentData.count})}exportPng(){if(!this.chartInstance)return;const timestamp=(new Date).toISOString().slice(0,19).replace(/:/g,"-");const filename=`grafico_comparativo_${timestamp}.png`;const canvas=this.chartInstance.canvas;const url=canvas.toDataURL("image/png");this._downloadFile(url,filename,"image/png",true);this._trackEvent("chart_modal.export",{format:"png",entityCount:this.currentData.count})}exportPdf(){this._showNotImplementedNotice("PDF export");this._trackEvent("chart_modal.export",{format:"pdf",entityCount:this.currentData?.count||0,status:"not_implemented"})}_init(){const store=this._getSelectionStore();if(store){store.on("comparison:open",data=>this.open(data));store.on("comparison:too_many",data=>this._showTooManyEntitiesWarning(data))}}async _createModal(){if(typeof document==="undefined")return;const existing=document.getElementById("myio-chart-modal");if(existing){existing.remove()}const modal=document.createElement("div");modal.id="myio-chart-modal";modal.className="chart-modal-overlay";modal.setAttribute("role","dialog");modal.setAttribute("aria-modal","true");modal.setAttribute("aria-labelledby","chart-modal-title");modal.innerHTML=this._generateModalHTML();document.body.appendChild(modal);this.modalElement=modal;this._attachModalEventListeners();this._trapFocus();this._announceToScreenReader("Chart comparison modal opened")}_generateModalHTML(){const{entities:entities,totals:totals,count:count}=this.currentData;return`\n <div class="chart-modal-container" \n style="\n background: rgba(20, 20, 20, 0.95);\n backdrop-filter: blur(10px);\n border: 1px solid rgba(255, 255, 255, 0.08);\n border-radius: 16px;\n padding: 20px;\n max-width: 900px;\n margin: auto;\n color: #fff;\n font-family: Arial, sans-serif;\n box-shadow: 0 8px 30px rgba(0,0,0,0.5);\n ">\n \n <div class="chart-modal-header" \n style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">\n <h2 id="chart-modal-title" \n style="font-size: 20px; font-weight: bold; margin: 0; color: #fff;">\n Comparativo de Dispositivos (${count} selecionados)\n </h2>\n <button class="chart-modal-close" aria-label="Fechar modal"\n style="background: none; border: none; color: #fff; font-size: 24px; cursor: pointer; padding: 5px; transition: 0.3s;">\n ×\n </button>\n </div>\n \n <div class="chart-modal-controls" \n style="display: flex; gap: 20px; flex-wrap: wrap; margin-bottom: 20px;">\n \n <div class="chart-type-controls" style="flex: 1; min-width: 180px;">\n <label style="display: block; margin-bottom: 6px; color: rgba(255,255,255,0.8); font-size: 14px;">\n Tipo de Gráfico:\n </label>\n <select class="chart-type-select" aria-label="Selecionar tipo de gráfico"\n style="width: 100%; padding: 8px 12px; border-radius: 8px; border: 1px solid rgba(255,255,255,0.08); \n background: rgba(255,255,255,0.08); color: #fff; outline: none; cursor: pointer;">\n <option value="line" ${this.chartConfig.type==="line"?"selected":""}>Linha</option>\n <option value="bar" ${this.chartConfig.type==="bar"?"selected":""}>Barras</option>\n </select>\n </div>\n \n <div class="chart-range-controls" style="flex: 1; min-width: 180px;">\n <label style="display: block; margin-bottom: 6px; color: rgba(255,255,255,0.8); font-size: 14px;">\n Período:\n </label>\n <select class="chart-range-select" aria-label="Selecionar período"\n style="width: 100%; padding: 8px 12px; border-radius: 8px; border: 1px solid rgba(255,255,255,0.08); \n background: rgba(255,255,255,0.08); color: #fff; outline: none; cursor: pointer;">\n <option value="7" ${this.chartConfig.timeRange===7?"selected":""}>7 dias</option>\n <option value="14" ${this.chartConfig.timeRange===14?"selected":""}>14 dias</option>\n <option value="30" ${this.chartConfig.timeRange===30?"selected":""}>30 dias</option>\n </select>\n </div>\n \n <div class="chart-export-controls" style="display: flex; gap: 10px; align-items: end;">\n <button class="export-csv-btn"\n style="padding: 8px 14px; border-radius: 8px; border: none; cursor: pointer; \n background: rgba(255,255,255,0.08); color: #fff; transition: 0.3s;">\n Exportar CSV\n </button>\n <button class="export-png-btn"\n style="padding: 8px 14px; border-radius: 8px; border: none; cursor: pointer; \n background: rgba(255,255,255,0.08); color: #fff; transition: 0.3s;">\n Exportar PNG\n </button>\n <button class="export-pdf-btn"\n style="padding: 8px 14px; border-radius: 8px; border: none; cursor: pointer; \n background: rgba(255,255,255,0.08); color: #fff; transition: 0.3s;">\n Exportar PDF\n </button>\n </div>\n </div>\n \n <div class="chart-modal-body" style="display: flex; flex-direction: column; gap: 20px;">\n <div class="chart-container" \n style="background: rgba(255,255,255,0.05); border-radius: 12px; padding: 15px;">\n <canvas id="comparison-chart" aria-label="Gráfico comparativo de dispositivos"></canvas>\n </div>\n \n <div class="chart-summary" \n style="background: rgba(255,255,255,0.05); border-radius: 12px; padding: 15px;">\n <h3 style="margin: 0 0 12px; font-size: 16px; font-weight: bold;">Resumo da Seleção</h3>\n <div class="summary-grid" \n style="display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px;">\n ${this._generateSummaryHTML(totals)}\n </div>\n </div>\n </div>\n \n <div class="chart-modal-footer" \n style="display: flex; justify-content: flex-end; margin-top: 20px;">\n <button class="chart-modal-close-btn"\n style="padding: 10px 20px; border-radius: 8px; border: none; cursor: pointer; \n background: rgba(255,255,255,0.08); color: #fff; font-weight: bold; transition: 0.3s;">\n Fechar\n </button>\n </div>\n </div>\n `}_generateSummaryHTML(totals){const items=[];if(totals.energyKwh>0){items.push(`<div class="summary-item" \n style="background: rgba(255,255,255,0.08); padding: 10px; border-radius: 8px;">\n <span class="summary-label" style="display: block; font-size: 13px; color: rgba(255,255,255,0.7);">Energia Total:</span>\n <span class="summary-value" style="font-weight: bold; font-size: 14px;">${this._formatNumber(totals.energyKwh)} kWh</span>\n </div>`)}if(totals.waterM3>0){items.push(`<div class="summary-item" \n style="background: rgba(255,255,255,0.08); padding: 10px; border-radius: 8px;">\n <span class="summary-label" style="display: block; font-size: 13px; color: rgba(255,255,255,0.7);">Água Total:</span>\n <span class="summary-value" style="font-weight: bold; font-size: 14px;">${this._formatNumber(totals.waterM3)} m³</span>\n </div>`)}if(totals.tempC>0){items.push(`<div class="summary-item" \n style="background: rgba(255,255,255,0.08); padding: 10px; border-radius: 8px;">\n <span class="summary-label" style="display: block; font-size: 13px; color: rgba(255,255,255,0.7);">Temperatura Média:</span>\n <span class="summary-value" style="font-weight: bold; font-size: 14px;">${this._formatNumber(totals.tempC/totals.count)} °C</span>\n </div>`)}items.push(`<div class="summary-item" \n style="background: rgba(255,255,255,0.08); padding: 10px; border-radius: 8px;">\n <span class="summary-label" style="display: block; font-size: 13px; color: rgba(255,255,255,0.7);">Dispositivos:</span>\n <span class="summary-value" style="font-weight: bold; font-size: 14px;">${totals.count}</span>\n </div>`);return items.join("")}_generateSummaryHTML(totals){const items=[];if(totals.energyKwh>0){items.push(`<div class="summary-item">\n <span class="summary-label">Energia Total:</span>\n <span class="summary-value">${this._formatNumber(totals.energyKwh)} kWh</span>\n </div>`)}if(totals.waterM3>0){items.push(`<div class="summary-item">\n <span class="summary-label">Água Total:</span>\n <span class="summary-value">${this._formatNumber(totals.waterM3)} m³</span>\n </div>`)}if(totals.tempC>0){items.push(`<div class="summary-item">\n <span class="summary-label">Temperatura Média:</span>\n <span class="summary-value">${this._formatNumber(totals.tempC/totals.count)} °C</span>\n </div>`)}items.push(`<div class="summary-item">\n <span class="summary-label">Dispositivos:</span>\n <span class="summary-value">${totals.count}</span>\n </div>`);return items.join("")}_attachModalEventListeners(){if(!this.modalElement)return;const closeButtons=this.modalElement.querySelectorAll(".chart-modal-close, .chart-modal-close-btn");closeButtons.forEach(btn=>{btn.addEventListener("click",()=>this.close())});const typeSelect=this.modalElement.querySelector(".chart-type-select");if(typeSelect){typeSelect.addEventListener("change",e=>{this.chartConfig.type=e.target.value;this._renderChart();this._trackEvent("chart_modal.type_change",{newType:e.target.value,entityCount:this.currentData.count})})}const rangeSelect=this.modalElement.querySelector(".chart-range-select");if(rangeSelect){rangeSelect.addEventListener("change",e=>{this.chartConfig.timeRange=parseInt(e.target.value);this._renderChart();this._trackEvent("chart_modal.range_change",{newRange:e.target.value,entityCount:this.currentData.count})})}const csvBtn=this.modalElement.querySelector(".export-csv-btn");const pngBtn=this.modalElement.querySelector(".export-png-btn");const pdfBtn=this.modalElement.querySelector(".export-pdf-btn");if(csvBtn)csvBtn.addEventListener("click",()=>this.exportCsv());if(pngBtn)pngBtn.addEventListener("click",()=>this.exportPng());if(pdfBtn)pdfBtn.addEventListener("click",()=>this.exportPdf());this.modalElement.addEventListener("keydown",e=>{if(e.key==="Escape"){this.close()}});this.modalElement.addEventListener("click",e=>{if(e.target===this.modalElement){this.close()}})}async _loadChartJS(){if(typeof globalThis!=="undefined"&&globalThis.Chart){return}return new Promise((resolve,reject)=>{if(typeof document==="undefined"){resolve();return}const script=document.createElement("script");script.src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.js";script.onload=resolve;script.onerror=reject;document.head.appendChild(script)})}async _renderChart(){if(typeof globalThis==="undefined"||!globalThis.Chart){console.error("Chart.js not loaded");return}const canvas=this.modalElement?.querySelector("#comparison-chart");if(!canvas)return;if(this.chartInstance){this.chartInstance.destroy()}const store=this._getSelectionStore();const entityIds=this.currentData.entities.map(e=>e.id);const endDate=new Date;const startDate=new Date;startDate.setDate(startDate.getDate()-this.chartConfig.timeRange);let timeSeriesData={};if(store&&store.getTimeSeriesData){timeSeriesData=await store.getTimeSeriesData(entityIds,startDate,endDate)}const chartData=this._prepareChartData(timeSeriesData);this.chartInstance=new globalThis.Chart(canvas,{type:this.chartConfig.type,data:chartData,options:this._getChartOptions()})}_prepareChartData(timeSeriesData){const datasets=[];const colors=["#FF6384","#36A2EB","#FFCE56","#4BC0C0","#9966FF","#FF9F40","#FF6384","#C9CBCF","#4BC0C0","#FF6384"];this.currentData.entities.forEach((entity,index)=>{const data=timeSeriesData[entity.id]||[];const color=colors[index%colors.length];datasets.push({label:entity.name,data:data.map(point=>({x:point.timestamp,y:point.value})),borderColor:color,backgroundColor:color+"20",fill:this.chartConfig.type==="line"?false:true})});return{datasets:datasets}}_getChartOptions(){return{responsive:true,maintainAspectRatio:false,scales:{x:{type:"time",time:{unit:"day"},title:{display:true,text:"Data"}},y:{title:{display:true,text:"Valor"}}},plugins:{title:{display:true,text:`Comparativo - ${this.chartConfig.timeRange} dias`},legend:{display:true,position:"top"},tooltip:{mode:"index",intersect:false}},interaction:{mode:"nearest",axis:"x",intersect:false}}}_generateCsvData(){const headers=["Data","Dispositivo","Valor","Unidade"];const rows=[headers];this.currentData.entities.forEach(entity=>{const today=new Date;for(let i=0;i<this.chartConfig.timeRange;i++){const date=new Date(today);date.setDate(date.getDate()-i);const value=Math.random()*100+50;rows.push([date.toLocaleDateString("pt-BR"),entity.name,this._formatNumber(value),entity.unit||""])}});return rows.map(row=>row.join(";")).join("\n")}_downloadFile(data,filename,mimeType,isDataUrl=false){if(typeof document==="undefined")return;const link=document.createElement("a");if(isDataUrl){link.href=data}else{const blob=new Blob([data],{type:mimeType});link.href=URL.createObjectURL(blob)}link.download=filename;document.body.appendChild(link);link.click();document.body.removeChild(link);if(!isDataUrl){URL.revokeObjectURL(link.href)}}_showTooManyEntitiesWarning(data){if(typeof document==="undefined")return;const message=`Muitos dispositivos selecionados (${data.count}). Máximo permitido: ${this.chartConfig.maxEntities}.`;if(typeof globalThis!=="undefined"&&globalThis.alert){globalThis.alert(message)}this._announceToScreenReader(message)}_showNotImplementedNotice(feature){const message=`${feature} será implementado em uma versão futura.`;if(typeof globalThis!=="undefined"&&globalThis.alert){globalThis.alert(message)}this._announceToScreenReader(message)}_trapFocus(){if(!this.modalElement||typeof document==="undefined")return;const focusableElements=this.modalElement.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');const firstElement=focusableElements[0];const lastElement=focusableElements[focusableElements.length-1];if(firstElement){firstElement.focus()}this.modalElement.addEventListener("keydown",e=>{if(e.key==="Tab"&&typeof document!=="undefined"){if(e.shiftKey){if(document.activeElement===firstElement){e.preventDefault();lastElement?.focus()}}else{if(document.activeElement===lastElement){e.preventDefault();firstElement?.focus()}}}})}_getSelectionStore(){if(typeof globalThis!=="undefined"&&globalThis.window?.MyIOSelectionStore){return globalThis.window.MyIOSelectionStore}return null}_trackEvent(eventName,payload={}){const store=this._getSelectionStore();if(store&&store.trackEvent){store.trackEvent(eventName,payload)}}_announceToScreenReader(message){const store=this._getSelectionStore();if(store&&store.announceToScreenReader){store.announceToScreenReader(message)}}_formatNumber(value){if(typeof value!=="number"||isNaN(value))return"0";return new Intl.NumberFormat("pt-BR",{minimumFractionDigits:0,maximumFractionDigits:2}).format(value)}};var MyIOChartModal=new MyIOChartModalClass;if(typeof globalThis!=="undefined"&&typeof globalThis.window!=="undefined"){globalThis.window.MyIOChartModal=MyIOChartModal}if(typeof module!=="undefined"&&module.exports){module.exports={MyIOChartModal:MyIOChartModal}}var MyIOToast=function(){let toastContainer=null;let toastTimeout=null;const TOAST_CSS=`\n #myio-global-toast-container {\n position: fixed;\n top: 25px;\n right: 25px;\n z-index: 99999;\n min-width: 320px;\n max-width: 480px;\n padding: 16px 20px;\n background-color: #323232;\n color: white;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;\n font-size: 14px;\n border-radius: 8px;\n transform: translateX(400px);\n transition: transform 0.3s ease-in-out, opacity 0.3s ease-in-out;\n opacity: 0;\n box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);\n border-left: 5px solid transparent;\n display: flex;\n align-items: center;\n word-wrap: break-word;\n pointer-events: auto;\n }\n\n #myio-global-toast-container.show {\n transform: translateX(0);\n opacity: 1;\n }\n\n #myio-global-toast-container.info {\n background-color: #2196f3;\n border-color: #1976d2;\n }\n\n #myio-global-toast-container.success {\n background-color: #4caf50;\n border-color: #388e3c;\n }\n\n #myio-global-toast-container.warning {\n background-color: #ff9800;\n border-color: #f57c00;\n }\n\n #myio-global-toast-container.error {\n background-color: #d32f2f;\n border-color: #b71c1c;\n }\n\n #myio-global-toast-container::before {\n content: 'ℹ️';\n margin-right: 12px;\n font-size: 20px;\n flex-shrink: 0;\n }\n\n #myio-global-toast-container.success::before {\n content: '✅';\n }\n\n #myio-global-toast-container.warning::before {\n content: '⚠️';\n }\n\n #myio-global-toast-container.error::before {\n content: '🚫';\n }\n\n @media (max-width: 480px) {\n #myio-global-toast-container {\n top: 10px;\n right: 10px;\n left: 10px;\n min-width: auto;\n max-width: none;\n }\n }\n `;function createToastElement(){if(document.getElementById("myio-global-toast-container")){toastContainer=document.getElementById("myio-global-toast-container");return}if(!document.getElementById("myio-global-toast-styles")){const style=document.createElement("style");style.id="myio-global-toast-styles";style.textContent=TOAST_CSS;document.head.appendChild(style)}toastContainer=document.createElement("div");toastContainer.id="myio-global-toast-container";document.body.appendChild(toastContainer)}function show(message,type="info",duration=3500){if(!toastContainer){createToastElement()}clearTimeout(toastTimeout);const validTypes=["info","success","warning","error"];if(!validTypes.includes(type)){console.warn(`[MyIOToast] Invalid type "${type}". Using "info" instead.`);type="info"}toastContainer.textContent=message;toastContainer.className="";toastContainer.classList.add(type);setTimeout(()=>{toastContainer.classList.add("show")},10);if(duration>0){toastTimeout=setTimeout(()=>{hide()},duration)}return{hide:hide}}function hide(){if(toastContainer){toastContainer.classList.remove("show");clearTimeout(toastTimeout)}}function info(message,duration=3500){return show(message,"info",duration)}function success(message,duration=3500){return show(message,"success",duration)}function warning(message,duration=3500){return show(message,"warning",duration)}function error(message,duration=5e3){return show(message,"error",duration)}if(typeof window!=="undefined"){if(document.readyState==="loading"){document.addEventListener("DOMContentLoaded",createToastElement)}else{createToastElement()}}return{show:show,hide:hide,info:info,success:success,warning:warning,error:error}}();if(typeof module!=="undefined"&&module.exports){module.exports={MyIOToast:MyIOToast}}var TELEMETRY_CONFIG={voltage_a:{label:"Tensão Fase A",unit:"V",icon:"⚡",decimals:1},voltage_b:{label:"Tensão Fase B",unit:"V",icon:"⚡",decimals:1},voltage_c:{label:"Tensão Fase C",unit:"V",icon:"⚡",decimals:1},total_current:{label:"Corrente Total",unit:"A",icon:"🔌",decimals:2},current:{label:"Corrente",unit:"A",icon:"🔌",decimals:2},consumption:{label:"Potência",unit:"W",icon:"⚙️",decimals:0},power:{label:"Potência",unit:"W",icon:"⚙️",decimals:0},energy:{label:"Energia",unit:"kWh",icon:"📊",decimals:1},activePower:{label:"Potência Ativa",unit:"kW",icon:"⚙️",decimals:2},reactivePower:{label:"Potência Reativa",unit:"kVAr",icon:"🔄",decimals:2},apparentPower:{label:"Potência Aparente",unit:"kVA",icon:"📈",decimals:2},powerFactor:{label:"Fator de Potência",unit:"",icon:"📐",decimals:3},temperature:{label:"Temperatura",unit:"°C",icon:"🌡️",decimals:1}};var STRINGS={"pt-BR":{title:"Telemetrias em Tempo Real",close:"Fechar",pause:"Pausar",resume:"Retomar",export:"Exportar CSV",autoUpdate:"Atualização automática",lastUpdate:"Última atualização",noData:"Sem dados",loading:"Carregando...",error:"Erro ao carregar telemetrias",trend_up:"Aumentando",trend_down:"Diminuindo",trend_stable:"Estável"},"en-US":{title:"Real-Time Telemetry",close:"Close",pause:"Pause",resume:"Resume",export:"Export CSV",autoUpdate:"Auto-update",lastUpdate:"Last update",noData:"No data",loading:"Loading...",error:"Error loading telemetry",trend_up:"Increasing",trend_down:"Decreasing",trend_stable:"Stable"}};async function openRealTimeTelemetryModal(params){const{token:token,deviceId:deviceId,deviceLabel:deviceLabel="Dispositivo",telemetryKeys:telemetryKeys=["voltage_a","voltage_b","voltage_c","total_current","consumption"],refreshInterval:refreshInterval=8e3,historyPoints:historyPoints=50,onClose:onClose,locale:locale="pt-BR"}=params;const strings=STRINGS[locale]||STRINGS["pt-BR"];let refreshIntervalId=null;let isPaused=false;let telemetryHistory=new Map;let lastKnownValues=new Map;let chart=null;let selectedChartKey="consumption";const overlay=document.createElement("div");overlay.className="myio-realtime-telemetry-overlay";overlay.innerHTML=`\n <style>\n .myio-realtime-telemetry-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10000;\n padding: 20px;\n animation: fadeIn 0.2s ease;\n }\n\n @keyframes fadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n\n .myio-realtime-telemetry-container {\n background: #ffffff;\n border-radius: 12px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);\n max-width: 1200px;\n width: 100%;\n max-height: 90vh;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n animation: slideUp 0.3s ease;\n }\n\n @keyframes slideUp {\n from { transform: translateY(20px); opacity: 0; }\n to { transform: translateY(0); opacity: 1; }\n }\n\n .myio-realtime-telemetry-header {\n padding: 20px 24px;\n border-bottom: 1px solid #e0e0e0;\n display: flex;\n justify-content: space-between;\n align-items: center;\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n color: white;\n }\n\n .myio-realtime-telemetry-title {\n font-size: 20px;\n font-weight: 600;\n margin: 0;\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .myio-realtime-telemetry-close {\n background: rgba(255, 255, 255, 0.2);\n border: none;\n color: white;\n font-size: 24px;\n width: 32px;\n height: 32px;\n border-radius: 6px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.2s ease;\n }\n\n .myio-realtime-telemetry-close:hover {\n background: rgba(255, 255, 255, 0.3);\n transform: scale(1.1);\n }\n\n .myio-realtime-telemetry-body {\n padding: 24px;\n overflow-y: auto;\n flex: 1;\n }\n\n .myio-telemetry-cards-grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\n gap: 16px;\n margin-bottom: 24px;\n }\n\n .myio-telemetry-card {\n background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);\n border-radius: 12px;\n padding: 16px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n transition: all 0.3s ease;\n }\n\n .myio-telemetry-card:hover {\n transform: translateY(-4px);\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);\n }\n\n .myio-telemetry-card-header {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 12px;\n font-size: 14px;\n color: #555;\n font-weight: 500;\n }\n\n .myio-telemetry-card-icon {\n font-size: 20px;\n }\n\n .myio-telemetry-card-value {\n font-size: 28px;\n font-weight: 700;\n color: #2c3e50;\n margin-bottom: 4px;\n }\n\n .myio-telemetry-card-trend {\n font-size: 12px;\n color: #777;\n display: flex;\n align-items: center;\n gap: 4px;\n }\n\n .myio-telemetry-card-trend.up {\n color: #27ae60;\n }\n\n .myio-telemetry-card-trend.down {\n color: #e74c3c;\n }\n\n .myio-telemetry-chart-container {\n background: white;\n border-radius: 12px;\n padding: 20px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n margin-bottom: 20px;\n max-height: 520px;\n overflow: hidden;\n }\n\n .myio-telemetry-chart-title {\n font-size: 16px;\n font-weight: 600;\n margin: 0 0 16px 0;\n color: #2c3e50;\n }\n\n .myio-telemetry-chart {\n height: 300px;\n max-height: 450px;\n width: 100%;\n }\n\n .myio-telemetry-selector {\n padding: 8px 12px;\n border: 1px solid #ddd;\n border-radius: 6px;\n font-size: 14px;\n background: white;\n cursor: pointer;\n transition: border-color 0.2s;\n }\n\n .myio-telemetry-selector:hover {\n border-color: #667eea;\n }\n\n .myio-telemetry-selector:focus {\n outline: none;\n border-color: #667eea;\n box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);\n }\n\n .myio-realtime-telemetry-footer {\n padding: 16px 24px;\n border-top: 1px solid #e0e0e0;\n display: flex;\n justify-content: space-between;\n align-items: center;\n background: #f8f9fa;\n }\n\n .myio-telemetry-status {\n display: flex;\n align-items: center;\n gap: 12px;\n font-size: 14px;\n color: #555;\n }\n\n .myio-telemetry-status-indicator {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: #27ae60;\n animation: pulse 2s infinite;\n }\n\n .myio-telemetry-status-indicator.paused {\n background: #e74c3c;\n animation: none;\n }\n\n @keyframes pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.5; }\n }\n\n .myio-telemetry-actions {\n display: flex;\n gap: 12px;\n }\n\n .myio-telemetry-btn {\n padding: 8px 16px;\n border: none;\n border-radius: 6px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n display: flex;\n align-items: center;\n gap: 6px;\n }\n\n .myio-telemetry-btn-primary {\n background: #667eea;\n color: white;\n }\n\n .myio-telemetry-btn-primary:hover {\n background: #5568d3;\n transform: translateY(-1px);\n }\n\n .myio-telemetry-btn-secondary {\n background: #e0e0e0;\n color: #333;\n }\n\n .myio-telemetry-btn-secondary:hover {\n background: #d0d0d0;\n transform: translateY(-1px);\n }\n\n .myio-telemetry-loading {\n text-align: center;\n padding: 40px;\n color: #999;\n font-size: 16px;\n }\n\n .myio-telemetry-error {\n text-align: center;\n padding: 40px;\n color: #e74c3c;\n font-size: 16px;\n }\n </style>\n\n <div class="myio-realtime-telemetry-container">\n <div class="myio-realtime-telemetry-header">\n <h2 class="myio-realtime-telemetry-title">\n ⚡ ${strings.title} - ${deviceLabel}\n </h2>\n <button class="myio-realtime-telemetry-close" id="close-btn" title="${strings.close}">\n ×\n </button>\n </div>\n\n <div class="myio-realtime-telemetry-body">\n <div class="myio-telemetry-loading" id="loading-state">\n ${strings.loading}\n </div>\n\n <div id="telemetry-content" style="display: none;">\n <div class="myio-telemetry-cards-grid" id="telemetry-cards"></div>\n\n <div class="myio-telemetry-chart-container" id="chart-container" style="display: none;">\n <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px;">\n <h3 class="myio-telemetry-chart-title" id="chart-title" style="margin: 0;">Histórico de Telemetria</h3>\n <select id="chart-key-selector" class="myio-telemetry-selector">\n <option value="consumption">Potência</option>\n <option value="total_current">Corrente Total</option>\n <option value="voltage_a">Tensão Fase A</option>\n <option value="voltage_b">Tensão Fase B</option>\n <option value="voltage_c">Tensão Fase C</option>\n </select>\n </div>\n <canvas class="myio-telemetry-chart" id="telemetry-chart"></canvas>\n </div>\n </div>\n\n <div class="myio-telemetry-error" id="error-state" style="display: none;">\n ${strings.error}\n </div>\n </div>\n\n <div class="myio-realtime-telemetry-footer">\n <div class="myio-telemetry-status">\n <span class="myio-telemetry-status-indicator" id="status-indicator"></span>\n <span id="status-text">${strings.autoUpdate}: ON</span>\n <span>•</span>\n <span id="last-update-text">${strings.lastUpdate}: --:--:--</span>\n </div>\n\n <div class="myio-telemetry-actions">\n <button class="myio-telemetry-btn myio-telemetry-btn-secondary" id="pause-btn">\n <span id="pause-btn-icon">⏸️</span>\n <span id="pause-btn-text">${strings.pause}</span>\n </button>\n <button class="myio-telemetry-btn myio-telemetry-btn-primary" id="export-btn">\n ⬇️ ${strings.export}\n </button>\n </div>\n </div>\n </div>\n `;document.body.appendChild(overlay);const closeBtn=overlay.querySelector("#close-btn");const pauseBtn=overlay.querySelector("#pause-btn");const pauseBtnIcon=overlay.querySelector("#pause-btn-icon");const pauseBtnText=overlay.querySelector("#pause-btn-text");const exportBtn=overlay.querySelector("#export-btn");const loadingState=overlay.querySelector("#loading-state");const telemetryContent=overlay.querySelector("#telemetry-content");const errorState=overlay.querySelector("#error-state");const telemetryCards=overlay.querySelector("#telemetry-cards");const chartContainer=overlay.querySelector("#chart-container");const chartCanvas=overlay.querySelector("#telemetry-chart");const chartKeySelector=overlay.querySelector("#chart-key-selector");const statusIndicator=overlay.querySelector("#status-indicator");const statusText=overlay.querySelector("#status-text");const lastUpdateText=overlay.querySelector("#last-update-text");function closeModal(){if(refreshIntervalId){clearInterval(refreshIntervalId);refreshIntervalId=null}if(chart){chart.destroy();chart=null}overlay.remove();if(onClose){onClose()}}async function fetchLatestTelemetry(){const keys=telemetryKeys.join(",");const url=`/api/plugins/telemetry/DEVICE/${deviceId}/values/timeseries?keys=${keys}&limit=1&agg=NONE`;const response=await fetch(url,{headers:{"X-Authorization":`Bearer ${token}`}});if(!response.ok){throw new Error(`Failed to fetch telemetry: ${response.statusText}`)}return await response.json()}function processTelemetryData(data){const values=[];for(const key of telemetryKeys){const telemetryData=data[key];if(!telemetryData||telemetryData.length===0)continue;const latest=telemetryData[0];const config=TELEMETRY_CONFIG[key]||{label:key,unit:"",icon:"📊",decimals:2};let numValue=Number(latest.value)||0;if(key==="total_current"||key==="current"){numValue=numValue/1e3}const formatted=numValue.toFixed(config.decimals);values.push({key:key,value:numValue,timestamp:latest.ts,formatted:`${formatted} ${config.unit}`,unit:config.unit,icon:config.icon,label:config.label,trend:"stable"})}return values}function calculateTrend(key,currentValue){const history=telemetryHistory.get(key);if(!history||history.length<2)return"stable";const previousValue=history[history.length-2].y;const diff=currentValue-previousValue;const threshold=previousValue*.02;if(diff>threshold)return"up";if(diff<-threshold)return"down";return"stable"}function updateTelemetryCards(values){telemetryCards.innerHTML=values.map(tel=>{const trend=calculateTrend(tel.key,tel.value);const trendIcon=trend==="up"?"↗":trend==="down"?"↘":"→";const trendClass=trend;const trendLabel=strings[`trend_${trend}`]||"";return`\n <div class="myio-telemetry-card">\n <div class="myio-telemetry-card-header">\n <span class="myio-telemetry-card-icon">${tel.icon}</span>\n <span>${tel.label}</span>\n </div>\n <div class="myio-telemetry-card-value">${tel.formatted}</div>\n <div class="myio-telemetry-card-trend ${trendClass}">\n ${trendIcon} ${trendLabel}\n </div>\n </div>\n `}).join("")}function updateHistory(values){const now=Date.now();for(const tel of values){if(!telemetryHistory.has(tel.key)){telemetryHistory.set(tel.key,[])}const history=telemetryHistory.get(tel.key);history.push({x:now,y:tel.value});lastKnownValues.set(tel.key,tel.value);if(history.length>historyPoints){history.shift()}}for(const key of telemetryKeys){const receivedKeys=values.map(v=>v.key);if(!receivedKeys.includes(key)&&lastKnownValues.has(key)){if(!telemetryHistory.has(key)){telemetryHistory.set(key,[])}const history=telemetryHistory.get(key);const lastValue=lastKnownValues.get(key);history.push({x:now,y:lastValue});if(history.length>historyPoints){history.shift()}}}if(telemetryHistory.has(selectedChartKey)&&chart){const selectedHistory=telemetryHistory.get(selectedChartKey);chart.data.datasets[0].data=selectedHistory;chart.update("none")}}function getFormattedValue(key,value){const config=TELEMETRY_CONFIG[key];if(!config)return value.toFixed(2);if(key==="consumption"||key==="power"){if(value>=1e3){return`${(value/1e3).toFixed(2)} kW`}return`${value.toFixed(0)} W`}return`${value.toFixed(config.decimals)} ${config.unit}`}function initializeChart(){const Chart2=window.Chart;if(!Chart2){console.warn("[RealTimeTelemetry] Chart.js not loaded");return}chartContainer.style.display="block";const config=TELEMETRY_CONFIG[selectedChartKey]||{label:selectedChartKey,unit:""};chart=new Chart2(chartCanvas,{type:"line",data:{datasets:[{label:config.label,data:[],borderColor:"#667eea",backgroundColor:"rgba(102, 126, 234, 0.1)",borderWidth:2,fill:true,tension:.4,pointRadius:3,pointHoverRadius:5}]},options:{responsive:true,maintainAspectRatio:false,animation:false,plugins:{legend:{display:false},tooltip:{callbacks:{title:function(context){const timestamp=context[0].parsed.x;const date=new Date(timestamp);return date.toLocaleString(locale,{day:"2-digit",month:"2-digit",year:"numeric",hour:"2-digit",minute:"2-digit",second:"2-digit"})},label:function(context){const value=context.parsed.y;return getFormattedValue(selectedChartKey,value)}}}},scales:{x:{type:"linear",ticks:{maxRotation:45,minRotation:0,callback:function(value){const date=new Date(value);return date.toLocaleTimeString(locale,{hour:"2-digit",minute:"2-digit",second:"2-digit"})}},title:{display:true,text:"Hora"}},y:{beginAtZero:true,ticks:{callback:function(value){if(selectedChartKey==="consumption"||selectedChartKey==="power"){if(value>=1e3){return`${(value/1e3).toFixed(1)} kW`}return`${value} W`}return`${value} ${config.unit}`}},title:{display:true,text:selectedChartKey==="consumption"||selectedChartKey==="power"?"W":config.unit}}}}})}function updateChartKey(newKey){selectedChartKey=newKey;if(chart){chart.destroy();chart=null}initializeChart();if(telemetryHistory.has(selectedChartKey)){const selectedHistory=telemetryHistory.get(selectedChartKey);chart.data.datasets[0].data=selectedHistory;chart.update("none")}}async function refreshData(){try{const data=await fetchLatestTelemetry();const values=processTelemetryData(data);updateHistory(values);updateTelemetryCards(values);const now=new Date;lastUpdateText.textContent=`${strings.lastUpdate}: ${now.toLocaleTimeString(locale)}`;if(loadingState.style.display!=="none"){loadingState.style.display="none";telemetryContent.style.display="block";initializeChart()}}catch(error){console.error("[RealTimeTelemetry] Error fetching data:",error);errorState.style.display="block";loadingState.style.display="none";telemetryContent.style.display="none"}}function togglePause(){isPaused=!isPaused;if(isPaused){if(refreshIntervalId){clearInterval(refreshIntervalId);refreshIntervalId=null}pauseBtnIcon.textContent="▶️";pauseBtnText.textContent=strings.resume;statusIndicator.classList.add("paused");statusText.textContent=`${strings.autoUpdate}: OFF`}else{refreshIntervalId=window.setInterval(refreshData,refreshInterval);pauseBtnIcon.textContent="⏸️";pauseBtnText.textContent=strings.pause;statusIndicator.classList.remove("paused");statusText.textContent=`${strings.autoUpdate}: ON`}}function exportToCSV2(){const rows=[];rows.push("Timestamp,"+telemetryKeys.map(k=>TELEMETRY_CONFIG[k]?.label||k).join(","));let maxLength=0;for(const key of telemetryKeys){const history=telemetryHistory.get(key);if(history&&history.length>maxLength){maxLength=history.length}}for(let i=0;i<maxLength;i++){const row=[];let timestamp="";for(const key of telemetryKeys){const history=telemetryHistory.get(key);if(history&&history[i]){if(!timestamp){timestamp=new Date(history[i].x).toISOString()}row.push(history[i].y.toFixed(2))}else{row.push("")}}if(timestamp){rows.push(timestamp+","+row.join(","))}}const csv=rows.join("\n");const blob=new Blob([csv],{type:"text/csv"});const url=URL.createObjectURL(blob);const a=document.createElement("a");a.href=url;a.download=`telemetry_${deviceLabel}_${(new Date).toISOString()}.csv`;a.click();URL.revokeObjectURL(url)}closeBtn.addEventListener("click",closeModal);pauseBtn.addEventListener("click",togglePause);exportBtn.addEventListener("click",exportToCSV2);chartKeySelector.addEventListener("change",e=>{const newKey=e.target.value;updateChartKey(newKey)});overlay.addEventListener("click",e=>{if(e.target===overlay){closeModal()}});await refreshData();refreshIntervalId=window.setInterval(refreshData,refreshInterval);return{destroy:closeModal}}var CSS_TOKENS=`\n:root {\n /* Brand Colors */\n --myio-brand-700: #3e1a7d;\n --myio-brand-600: #2d1458;\n --myio-accent: #2d1458;\n --myio-danger: #d32f2f;\n --myio-success: #388E3C;\n --myio-warning: #f57c00;\n --myio-info: #1976d2;\n \n /* Neutral Colors */\n --myio-bg: #f7f7f7;\n --myio-card: #ffffff;\n --myio-border: #e0e0e0;\n --myio-text: #333333;\n --myio-text-muted: #666666;\n --myio-text-light: #999999;\n \n /* Layout */\n --myio-shadow: 0 2px 6px rgba(0,0,0,0.08);\n --myio-shadow-lg: 0 4px 12px rgba(0,0,0,0.15);\n --myio-radius: 10px;\n --myio-radius-sm: 6px;\n --myio-spacing: 16px;\n --myio-spacing-sm: 8px;\n --myio-spacing-lg: 24px;\n \n /* Typography */\n --myio-font: 'Roboto', Arial, sans-serif;\n --myio-font-size: 14px;\n --myio-font-size-sm: 12px;\n --myio-font-size-lg: 16px;\n --myio-line-height: 1.4;\n \n /* Z-index */\n --myio-z-modal: 9999;\n --myio-z-backdrop: 9998;\n --myio-z-sticky: 100;\n \n /* Animation */\n --myio-transition: 0.2s ease;\n --myio-transition-slow: 0.3s ease;\n}\n\n/* Dark theme tokens */\n[data-theme="dark"] {\n --myio-bg: #1a1a1a;\n --myio-card: #2d2d2d;\n --myio-border: #404040;\n --myio-text: #ffffff;\n --myio-text-muted: #cccccc;\n --myio-text-light: #999999;\n --myio-shadow: 0 2px 6px rgba(0,0,0,0.3);\n --myio-shadow-lg: 0 4px 12px rgba(0,0,0,0.4);\n}\n\n/* Reduced motion */\n@media (prefers-reduced-motion: reduce) {\n :root {\n --myio-transition: 0s;\n --myio-transition-slow: 0s;\n }\n}\n`;var MODAL_STYLES=`\n.myio-modal-scope {\n font-family: var(--myio-font);\n font-size: var(--myio-font-size);\n line-height: var(--myio-line-height);\n color: var(--myio-text);\n box-sizing: border-box;\n}\n\n.myio-modal-scope *,\n.myio-modal-scope *::before,\n.myio-modal-scope *::after {\n box-sizing: inherit;\n}\n\n.myio-modal-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n backdrop-filter: blur(2px);\n z-index: var(--myio-z-backdrop);\n display: flex;\n align-items: center;\n justify-content: center;\n padding: var(--myio-spacing);\n}\n\n.myio-modal {\n background: var(--myio-card);\n border-radius: var(--myio-radius);\n box-shadow: var(--myio-shadow-lg);\n max-width: 90vw;\n max-height: 90vh;\n overflow: hidden;\n display: flex;\n flex-direction: column;\n transform: scale(0.95);\n opacity: 0;\n transition: transform var(--myio-transition), opacity var(--myio-transition);\n}\n\n.myio-modal.myio-modal-open {\n transform: scale(1);\n opacity: 1;\n}\n\n.myio-modal-header {\n padding: 4px;\n border-bottom: none;\n display: flex;\n align-items: center;\n justify-content: space-between;\n background: var(--myio-brand-700);\n color: white;\n border-radius: var(--myio-radius) var(--myio-radius) 0 0;\n min-height: 20px;\n}\n\n.myio-modal-title {\n font-size: 18px !important;\n font-weight: 600;\n margin: 6px !important;\n color: white;\n line-height: 2 !important;\n}\n\n.myio-modal-close {\n background: none;\n border: none;\n font-size: 20px;\n cursor: pointer;\n padding: 4px 8px;\n border-radius: var(--myio-radius-sm);\n color: rgba(255, 255, 255, 0.8);\n transition: background-color var(--myio-transition);\n}\n\n.myio-modal-close:hover {\n background: rgba(255, 255, 255, 0.1);\n color: white;\n}\n\n.myio-modal-close:focus-visible {\n outline: 2px solid rgba(255, 255, 255, 0.5);\n outline-offset: 2px;\n}\n\n.myio-modal-body {\n flex: 1;\n overflow: auto;\n padding: var(--myio-spacing);\n}\n\n.myio-modal-footer {\n padding: var(--myio-spacing);\n border-top: 1px solid var(--myio-border);\n display: flex;\n gap: var(--myio-spacing-sm);\n justify-content: flex-end;\n}\n\n/* Button styles */\n.myio-btn {\n padding: 8px 16px;\n border: none;\n border-radius: var(--myio-radius-sm);\n font-family: inherit;\n font-size: var(--myio-font-size);\n cursor: pointer;\n transition: background-color var(--myio-transition);\n display: inline-flex;\n align-items: center;\n gap: var(--myio-spacing-sm);\n}\n\n.myio-btn-primary {\n background: var(--myio-brand-700);\n color: white;\n}\n\n.myio-btn-primary:hover {\n background: var(--myio-brand-600);\n}\n\n.myio-btn-secondary {\n background: var(--myio-bg);\n color: var(--myio-text);\n border: 1px solid var(--myio-border);\n}\n\n.myio-btn-secondary:hover {\n background: var(--myio-border);\n}\n\n.myio-btn:focus-visible {\n outline: 2px solid var(--myio-brand-700);\n outline-offset: 2px;\n}\n\n.myio-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n/* Form styles */\n.myio-form-group {\n margin-bottom: var(--myio-spacing);\n}\n\n.myio-label {\n display: block;\n margin-bottom: var(--myio-spacing-sm);\n font-weight: 500;\n color: var(--myio-text);\n}\n\n.myio-input {\n width: 100%;\n padding: var(--myio-spacing-sm);\n border: 1px solid var(--myio-border);\n border-radius: var(--myio-radius-sm);\n font-family: inherit;\n font-size: var(--myio-font-size);\n transition: border-color var(--myio-transition);\n}\n\n.myio-input:focus {\n outline: none;\n border-color: var(--myio-brand-700);\n}\n\n/* Table styles */\n.myio-table {\n width: 100%;\n border-collapse: collapse;\n font-size: var(--myio-font-size);\n}\n\n.myio-table th,\n.myio-table td {\n padding: var(--myio-spacing-sm) var(--myio-spacing);\n text-align: left;\n border-bottom: 1px solid var(--myio-border);\n}\n\n.myio-table th:first-child,\n.myio-table td:first-child {\n padding-left: var(--myio-spacing-lg);\n}\n\n.myio-table th:last-child,\n.myio-table td:last-child {\n padding-right: var(--myio-spacing-lg);\n}\n\n.myio-table th {\n background: var(--myio-brand-700);\n color: white;\n font-weight: 600;\n position: sticky;\n top: 0;\n z-index: var(--myio-z-sticky);\n}\n\n.myio-table tbody tr:nth-child(even) {\n background: var(--myio-bg);\n}\n\n.myio-table tbody tr:hover {\n background: var(--myio-border);\n}\n\n/* Loading spinner */\n.myio-spinner {\n width: 20px;\n height: 20px;\n border: 2px solid var(--myio-border);\n border-top: 2px solid var(--myio-brand-700);\n border-radius: 50%;\n animation: myio-spin 1s linear infinite;\n}\n\n@keyframes myio-spin {\n 0% { transform: rotate(0deg); }\n 100% { transform: rotate(360deg); }\n}\n\n/* Utility classes */\n.myio-text-center { text-align: center; }\n.myio-text-right { text-align: right; }\n.myio-text-muted { color: var(--myio-text-muted); }\n.myio-text-success { color: var(--myio-success); }\n.myio-text-danger { color: var(--myio-danger); }\n.myio-mb-0 { margin-bottom: 0; }\n.myio-mt-0 { margin-top: 0; }\n.myio-p-0 { padding: 0; }\n`;var DATERANGEPICKER_STYLES=`\n/* MyIO Premium DateRangePicker Styling */\n.myio-modal-scope .daterangepicker {\n font-family: var(--myio-font);\n border: 1px solid var(--myio-border);\n border-radius: var(--myio-radius);\n box-shadow: var(--myio-shadow-lg);\n background: var(--myio-card);\n z-index: var(--myio-z-popover, 10000);\n}\n\n.myio-modal-scope .daterangepicker .calendar-table {\n background: var(--myio-card);\n border: none;\n}\n\n.myio-modal-scope .daterangepicker .calendar-table th,\n.myio-modal-scope .daterangepicker .calendar-table td {\n border: none;\n padding: 8px;\n text-align: center;\n font-size: 13px;\n}\n\n.myio-modal-scope .daterangepicker .calendar-table th {\n background: var(--myio-bg);\n color: var(--myio-text);\n font-weight: 600;\n}\n\n.myio-modal-scope .daterangepicker .calendar-table td.available:hover {\n background: var(--myio-bg);\n color: var(--myio-text);\n}\n\n.myio-modal-scope .daterangepicker .calendar-table td.in-range {\n background: rgba(74, 20, 140, 0.1);\n color: var(--myio-text);\n}\n\n.myio-modal-scope .daterangepicker .calendar-table td.start-date,\n.myio-modal-scope .daterangepicker .calendar-table td.end-date {\n background: var(--myio-brand-700);\n color: white;\n border-radius: 4px;\n}\n\n.myio-modal-scope .daterangepicker .calendar-table td.active {\n background: var(--myio-brand-700);\n color: white;\n border-radius: 4px;\n}\n\n/* Premium button styling for Aplicar/Cancelar */\n.myio-modal-scope .daterangepicker .drp-buttons {\n border-top: 1px solid var(--myio-border);\n padding: 12px 16px;\n background: var(--myio-bg);\n text-align: right;\n}\n\n.myio-modal-scope .daterangepicker .drp-buttons .btn {\n margin-left: 8px;\n padding: 8px 16px;\n border-radius: var(--myio-radius-sm);\n font-family: var(--myio-font);\n font-size: 14px;\n font-weight: 500;\n border: none;\n cursor: pointer;\n transition: all var(--myio-transition);\n min-width: 80px;\n}\n\n/* Aplicar button - MyIO Primary */\n.myio-modal-scope .daterangepicker .drp-buttons .applyBtn {\n background: linear-gradient(135deg, var(--myio-brand-700) 0%, var(--myio-accent) 100%);\n color: white;\n box-shadow: 0 2px 4px rgba(74, 20, 140, 0.2);\n}\n\n.myio-modal-scope .daterangepicker .drp-buttons .applyBtn:hover {\n background: linear-gradient(135deg, var(--myio-brand-600) 0%, var(--myio-brand-700) 100%);\n box-shadow: 0 4px 8px rgba(74, 20, 140, 0.3);\n transform: translateY(-1px);\n}\n\n.myio-modal-scope .daterangepicker .drp-buttons .applyBtn:active {\n transform: translateY(0);\n box-shadow: 0 2px 4px rgba(74, 20, 140, 0.2);\n}\n\n/* Cancelar button - MyIO Secondary */\n.myio-modal-scope .daterangepicker .drp-buttons .cancelBtn {\n background: var(--myio-card);\n color: var(--myio-text);\n border: 1px solid var(--myio-border);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n}\n\n.myio-modal-scope .daterangepicker .drp-buttons .cancelBtn:hover {\n background: var(--myio-bg);\n border-color: var(--myio-brand-700);\n color: var(--myio-brand-700);\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);\n transform: translateY(-1px);\n}\n\n.myio-modal-scope .daterangepicker .drp-buttons .cancelBtn:active {\n transform: translateY(0);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n}\n\n/* Ranges (preset buttons) styling */\n.myio-modal-scope .daterangepicker .ranges {\n background: var(--myio-bg);\n border-right: 1px solid var(--myio-border);\n}\n\n.myio-modal-scope .daterangepicker .ranges li {\n padding: 10px 16px;\n margin: 2px 8px;\n border-radius: var(--myio-radius-sm);\n cursor: pointer;\n transition: all var(--myio-transition);\n font-size: 13px;\n color: var(--myio-text);\n}\n\n.myio-modal-scope .daterangepicker .ranges li:hover {\n background: rgba(74, 20, 140, 0.1);\n color: var(--myio-brand-700);\n}\n\n.myio-modal-scope .daterangepicker .ranges li.active {\n background: var(--myio-brand-700);\n color: white;\n font-weight: 500;\n}\n\n/* Time picker styling */\n.myio-modal-scope .daterangepicker .calendar-time {\n border-top: 1px solid var(--myio-border);\n background: var(--myio-bg);\n padding: 8px;\n}\n\n.myio-modal-scope .daterangepicker .calendar-time select {\n border: 1px solid var(--myio-border);\n border-radius: var(--myio-radius-sm);\n padding: 4px 8px;\n font-family: var(--myio-font);\n background: var(--myio-card);\n color: var(--myio-text);\n}\n\n.myio-modal-scope .daterangepicker .calendar-time select:focus {\n outline: none;\n border-color: var(--myio-brand-700);\n box-shadow: 0 0 0 2px rgba(74, 20, 140, 0.2);\n}\n\n/* Header styling */\n.myio-modal-scope .daterangepicker .drp-calendar {\n background: var(--myio-card);\n}\n\n.myio-modal-scope .daterangepicker .month {\n color: var(--myio-brand-700);\n font-weight: 600;\n font-size: 14px;\n}\n\n.myio-modal-scope .daterangepicker .prev,\n.myio-modal-scope .daterangepicker .next {\n color: var(--myio-brand-700);\n border: 1px solid var(--myio-border);\n border-radius: var(--myio-radius-sm);\n background: var(--myio-card);\n transition: all var(--myio-transition);\n}\n\n.myio-modal-scope .daterangepicker .prev:hover,\n.myio-modal-scope .daterangepicker .next:hover {\n background: var(--myio-brand-700);\n color: white;\n border-color: var(--myio-brand-700);\n}\n`;var ModalPremiumShell=class{constructor(options){this.options=options;this.originalActiveElement=document.activeElement;this.originalBodyOverflow=document.body.style.overflow;this.injectStyles();this.createElements();this.setupEventListeners();this.setupFocusTrap();this.lockBodyScroll();this.setInertBackground()}backdrop;modal;header;body;footer;closeButton;styleElement;focusTrap;originalBodyOverflow;originalActiveElement;closeHandlers=[];injectStyles(){this.styleElement=document.createElement("style");this.styleElement.textContent=CSS_TOKENS+MODAL_STYLES;document.head.appendChild(this.styleElement)}createElements(){this.backdrop=document.createElement("div");this.backdrop.className="myio-modal-backdrop myio-modal-scope";this.backdrop.setAttribute("role","dialog");this.backdrop.setAttribute("aria-modal","true");this.backdrop.setAttribute("aria-labelledby","myio-modal-title");if(this.options.theme==="dark"){this.backdrop.setAttribute("data-theme","dark")}this.modal=document.createElement("div");this.modal.className="myio-modal";if(this.options.width){const width=typeof this.options.width==="number"?`${this.options.width}px`:this.options.width;this.modal.style.width=width}if(this.options.height){const height=typeof this.options.height==="number"?`${this.options.height}px`:this.options.height;this.modal.style.height=height}this.header=document.createElement("div");this.header.className="myio-modal-header";const title=document.createElement("h2");title.id="myio-modal-title";title.className="myio-modal-title";title.textContent=this.options.title;this.closeButton=document.createElement("button");this.closeButton.className="myio-modal-close";this.closeButton.innerHTML="×";this.closeButton.setAttribute("aria-label","Fechar modal");this.closeButton.type="button";this.header.appendChild(title);this.header.appendChild(this.closeButton);this.body=document.createElement("div");this.body.className="myio-modal-body";this.footer=document.createElement("div");this.footer.className="myio-modal-footer";this.footer.style.display="none";this.modal.appendChild(this.header);this.modal.appendChild(this.body);this.modal.appendChild(this.footer);this.backdrop.appendChild(this.modal)}setupEventListeners(){this.closeButton.addEventListener("click",()=>this.close());if(this.options.closeOnBackdrop!==false){this.backdrop.addEventListener("click",e=>{if(e.target===this.backdrop){this.close()}})}if(this.options.closeOnEscape!==false){document.addEventListener("keydown",this.handleKeyDown)}}handleKeyDown=e=>{if(e.key==="Escape"){this.close()}};setupFocusTrap(){this.focusTrap=new FocusTrap(this.modal)}lockBodyScroll(){document.body.style.overflow="hidden"}unlockBodyScroll(){document.body.style.overflow=this.originalBodyOverflow}setInertBackground(){const topLevelElements=Array.from(document.body.children);topLevelElements.forEach(element=>{if(element!==this.backdrop&&element.tagName!=="SCRIPT"&&element.tagName!=="STYLE"){element.inert=true}})}removeInertBackground(){const topLevelElements=Array.from(document.body.children);topLevelElements.forEach(element=>{element.inert=false})}show(){document.body.appendChild(this.backdrop);requestAnimationFrame(()=>{this.modal.classList.add("myio-modal-open");this.focusTrap.activate()});return{element:this.body,close:()=>this.close(),setContent:content=>this.setContent(content),setFooter:footer=>this.setFooter(footer),on:(event,handler)=>this.on(event,handler)}}setContent(content){if(typeof content==="string"){this.body.innerHTML=content}else{this.body.innerHTML="";this.body.appendChild(content)}}setFooter(footer){if(typeof footer==="string"){this.footer.innerHTML=footer}else{this.footer.innerHTML="";this.footer.appendChild(footer)}this.footer.style.display="flex"}on(event,handler){if(event==="close"){this.closeHandlers.push(handler)}}close(){this.modal.classList.remove("myio-modal-open");setTimeout(()=>{this.cleanup();this.closeHandlers.forEach(handler=>handler())},200)}cleanup(){document.removeEventListener("keydown",this.handleKeyDown);if(this.originalActiveElement&&"focus"in this.originalActiveElement){this.originalActiveElement.focus()}this.focusTrap.deactivate();this.unlockBodyScroll();this.removeInertBackground();if(this.backdrop.parentNode){this.backdrop.parentNode.removeChild(this.backdrop)}if(this.styleElement.parentNode){this.styleElement.parentNode.removeChild(this.styleElement)}}};var FocusTrap=class{constructor(container){this.container=container}focusableElements=[];firstFocusable=null;lastFocusable=null;activate(){this.updateFocusableElements();this.container.addEventListener("keydown",this.handleTabKey);if(this.firstFocusable){this.firstFocusable.focus()}}deactivate(){this.container.removeEventListener("keydown",this.handleTabKey)}updateFocusableElements(){const focusableSelectors=["button:not([disabled])","input:not([disabled])","select:not([disabled])","textarea:not([disabled])","a[href]",'[tabindex]:not([tabindex="-1"])'].join(", ");this.focusableElements=Array.from(this.container.querySelectorAll(focusableSelectors));this.firstFocusable=this.focusableElements[0]||null;this.lastFocusable=this.focusableElements[this.focusableElements.length-1]||null}handleTabKey=e=>{if(e.key!=="Tab")return;if(e.shiftKey){if(document.activeElement===this.firstFocusable){e.preventDefault();this.lastFocusable?.focus()}}else{if(document.activeElement===this.lastFocusable){e.preventDefault();this.firstFocusable?.focus()}}}};function createModal(options){const shell=new ModalPremiumShell(options);return shell.show()}var AuthClient=class{constructor(cfg){this.cfg=cfg}token;async getBearer(){const now=Date.now()/1e3;if(this.token&&this.token.exp-now>60)return this.token.value;this.token={value:"",exp:now+300};return this.token.value}clearCache(){this.token=void 0}};function validateOptions(options){const mode=options.mode||"single";if(mode==="single"){if(!options.tbJwtToken){throw new Error("tbJwtToken is required for ThingsBoard API access in single mode")}if(!options.deviceId){throw new Error("deviceId is required for single mode")}}else if(mode==="comparison"){if(!options.dataSources||options.dataSources.length===0){throw new Error("dataSources is required for comparison mode")}if(!options.granularity){throw new Error("granularity is required for comparison mode")}}if(!options.startDate||!options.endDate){throw new Error("startDate and endDate are required")}if(mode==="single"){const hasIngestionToken=!!options.ingestionToken;const hasClientCredentials=!!(options.clientId&&options.clientSecret);if(!hasIngestionToken&&!hasClientCredentials){throw new Error("Either ingestionToken or clientId/clientSecret must be provided")}}else if(mode==="comparison"){if(!options.clientId||!options.clientSecret){throw new Error("clientId and clientSecret are required for comparison mode")}}}function normalizeToSaoPauloISO(dateLike,endOfDay=false){let date;if(typeof dateLike==="string"){if(/^\d{4}-\d{2}-\d{2}$/.test(dateLike)){date=new Date(dateLike+"T00:00:00-03:00")}else{date=new Date(dateLike)}}else{date=new Date(dateLike)}const saoPauloOffset=-3*60;const localOffset=date.getTimezoneOffset();const offsetDiff=saoPauloOffset-localOffset;if(offsetDiff!==0){date.setMinutes(date.getMinutes()+offsetDiff)}if(endOfDay){date.setHours(23,59,59,999)}else{date.setHours(0,0,0,0)}return date.toISOString().replace("Z","-03:00")}function resolveDeviceAttributes(attributes){return{ingestionId:attributes.ingestionId||attributes.INGESTION_ID,centralId:attributes.centralId||attributes.CENTRAL_ID,slaveId:attributes.slaveId||attributes.SLAVE_ID,customerId:attributes.customerId||attributes.CUSTOMER_ID,floor:attributes.floor||attributes.FLOOR,storeNumber:attributes.NumLoja||attributes.storeNumber}}function mapHttpError(status,body=""){switch(status){case 400:return{code:"VALIDATION_ERROR",message:"Invalid request parameters",userAction:"FIX_INPUT"};case 401:return{code:"TOKEN_EXPIRED",message:"Authentication token has expired",userAction:"RE_AUTH"};case 403:return{code:"AUTH_ERROR",message:"Insufficient permissions",userAction:"RE_AUTH"};case 404:return{code:"NETWORK_ERROR",message:"Device not found",userAction:"CONTACT_ADMIN"};case 409:return{code:"VALIDATION_ERROR",message:"Concurrent modification detected",userAction:"RETRY"};case 422:return{code:"VALIDATION_ERROR",message:"Server-side validation failed",userAction:"FIX_INPUT"};default:if(status>=500){return{code:"NETWORK_ERROR",message:"Server error occurred",userAction:"RETRY"}}return{code:"NETWORK_ERROR",message:"Network error occurred",userAction:"RETRY"}}}function generateDateRange(startISO,endISO,granularity){const dates=[];const start=new Date(startISO);const end=new Date(endISO);while(start<=end){dates.push(start.toISOString());switch(granularity){case"15m":start.setMinutes(start.getMinutes()+15);break;case"1h":start.setHours(start.getHours()+1);break;case"1d":default:start.setDate(start.getDate()+1);break}}return dates}function formatNumber(value){return new Intl.NumberFormat("pt-BR",{minimumFractionDigits:2,maximumFractionDigits:2}).format(value)}function formatDate(dateStr){const date=new Date(dateStr);return date.toLocaleDateString("pt-BR")}function validateJwtToken(token){if(!token||typeof token!=="string"){return false}const parts=token.split(".");if(parts.length!==3){return false}const base64Regex=/^[A-Za-z0-9_-]+$/;return parts.every(part=>base64Regex.test(part))}function createSafeErrorMessage(error){if(error instanceof Error){return error.message.replace(/Bearer\s+[^\s]+/gi,"Bearer [REDACTED]")}return"An unknown error occurred"}function createModalId(){return`myio-energy-modal-${Date.now()}-${Math.random().toString(36).substr(2,9)}`}var EnergyDataFetcher=class{config;authClient=null;constructor(config){this.config={dataApiHost:config.dataApiHost||"https://api.data.apps.myio-bas.com",...config};if(config.clientId&&config.clientSecret){this.authClient=new AuthClient({clientId:config.clientId,clientSecret:config.clientSecret,base:this.config.dataApiHost})}}async fetchEnergyData(params){try{const token=await this.getAuthToken();const url=this.buildEnergyApiUrl(params);console.log("[EnergyDataFetcher] Fetching energy data:",{url:url.replace(/Bearer\s+[^\s&]+/gi,"Bearer [REDACTED]"),ingestionId:params.ingestionId,granularity:params.granularity});const response=await fetch(url,{method:"GET",headers:{Authorization:`Bearer ${token}`,"Content-Type":"application/json"}});if(!response.ok){const errorText=await response.text().catch(()=>"");const error=mapHttpError(response.status,errorText);console.error("[EnergyDataFetcher] API request failed:",{status:response.status,statusText:response.statusText,error:error});throw new Error(`Energy data fetch failed: ${error.message}`)}const apiResponse=await response.json();console.log("[EnergyDataFetcher] API response received:",{hasData:!!apiResponse.data,dataLength:Array.isArray(apiResponse.data)?apiResponse.data.length:0});return this.processEnergyResponse(apiResponse,params)}catch(error){console.error("[EnergyDataFetcher] Error fetching energy data:",error);if(error instanceof Error&&"code"in error){throw error}throw new Error(createSafeErrorMessage(error))}}async getAuthToken(){if(this.config.ingestionToken){return this.config.ingestionToken}if(this.authClient){try{return await this.authClient.getBearer()}catch(error){console.error("[EnergyDataFetcher] AuthClient failed:",error);throw new Error("Failed to obtain authentication token via client credentials")}}throw new Error("No authentication method available. Provide either ingestionToken or clientId/clientSecret.")}buildEnergyApiUrl(params){const baseUrl=this.config.dataApiHost;const endpoint=`/api/v1/telemetry/devices/${params.ingestionId}/energy`;const queryParams=new URLSearchParams({startTime:params.startISO,endTime:params.endISO,granularity:params.granularity,page:"1",pageSize:"1000",deep:"0"});return`${baseUrl}${endpoint}?${queryParams.toString()}`}processEnergyResponse(apiResponse,params){const dataArray=Array.isArray(apiResponse)?apiResponse:apiResponse.data||[];if(!Array.isArray(dataArray)||dataArray.length===0){console.warn("[EnergyDataFetcher] Empty or invalid API response, creating zero-filled data");return this.createEmptyEnergyData(params)}const deviceData=dataArray[0];const consumption=deviceData.consumption||[];console.log("[EnergyDataFetcher] Processing device data:",{deviceId:deviceData.deviceId||params.ingestionId,consumptionPoints:consumption.length});const consumptionPoints=consumption.map(item=>({timestamp:item.timestamp,value:Number(item.value)||0})).filter(item=>item.timestamp).sort((a,b)=>new Date(a.timestamp).getTime()-new Date(b.timestamp).getTime());if(consumptionPoints.length===0){console.warn("[EnergyDataFetcher] No valid consumption points found, creating zero-filled data");return this.createEmptyEnergyData(params)}return{deviceId:deviceData.deviceId||params.ingestionId,consumption:consumptionPoints,granularity:params.granularity,dateRange:{start:params.startISO,end:params.endISO}}}createEmptyEnergyData(params){const dateRange=generateDateRange(params.startISO,params.endISO,params.granularity);return{deviceId:params.ingestionId,consumption:dateRange.map(date=>({timestamp:date,value:0})),granularity:params.granularity,dateRange:{start:params.startISO,end:params.endISO}}}clearCache(){if(this.authClient){this.authClient.clearCache()}}getConfig(){return{dataApiHost:this.config.dataApiHost,ingestionToken:this.config.ingestionToken?"[REDACTED]":void 0,clientId:this.config.clientId||void 0,clientSecret:this.config.clientSecret?"[REDACTED]":void 0}}};var toCsv=(rows,locale="pt-BR",sep=";")=>{const fmt=new Intl.NumberFormat(locale,{minimumFractionDigits:2,maximumFractionDigits:2});const esc=v=>(typeof v==="number"?fmt.format(v):String(v)).replace(/"/g,'""');return rows.map(r=>r.map(c=>`"${esc(c)}"`).join(sep)).join("\r\n")};var CDN_RESOURCES=[{id:"jquery-3.7.1",src:"https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js",integrity:"sha384-1H217gwSVyLSIfaLxHbE7dRb3v4mYCKbpQvzx0cegeju1MVsGrX5xXxAvs/HgeFs",crossorigin:"anonymous"},{id:"moment-2.29.4",src:"https://cdn.jsdelivr.net/npm/moment@2.29.4/min/moment.min.js",integrity:"sha384-2xoILS8hBHw+Atyv/qJLEdk8dFdW1hbGjfeQ3G0GU3pGNPlqck0chRqjMTZ5blGf",crossorigin:"anonymous"},{id:"daterangepicker-3.1.0",src:"https://cdn.jsdelivr.net/npm/daterangepicker@3.1.0/daterangepicker.min.js",integrity:"sha384-IbJFThFkdkMvvxP0U8wOffxBHPYEJE65UtA/l25/jJQUt/hft6OdAuKLxGjtOVnL",crossorigin:"anonymous"}];var CSS_RESOURCE={href:"https://cdn.jsdelivr.net/npm/daterangepicker@3.1.0/daterangepicker.css",integrity:"sha384-zLkQsiLfAQqGeIJeKLC+rcCR1YoYaQFLCL7cLDUoKE1ajKJzySpjzWGfYS2vjSG+"};var FALLBACK_PATHS=["/assets/vendor/jquery.min.js","/assets/vendor/moment.min.js","/assets/vendor/daterangepicker.min.js"];function getLocaleConfig(includeTime=false){return{format:includeTime?"DD/MM/YY HH:mm":"DD/MM/YYYY",separator:" até ",applyLabel:"Aplicar",cancelLabel:"Cancelar",fromLabel:"De",toLabel:"Até",customRangeLabel:"Personalizado",daysOfWeek:["Do","Se","Te","Qa","Qi","Se","Sa"],monthNames:["Jan","Fev","Mar","Abr","Mai","Jun","Jul","Ago","Set","Out","Nov","Dez"],firstDay:1}}var CDNLoader=class{static jQueryInstance=null;static loadingPromise=null;static loaded=false;static async ensureLoaded(){if(this.loaded&&this.jQueryInstance){return this.jQueryInstance}if(this.loadingPromise){return this.loadingPromise}this.loadingPromise=this.loadResources();return this.loadingPromise}static async loadResources(){if(window.jQuery&&window.moment&&window.jQuery.fn.daterangepicker){this.jQueryInstance=window.jQuery.noConflict(true);this.loaded=true;return this.jQueryInstance}try{await this.loadFromCDN();console.log("DateRangePicker: CDN loaded successfully")}catch(cdnError){console.warn("DateRangePicker: CDN failed, trying local assets:",cdnError);try{await this.loadFromLocal();console.log("DateRangePicker: Local assets loaded successfully")}catch(localError){console.warn("DateRangePicker: Local assets failed, using native inputs:",localError);throw new Error("DateRangePicker unavailable - using native inputs")}}this.jQueryInstance=window.jQuery?.noConflict(true);this.loaded=true;if(!this.jQueryInstance){throw new Error("jQuery not available after loading")}return this.jQueryInstance}static async loadFromCDN(){await this.loadCSS(CSS_RESOURCE.href,CSS_RESOURCE.integrity);for(const resource of CDN_RESOURCES){await this.loadScript(resource.src,resource.integrity)}}static async loadFromLocal(){for(const path of FALLBACK_PATHS){await this.loadScript(path)}}static loadScript(src,integrity){return new Promise((resolve,reject)=>{if(document.querySelector(`script[src="${src}"]`)){resolve();return}const script=document.createElement("script");script.src=src;script.async=false;if(integrity){script.integrity=integrity;script.crossOrigin="anonymous"}script.onload=()=>resolve();script.onerror=()=>reject(new Error(`Failed to load script: ${src}`));document.head.appendChild(script)})}static loadCSS(href,integrity){return new Promise((resolve,reject)=>{if(document.querySelector(`link[href="${href}"]`)){resolve();return}const link=document.createElement("link");link.rel="stylesheet";link.href=href;if(integrity){link.integrity=integrity;link.crossOrigin="anonymous"}link.onload=()=>resolve();link.onerror=()=>reject(new Error(`Failed to load CSS: ${href}`));document.head.appendChild(link)})}};function injectPremiumStyling(){const styleId="myio-daterangepicker-styles";if(document.getElementById(styleId)){return}const style=document.createElement("style");style.id=styleId;style.textContent=DATERANGEPICKER_STYLES;document.head.appendChild(style)}async function attach(input,opts={}){try{const $2=await CDNLoader.ensureLoaded();return createDateRangePicker($2,input,opts)}catch(error){console.error("DateRangePicker: Failed to load dependencies. Native date inputs are forbidden in this project.",error);throw new Error("DateRangePicker dependencies unavailable. Please ensure jQuery, moment.js, and daterangepicker are accessible.")}}function createDateRangePicker($2,input,opts){const $input=$2(input);const maxRangeDays=opts.maxRangeDays??31;injectPremiumStyling();input.readOnly=true;input.setAttribute("aria-label","Período de datas");const helpText=document.createElement("div");helpText.className="myio-text-muted";helpText.style.fontSize="12px";helpText.style.marginTop="4px";helpText.style.display="flex";helpText.style.alignItems="center";input.parentNode?.appendChild(helpText);const includeTime=opts.includeTime===true;const timePrecision=opts.timePrecision||"minute";const localeConfig=getLocaleConfig(includeTime);const moment=window.moment;let startDate,endDate;if(includeTime){startDate=opts.presetStart?moment(opts.presetStart):moment().startOf("day");endDate=opts.presetEnd?moment(opts.presetEnd):moment()}else{startDate=opts.presetStart?moment(opts.presetStart).startOf("day"):moment().startOf("month");endDate=opts.presetEnd?moment(opts.presetEnd).endOf("day"):moment().endOf("day")}let ranges;if(includeTime){const now=moment();ranges={"Última hora":[moment().subtract(1,"hours"),now.clone()],"Últimas 6 horas":[moment().subtract(6,"hours"),now.clone()],"Últimas 12 horas":[moment().subtract(12,"hours"),now.clone()],"Últimas 24 horas":[moment().subtract(24,"hours"),now.clone()],Hoje:[moment().startOf("day"),now.clone()],Ontem:[moment().subtract(1,"day").startOf("day"),moment().subtract(1,"day").endOf("day")],"Últimos 7 dias":[moment().subtract(6,"days").startOf("day"),now.clone()],"Este mês":[moment().startOf("month"),now.clone()]}}else{ranges={Hoje:[moment().startOf("day"),moment().endOf("day")],"Últimos 7 dias":[moment().subtract(6,"days").startOf("day"),moment().endOf("day")],"Últimos 30 dias":[moment().subtract(29,"days").startOf("day"),moment().endOf("day")],"Mês Anterior":[moment().subtract(1,"month").startOf("month"),moment().subtract(1,"month").endOf("month")]}}$input.daterangepicker({parentEl:opts.parentEl||document.body,timePicker:includeTime,timePicker24Hour:true,timePickerIncrement:timePrecision==="hour"?60:1,autoApply:true,autoUpdateInput:true,linkedCalendars:true,showCustomRangeLabel:true,maxSpan:{days:maxRangeDays},maxDate:moment().endOf("day"),startDate:startDate,endDate:endDate,opens:"right",drops:"down",locale:localeConfig,applyButtonClasses:"btn btn-primary",cancelClass:"btn btn-muted",ranges:ranges});updateInputDisplay();$input.on("apply.daterangepicker.myio",()=>{updateInputDisplay();if(opts.onApply){const result=getDates();opts.onApply(result)}});$input.on("cancel.daterangepicker.myio",()=>{$input.val("");if(opts.onApply){opts.onApply({startISO:"",endISO:"",startLabel:"",endLabel:""})}});function updateInputDisplay(){const picker=$input.data("daterangepicker");if(picker){const formatted=`${picker.startDate.format(localeConfig.format)}${localeConfig.separator}${picker.endDate.format(localeConfig.format)}`;$input.val(formatted)}}function getDates(){const picker=$input.data("daterangepicker");const startISO=picker.startDate.format("YYYY-MM-DD[T]HH:mm:ssZ");const endISO=picker.endDate.format("YYYY-MM-DD[T]HH:mm:ssZ");const startLabel=picker.startDate.format(localeConfig.format);const endLabel=picker.endDate.format(localeConfig.format);return{startISO:startISO,endISO:endISO,startLabel:startLabel,endLabel:endLabel}}function setDates(startISO,endISO){const picker=$input.data("daterangepicker");picker.setStartDate(moment(startISO));picker.setEndDate(moment(endISO));updateInputDisplay()}function destroy(){const picker=$input.data("daterangepicker");$input.off(".daterangepicker.myio");picker?.remove?.();$input.removeData("daterangepicker");helpText?.remove()}return{getDates:getDates,setDates:setDates,destroy:destroy}}var DateRangePickerJQ={attach:attach};function detectTelemetryType(keys){if(!keys){return TELEMETRY_TYPES.total_power}const keyStr=Array.isArray(keys)?keys.map(k=>k.trim()).join(","):keys.trim();for(const type of Object.values(TELEMETRY_TYPES)){const typeKeys=Array.isArray(type.keys)?type.keys.map(k=>k.trim()).join(","):type.keys.trim();if(typeKeys===keyStr){return type}}return TELEMETRY_TYPES.total_power}var telemetryCache=new Map;var DEFAULT_CACHE_TTL=5*60*1e3;var MAX_CACHE_SIZE=50;function getCacheKey(params){const keysStr=Array.isArray(params.keys)?params.keys.join(","):params.keys;const agg=params.agg||"MAX";const interval=params.interval||864e5;return`${params.deviceId}:${params.startDate}:${params.endDate}:${keysStr}:${agg}:${interval}`}function getCachedData(cacheKey){const entry=telemetryCache.get(cacheKey);if(!entry){return null}const now=Date.now();if(now-entry.timestamp>entry.ttl){telemetryCache.delete(cacheKey);return null}return entry.data}function setCachedData(cacheKey,data,ttl=DEFAULT_CACHE_TTL){if(telemetryCache.size>=MAX_CACHE_SIZE){const firstKey=telemetryCache.keys().next().value;if(firstKey){telemetryCache.delete(firstKey)}}telemetryCache.set(cacheKey,{data:data,timestamp:Date.now(),ttl:ttl})}var TELEMETRY_TYPES={total_power:{id:"total_power",label:"Potência Total",keys:"consumption",defaultAggregation:"MAX",unit:"kW",color:"#4A148C"},power_phases:{id:"power_phases",label:"Potência A, B, C",keys:["a","b","c"],defaultAggregation:"MAX",unit:"kW",color:["#FF5722","#4CAF50","#2196F3"]},current_phases:{id:"current_phases",label:"Corrente A, B, C",keys:["current_a","current_b","current_c"],defaultAggregation:"AVG",unit:"A",color:["#FF5722","#4CAF50","#2196F3"]},voltage_phases:{id:"voltage_phases",label:"Tensão A, B, C",keys:["voltage_a","voltage_b","voltage_c"],defaultAggregation:"AVG",unit:"V",color:["#FF5722","#4CAF50","#2196F3"]}};var DEFAULT_STYLES={primaryColor:"#4A148C",accentColor:"#EDE7F3",dangerColor:"#f44336",infoColor:"#2196F3",textPrimary:"#212121",textSecondary:"#757575",backgroundColor:"#ffffff",overlayColor:"rgba(0, 0, 0, 0.5)",borderRadius:"8px",buttonRadius:"6px",pillRadius:"20px",zIndex:1e4,spacingXs:"4px",spacingSm:"8px",spacingMd:"16px",spacingLg:"24px",spacingXl:"32px",fontFamily:'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',fontSizeXs:"12px",fontSizeSm:"14px",fontSizeMd:"16px",fontSizeLg:"18px",fontSizeXl:"20px",fontWeight:"400",fontWeightBold:"600"};var CHART_JS_CDN="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.js";var ZOOM_PLUGIN_CDN="https://cdn.jsdelivr.net/npm/chartjs-plugin-zoom@2.0.1/dist/chartjs-plugin-zoom.min.js";var JSPDF_VERSION="2.5.1";var JSPDF_CDN=`https://cdnjs.cloudflare.com/ajax/libs/jspdf/${JSPDF_VERSION}/jspdf.umd.min.js`;var chartJsLoaded=false;var zoomPluginLoaded=false;var jsPdfLoaded=false;var _jspdfPromise=null;var cssInjected=false;var STRINGS2={"pt-BR":{title:"Demanda",period:"Período",maximum:"Máxima",at:"em",exportPdf:"Exportar PDF",exportCsv:"Exportar CSV",fullscreen:"Tela cheia",close:"Fechar",resetZoom:"Reset Zoom",loading:"Carregando dados...",noData:"Sem pontos de demanda no período selecionado",error:"Erro ao carregar dados",zoomHelp:"Scroll: zoom | Arraste: mover | Ctrl+Arraste: selecionar",demand:"Demanda (kW)",reportTitle:"Relatório de Demanda",reportFooter:"MyIO Energy Management System",startDate:"Data Inicial",endDate:"Data Final",updatePeriod:"Atualizar",invalidDateRange:"Data final deve ser maior que data inicial",maxRangeExceeded:"Período máximo de 30 dias",telemetryType:"Tipo de Telemetria"},"en-US":{title:"Demand",period:"Period",maximum:"Maximum",at:"on",exportPdf:"Export PDF",exportCsv:"Export CSV",fullscreen:"Fullscreen",close:"Close",resetZoom:"Reset Zoom",loading:"Loading data...",noData:"No demand points in the selected period",error:"Error loading data",zoomHelp:"Scroll: zoom | Drag: pan | Ctrl+Drag: select",demand:"Demand (kW)",reportTitle:"Demand Report",reportFooter:"MyIO Energy Management System",startDate:"Start Date",endDate:"End Date",updatePeriod:"Update",invalidDateRange:"End date must be greater than start date",maxRangeExceeded:"Maximum range of 30 days",telemetryType:"Telemetry Type"}};async function loadScript(url,checkGlobal){return new Promise((resolve,reject)=>{if(window[checkGlobal]){resolve();return}const existingScript=document.querySelector(`script[src="${url}"]`);if(existingScript){existingScript.addEventListener("load",()=>{if(window[checkGlobal]){resolve()}else{reject(new Error(`Library ${checkGlobal} not available after loading ${url}`))}});existingScript.addEventListener("error",()=>reject(new Error(`Failed to load ${url}`)));return}const script=document.createElement("script");script.src=url;script.onload=()=>{if(window[checkGlobal]){resolve()}else{reject(new Error(`Library ${checkGlobal} not available after loading ${url}`))}};script.onerror=()=>reject(new Error(`Failed to load ${url}`));document.head.appendChild(script)})}async function loadExternalLibraries(){try{if(!chartJsLoaded){await loadScript(CHART_JS_CDN,"Chart");chartJsLoaded=true}if(!zoomPluginLoaded){await loadScript(ZOOM_PLUGIN_CDN,"ChartZoom");zoomPluginLoaded=true}if(!jsPdfLoaded){await ensureJsPDF();jsPdfLoaded=true}}catch(error){throw new Error(`Failed to load external libraries: ${error}`)}}function ensureJsPDF(){if(window.jspdf?.jsPDF){console.info("jsPDF already loaded.");return Promise.resolve()}if(_jspdfPromise){console.info("jsPDF loading already in progress.");return _jspdfPromise}_jspdfPromise=new Promise((resolve,reject)=>{const existing=document.querySelector('script[data-lib="jspdf"]');if(existing){existing.addEventListener("load",()=>{if(window.jspdf?.jsPDF){console.info("jsPDF loaded via existing script.");resolve()}else{reject(new Error("jsPDF loaded but window.jspdf.jsPDF missing"))}});existing.addEventListener("error",()=>reject(new Error("Failed to load jsPDF via existing script")));return}const s=document.createElement("script");s.src=JSPDF_CDN;s.async=true;s.defer=true;s.dataset.lib="jspdf";s.onload=()=>{if(window.jspdf?.jsPDF){console.info("jsPDF loaded successfully.");resolve()}else{reject(new Error("jsPDF loaded but window.jspdf.jsPDF missing"))}};s.onerror=()=>reject(new Error("Failed to load jsPDF from CDN"));document.head.appendChild(s)}).finally(()=>{_jspdfPromise=null});return _jspdfPromise}function getJsPDFCtor(){if(window.jspdf?.jsPDF)return window.jspdf.jsPDF;if(window.jsPDF?.jsPDF)return window.jsPDF.jsPDF;if(window.jsPDF)return window.jsPDF;throw new Error("jsPDF constructor not found on window")}function savePdfSafe(doc,filename){try{doc.save(filename)}catch(e){console.warn("doc.save() failed, attempting Blob URL fallback:",e);const blob=doc.output("blob");const url=URL.createObjectURL(blob);window.open(url,"_blank")||alert("Pop-up blocked. Allow pop-ups to download the PDF.");setTimeout(()=>URL.revokeObjectURL(url),3e4)}}function addCanvasToPdf(doc,canvas,x=10,y=20,maxWmm=190){const img=canvas.toDataURL("image/png",1);const pageWmm=doc.internal.pageSize.getWidth();const mmW=Math.min(maxWmm,pageWmm-x*2);const mmH=canvas.height/canvas.width*mmW;doc.addImage(img,"PNG",x,y,mmW,mmH,void 0,"FAST");return y+mmH}function ensureRoom(doc,nextY,minRoom=40){const h=doc.internal.pageSize.getHeight();if(nextY+minRoom>h){doc.addPage();return 20}return nextY}function injectCSS(styles){if(cssInjected)return;const css=`\n .myio-demand-modal-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: ${styles.overlayColor};\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: ${styles.zIndex};\n font-family: ${styles.fontFamily};\n font-size: ${styles.fontSizeMd};\n color: ${styles.textPrimary};\n }\n\n .myio-demand-modal-card {\n background: ${styles.backgroundColor};\n border-radius: ${styles.borderRadius};\n box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);\n max-width: 90vw;\n max-height: 90vh;\n width: 1040px;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n }\n\n .myio-demand-modal-card.fullscreen {\n width: 100vw;\n height: 100vh;\n max-width: 100vw;\n max-height: 100vh;\n border-radius: 0;\n }\n\n .myio-demand-modal-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: ${styles.spacingMd};\n background: #4A148C;\n color: white;\n }\n\n .myio-demand-modal-title-section {\n display: flex;\n align-items: center;\n gap: ${styles.spacingSm};\n }\n\n .myio-demand-modal-icon {\n font-size: ${styles.fontSizeLg};\n }\n\n .myio-demand-modal-title {\n margin: 0;\n font-size: ${styles.fontSizeLg};\n font-weight: ${styles.fontWeightBold};\n }\n\n .myio-demand-modal-actions {\n display: flex;\n gap: ${styles.spacingSm};\n }\n\n .myio-demand-modal-btn {\n background: rgba(255, 255, 255, 0.2);\n border: 1px solid rgba(255, 255, 255, 0.3);\n color: white;\n padding: ${styles.spacingSm} ${styles.spacingMd};\n border-radius: ${styles.buttonRadius};\n cursor: pointer;\n font-size: ${styles.fontSizeSm};\n font-family: inherit;\n transition: background-color 0.2s;\n }\n\n .myio-demand-modal-btn:hover {\n background: rgba(255, 255, 255, 0.3);\n }\n\n .myio-demand-modal-btn:focus {\n outline: 2px solid white;\n outline-offset: 2px;\n }\n\n .myio-demand-modal-btn-close {\n font-size: ${styles.fontSizeLg};\n padding: ${styles.spacingSm};\n min-width: 32px;\n background: ${styles.dangerColor} !important;\n }\n\n .myio-demand-modal-btn-close:hover {\n background: #d32f2f !important;\n }\n\n .myio-demand-modal-period {\n padding: ${styles.spacingMd} ${styles.spacingLg};\n background: #f5f5f5;\n font-size: ${styles.fontSizeSm};\n color: ${styles.textSecondary};\n border-bottom: 1px solid #e0e0e0;\n }\n\n .myio-demand-modal-period-selector {\n display: flex;\n align-items: center;\n gap: ${styles.spacingMd};\n padding: ${styles.spacingMd} ${styles.spacingLg};\n background: #f9f9f9;\n border-bottom: 1px solid #e0e0e0;\n flex-wrap: wrap;\n }\n\n .myio-demand-modal-period-selector label {\n display: flex;\n flex-direction: column;\n gap: ${styles.spacingXs};\n font-size: ${styles.fontSizeSm};\n color: ${styles.textPrimary};\n font-weight: ${styles.fontWeightBold};\n }\n\n .myio-demand-modal-date-input {\n padding: ${styles.spacingSm};\n border: 1px solid #ccc;\n border-radius: ${styles.buttonRadius};\n font-size: ${styles.fontSizeSm};\n font-family: inherit;\n min-width: 150px;\n }\n\n .myio-demand-modal-date-input:focus {\n outline: 2px solid ${styles.primaryColor};\n outline-offset: 1px;\n }\n\n /* RFC-0061: Telemetry selector styles */\n .myio-demand-modal-select {\n padding-right: 32px;\n background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="%23333"><path d="M4 6l4 4 4-4z"/></svg>');\n background-repeat: no-repeat;\n background-position: right 8px center;\n background-size: 16px;\n cursor: pointer;\n appearance: none;\n -webkit-appearance: none;\n -moz-appearance: none;\n }\n\n .myio-demand-modal-select:hover {\n border-color: ${styles.primaryColor};\n background-color: #fafafa;\n }\n\n .myio-demand-modal-select:focus {\n outline: 2px solid ${styles.primaryColor};\n outline-offset: 1px;\n box-shadow: 0 0 0 3px rgba(74, 20, 140, 0.1);\n }\n\n .myio-demand-modal-select:disabled {\n background-color: #f5f5f5;\n cursor: not-allowed;\n opacity: 0.6;\n }\n\n .myio-demand-modal-btn-update {\n background: ${styles.primaryColor};\n border: none;\n color: white;\n padding: ${styles.spacingSm} ${styles.spacingMd};\n border-radius: ${styles.buttonRadius};\n cursor: pointer;\n font-size: ${styles.fontSizeSm};\n font-family: inherit;\n font-weight: ${styles.fontWeightBold};\n align-self: flex-end;\n margin-bottom: 2px;\n }\n\n .myio-demand-modal-btn-update:hover {\n background: #6A1B9A;\n }\n\n .myio-demand-modal-btn-update:disabled {\n background: #ccc;\n cursor: not-allowed;\n }\n\n /* RFC-0082: Real-time button styles */\n .myio-demand-modal-btn-realtime {\n background: transparent;\n border: 2px solid #666;\n color: #666;\n padding: ${styles.spacingSm} ${styles.spacingMd};\n border-radius: ${styles.buttonRadius};\n cursor: pointer;\n font-size: ${styles.fontSizeSm};\n font-family: inherit;\n font-weight: ${styles.fontWeightBold};\n align-self: flex-end;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n transition: all 0.3s ease;\n white-space: nowrap;\n }\n\n .myio-demand-modal-btn-realtime:hover {\n border-color: #999;\n color: #999;\n }\n\n .myio-demand-modal-btn-realtime.active {\n background: linear-gradient(135deg, #d32f2f 0%, #f44336 100%);\n border-color: #d32f2f;\n color: white;\n box-shadow: 0 0 12px rgba(244, 67, 54, 0.5);\n }\n\n .myio-demand-modal-btn-realtime.active:hover {\n background: linear-gradient(135deg, #c62828 0%, #e53935 100%);\n }\n\n .realtime-indicator {\n display: inline-block;\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: #666;\n transition: all 0.3s ease;\n }\n\n .myio-demand-modal-btn-realtime.active .realtime-indicator {\n background: white;\n animation: pulse 1.5s ease-in-out infinite;\n }\n\n @keyframes pulse {\n 0%, 100% { opacity: 1; transform: scale(1); }\n 50% { opacity: 0.6; transform: scale(1.2); }\n }\n\n .myio-demand-modal-btn-realtime.active .realtime-text::before {\n content: "AO VIVO";\n }\n\n .myio-demand-modal-btn-realtime:not(.active) .realtime-text::before {\n content: "REAL TIME";\n }\n\n .realtime-text {\n font-size: 0; /* Hide original text, use ::before pseudo-element */\n }\n\n .myio-demand-modal-period-error {\n flex-basis: 100%;\n color: ${styles.dangerColor};\n font-size: ${styles.fontSizeXs};\n margin-top: -${styles.spacingSm};\n }\n\n .myio-demand-modal-peak {\n margin: ${styles.spacingMd} ${styles.spacingLg} 0;\n padding: ${styles.spacingSm} ${styles.spacingMd};\n background: ${styles.accentColor};\n color: #333;\n border-radius: ${styles.pillRadius};\n font-size: ${styles.fontSizeSm};\n font-weight: ${styles.fontWeightBold};\n display: inline-block;\n width: fit-content;\n }\n\n .myio-demand-modal-content {\n flex: 1;\n padding: ${styles.spacingLg};\n display: flex;\n flex-direction: column;\n min-height: 400px;\n }\n\n .myio-demand-modal-chart-container {\n flex: 1;\n position: relative;\n min-height: 300px;\n }\n\n .myio-demand-modal-chart {\n width: 100% !important;\n height: 100% !important;\n }\n\n .myio-demand-modal-zoom-controls {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-top: ${styles.spacingMd};\n padding-top: ${styles.spacingMd};\n border-top: 1px solid #e0e0e0;\n }\n\n .myio-demand-modal-btn-reset {\n background: ${styles.infoColor};\n border: none;\n color: white;\n padding: ${styles.spacingSm} ${styles.spacingMd};\n border-radius: ${styles.buttonRadius};\n cursor: pointer;\n font-size: ${styles.fontSizeSm};\n font-family: inherit;\n }\n\n .myio-demand-modal-btn-reset:hover {\n background: #1976D2;\n }\n\n .myio-demand-modal-zoom-help {\n font-size: ${styles.fontSizeXs};\n color: ${styles.textSecondary};\n }\n\n .myio-demand-modal-loading,\n .myio-demand-modal-error {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: ${styles.spacingXl};\n text-align: center;\n }\n\n .myio-demand-modal-spinner {\n font-size: 2rem;\n animation: spin 1s linear infinite;\n margin-bottom: ${styles.spacingMd};\n }\n\n @keyframes spin {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n }\n\n .myio-demand-modal-loading-text {\n color: ${styles.textSecondary};\n }\n\n .myio-demand-modal-error-icon {\n font-size: 2rem;\n color: ${styles.dangerColor};\n margin-bottom: ${styles.spacingMd};\n }\n\n .myio-demand-modal-error-text {\n color: ${styles.dangerColor};\n }\n\n /* Responsive design */\n @media (max-width: 768px) {\n .myio-demand-modal-card {\n width: 95vw;\n height: 90vh;\n max-height: 90vh;\n }\n\n .myio-demand-modal-header {\n padding: ${styles.spacingMd};\n }\n\n .myio-demand-modal-title {\n font-size: ${styles.fontSizeMd};\n }\n\n .myio-demand-modal-actions {\n gap: ${styles.spacingXs};\n }\n\n .myio-demand-modal-btn {\n padding: ${styles.spacingXs} ${styles.spacingSm};\n font-size: ${styles.fontSizeXs};\n }\n\n .myio-demand-modal-content {\n padding: ${styles.spacingMd};\n }\n }\n `;const style=document.createElement("style");style.textContent=css;document.head.appendChild(style);cssInjected=true}function formatDate2(date,locale){return date.toLocaleDateString(locale,{day:"2-digit",month:"2-digit",year:"numeric"})}function formatDateTime(date,locale){return date.toLocaleDateString(locale,{day:"2-digit",month:"2-digit",year:"numeric"})}function interpolateTemperatureData(rawPoints){if(rawPoints.length===0)return[];const interpolated=[];const interval=30*60*1e3;const sorted=[...rawPoints].sort((a,b)=>a.x-b.x);const startTime=sorted[0].x;const endTime=sorted[sorted.length-1].x;let lastKnownValue=sorted[0].y;let dataIndex=0;for(let time=startTime;time<=endTime;time+=interval){const actualPoint=sorted.find((p,idx)=>{if(idx>=dataIndex&&Math.abs(p.x-time)<5*60*1e3){dataIndex=idx+1;return true}return false});if(actualPoint){lastKnownValue=actualPoint.y;interpolated.push({x:time,y:lastKnownValue})}else{interpolated.push({x:time,y:lastKnownValue})}}return interpolated}async function fetchTelemetryData(token,deviceId,startDate,endDate,queryParams){const startTs=new Date(startDate).getTime();const endTs=new Date(endDate).getTime();const keys=queryParams?.keys||"consumption";const intervalType=queryParams?.intervalType||"MILLISECONDS";const interval=queryParams?.interval||864e5;const agg=queryParams?.agg||"MAX";const orderBy=queryParams?.orderBy||"ASC";let url=`/api/plugins/telemetry/DEVICE/${deviceId}/values/timeseries?keys=${keys}&startTs=${startTs}&endTs=${endTs}&intervalType=${intervalType}&interval=${interval}&agg=${agg}&orderBy=${orderBy}`;if(agg==="NONE"&&queryParams?.limit){url+=`&limit=${queryParams.limit}`}const response=await fetch(url,{headers:{"X-Authorization":`Bearer ${token}`,"Content-Type":"application/json"}});if(!response.ok){throw new Error(`HTTP ${response.status}: ${response.statusText}`)}const data=await response.json();return data}function processMultiSeriesChartData(rawData,keys,correctionFactor,locale,aggregation,timezoneOffset){const seriesKeys=keys.split(",").map(k=>k.trim());const seriesData=[];let globalPeak=null;let isEmpty=true;const tzOffset=timezoneOffset!==void 0?timezoneOffset:-3;const tzOffsetMs=tzOffset*60*60*1e3;const colors=["#4A148C","#2196F3","#4CAF50","#FF9800","#F44336","#9C27B0","#795548","#607D8B"];seriesKeys.forEach((key,index)=>{const rawSeries=rawData[key]||[];if(rawSeries.length===0){seriesData.push({key:key,label:key,points:[],peak:null,color:colors[index%colors.length]});return}isEmpty=false;const sortedData=rawSeries.sort((a,b)=>a.ts-b.ts);const points=[];const isAggregated=aggregation!=="NONE";if(isAggregated){for(let i=0;i<sortedData.length;i++){const current=sortedData[i];const value=parseFloat(current.value)*correctionFactor;const timestamp=current.ts+tzOffsetMs;points.push({x:timestamp,y:value})}}else{let previousValue=0;let previousTs=0;for(let i=0;i<sortedData.length;i++){const current=sortedData[i];const currentValue=parseFloat(current.value);const currentTs=current.ts;if(i>0){const deltaWh=currentValue-previousValue;const deltaHours=(currentTs-previousTs)/(1e3*60*60);if(deltaWh>0&&deltaHours>0){const demandKw=deltaWh/1e3/deltaHours*correctionFactor;const timestamp=currentTs+tzOffsetMs;points.push({x:timestamp,y:demandKw})}}previousValue=currentValue;previousTs=currentTs}}let seriesPeak=null;if(points.length>0){const maxPoint=points.reduce((max,point)=>point.y>max.y?point:max);seriesPeak={value:maxPoint.y,timestamp:maxPoint.x,formattedValue:maxPoint.y.toFixed(2),formattedTime:formatDateTime(new Date(maxPoint.x),locale),key:key};if(!globalPeak||seriesPeak.value>globalPeak.value){globalPeak=seriesPeak}}seriesData.push({key:key,label:key,points:points,peak:seriesPeak,color:colors[index%colors.length]})});return{series:seriesData,globalPeak:globalPeak,isEmpty:isEmpty}}function createFocusTrap(container){const focusableElements=container.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');const firstElement=focusableElements[0];const lastElement=focusableElements[focusableElements.length-1];function handleTabKey(e){if(e.key!=="Tab")return;if(e.shiftKey){if(document.activeElement===firstElement){lastElement.focus();e.preventDefault()}}else{if(document.activeElement===lastElement){firstElement.focus();e.preventDefault()}}}container.addEventListener("keydown",handleTabKey);firstElement?.focus();return()=>{container.removeEventListener("keydown",handleTabKey)}}async function openDemandModal(params){if(!params.token||!params.deviceId||!params.startDate||!params.endDate){throw new Error("Missing required parameters: token, deviceId, startDate, endDate")}const styles={...DEFAULT_STYLES,...params.styles};const locale=params.locale||"pt-BR";const strings=STRINGS2[locale]||STRINGS2["pt-BR"];await loadExternalLibraries();injectCSS(styles);const container=typeof params.container==="string"?document.querySelector(params.container):params.container||document.body;if(!container){throw new Error("Container element not found")}const overlay=document.createElement("div");overlay.className="myio-demand-modal-overlay";overlay.setAttribute("role","dialog");overlay.setAttribute("aria-modal","true");overlay.setAttribute("aria-labelledby","modal-title");const label=params.label||"Dispositivo";const allowTelemetrySwitch=params.allowTelemetrySwitch!==false;const currentTelemetryType=detectTelemetryType(params.telemetryQuery?.keys);const availableTypes=params.availableTelemetryTypes?Object.values(TELEMETRY_TYPES).filter(type=>params.availableTelemetryTypes.includes(type.id)):Object.values(TELEMETRY_TYPES);const telemetrySelectorOptions=availableTypes.map(type=>`<option value="${type.id}" ${type.id===currentTelemetryType.id?"selected":""}>${type.label}</option>`).join("");const telemetrySelectorHTML=allowTelemetrySwitch?`\n <label>\n ${strings.telemetryType}:\n <select id="telemetry-type-select" class="myio-demand-modal-select myio-demand-modal-date-input" aria-label="${strings.telemetryType}">\n ${telemetrySelectorOptions}\n </select>\n </label>\n `:"";overlay.innerHTML=`\n <div class="myio-demand-modal-card">\n <div class="myio-demand-modal-header">\n <div class="myio-demand-modal-title-section">\n <span class="myio-demand-modal-icon">⚡</span>\n <h2 id="modal-title" class="myio-demand-modal-title">${strings.title} – ${label}</h2>\n </div>\n <div class="myio-demand-modal-actions">\n <button class="myio-demand-modal-btn myio-demand-modal-btn-pdf" type="button" style="display: none;">\n ${strings.exportPdf}\n </button>\n <button class="myio-demand-modal-btn myio-demand-modal-btn-csv" type="button">\n ${strings.exportCsv}\n </button>\n <button class="myio-demand-modal-btn myio-demand-modal-btn-fullscreen" type="button" aria-label="${strings.fullscreen}">\n ⛶\n </button>\n <button class="myio-demand-modal-btn myio-demand-modal-btn-close" type="button" aria-label="${strings.close}">\n ×\n </button>\n </div>\n </div>\n\n <div class="myio-demand-modal-period-selector">\n <label>\n ${strings.startDate}:\n <input type="date" class="myio-demand-modal-date-input myio-demand-modal-date-start" />\n </label>\n <label>\n ${strings.endDate}:\n <input type="date" class="myio-demand-modal-date-input myio-demand-modal-date-end" />\n </label>\n ${telemetrySelectorHTML}\n <label>\n Intervalo:\n <select id="demand-interval-select" class="myio-demand-modal-select myio-demand-modal-date-input" aria-label="Intervalo">\n <option value="86400000">24 horas</option>\n <option value="3600000">1 hora</option>\n <option value="60000">1 minuto (60s)</option>\n </select>\n </label>\n <label>\n Agregador:\n <select id="demand-agg-select" class="myio-demand-modal-select myio-demand-modal-date-input" aria-label="Agregador">\n <option value="MAX">Máximo</option>\n <option value="AVG">Média</option>\n </select>\n </label>\n <button class="myio-demand-modal-btn-update" type="button">\n ${strings.updatePeriod}\n </button>\n \x3c!-- RFC-0084: REAL TIME button removed - use RealTimeTelemetryModal instead --\x3e\n <div class="myio-demand-modal-period-error" style="display: none;"></div>\n </div>\n\n <div class="myio-demand-modal-peak" style="display: none;"></div>\n \n <div class="myio-demand-modal-content">\n <div class="myio-demand-modal-chart-container">\n <canvas class="myio-demand-modal-chart"></canvas>\n </div>\n \n <div class="myio-demand-modal-zoom-controls">\n <button class="myio-demand-modal-btn-reset" type="button">\n ${strings.resetZoom}\n </button>\n <div class="myio-demand-modal-zoom-help">\n <small>${strings.zoomHelp}</small>\n </div>\n </div>\n </div>\n \n <div class="myio-demand-modal-loading">\n <div class="myio-demand-modal-spinner">⧗</div>\n <div class="myio-demand-modal-loading-text">${strings.loading}</div>\n </div>\n \n <div class="myio-demand-modal-error" style="display: none;">\n <div class="myio-demand-modal-error-icon">⚠</div>\n <div class="myio-demand-modal-error-text"></div>\n </div>\n </div>\n `;container.appendChild(overlay);const card=overlay.querySelector(".myio-demand-modal-card");const closeBtn=overlay.querySelector(".myio-demand-modal-btn-close");const fullscreenBtn=overlay.querySelector(".myio-demand-modal-btn-fullscreen");const pdfBtn=overlay.querySelector(".myio-demand-modal-btn-pdf");const csvBtn=overlay.querySelector(".myio-demand-modal-btn-csv");const resetBtn=overlay.querySelector(".myio-demand-modal-btn-reset");const chartCanvas=overlay.querySelector(".myio-demand-modal-chart");const loadingEl=overlay.querySelector(".myio-demand-modal-loading");const errorEl=overlay.querySelector(".myio-demand-modal-error");const errorText=overlay.querySelector(".myio-demand-modal-error-text");const peakEl=overlay.querySelector(".myio-demand-modal-peak");const contentEl=overlay.querySelector(".myio-demand-modal-content");const dateStartInput=overlay.querySelector(".myio-demand-modal-date-start");const dateEndInput=overlay.querySelector(".myio-demand-modal-date-end");const updateBtn=overlay.querySelector(".myio-demand-modal-btn-update");const periodErrorEl=overlay.querySelector(".myio-demand-modal-period-error");const telemetryTypeSelect=overlay.querySelector("#telemetry-type-select");const intervalSelect=overlay.querySelector("#demand-interval-select");const aggSelect=overlay.querySelector("#demand-agg-select");let chart=null;let chartData=null;let isFullscreen=false;let currentStartDate=params.startDate;let currentEndDate=params.endDate;let activeTelemetryType=currentTelemetryType;const originalOverflow=document.body.style.overflow;document.body.style.overflow="hidden";const releaseFocusTrap=createFocusTrap(overlay);function closeModal(){if(chart){chart.destroy()}overlay.remove();document.body.style.overflow=originalOverflow;releaseFocusTrap();params.onClose?.()}function toggleFullscreen(){isFullscreen=!isFullscreen;card.classList.toggle("fullscreen",isFullscreen);if(chart){setTimeout(()=>chart.resize(),100)}}function resetZoom(){if(chart){chart.resetZoom()}}function exportCsv(){if(!chartData){alert("Nenhum dado disponível para exportar");return}const btn=csvBtn;const originalHtml=btn.innerHTML;btn.disabled=true;btn.innerHTML="<span>⏳</span> Gerando CSV...";try{const BOM="\ufeff";let csv=BOM+"Data,Série,Valor (kW)\n";chartData.series.forEach(series=>{series.points.forEach(point=>{const dateStr2=formatDate2(new Date(point.x),locale);const value=point.y.toFixed(2);csv+=`${dateStr2},${series.label},${value}\n`})});const blob=new Blob([csv],{type:"text/csv;charset=utf-8;"});const url=URL.createObjectURL(blob);const link=document.createElement("a");link.href=url;const labelSafe=label.replace(/\s+/g,"_");const dateStr=(new Date).toISOString().slice(0,10);link.download=`demanda_${labelSafe}_${dateStr}.csv`;document.body.appendChild(link);link.click();document.body.removeChild(link);URL.revokeObjectURL(url)}catch(error){console.error("[CSV Export] Error:",error);alert("Erro ao gerar CSV. Por favor, tente novamente.")}finally{btn.disabled=false;btn.innerHTML=originalHtml}}async function exportPdf(){if(!chartData||!chart){alert("Nenhum dado disponível para exportar");return}const btn=pdfBtn;const originalHtml=btn.innerHTML;btn.disabled=true;btn.innerHTML="<span>⏳</span> Gerando PDF...";try{await ensureJsPDF();await(document.fonts?.ready?.catch(e=>console.warn("Font loading interrupted or failed:",e)));const JsPDF=getJsPDFCtor();const doc=new JsPDF("p","mm","a4");doc.setFontSize(20);doc.setTextColor(74,20,140);doc.text(strings.reportTitle,20,20);doc.setFontSize(14);doc.setTextColor(0,0,0);const label2=params.label||"Dispositivo";doc.text(`${strings.title} - ${label2}`,20,35);const startDate=formatDate2(new Date(params.startDate),params.locale||"pt-BR");const endDate=formatDate2(new Date(params.endDate),params.locale||"pt-BR");doc.text(`${strings.period}: ${startDate} - ${endDate}`,20,45);let currentY=55;if(chartData.globalPeak){const peak=chartData.globalPeak;doc.setFontSize(12);doc.setTextColor(255,152,0);doc.text(`${strings.maximum}: ${peak.formattedValue} kW ${peak.key?`(${peak.key}) `:""}${strings.at} ${peak.formattedTime}`,20,currentY);currentY+=10}currentY=ensureRoom(doc,currentY,120);currentY=addCanvasToPdf(doc,chartCanvas,20,currentY);currentY+=10;if(chartData.series.length>0&&chartData.series[0].points.length>0){currentY=ensureRoom(doc,currentY,60);doc.setFontSize(12);doc.setTextColor(0,0,0);doc.text("Amostra de Dados:",20,currentY);currentY+=10;const samplePoints=chartData.series[0].points.slice(0,10);doc.setFontSize(10);doc.text("Data",20,currentY);doc.text(params.yAxisLabel||strings.demand,100,currentY);currentY+=7;samplePoints.forEach(point=>{currentY=ensureRoom(doc,currentY,10);const dateStr=formatDate2(new Date(point.x),params.locale||"pt-BR");doc.text(dateStr,20,currentY);doc.text(point.y.toFixed(2),100,currentY);currentY+=7})}currentY=ensureRoom(doc,currentY,20);doc.setFontSize(8);doc.setTextColor(128,128,128);doc.text(`${strings.reportFooter}`,20,doc.internal.pageSize.getHeight()-15);doc.text(`Gerado em: ${(new Date).toLocaleString(params.locale||"pt-BR")}`,20,doc.internal.pageSize.getHeight()-10);const fileName=params.pdf?.fileName||`demanda_${label2.replace(/\s+/g,"_")}_${(new Date).toISOString().slice(0,10)}.pdf`;savePdfSafe(doc,fileName)}catch(error){console.error("[PDF Export] Error:",error);alert("Erro ao gerar PDF. Por favor, tente novamente. Verifique o console para mais detalhes.")}finally{btn.disabled=false;btn.innerHTML=originalHtml}}function initializeDateInputs(){const startDate=new Date(currentStartDate);const endDate=new Date(currentEndDate);const formatLocalDate=date=>{const year=date.getFullYear();const month=String(date.getMonth()+1).padStart(2,"0");const day=String(date.getDate()).padStart(2,"0");return`${year}-${month}-${day}`};dateStartInput.value=formatLocalDate(startDate);dateEndInput.value=formatLocalDate(endDate)}function initializeQuerySelects(){if(intervalSelect&¶ms.telemetryQuery?.interval){intervalSelect.value=params.telemetryQuery.interval.toString()}if(aggSelect&¶ms.telemetryQuery?.agg){aggSelect.value=params.telemetryQuery.agg}}function debounce(func,wait){let timeout=null;return function(...args){const context=this;if(timeout)clearTimeout(timeout);timeout=setTimeout(()=>func.apply(context,args),wait)}}async function switchTelemetryType(newTypeId){const newType=TELEMETRY_TYPES[newTypeId];if(!newType||newType.id===activeTelemetryType.id){return}try{loadingEl.style.display="flex";contentEl.style.display="none";errorEl.style.display="none";peakEl.style.display="none";activeTelemetryType=newType;await loadData()}catch(error){console.error("[DemandModal] Error switching telemetry type:",error);errorEl.style.display="flex";errorText.textContent=`${strings.error}: ${error instanceof Error?error.message:"Unknown error"}`;loadingEl.style.display="none"}}async function updatePeriod(){periodErrorEl.style.display="none";const newStartDate=dateStartInput.value;const newEndDate=dateEndInput.value;if(!newStartDate||!newEndDate){periodErrorEl.textContent=strings.error;periodErrorEl.style.display="block";return}const startDateObj=new Date(newStartDate);const endDateObj=new Date(newEndDate);if(endDateObj<startDateObj){periodErrorEl.textContent=strings.invalidDateRange;periodErrorEl.style.display="block";return}const diffTime=Math.abs(endDateObj.getTime()-startDateObj.getTime());const diffDays=Math.ceil(diffTime/(1e3*60*60*24));if(diffDays>30){periodErrorEl.textContent=strings.maxRangeExceeded;periodErrorEl.style.display="block";return}currentStartDate=startDateObj.toISOString();currentEndDate=endDateObj.toISOString();await loadData()}closeBtn.addEventListener("click",closeModal);fullscreenBtn.addEventListener("click",toggleFullscreen);resetBtn.addEventListener("click",resetZoom);pdfBtn.addEventListener("click",exportPdf);csvBtn.addEventListener("click",exportCsv);updateBtn.addEventListener("click",updatePeriod);if(telemetryTypeSelect&&allowTelemetrySwitch){const debouncedSwitch=debounce(switchTelemetryType,300);telemetryTypeSelect.addEventListener("change",e=>{const newTypeId=e.target.value;debouncedSwitch(newTypeId)})}if(intervalSelect){intervalSelect.addEventListener("change",()=>{loadData()})}if(aggSelect){aggSelect.addEventListener("change",()=>{loadData()})}overlay.addEventListener("click",e=>{if(e.target===overlay){closeModal()}});overlay.addEventListener("keydown",e=>{if(e.key==="Escape"){closeModal()}});async function loadData(){try{loadingEl.style.display="flex";contentEl.style.display="none";errorEl.style.display="none";peakEl.style.display="none";const startDateObj=new Date(currentStartDate);const endDateObj=new Date(currentEndDate);const diffTime=Math.abs(endDateObj.getTime()-startDateObj.getTime());const diffDays=Math.ceil(diffTime/(1e3*60*60*24));if(diffDays>30){loadingEl.style.display="none";errorEl.style.display="flex";errorText.textContent="O Limite de busca é de 30 dias de intervalo.";return}const keysStr=Array.isArray(activeTelemetryType.keys)?activeTelemetryType.keys.join(","):activeTelemetryType.keys;const selectedInterval=intervalSelect?parseInt(intervalSelect.value):864e5;const selectedAgg=aggSelect?aggSelect.value:"MAX";let queryLimit=params.telemetryQuery?.limit||1e4;if(selectedInterval===6e4){queryLimit=1440}const telemetryQuery={...params.telemetryQuery,keys:keysStr,interval:selectedInterval,agg:selectedAgg,limit:queryLimit};const cacheKey=getCacheKey({deviceId:params.deviceId,startDate:currentStartDate,endDate:currentEndDate,keys:keysStr,agg:telemetryQuery.agg,interval:telemetryQuery.interval||864e5});let rawData=getCachedData(cacheKey);if(!rawData){rawData=params.fetcher?await params.fetcher({token:params.token,deviceId:params.deviceId,startDate:currentStartDate,endDate:currentEndDate,telemetryQuery:telemetryQuery}):await fetchTelemetryData(params.token,params.deviceId,currentStartDate,currentEndDate,telemetryQuery);setCachedData(cacheKey,rawData)}chartData=processMultiSeriesChartData(rawData,keysStr,params.correctionFactor||1,locale,telemetryQuery.agg||"MAX",params.timezoneOffset);if(params.readingType==="temperature"&&!chartData.isEmpty){chartData.series=chartData.series.map(series=>({...series,points:interpolateTemperatureData(series.points)}))}if(chartData.isEmpty){errorEl.style.display="flex";errorText.textContent=strings.noData;return}if(chartData.globalPeak){const peak=chartData.globalPeak;const date=new Date(peak.timestamp);const dateStr=date.toLocaleDateString(locale,{day:"2-digit",month:"2-digit",year:"numeric"});peakEl.textContent=`${strings.maximum}: ${peak.formattedValue} kW ${peak.key?`(${peak.key}) `:""}${strings.at} ${dateStr}`;peakEl.style.display="block"}const Chart2=window.Chart;Chart2.register(window.ChartZoom);if(chart){chart.data.datasets=chartData.series.map(series=>({label:series.label,data:series.points,borderColor:series.color,backgroundColor:series.color+"CC",borderWidth:1}));chart.options.plugins.legend.display=chartData.series.length>1;chart.options.plugins.tooltip.callbacks={title:function(context){const timestamp=context[0].parsed.x;const date=new Date(timestamp);if(params.readingType==="temperature"){return date.toLocaleString(locale,{day:"2-digit",month:"2-digit",year:"numeric",hour:"2-digit",minute:"2-digit"})}else{return date.toLocaleDateString(locale,{day:"2-digit",month:"2-digit",year:"numeric"})}},label:function(context){const seriesLabel=context.dataset.label||"";const value=context.parsed.y;return`${seriesLabel}: ${value.toFixed(2)} kW`}};chart.options.scales.x.ticks.callback=function(value){const date=new Date(value);if(params.readingType==="temperature"){return date.toLocaleTimeString(locale,{hour:"2-digit",minute:"2-digit"})}else{return date.toLocaleDateString(locale,{day:"2-digit",month:"2-digit"})}};chart.update()}else{chart=new Chart2(chartCanvas,{type:"line",data:{datasets:chartData.series.map(series=>({label:series.label,data:series.points,borderColor:series.color,backgroundColor:series.color+"33",borderWidth:2,fill:true,tension:.4}))},options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{display:chartData.series.length>1,position:"top"},tooltip:{callbacks:{title:function(context){const timestamp=context[0].parsed.x;const date=new Date(timestamp);if(params.readingType==="temperature"){return date.toLocaleString(locale,{day:"2-digit",month:"2-digit",year:"numeric",hour:"2-digit",minute:"2-digit"})}else{return date.toLocaleDateString(locale,{day:"2-digit",month:"2-digit",year:"numeric"})}},label:function(context){const seriesLabel=context.dataset.label||"";const value=context.parsed.y;return`${seriesLabel}: ${value.toFixed(2)} kW`}}},zoom:{zoom:{wheel:{enabled:true},pinch:{enabled:true},drag:{enabled:true,modifierKey:"ctrl"},mode:"x"},pan:{enabled:true,mode:"x"}}},scales:{x:{type:"linear",position:"bottom",title:{display:true,text:"Tempo"},ticks:{callback:function(value){const date=new Date(value);if(params.readingType==="temperature"){return date.toLocaleTimeString(locale,{hour:"2-digit",minute:"2-digit"})}else{return date.toLocaleDateString(locale,{day:"2-digit",month:"2-digit"})}}}},y:{title:{display:true,text:params.yAxisLabel||strings.demand},beginAtZero:true}},interaction:{intersect:false,mode:"index"}}})}loadingEl.style.display="none";contentEl.style.display="flex"}catch(error){console.error("Error loading demand data:",error);loadingEl.style.display="none";errorEl.style.display="flex";errorText.textContent=`${strings.error}: ${error instanceof Error?error.message:"Unknown error"}`}}initializeDateInputs();initializeQuerySelects();loadData();return{destroy:closeModal}}var DEFAULT_I18N2={title:"Dashboard - Gráfico",loading:"Carregando dados...",error:"Ocorreu um erro ao carregar os dados",noData:"Nenhum dado disponível para o período selecionado",exportCsv:"Exportar CSV",close:"Fechar",totalConsumption:"Consumo total",averageDaily:"Média diária",peakDay:"Pico do dia",dateRange:"Período",deviceSummary:"Detalhes do dispositivo",energyChart:"Consumo de Energia",kwhUnit:"kWh"};var DEFAULT_STYLES2={primaryColor:"#6366f1",backgroundColor:"#ffffff",textColor:"#1f2937",borderColor:"#e5e7eb",borderRadius:"8px",fontFamily:'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',modalWidth:"90vw",modalHeight:"90vh"};var EnergyModalView=class{modal;container=null;chartContainer=null;config;currentEnergyData=null;dateRangePicker=null;isLoading=false;currentTheme="dark";currentBarMode="stacked";currentGranularity="1d";constructor(modal,config){this.modal=modal;this.config=config;this.initializeTheme();this.initializeBarMode();this.initializeGranularity();this.validateConfiguration();this.render()}initializeTheme(){const savedTheme=localStorage.getItem("myio-modal-theme");this.currentTheme=savedTheme||this.config.params.theme||"dark"}initializeBarMode(){const savedBarMode=localStorage.getItem("myio-modal-bar-mode");this.currentBarMode=savedBarMode||"stacked"}initializeGranularity(){const savedGranularity=localStorage.getItem("myio-modal-granularity");const configGranularity=this.config.params.granularity;const candidate=savedGranularity||configGranularity||"1d";this.currentGranularity=candidate==="1h"||candidate==="1d"?candidate:"1d"}setGranularity(granularity){if(this.currentGranularity===granularity)return;this.currentGranularity=granularity;const buttons=document.querySelectorAll(".myio-btn-granularity");buttons.forEach(btn=>{const btnEl=btn;if(btnEl.dataset.granularity===granularity){btnEl.classList.add("active")}else{btnEl.classList.remove("active")}});localStorage.setItem("myio-modal-granularity",granularity);this.reRenderChart();console.log("[EnergyModalView] [RFC-0097] Granularity changed to:",granularity)}calculateSuggestedGranularity(startDate,endDate){const start=new Date(startDate);const end=new Date(endDate);const diffDays=Math.ceil((end.getTime()-start.getTime())/(1e3*60*60*24));if(diffDays<=1)return"1h";return"1d"}applyGranularityUI(){const buttons=document.querySelectorAll(".myio-btn-granularity");buttons.forEach(btn=>{const btnEl=btn;if(btnEl.dataset.granularity===this.currentGranularity){btnEl.classList.add("active")}else{btnEl.classList.remove("active")}})}toggleTheme(){this.currentTheme=this.currentTheme==="dark"?"light":"dark";localStorage.setItem("myio-modal-theme",this.currentTheme);this.applyTheme();console.log("[EnergyModalView] Theme toggled to:",this.currentTheme)}toggleBarMode(){this.currentBarMode=this.currentBarMode==="stacked"?"grouped":"stacked";localStorage.setItem("myio-modal-bar-mode",this.currentBarMode);this.applyBarMode();console.log("[EnergyModalView] Bar mode toggled to:",this.currentBarMode)}applyTheme(){const themeToggleBtn=document.getElementById("theme-toggle-btn");const modalContent=this.container;const externalBody=this.container?.closest(".myio-modal-body")||document.querySelector(".myio-modal-body");const rootDiv=document.querySelector("#root > div");if(themeToggleBtn){const sunIcon=themeToggleBtn.querySelector(".myio-theme-icon-sun");const moonIcon=themeToggleBtn.querySelector(".myio-theme-icon-moon");if(this.currentTheme==="light"){if(sunIcon){sunIcon.style.opacity="1";sunIcon.style.transform="translate(-50%, -50%) rotate(0deg) scale(1)"}if(moonIcon){moonIcon.style.opacity="0";moonIcon.style.transform="translate(-50%, -50%) rotate(90deg) scale(0)"}if(externalBody){externalBody.style.backgroundColor="#ffffff";externalBody.style.color="#1f2937"}if(rootDiv){rootDiv.style.backgroundColor="#ffffff";rootDiv.style.color="#1f2937"}}else{if(sunIcon){sunIcon.style.opacity="0";sunIcon.style.transform="translate(-50%, -50%) rotate(-90deg) scale(0)"}if(moonIcon){moonIcon.style.opacity="1";moonIcon.style.transform="translate(-50%, -50%) rotate(0deg) scale(1)"}if(externalBody){externalBody.style.backgroundColor="#1f1f1f";externalBody.style.color="#f3f4f6"}if(rootDiv){rootDiv.style.backgroundColor="#1f1f1f";rootDiv.style.color="#f3f4f6"}}}if(modalContent){modalContent.setAttribute("data-theme",this.currentTheme)}this.reRenderChart()}applyBarMode(){const barModeToggleBtn=document.getElementById("bar-mode-toggle-btn");if(barModeToggleBtn){const stackedIcon=barModeToggleBtn.querySelector(".myio-bar-mode-icon-stacked");const groupedIcon=barModeToggleBtn.querySelector(".myio-bar-mode-icon-grouped");if(this.currentBarMode==="grouped"){if(groupedIcon){groupedIcon.style.opacity="1";groupedIcon.style.transform="translate(-50%, -50%) scale(1)"}if(stackedIcon){stackedIcon.style.opacity="0";stackedIcon.style.transform="translate(-50%, -50%) scale(0)"}}else{if(stackedIcon){stackedIcon.style.opacity="1";stackedIcon.style.transform="translate(-50%, -50%) scale(1)"}if(groupedIcon){groupedIcon.style.opacity="0";groupedIcon.style.transform="translate(-50%, -50%) scale(0)"}}}this.reRenderChart()}reRenderChart(){const mode=this.config.params.mode||"single";console.log("[EnergyModalView] reRenderChart called, mode:",mode,"barMode:",this.currentBarMode,"theme:",this.currentTheme);if(mode==="comparison"){console.log("[EnergyModalView] Calling renderComparisonChart...");const result=this.renderComparisonChart();console.log("[EnergyModalView] renderComparisonChart result:",result)}else{if(this.currentEnergyData){this.renderChart(this.currentEnergyData)}}}validateConfiguration(){const mode=this.config.params.mode||"single";if(mode==="single"){if(!this.config.params.deviceId){console.error("[EnergyModalView] deviceId is required for single mode");throw new Error("deviceId is required for single mode")}}else if(mode==="comparison"){if(!this.config.params.dataSources||this.config.params.dataSources.length===0){console.error("[EnergyModalView] dataSources is required for comparison mode");throw new Error("dataSources is required for comparison mode with at least 1 device")}if(this.config.params.dataSources.length<2){console.warn("[EnergyModalView] Comparison with less than 2 devices")}if(!this.config.params.granularity){console.error("[EnergyModalView] granularity is required for comparison mode");throw new Error("granularity is required for comparison mode")}}}getModalTitle(){const mode=this.config.params.mode||"single";if(mode==="comparison"){const count=this.config.params.dataSources?.length||0;return`Comparação de ${count} Dispositivos`}else{const{device:device}=this.config.context;const label=device.label||device.id||"Dispositivo";return`Consumo - ${label}`}}render(){const content=this.createModalContent();this.modal.setContent(content);this.setupEventListeners()}createModalContent(){const container=document.createElement("div");container.className="myio-energy-modal-scope";this.getModalTitle();container.innerHTML=`\n <style>\n ${this.getModalStyles()}\n </style>\n <div class="myio-modal-scope" style="height: 100%; display: flex; flex-direction: column;">\n \x3c!-- Controls Section --\x3e\n <div style="margin-bottom: 16px; flex-shrink: 0;">\n <div style="display: flex; gap: 16px; align-items: end; margin-bottom: 16px;">\n <div class="myio-form-group" style="margin-bottom: 0;">\n <label class="myio-label" for="date-range">Período</label>\n <input type="text" id="date-range" class="myio-input" readonly placeholder="Selecione o período" style="width: 300px;">\n </div>\n <button id="load-btn" class="myio-btn myio-btn-primary">\n <span class="myio-spinner" id="load-spinner" style="display: none;"></span>\n Carregar\n </button>\n <button id="export-csv-btn" class="myio-btn myio-btn-secondary" disabled>\n Exportar CSV\n </button>\n ${this.config.params.readingType==="energy"&&this.config.params.mode!=="comparison"?`\n <button id="view-demand-btn" class="myio-btn myio-btn-secondary" style="\n background: linear-gradient(135deg, #1976D2 0%, #2196F3 100%);\n color: white;\n border: none;\n transition: all 0.3s ease;\n box-shadow: 0 2px 8px rgba(25, 118, 210, 0.3);\n ">\n <span style="font-size: 16px; margin-right: 4px;">📊</span>\n Pico de Demanda\n </button>\n <button id="view-telemetry-btn" class="myio-btn myio-btn-secondary" style="\n background: linear-gradient(135deg, #4A148C 0%, #6A1B9A 100%);\n color: white;\n border: none;\n transition: all 0.3s ease;\n box-shadow: 0 2px 8px rgba(74, 20, 140, 0.3);\n ">\n <span style="font-size: 16px; margin-right: 4px;">⚡</span>\n Telemetrias Instantâneas\n </button>\n `:""}\n <button id="theme-toggle-btn" class="myio-btn myio-btn-secondary" title="Alternar tema (claro/escuro)" style="\n position: relative;\n width: 40px;\n height: 40px;\n padding: 0;\n overflow: hidden;\n ">\n <svg class="myio-theme-icon-sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%) rotate(-90deg) scale(0);\n width: 18px;\n height: 18px;\n opacity: 0;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n ">\n <circle cx="12" cy="12" r="5"></circle>\n <line x1="12" y1="1" x2="12" y2="3"></line>\n <line x1="12" y1="21" x2="12" y2="23"></line>\n <line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>\n <line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>\n <line x1="1" y1="12" x2="3" y2="12"></line>\n <line x1="21" y1="12" x2="23" y2="12"></line>\n <line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>\n <line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>\n </svg>\n <svg class="myio-theme-icon-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%) rotate(0deg) scale(1);\n width: 18px;\n height: 18px;\n opacity: 1;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n ">\n <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>\n </svg>\n </button>\n ${this.config.params.mode==="comparison"&&this.config.params.readingType!=="temperature"?`\n <button id="bar-mode-toggle-btn" class="myio-btn myio-btn-secondary" title="Alternar modo (empilhado/agrupado)" style="\n position: relative;\n width: 40px;\n height: 40px;\n padding: 0;\n overflow: hidden;\n ">\n <svg class="myio-bar-mode-icon-stacked" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%) scale(1);\n width: 18px;\n height: 18px;\n opacity: 1;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n ">\n <rect x="3" y="13" width="18" height="8"></rect>\n <rect x="3" y="7" width="18" height="5"></rect>\n <rect x="3" y="3" width="18" height="3"></rect>\n </svg>\n <svg class="myio-bar-mode-icon-grouped" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%) scale(0);\n width: 18px;\n height: 18px;\n opacity: 0;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n ">\n <rect x="2" y="10" width="5" height="11"></rect>\n <rect x="9.5" y="5" width="5" height="16"></rect>\n <rect x="17" y="8" width="5" height="13"></rect>\n </svg>\n </button>\n `:""}\n ${this.config.params.mode==="comparison"?`\n \x3c!-- RFC-0097: Granularity Selector (only 1h and 1d supported) --\x3e\n <div class="myio-granularity-selector" style="display: flex; align-items: center; gap: 4px; margin-left: 8px; padding: 4px 8px; background: rgba(0,0,0,0.05); border-radius: 8px;">\n <span class="myio-label-secondary" style="font-size: 11px; margin-right: 4px; white-space: nowrap;">Granularidade:</span>\n <button class="myio-btn myio-btn-granularity ${this.currentGranularity==="1h"?"active":""}" data-granularity="1h" title="Hora">1h</button>\n <button class="myio-btn myio-btn-granularity ${this.currentGranularity==="1d"?"active":""}" data-granularity="1d" title="Dia">1d</button>\n </div>\n `:""}\n <button id="close-btn" class="myio-btn myio-btn-secondary">\n Fechar\n </button>\n </div>\n </div>\n \n \x3c!-- Error Container --\x3e\n <div id="energy-error" class="myio-energy-error" style="display: none; flex-shrink: 0;">\n \x3c!-- Error messages will be displayed here --\x3e\n </div>\n \n \x3c!-- Main Chart Container - Full Width --\x3e\n <div id="energy-chart-container" class="myio-energy-chart-container" style="width: 100%; flex: 1; box-sizing: border-box;">\n <div class="myio-loading-state">\n <div class="myio-spinner"></div>\n <p>${this.getI18nText("loading")}</p>\n </div>\n </div>\n \n \x3c!-- KPI Button Section --\x3e\n <div id="energy-kpi-btn" style="display: none; margin-top: 16px; text-align: center; flex-shrink: 0;">\n <button id="show-kpis-btn" class="myio-btn myio-btn-secondary" title="Show detailed metrics" style="display: none;">\n <span style="font-size: 16px; font-weight: bold;">+</span>\n <span style="margin-left: 8px;">Show Metrics</span>\n </button>\n </div>\n \n </div>\n `;this.container=container;this.chartContainer=container.querySelector("#energy-chart-container");return container}showLoadingState(){if(this.chartContainer){this.chartContainer.innerHTML=`\n <div class="myio-loading-state">\n <div class="myio-spinner"></div>\n <p>${this.getI18nText("loading")}</p>\n </div>\n `}this.hideError();this.hideKPIButton()}showError(message){const errorContainer=document.getElementById("energy-error");if(errorContainer){errorContainer.innerHTML=`\n <div class="myio-error-content">\n <div class="myio-error-icon">⚠️</div>\n <div class="myio-error-message">${message}</div>\n </div>\n `;errorContainer.style.display="block"}this.hideLoadingState()}renderEnergyData(energyData){this.currentEnergyData=energyData;this.hideLoadingState();this.hideError();this.renderChart(energyData);this.showKPIButton();const exportBtn=document.getElementById("export-csv-btn");if(exportBtn){exportBtn.disabled=false}}renderChart(energyData){if(!this.chartContainer)return;if(this.tryRenderWithSDK(energyData)){return}this.renderFallbackChart(energyData)}tryRenderWithSDK(energyData){const mode=this.config.params.mode||"single";if(mode==="single"){return this.renderSingleDeviceChart(energyData)}else if(mode==="comparison"){return this.renderComparisonChart()}return false}renderSingleDeviceChart(energyData){try{if(this.chartInstance&&typeof this.chartInstance.destroy==="function"){this.chartInstance.destroy();this.chartInstance=null}if(this.chartContainer){this.chartContainer.innerHTML=""}let renderTelemetryChart;if(typeof window!=="undefined"&&window.EnergyChartSDK&&typeof window.EnergyChartSDK.renderTelemetryChart==="function"){renderTelemetryChart=window.EnergyChartSDK.renderTelemetryChart}else{console.error("[EnergyModalView] EnergyChartSDK v2 (renderTelemetryChart) not loaded!");if(this.chartContainer){this.chartContainer.innerHTML='<div style="padding: 20px; text-align: center; color: red;">EnergyChartSDK v2 (renderTelemetryChart) not loaded. Check widget configuration and browser console.</div>'}return false}let startISO,endISO;if(this.dateRangePicker){const dates=this.dateRangePicker.getDates();startISO=dates.startISO;endISO=dates.endISO}else{startISO=this.normalizeToSaoPauloISO(this.config.params.startDate,false);endISO=this.normalizeToSaoPauloISO(this.config.params.endDate,true)}const tzIdentifier=this.config.params.timezone||"America/Sao_Paulo";const granularity=this.config.params.granularity||"1d";const ingestionId=this.config.context.resolved.ingestionId;console.log(`[EnergyModalView] Initializing v2 chart with: deviceId=${ingestionId}, startDate=${startISO}, endDate=${endISO}, granularity=${granularity}, theme=${this.currentTheme}, timezone=${tzIdentifier}`);const chartConfig={version:"v2",clientId:this.config.params.clientId||"ADMIN_DASHBOARD_CLIENT",clientSecret:this.config.params.clientSecret||"admin_dashboard_secret_2025",deviceId:ingestionId,readingType:this.config.params.readingType||"energy",startDate:startISO,endDate:endISO,granularity:granularity,theme:this.currentTheme,timezone:tzIdentifier,iframeBaseUrl:this.config.params.chartsBaseUrl||"https://graphs.apps.myio-bas.com",apiBaseUrl:this.config.params.dataApiHost||"https://api.data.apps.myio-bas.com"};this.chartInstance=renderTelemetryChart(this.chartContainer,chartConfig);if(this.chartInstance&&typeof this.chartInstance.on==="function"){this.chartInstance.on("drilldown",data=>{console.log("[EnergyModalView] v2 SDK Drilldown Event:",data)});this.chartInstance.on("error",errorData=>{console.error("[EnergyModalView] v2 SDK Error Event:",errorData);if(this.chartContainer){this.chartContainer.innerHTML=`<div style="padding: 20px; text-align: center; color: red;">v2 Chart Error: ${errorData.message||"Unknown error"}</div>`}})}else if(this.chartInstance){console.warn("[EnergyModalView] EnergyChartSDK v2 instance does not have an 'on' method for event listeners.")}return true}catch(error){console.warn("[EnergyModalView] EnergyChartSDK failed, using fallback:",error)}return false}renderComparisonChart(){const readingType=this.config.params.readingType||"energy";if(readingType==="temperature"){return this.renderTemperatureComparisonChart()}try{if(this.chartInstance&&typeof this.chartInstance.destroy==="function"){this.chartInstance.destroy();this.chartInstance=null}if(this.chartContainer){this.chartContainer.innerHTML=""}let renderTelemetryStackedChart;if(typeof window!=="undefined"&&window.EnergyChartSDK&&typeof window.EnergyChartSDK.renderTelemetryStackedChart==="function"){renderTelemetryStackedChart=window.EnergyChartSDK.renderTelemetryStackedChart}else{console.error("[EnergyModalView] renderTelemetryStackedChart not available in SDK");if(this.chartContainer){this.chartContainer.innerHTML='<div style="padding: 20px; text-align: center; color: red;">EnergyChartSDK renderTelemetryStackedChart not loaded. Check SDK version.</div>'}return false}let startDateStr,endDateStr;if(this.dateRangePicker){const dates=this.dateRangePicker.getDates();startDateStr=dates.startISO.split("T")[0];endDateStr=dates.endISO.split("T")[0]}else{const startDate=new Date(this.config.params.startDate);const endDate=new Date(this.config.params.endDate);startDateStr=startDate.toISOString().split("T")[0];endDateStr=endDate.toISOString().split("T")[0]}const tzIdentifier=this.config.params.timezone||"America/Sao_Paulo";const chartConfig={version:"v2",clientId:this.config.params.clientId||"ADMIN_DASHBOARD_CLIENT",clientSecret:this.config.params.clientSecret||"admin_dashboard_secret_2025",dataSources:this.config.params.dataSources,readingType:this.config.params.readingType||"energy",startDate:startDateStr,endDate:endDateStr,granularity:this.currentGranularity,theme:this.currentTheme,bar_mode:this.currentBarMode,timezone:tzIdentifier,iframeBaseUrl:this.config.params.chartsBaseUrl||"https://graphs.apps.myio-bas.com",apiBaseUrl:this.config.params.dataApiHost||"https://api.data.apps.myio-bas.com",deep:this.config.params.deep||false};console.log("[EnergyModalView] Rendering comparison chart with SDK:",chartConfig);this.chartInstance=renderTelemetryStackedChart(this.chartContainer,chartConfig);if(this.chartInstance&&typeof this.chartInstance.on==="function"){this.chartInstance.on("error",errorData=>{console.error("[EnergyModalView] Comparison chart error:",errorData);if(this.chartContainer){this.chartContainer.innerHTML=`<div style="padding: 20px; text-align: center; color: red;">Comparison Chart Error: ${errorData.message||"Unknown error"}</div>`}})}return true}catch(error){console.error("[EnergyModalView] Error rendering comparison chart:",error);if(this.chartContainer){this.chartContainer.innerHTML=`<div style="padding: 20px; text-align: center; color: red;">Error: ${error.message}</div>`}}return false}renderTemperatureComparisonChart(){try{if(this.chartInstance&&typeof this.chartInstance.destroy==="function"){this.chartInstance.destroy();this.chartInstance=null}if(this.chartContainer){this.chartContainer.innerHTML=""}let renderTelemetryLineChart;if(typeof window!=="undefined"&&window.EnergyChartSDK&&typeof window.EnergyChartSDK.renderTelemetryLineChart==="function"){renderTelemetryLineChart=window.EnergyChartSDK.renderTelemetryLineChart}else{console.error("[EnergyModalView] renderTelemetryLineChart not available in SDK");if(this.chartContainer){this.chartContainer.innerHTML='<div style="padding: 20px; text-align: center; color: red;">EnergyChartSDK renderTelemetryLineChart not loaded. Check SDK version.</div>'}return false}let startDateStr,endDateStr;if(this.dateRangePicker){const dates=this.dateRangePicker.getDates();startDateStr=dates.startISO.split("T")[0];endDateStr=dates.endISO.split("T")[0]}else{const startDate=new Date(this.config.params.startDate);const endDate=new Date(this.config.params.endDate);startDateStr=startDate.toISOString().split("T")[0];endDateStr=endDate.toISOString().split("T")[0]}const tzIdentifier=this.config.params.timezone||"America/Sao_Paulo";const chartConfig={version:"v2",clientId:this.config.params.clientId||"ADMIN_DASHBOARD_CLIENT",clientSecret:this.config.params.clientSecret||"admin_dashboard_secret_2025",dataSources:this.config.params.dataSources,readingType:"temperature",startDate:startDateStr,endDate:endDateStr,granularity:this.currentGranularity,theme:this.currentTheme,timezone:tzIdentifier,iframeBaseUrl:this.config.params.chartsBaseUrl||"https://graphs.apps.myio-bas.com",apiBaseUrl:this.config.params.dataApiHost||"https://api.data.apps.myio-bas.com",deep:this.config.params.deep||false,showMinMax:false,yAxisTitle:"Temperatura (°C)"};console.log("[EnergyModalView] Rendering temperature comparison chart with SDK:",chartConfig);this.chartInstance=renderTelemetryLineChart(this.chartContainer,chartConfig);if(this.chartInstance&&typeof this.chartInstance.on==="function"){this.chartInstance.on("error",errorData=>{console.error("[EnergyModalView] Temperature chart error:",errorData);if(this.chartContainer){this.chartContainer.innerHTML=`<div style="padding: 20px; text-align: center; color: red;">Temperature Chart Error: ${errorData.message||"Unknown error"}</div>`}})}return true}catch(error){console.error("[EnergyModalView] Error rendering temperature comparison chart:",error);if(this.chartContainer){this.chartContainer.innerHTML=`<div style="padding: 20px; text-align: center; color: red;">Error: ${error.message}</div>`}return false}}normalizeToSaoPauloISO(dateLike,endOfDay=false){let date;if(typeof dateLike==="string"){if(/^\d{4}-\d{2}-\d{2}$/.test(dateLike)){date=new Date(dateLike+"T00:00:00-03:00")}else{date=new Date(dateLike)}}else{date=new Date(dateLike)}if(endOfDay){date.setHours(23,59,59,999)}else{date.setHours(0,0,0,0)}return date.toISOString().replace("Z","-03:00")}renderFallbackChart(energyData){if(!this.chartContainer)return;const maxValue=Math.max(...energyData.consumption.map(p=>p.value));const chartHeight=300;const chartHTML=`\n <div class="myio-fallback-chart">\n <h4>${this.getI18nText("energyChart")}</h4>\n <div class="myio-chart-container" style="height: ${chartHeight}px;">\n <svg width="100%" height="100%" viewBox="0 0 800 ${chartHeight}">\n ${energyData.consumption.map((point,index)=>{const x=index/(energyData.consumption.length-1)*750+25;const y=chartHeight-50-point.value/maxValue*(chartHeight-100);const barWidth=Math.max(2,750/energyData.consumption.length-2);return`\n <rect x="${x-barWidth/2}" y="${y}" width="${barWidth}" height="${chartHeight-50-y}" \n fill="var(--myio-energy-primary, #6366f1)" opacity="0.7">\n <title>${formatDate(point.timestamp)}: ${formatNumber(point.value)} kWh</title>\n </rect>\n `}).join("")}\n \n \x3c!-- Y-axis --\x3e\n <line x1="25" y1="25" x2="25" y2="${chartHeight-25}" stroke="#ccc" stroke-width="1"/>\n \x3c!-- X-axis --\x3e\n <line x1="25" y1="${chartHeight-25}" x2="775" y2="${chartHeight-25}" stroke="#ccc" stroke-width="1"/>\n \n \x3c!-- Y-axis labels --\x3e\n <text x="15" y="30" text-anchor="end" font-size="12" fill="#666">${formatNumber(maxValue)}</text>\n <text x="15" y="${chartHeight-30}" text-anchor="end" font-size="12" fill="#666">0</text>\n </svg>\n </div>\n <p class="myio-chart-note">\n ${this.getI18nText("kwhUnit")} consumption over time. Hover over bars for details.\n </p>\n </div>\n `;this.chartContainer.innerHTML=chartHTML}showKPIButton(){const kpiButtonContainer=document.getElementById("energy-kpi-btn");if(kpiButtonContainer){kpiButtonContainer.style.display="block"}}exportToCsv(){if(!this.currentEnergyData){throw new Error("No data available for export")}const{device:device}=this.config.context;const totalConsumption=this.currentEnergyData.consumption.reduce((sum,item)=>sum+item.value,0);const now=new Date;const timestamp=now.toLocaleDateString("pt-BR")+" - "+now.toLocaleTimeString("pt-BR",{hour:"2-digit",minute:"2-digit"});const csvData=[["ENERGY REPORT - DEVICE DETAILS","",""],["Device",device.label,""],["Device ID",device.id,""],["Export Date",timestamp,""],["Total Consumption",formatNumber(totalConsumption),"kWh"],["","",""],["Date","Consumption (kWh)",""],...this.currentEnergyData.consumption.map(row=>[formatDate(row.timestamp),formatNumber(row.value),""])];const csvContent=toCsv(csvData);this.downloadCSV(csvContent,`energy-report-${device.id}-${(new Date).toISOString().split("T")[0]}.csv`)}downloadCSV(content,filename){const BOM="\ufeff";const csvWithBOM=BOM+content;const blob=new Blob([csvWithBOM],{type:"text/csv;charset=utf-8;"});const link=document.createElement("a");const url=URL.createObjectURL(blob);link.setAttribute("href",url);link.setAttribute("download",filename);link.style.visibility="hidden";document.body.appendChild(link);link.click();document.body.removeChild(link);URL.revokeObjectURL(url)}getDefaultStartDate(){const date=new Date;date.setDate(1);return date.toISOString().split("T")[0]}getDefaultEndDate(){return(new Date).toISOString().split("T")[0]}async loadData(){if(this.isLoading)return;const mode=this.config.params.mode||"single";const loadBtn=document.getElementById("load-btn");const exportBtn=document.getElementById("export-csv-btn");const spinner=document.getElementById("load-spinner");if(!this.dateRangePicker){this.showError("Seletor de data não inicializado");return}this.isLoading=true;loadBtn.disabled=true;exportBtn.disabled=true;spinner.style.display="inline-block";try{const{startISO:startISO,endISO:endISO}=this.dateRangePicker.getDates();if(!startISO||!endISO){this.showError("Selecione um período válido");return}this.showLoadingState();if(mode==="comparison"){console.log("[EnergyModalView] Comparison mode: rendering chart directly");const success=this.tryRenderWithSDK(null);if(success){this.hideLoadingState();this.hideError()}else{this.showError("Erro ao carregar gráfico de comparação")}return}if(this.config.onDateRangeChange){await this.config.onDateRangeChange(startISO,endISO)}}catch(error){this.showError("Erro ao carregar dados: "+error.message);console.error("Error loading data:",error)}finally{this.isLoading=false;loadBtn.disabled=false;spinner.style.display="none"}}async setupEventListeners(){const exportBtn=document.getElementById("export-csv-btn");const closeBtn=document.getElementById("close-btn");const loadBtn=document.getElementById("load-btn");const showKpisBtn=document.getElementById("show-kpis-btn");const dateRangeInput=document.getElementById("date-range");if(exportBtn){exportBtn.addEventListener("click",()=>{try{this.exportToCsv()}catch(error){console.error("[EnergyModalView] Export error:",error);this.config.onError({code:"UNKNOWN_ERROR",message:"Failed to export data",cause:error})}})}if(closeBtn){closeBtn.addEventListener("click",()=>{this.modal.close()})}if(loadBtn){loadBtn.addEventListener("click",()=>this.loadData())}const viewDemandBtn=document.getElementById("view-demand-btn");if(viewDemandBtn){viewDemandBtn.addEventListener("click",async()=>{try{console.log("[EnergyModalView] Opening demand modal (Pico de Demanda)");const jwtToken=localStorage.getItem("jwt_token");if(!jwtToken){throw new Error("Token de autenticação não encontrado")}let startDate;let endDate;if(this.dateRangePicker){const dates=this.dateRangePicker.getDates();startDate=dates.startISO;endDate=dates.endISO}else{startDate=this.config.params.startDate instanceof Date?this.config.params.startDate.toISOString():this.config.params.startDate;endDate=this.config.params.endDate instanceof Date?this.config.params.endDate.toISOString():this.config.params.endDate}await openDemandModal({token:jwtToken,deviceId:this.config.params.deviceId,startDate:startDate,endDate:endDate,label:this.config.params.deviceLabel||"Dispositivo",locale:"pt-BR",readingType:this.config.params.readingType||"energy",enableRealTimeMode:true,realTimeInterval:8e3,realTimeAutoScroll:true})}catch(error){console.error("[EnergyModalView] Error opening demand modal:",error);this.showError("Erro ao abrir pico de demanda: "+error.message)}})}const viewTelemetryBtn=document.getElementById("view-telemetry-btn");if(viewTelemetryBtn){viewTelemetryBtn.addEventListener("click",async()=>{try{console.log("[EnergyModalView] Opening real-time telemetry modal");const jwtToken=localStorage.getItem("jwt_token");if(!jwtToken){throw new Error("Token de autenticação não encontrado")}await openRealTimeTelemetryModal({token:jwtToken,deviceId:this.config.params.deviceId,deviceLabel:this.config.params.deviceLabel||"Dispositivo",telemetryKeys:["voltage_a","voltage_b","voltage_c","total_current","consumption"],refreshInterval:8e3,historyPoints:50,locale:"pt-BR"})}catch(error){console.error("[EnergyModalView] Error opening real-time telemetry modal:",error);this.showError("Erro ao abrir telemetrias: "+error.message)}})}if(showKpisBtn){showKpisBtn.addEventListener("click",()=>{console.log("[EnergyModalView] Show KPIs modal clicked");alert("KPI modal functionality to be implemented")})}const themeToggleBtn=document.getElementById("theme-toggle-btn");if(themeToggleBtn){this.applyTheme();themeToggleBtn.addEventListener("click",()=>{this.toggleTheme()})}const barModeToggleBtn=document.getElementById("bar-mode-toggle-btn");if(barModeToggleBtn){this.applyBarMode();barModeToggleBtn.addEventListener("click",()=>{this.toggleBarMode()})}const granularityButtons=document.querySelectorAll(".myio-btn-granularity");if(granularityButtons.length>0){this.applyGranularityUI();granularityButtons.forEach(btn=>{btn.addEventListener("click",e=>{const target=e.currentTarget;const newGranularity=target.dataset.granularity;if(newGranularity){this.setGranularity(newGranularity)}})});console.log("[EnergyModalView] [RFC-0097] Granularity selector initialized with:",this.currentGranularity)}try{this.dateRangePicker=await attach(dateRangeInput,{presetStart:this.config.params.startDate instanceof Date?this.config.params.startDate.toISOString().split("T")[0]:this.config.params.startDate,presetEnd:this.config.params.endDate instanceof Date?this.config.params.endDate.toISOString().split("T")[0]:this.config.params.endDate,maxRangeDays:31,parentEl:this.modal.element,onApply:({startISO:startISO,endISO:endISO})=>{this.hideError();console.log("Date range selected:",{startISO:startISO,endISO:endISO})}})}catch(error){console.warn("DateRangePicker initialization failed, using fallback:",error)}}hideLoadingState(){const loadingState=this.chartContainer?.querySelector(".myio-loading-state");if(loadingState){loadingState.remove()}}hideError(){const errorContainer=document.getElementById("energy-error");if(errorContainer){errorContainer.style.display="none"}}hideKPIButton(){const kpiButtonContainer=document.getElementById("energy-kpi-btn");if(kpiButtonContainer){kpiButtonContainer.style.display="none"}}getI18nText(key){const i18n=this.config.params.i18n||DEFAULT_I18N2;return i18n[key]||DEFAULT_I18N2[key]}getModalStyles(){const styles=this.config.params.styles||{};const defaultPrimary=styles.primaryColor||"#4A148C";const defaultFont=styles.fontFamily||'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';return`\n /* --- VARIÁVEIS DE TEMA (LIGHT MODE - PADRÃO) --- */\n .myio-energy-modal-scope {\n --myio-energy-primary: ${defaultPrimary};\n --myio-energy-font: ${defaultFont};\n --myio-energy-radius: 8px;\n \n /* Cores Gerais (Light Mode) */\n --myio-energy-bg: #ffffff;\n --myio-energy-text: #1f2937;\n --myio-energy-text-secondary: #6b7280;\n \n /* Borda GERAL (para botões e inputs) - Cinza suave */\n --myio-energy-border: #e5e7eb;\n \n /* Borda ESPECÍFICA DO GRÁFICO (Aqui está a correção) */\n /* No Light Mode, definimos como transparente ou branco para "sumir" */\n --myio-chart-border: transparent; \n\n --myio-energy-btn-bg: #f3f4f6;\n --myio-energy-btn-hover: #e5e7eb;\n --myio-energy-input-bg: #ffffff;\n --myio-granularity-bg: #f9fafb;\n \n font-family: var(--myio-energy-font);\n background-color: var(--myio-energy-bg);\n color: var(--myio-energy-text);\n height: -webkit-fill-available;\n transition: background-color 0.3s ease, color 0.3s ease;\n }\n\n /* --- DARK MODE OVERRIDES --- */\n .myio-energy-modal-scope[data-theme="dark"] {\n --myio-energy-bg: #1f1f1f;\n --myio-energy-text: #f3f4f6;\n --myio-energy-text-secondary: #9ca3af;\n \n /* No Dark Mode, as bordas precisam aparecer */\n --myio-energy-border: #374151;\n --myio-chart-border: #374151; /* Borda visível no escuro */\n\n --myio-energy-btn-bg: #374151;\n --myio-energy-btn-hover: #4b5563;\n --myio-energy-input-bg: #111827;\n --myio-granularity-bg: #111827;\n }\n\n /* --- COMPONENTES --- */\n\n .myio-energy-chart-container {\n flex: 1 !important;\n min-height: 353px !important;\n height: 353px !important;\n background: var(--myio-energy-bg);\n border-radius: var(--myio-energy-radius);\n \n /* USO DA VARIÁVEL ESPECÍFICA AQUI */\n border: 1px solid var(--myio-chart-border);\n \n padding: 10px !important;\n display: block !important;\n overflow: hidden !important;\n }\n\n .myio-chart-container {\n /* Aplica a mesma lógica para o gráfico de fallback */\n border: 1px solid var(--myio-chart-border);\n border-radius: var(--myio-energy-radius);\n overflow: hidden;\n }\n\n /* --- Resto dos estilos (Botões, Labels, etc.) --- */\n \n .myio-label-secondary {\n color: var(--myio-energy-text-secondary);\n font-weight: 500;\n }\n\n .myio-granularity-selector {\n display: flex; \n align-items: center; \n gap: 4px; \n margin-left: 8px; \n padding: 4px 8px; \n border-radius: 8px;\n background: var(--myio-granularity-bg);\n border: 1px solid var(--myio-energy-border); /* Granularidade mantém borda suave */\n }\n\n .myio-btn-granularity {\n padding: 4px 10px;\n font-size: 12px;\n font-weight: 600;\n border-radius: 6px;\n cursor: pointer;\n transition: all 0.2s ease;\n min-width: 36px;\n background: transparent;\n border: 1px solid transparent;\n color: var(--myio-energy-text-secondary);\n }\n\n .myio-btn-granularity:hover:not(.active) {\n background: var(--myio-energy-btn-hover);\n color: var(--myio-energy-text);\n }\n\n .myio-btn-granularity.active {\n background: var(--myio-energy-primary);\n color: white;\n border-color: var(--myio-energy-primary);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);\n }\n\n .myio-btn {\n padding: 8px 16px;\n border-radius: var(--myio-energy-radius);\n border: 1px solid var(--myio-energy-border);\n background: var(--myio-energy-btn-bg);\n color: var(--myio-energy-text);\n cursor: pointer;\n font-size: 14px;\n transition: all 0.2s;\n }\n\n .myio-btn:hover:not(:disabled) {\n background: var(--myio-energy-btn-hover);\n border-color: var(--myio-energy-border);\n }\n\n .myio-btn-primary {\n background: var(--myio-energy-primary);\n color: white;\n border-color: var(--myio-energy-primary);\n }\n\n .myio-btn-primary:hover:not(:disabled) {\n opacity: 0.9;\n }\n\n .myio-btn-secondary {\n background: var(--myio-energy-btn-bg);\n color: var(--myio-energy-text);\n border-color: var(--myio-energy-border);\n }\n\n .myio-modal-scope {\n height: 100% !important;\n display: flex !important;\n flex-direction: column !important;\n }\n\n .myio-form-group {\n display: flex;\n flex-direction: column;\n }\n\n .myio-label {\n font-weight: 500;\n margin-bottom: 5px;\n color: var(--myio-energy-text);\n }\n\n .myio-input {\n padding: 8px 12px;\n border: 1px solid var(--myio-energy-border);\n border-radius: var(--myio-energy-radius);\n font-size: 14px;\n background: var(--myio-energy-input-bg);\n color: var(--myio-energy-text);\n }\n\n .myio-input:focus {\n outline: none;\n border-color: var(--myio-energy-primary);\n box-shadow: 0 0 0 1px var(--myio-energy-primary);\n }\n\n .myio-spinner {\n width: 40px;\n height: 40px;\n border: 4px solid var(--myio-energy-border);\n border-top: 4px solid var(--myio-energy-primary);\n border-radius: 50%;\n animation: spin 1s linear infinite;\n margin: 0 auto 16px;\n }\n \n @keyframes spin {\n 0% { transform: rotate(0deg); }\n 100% { transform: rotate(360deg); }\n }\n\n .myio-loading-state p {\n color: var(--myio-energy-text);\n }\n\n .myio-energy-error {\n background: rgba(254, 202, 202, 0.15);\n border: 1px solid rgba(248, 113, 113, 0.5);\n border-radius: var(--myio-energy-radius);\n padding: 16px;\n }\n \n .myio-error-message {\n color: #ef4444;\n font-weight: 500;\n }\n\n .myio-chart-note {\n margin: 12px 0 0 0;\n font-size: 12px;\n color: var(--myio-energy-text);\n opacity: 0.7;\n text-align: center;\n }\n\n .myio-energy-chart-container > iframe {\n width: 100% !important;\n height: 100% !important;\n border: none !important;\n }\n\n @media (max-width: 768px) {\n .myio-energy-modal-layout {\n grid-template-columns: 1fr;\n grid-template-rows: auto 1fr;\n }\n }\n `}destroy(){if(this.chartInstance&&typeof this.chartInstance.destroy==="function"){this.chartInstance.destroy();this.chartInstance=null}if(this.dateRangePicker){this.dateRangePicker.destroy();this.dateRangePicker=null}if(this.chartContainer){this.chartContainer.innerHTML=""}this.currentEnergyData=null;this.container=null;this.chartContainer=null}};var EnergyModal=class{modal;view=null;dataFetcher;params;context=null;modalId;eventHandlers={};constructor(params){validateOptions(params);this.params=this.normalizeParams(params);this.modalId=createModalId();this.dataFetcher=new EnergyDataFetcher({dataApiHost:this.params.dataApiHost,ingestionToken:this.params.ingestionToken,clientId:this.params.clientId,clientSecret:this.params.clientSecret});console.log("[EnergyModal] Initialized with params:",{deviceId:this.params.deviceId,hasIngestionToken:!!this.params.ingestionToken,hasClientCredentials:!!(this.params.clientId&&this.params.clientSecret),dataApiHost:this.params.dataApiHost,modalId:this.modalId})}async show(){try{console.log("[EnergyModal] Starting modal show process");const mode=this.params.mode||"single";console.log(`[EnergyModal] Mode: ${mode}`);if(mode==="single"){this.context=await this.fetchDeviceContext();const identifier=this.context.device.attributes.identifier||"SEM IDENTIFICADOR";const label=this.context.device.label||"SEM ETIQUETA";this.modal=createModal({title:`Energy Report - ${identifier} - ${label}`,width:"80vw",height:"90vh",theme:this.params.theme==="dark"?"dark":"light"})}else if(mode==="comparison"){this.context=this.createComparisonContext();const deviceCount=this.params.dataSources?.length||0;this.modal=createModal({title:`Comparação de ${deviceCount} Dispositivos`,width:"80vw",height:"90vh",theme:this.params.theme==="dark"?"dark":"light"})}this.view=new EnergyModalView(this.modal,{context:this.context,params:this.params,onExport:()=>this.handleExport(),onError:error=>this.handleEnergyModalError(error),onDateRangeChange:(startISO,endISO)=>this.handleDateRangeChange(startISO,endISO)});this.setupModalEventHandlers();if(mode==="single"){await this.loadEnergyData()}else if(mode==="comparison"){console.log("[EnergyModal] Triggering comparison chart render");const success=this.view.tryRenderWithSDK(null);if(!success){const error=new Error("Failed to render comparison chart. Check if EnergyChartSDK is loaded.");this.handleError(error)}}if(this.params.onOpen){try{this.params.onOpen(this.context)}catch(error){console.warn("[EnergyModal] onOpen callback error:",error)}}console.log("[EnergyModal] Modal successfully opened");return{close:()=>this.close()}}catch(error){console.error("[EnergyModal] Error showing modal:",error);this.handleError(error);throw error}}normalizeParams(params){if(!validateJwtToken(params.tbJwtToken)){throw new Error("Invalid JWT token format")}return{...params,dataApiHost:params.dataApiHost||"https://api.data.apps.myio-bas.com",chartsBaseUrl:params.chartsBaseUrl||"https://graphs.apps.myio-bas.com",timezone:params.timezone||"America/Sao_Paulo",theme:params.theme||"light",granularity:params.granularity||"1d",closeOnEsc:params.closeOnEsc!==false,zIndex:params.zIndex||1e4,i18n:{...DEFAULT_I18N2,...params.i18n},styles:{...DEFAULT_STYLES2,...params.styles}}}createComparisonContext(){const deviceCount=this.params.dataSources?.length||0;return{device:{id:"comparison",label:`Comparação (${deviceCount} dispositivos)`,attributes:{}},resolved:{ingestionId:null,centralId:null,slaveId:null,customerId:null}}}async fetchDeviceContext(){console.log("[EnergyModal] Fetching device context for:",this.params.deviceId);try{const[entityInfo,attributes]=await Promise.all([this.fetchEntityInfo(),this.fetchEntityAttributes()]);const resolvedAttributes=resolveDeviceAttributes(attributes);const context={device:{id:this.params.deviceId,label:this.params.label||entityInfo.label||entityInfo.name||"Unknown Device",attributes:attributes},resolved:{ingestionId:this.params.ingestionId||resolvedAttributes.ingestionId,centralId:this.params.centralId||resolvedAttributes.centralId,slaveId:this.params.slaveId||resolvedAttributes.slaveId,customerId:this.params.customerId||resolvedAttributes.customerId}};console.log("[EnergyModal] Device context resolved:",{deviceLabel:context.device.label,hasIngestionId:!!context.resolved.ingestionId,attributeCount:Object.keys(attributes).length});return context}catch(error){console.error("[EnergyModal] Error fetching device context:",error);throw new Error(`Failed to fetch device information: ${createSafeErrorMessage(error)}`)}}async fetchEntityInfo(){const url=`/api/device/${this.params.deviceId}`;const response=await fetch(url,{method:"GET",headers:{"X-Authorization":`Bearer ${this.params.tbJwtToken}`,"Content-Type":"application/json"}});if(!response.ok){const error=mapHttpError(response.status);throw new Error(`Failed to fetch device entity: ${error.message}`)}return response.json()}async fetchEntityAttributes(){const url=`/api/plugins/telemetry/DEVICE/${this.params.deviceId}/values/attributes?scope=SERVER_SCOPE`;const response=await fetch(url,{method:"GET",headers:{"X-Authorization":`Bearer ${this.params.tbJwtToken}`,"Content-Type":"application/json"}});if(!response.ok){const error=mapHttpError(response.status);throw new Error(`Failed to fetch device attributes: ${error.message}`)}const attributes=await response.json();return attributes.reduce((acc,attr)=>{acc[attr.key]=attr.value;return acc},{})}async loadEnergyData(){const mode=this.params.mode||"single";if(mode!=="single"){console.log("[EnergyModal] Skipping loadEnergyData in comparison mode");return}if(!this.context?.resolved.ingestionId){const error=new Error("ingestionId not found in device attributes. Please configure the device properly.");this.handleError(error);return}if(!this.view){throw new Error("View not initialized")}try{console.log("[EnergyModal] Loading energy data");this.view.showLoadingState();const startISO=normalizeToSaoPauloISO(this.params.startDate,false);const endISO=normalizeToSaoPauloISO(this.params.endDate,true);const energyData=await this.dataFetcher.fetchEnergyData({ingestionId:this.context.resolved.ingestionId,startISO:startISO,endISO:endISO,granularity:this.params.granularity||"1d"});console.log("[EnergyModal] Energy data loaded:",{dataPoints:energyData.consumption.length,totalConsumption:energyData.consumption.reduce((sum,point)=>sum+point.value,0)});this.view.renderEnergyData(energyData)}catch(error){console.error("[EnergyModal] Error loading energy data:",error);this.handleError(error)}}buildModalTitle(){const i18n=this.params.i18n||DEFAULT_I18N2;const deviceLabel=this.context?.device.label||this.params.label||"Device";return`${i18n.title} - ${deviceLabel}`}setupModalEventHandlers(){if(!this.modal)return;this.modal.on("close",()=>{console.log("[EnergyModal] Modal closing");this.cleanup();this.emit("close");if(this.params.onClose){try{this.params.onClose()}catch(error){console.warn("[EnergyModal] onClose callback error:",error)}}});if(this.params.closeOnEsc){const handleKeyDown=event=>{if(event.key==="Escape"){event.preventDefault();this.close()}};document.addEventListener("keydown",handleKeyDown);this.on("close",()=>{document.removeEventListener("keydown",handleKeyDown)})}}async handleDateRangeChange(startISO,endISO){const mode=this.params.mode||"single";if(!this.view){return}try{console.log("[EnergyModal] Date range changed:",{startISO:startISO,endISO:endISO,mode:mode});if(mode==="comparison"){console.log("[EnergyModal] Comparison mode: re-rendering chart with new dates");this.params.startDate=startISO;this.params.endDate=endISO;this.view.showLoadingState();const success=this.view.tryRenderWithSDK(null);if(success){this.view.hideLoadingState();this.view.hideError()}else{this.view.showError("Erro ao recarregar gráfico de comparação")}return}if(!this.context?.resolved.ingestionId){return}this.view.showLoadingState();const energyData=await this.dataFetcher.fetchEnergyData({ingestionId:this.context.resolved.ingestionId,startISO:startISO,endISO:endISO,granularity:this.params.granularity||"1d"});console.log("[EnergyModal] Energy data reloaded:",{dataPoints:energyData.consumption.length,totalConsumption:energyData.consumption.reduce((sum,point)=>sum+point.value,0)});this.view.renderEnergyData(energyData)}catch(error){console.error("[EnergyModal] Error reloading energy data:",error);this.handleError(error)}}handleExport(){const mode=this.params.mode||"single";if(!this.view){console.warn("[EnergyModal] Cannot export: view not initialized");return}try{if(mode==="comparison"){alert("Export não disponível no modo de comparação");return}this.view.exportToCsv();console.log("[EnergyModal] CSV export completed")}catch(error){console.error("[EnergyModal] Export error:",error);this.handleError(new Error("Failed to export data to CSV"))}}handleEnergyModalError(error){console.error("[EnergyModal] EnergyModalError occurred:",error);if(this.view){this.view.showError(error.message)}if(this.params.onError){try{this.params.onError(error)}catch(callbackError){console.warn("[EnergyModal] onError callback error:",callbackError)}}this.emit("error",{message:error.message,error:error})}handleError(error){console.error("[EnergyModal] Error occurred:",error);if(this.view){this.view.showError(error.message)}if(this.params.onError){try{const modalError={code:"UNKNOWN_ERROR",message:error.message,cause:error};this.params.onError(modalError)}catch(callbackError){console.warn("[EnergyModal] onError callback error:",callbackError)}}this.emit("error",{message:error.message,error:error})}close(){if(this.modal){this.modal.close()}}cleanup(){console.log("[EnergyModal] Cleaning up resources");this.dataFetcher.clearCache();if(this.view){this.view.destroy();this.view=null}this.context=null;this.eventHandlers={}}on(event,handler){if(!this.eventHandlers[event]){this.eventHandlers[event]=[]}this.eventHandlers[event].push(handler)}emit(event,payload){if(this.eventHandlers[event]){this.eventHandlers[event].forEach(handler=>{try{handler()}catch(error){console.warn(`[EnergyModal] Event handler error for ${event}:`,error)}})}}};async function openDashboardPopupEnergy(options){try{validateOptions(options);console.log("[openDashboardPopupEnergy] Opening energy modal with options:",{deviceId:options.deviceId,hasIngestionToken:!!options.ingestionToken,hasClientCredentials:!!(options.clientId&&options.clientSecret),startDate:options.startDate,endDate:options.endDate,theme:options.theme||"light"});const modal=new EnergyModal(options);const modalHandle=await modal.show();console.log("[openDashboardPopupEnergy] Energy modal opened successfully");return modalHandle}catch(error){console.error("[openDashboardPopupEnergy] Error opening modal:",error);if(options.onError){try{options.onError({code:error instanceof Error&&error.message.includes("validation")?"VALIDATION_ERROR":"UNKNOWN_ERROR",message:error instanceof Error?error.message:"Unknown error occurred",cause:error})}catch(callbackError){console.warn("[openDashboardPopupEnergy] onError callback failed:",callbackError)}}throw error}}var rangeDaysInclusive=(start,end)=>{const out=[];let cur=new Date(start);const last=new Date(end);while(cur<=last){out.push(cur.toISOString().slice(0,10));cur.setDate(cur.getDate()+1)}return out};var fmtPt=n=>new Intl.NumberFormat("pt-BR",{minimumFractionDigits:2,maximumFractionDigits:2}).format(n);var DOMAIN_CONFIG={energy:{endpoint:"energy",unit:"kWh",label:"Consumo (kWh)",formatter:v=>fmtPt(v),summaryType:"total",summaryLabel:"Total"},water:{endpoint:"water",unit:"m³",label:"Consumo (m³)",formatter:v=>fmtPt(v),summaryType:"total",summaryLabel:"Total"},temperature:{endpoint:"temperature",unit:"°C",label:"Temperatura (°C)",formatter:v=>fmtPt(v),summaryType:"average",summaryLabel:"Média"}};var createDefaultEnergyFetcher=params=>async({baseUrl:baseUrl,ingestionId:ingestionId,startISO:startISO,endISO:endISO})=>{const domain=params.domain||"energy";const endpoint=DOMAIN_CONFIG[domain].endpoint;const url=`${baseUrl}/api/v1/telemetry/devices/${ingestionId}/${endpoint}?startTime=${encodeURIComponent(startISO)}&endTime=${encodeURIComponent(endISO)}&granularity=1d&page=1&pageSize=1000&deep=0`;const token=params.api.ingestionToken;if(!token){throw new Error("ingestionToken is required for Data API calls to data.apps.myio-bas.com")}const response=await fetch(url,{headers:{Authorization:`Bearer ${token}`,"Content-Type":"application/json"}});if(!response.ok){throw new Error(`HTTP ${response.status}: ${response.statusText}`)}return response.json()};var DeviceReportModal=class{constructor(params){this.params=params;this.authClient=new AuthClient({clientId:params.api.clientId,clientSecret:params.api.clientSecret,base:params.api.dataApiBaseUrl});const domain=params.domain||"energy";this.domainConfig=DOMAIN_CONFIG[domain];this.energyFetcher=params.fetcher||createDefaultEnergyFetcher(params)}modal;authClient;energyFetcher;data=[];isLoading=false;eventHandlers={};dateRangePicker=null;sortState={key:null,direction:"asc"};domainConfig;show(){this.modal=createModal({title:`Relatório - ${this.params.identifier||"SEM IDENTIFICADOR"} - ${this.params.label||"SEM ETIQUETA"}`,width:"80vw",height:"90vh",theme:this.params.ui?.theme||"light"});this.renderContent();this.modal.on("close",()=>{if(this.dateRangePicker){this.dateRangePicker.destroy();this.dateRangePicker=null}this.authClient.clearCache();this.emit("close")});return{close:()=>this.modal.close(),on:(event,handler)=>this.on(event,handler)}}renderContent(){const content=document.createElement("div");content.innerHTML=`\n <div class="myio-modal-scope">\n <div style="margin-bottom: 16px;">\n <div style="display: flex; gap: 16px; align-items: end; margin-bottom: 16px;">\n <div class="myio-form-group" style="margin-bottom: 0;">\n <label class="myio-label" for="date-range">Período</label>\n <input type="text" id="date-range" class="myio-input" readonly placeholder="Selecione o período" style="width: 300px;">\n </div>\n <button id="load-btn" class="myio-btn myio-btn-primary">\n <span class="myio-spinner" id="load-spinner" style="display: none;"></span>\n Carregar\n </button>\n <button id="export-btn" class="myio-btn myio-btn-secondary" disabled>\n Exportar CSV\n </button>\n </div>\n </div>\n \n <div id="error-container" style="display: none; background: #ffebee; color: #c62828; padding: 12px; border-radius: 6px; margin-bottom: 16px;">\n </div>\n \n <div id="table-container">\n <div style="text-align: center; padding: 40px; color: var(--myio-text-muted);">\n Selecione um período e clique em "Carregar" para visualizar os dados.\n </div>\n </div>\n </div>\n `;this.modal.setContent(content);this.setupEventListeners()}async setupEventListeners(){const loadBtn=document.getElementById("load-btn");const exportBtn=document.getElementById("export-btn");const dateRangeInput=document.getElementById("date-range");loadBtn?.addEventListener("click",()=>this.loadData());exportBtn?.addEventListener("click",()=>this.exportCSV());try{this.dateRangePicker=await attach(dateRangeInput,{presetStart:this.getDefaultStartDate(),presetEnd:this.getDefaultEndDate(),maxRangeDays:31,parentEl:this.modal.element,onApply:({startISO:startISO,endISO:endISO})=>{this.hideError();console.log("Date range selected:",{startISO:startISO,endISO:endISO})}})}catch(error){console.warn("DateRangePicker initialization failed, using fallback:",error)}}async loadData(){if(this.isLoading)return;const loadBtn=document.getElementById("load-btn");const exportBtn=document.getElementById("export-btn");const spinner=document.getElementById("load-spinner");if(!this.dateRangePicker){this.showError("Seletor de data não inicializado");return}this.isLoading=true;loadBtn.disabled=true;exportBtn.disabled=true;spinner.style.display="inline-block";try{const{startISO:startISO,endISO:endISO}=this.dateRangePicker.getDates();if(!startISO||!endISO){this.showError("Selecione um período válido");return}const startDate=startISO.split("T")[0];const endDate=endISO.split("T")[0];const dateRange=rangeDaysInclusive(startDate,endDate);const apiResponse=await this.energyFetcher({baseUrl:this.params.api.dataApiBaseUrl||"https://api.data.apps.myio-bas.com",ingestionId:this.params.ingestionId,startISO:startISO,endISO:endISO});this.data=this.processApiResponse(apiResponse,dateRange);this.renderTable();exportBtn.disabled=false;this.emit("loaded",{date:{start:startDate,end:endDate},count:this.data.length,total:this.calculateTotal()})}catch(error){this.showError("Erro ao carregar dados: "+error.message);console.error("Error loading data:",error);this.emit("error",{message:error.message,context:"loadData"})}finally{this.isLoading=false;loadBtn.disabled=false;spinner.style.display="none"}}processApiResponse(apiResponse,dateRange){const dataArray=Array.isArray(apiResponse)?apiResponse:apiResponse.data||[];if(!Array.isArray(dataArray)||dataArray.length===0){console.warn("[DeviceReportModal] API returned empty or invalid response, zero-filling date range");return dateRange.map(date=>({date:date,consumption:0}))}const deviceData=dataArray[0];const consumption=deviceData.consumption||[];const dailyMap={};consumption.forEach(item=>{if(item.timestamp&&item.value!=null){const date=item.timestamp.slice(0,10);const value=Number(item.value);if(!dailyMap[date])dailyMap[date]=0;dailyMap[date]+=value}});return dateRange.map(date=>({date:date,consumption:dailyMap[date]||0}))}generateMockData(dateRange){return dateRange.map(date=>({date:date,consumption:Math.random()*50+10}))}renderTable(){const container=document.getElementById("table-container");if(!container)return;const total=this.calculateTotal();const summaryValue=this.domainConfig.summaryType==="average"?this.data.length>0?total/this.data.length:0:total;const getSortIndicator=columnKey=>{if(this.sortState.key===columnKey){return this.sortState.direction==="asc"?"↑":"↓"}return"↕"};container.innerHTML=`\n <div style="margin-bottom: 16px; padding: 12px; background: var(--myio-bg); border-radius: 6px;">\n <strong>${this.domainConfig.summaryLabel}: ${this.domainConfig.formatter(summaryValue)} ${this.domainConfig.unit}</strong>\n </div>\n\n <div style="max-height: 400px; overflow-y: auto; border: 1px solid var(--myio-border); border-radius: 6px;">\n <table class="myio-table">\n <thead>\n <tr>\n <th style="cursor: pointer;" data-sort="date">\n Data\n <span style="margin-left: 4px; opacity: ${this.sortState.key==="date"?"1":"0.5"};">${getSortIndicator("date")}</span>\n </th>\n <th style="cursor: pointer; text-align: right;" data-sort="consumption">\n ${this.domainConfig.label}\n <span style="margin-left: 4px; opacity: ${this.sortState.key==="consumption"?"1":"0.5"};">${getSortIndicator("consumption")}</span>\n </th>\n </tr>\n </thead>\n <tbody>\n ${this.data.map(row=>`\n <tr>\n <td>${this.formatDate(row.date)}</td>\n <td style="text-align: right;">${this.domainConfig.formatter(row.consumption)}</td>\n </tr>\n `).join("")}\n </tbody>\n </table>\n </div>\n `;this.setupTableSorting()}setupTableSorting(){const headers=document.querySelectorAll("[data-sort]");headers.forEach(header=>{header.addEventListener("click",()=>{const sortKey=header.getAttribute("data-sort");this.sortData(sortKey);this.renderTable()})})}sortData(key){if(this.sortState.key===key){this.sortState.direction=this.sortState.direction==="asc"?"desc":"asc"}else{this.sortState.key=key;this.sortState.direction="asc"}this.data.sort((a,b)=>{let comparison=0;if(key==="date"){comparison=new Date(a.date).getTime()-new Date(b.date).getTime()}else{comparison=a.consumption-b.consumption}return this.sortState.direction==="desc"?-comparison:comparison})}calculateTotal(){return this.data.reduce((sum,row)=>sum+row.consumption,0)}formatDate(dateStr){const date=new Date(dateStr+"T00:00:00");return date.toLocaleDateString("pt-BR")}exportCSV(){const total=this.calculateTotal();const summaryValue=this.domainConfig.summaryType==="average"?this.data.length>0?total/this.data.length:0:total;const now=new Date;const timestamp=now.toLocaleDateString("pt-BR")+" - "+now.toLocaleTimeString("pt-BR",{hour:"2-digit",minute:"2-digit"});const csvData=[["Dispositivo/Loja",this.params.identifier||"N/A",this.params.label||""],["DATA EMISSÃO",timestamp,""],[this.domainConfig.summaryLabel,this.domainConfig.formatter(summaryValue),this.domainConfig.unit],["Data",this.domainConfig.label,""],...this.data.map(row=>[this.formatDate(row.date),this.domainConfig.formatter(row.consumption)])];const csvContent=toCsv(csvData);this.downloadCSV(csvContent,`relatorio-${this.params.identifier||"dispositivo"}-${(new Date).toISOString().split("T")[0]}.csv`)}downloadCSV(content,filename){const BOM="\ufeff";const csvWithBOM=BOM+content;const blob=new Blob([csvWithBOM],{type:"text/csv;charset=utf-8;"});const link=document.createElement("a");const url=URL.createObjectURL(blob);link.setAttribute("href",url);link.setAttribute("download",filename);link.style.visibility="hidden";document.body.appendChild(link);link.click();document.body.removeChild(link);URL.revokeObjectURL(url)}getDefaultStartDate(){const date=new Date;date.setDate(1);return date.toISOString().split("T")[0]}getDefaultEndDate(){return(new Date).toISOString().split("T")[0]}showError(message){const container=document.getElementById("error-container");if(container){container.textContent=message;container.style.display="block"}}hideError(){const container=document.getElementById("error-container");if(container){container.style.display="none"}}on(event,handler){if(!this.eventHandlers[event]){this.eventHandlers[event]=[]}this.eventHandlers[event].push(handler)}emit(event,payload){if(this.eventHandlers[event]){this.eventHandlers[event].forEach(handler=>handler())}}};function openDashboardPopupReport(params){const modal=new DeviceReportModal(params);return modal.show()}var defaultI18n={selectAll:"Selecionar todas",clear:"Limpar",searchPlaceholder:"Buscar lojas...",sortingTitle:"Ordenação",consumptionDesc:"Consumo ↓ (padrão)",consumptionAsc:"Consumo ↑",aToZ:"A → Z",zToA:"Z → A",tieNote:"Caso o consumo seja o mesmo é considerada a ordem alfabética.",apply:"Aplicar",reset:"Resetar",totalLabel:"Lojas",selectedLabel:"Selecionadas",closeLabel:"Fechar"};var FilterOrderingModal=class{state;props;i18n;dom=null;searchTimeout=null;itemsById;initialState;openerEl=null;constructor(props){this.props=props;this.i18n={...defaultI18n,...props.i18n};this.itemsById=new Map(props.items.map(item=>[item.id,item]));const initialSelected=new Set(props.initialSelected||[]);const initialSort=props.initialSort||"CONSUMPTION_DESC";this.state={allIds:props.items.map(item=>item.id),selected:new Set(initialSelected),query:"",sort:initialSort};this.initialState={selected:new Set(initialSelected),sort:initialSort}}open(){if(!this.dom){this.createDOM()}this.openerEl=document.activeElement instanceof HTMLElement?document.activeElement:null;this.updateCounters();this.renderList();this.dom.overlay.setAttribute("aria-hidden","false");this.dom.root.setAttribute("aria-hidden","false");this.lockBodyScroll();this.dom.root.addEventListener("keydown",this.trapFocus);setTimeout(()=>{const search=this.dom.searchInput||this.dom.root.querySelector("button, input");if(search)search.focus()},200);this.emit("myio:filter:open",{})}close(){if(this.dom){this.dom.overlay.setAttribute("aria-hidden","true");this.dom.root.setAttribute("aria-hidden","true");if(this.openerEl&&document.contains(this.openerEl)){this.openerEl.focus()}this.openerEl=null;this.unlockBodyScroll();this.dom.root.removeEventListener("keydown",this.trapFocus)}this.emit("myio:filter:close",{});this.props.onClose?.()}lockBodyScroll(){const hasScrollbar=document.documentElement.scrollHeight>document.documentElement.clientHeight;if(hasScrollbar){const scrollBarW=window.innerWidth-document.documentElement.clientWidth;document.documentElement.style.paddingRight=`${scrollBarW}px`}document.body.classList.add("body--myio-modal-open")}unlockBodyScroll(){document.body.classList.remove("body--myio-modal-open");document.documentElement.style.paddingRight=""}getState(){return{selected:Array.from(this.state.selected),sort:this.state.sort}}setSelection(ids){this.state.selected=new Set(ids);this.updateCounters();this.renderList()}setSort(sort){this.state.sort=sort;this.renderList();if(this.dom){this.dom.sortRadios.forEach(radio=>{radio.checked=radio.value===sort})}}destroy(){if(this.dom){this.close();this.dom.overlay.remove();this.dom.root.remove();this.dom=null}}trapFocus=e=>{if(e.isComposing)return;if(!this.dom||e.key!=="Tab"||this.dom.root.getAttribute("aria-hidden")==="true")return;const focusable=[...this.dom.root.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])')].filter(el=>{const htmlEl=el;return!htmlEl.hasAttribute("disabled")&&htmlEl.tabIndex!==-1&&htmlEl.offsetParent!==null});if(!focusable.length)return;const first=focusable[0];const last=focusable[focusable.length-1];if(e.shiftKey&&document.activeElement===first){last.focus();e.preventDefault()}else if(!e.shiftKey&&document.activeElement===last){first.focus();e.preventDefault()}};createDOM(){const existing=document.querySelector(".myio-modal-root");if(existing){existing.remove()}const overlay=document.createElement("div");overlay.className="myio-modal-overlay";overlay.setAttribute("aria-hidden","true");const root=document.createElement("div");root.className="myio-modal-root myio-modal-portal";root.setAttribute("aria-hidden","true");root.setAttribute("role","dialog");root.setAttribute("aria-modal","true");root.setAttribute("aria-labelledby","myioFilterTitle");const card=document.createElement("div");card.className="myio-modal-card";card.setAttribute("role","document");root.appendChild(card);card.addEventListener("click",e=>e.stopPropagation());card.innerHTML=`\n <header class="myio-header">\n <h2 id="myioFilterTitle">${this.props.title||"Filtros & Ordenação"}</h2>\n <div class="myio-counters" aria-live="polite">\n <span class="counter">\n <strong class="value" data-total>0</strong> <span>${this.i18n.totalLabel}</span>\n </span>\n <span class="sep">•</span>\n <span class="counter">\n <strong class="value" data-selected>0</strong> <span>${this.i18n.selectedLabel}</span>\n </span>\n </div>\n <button class="icon-btn close" aria-label="${this.i18n.closeLabel}" data-close>×</button>\n </header>\n\n <section class="myio-toolbar">\n <div class="actions">\n <button class="btn ghost" data-select-all>${this.i18n.selectAll}</button>\n <button class="btn ghost" data-clear>${this.i18n.clear}</button>\n </div>\n <div class="search">\n <div class="search-input-wrapper">\n <span class="search-icon">🔍</span>\n <input type="text" placeholder="${this.i18n.searchPlaceholder}" aria-label="Buscar" data-search />\n <button class="icon-btn clear-search" aria-label="Limpar busca" data-clear-search hidden>✕</button>\n </div>\n </div>\n </section>\n\n <section class="myio-list" role="listbox" aria-multiselectable="true" data-virtualized>\n \x3c!-- Items will be rendered here --\x3e\n </section>\n\n <section class="myio-sorting">\n <h3>${this.i18n.sortingTitle}</h3>\n <div class="radio-group">\n <label><input type="radio" name="sort" value="CONSUMPTION_DESC" checked /> ${this.i18n.consumptionDesc}</label>\n <label><input type="radio" name="sort" value="CONSUMPTION_ASC" /> ${this.i18n.consumptionAsc}</label>\n <label><input type="radio" name="sort" value="ALPHA_ASC" /> ${this.i18n.aToZ}</label>\n <label><input type="radio" name="sort" value="ALPHA_DESC" /> ${this.i18n.zToA}</label>\n </div>\n <p class="hint">${this.i18n.tieNote}</p>\n </section>\n\n <footer class="myio-footer">\n <button class="btn secondary" data-reset>${this.i18n.reset}</button>\n <button class="btn primary" data-apply>${this.i18n.apply}</button>\n </footer>\n `;this.addStyles();document.body.appendChild(overlay);document.body.appendChild(root);const totalCounter=card.querySelector("[data-total]");const selectedCounter=card.querySelector("[data-selected]");const searchInput=card.querySelector("[data-search]");const listContainer=card.querySelector(".myio-list");const sortRadios=card.querySelectorAll('input[name="sort"]');const clearSearchBtn=card.querySelector("[data-clear-search]");this.dom={overlay:overlay,root:root,card:card,totalCounter:totalCounter,selectedCounter:selectedCounter,searchInput:searchInput,listContainer:listContainer,sortRadios:sortRadios,clearSearchBtn:clearSearchBtn};this.attachEventListeners()}addStyles(){if(document.getElementById("myio-filter-styles"))return;const style=document.createElement("style");style.id="myio-filter-styles";style.textContent=`\n /* === Overlay / Scrim ==================================================== */\n .myio-modal-overlay {\n position: fixed !important;\n top: 0 !important;\n left: 0 !important;\n right: 0 !important;\n bottom: 0 !important;\n background: rgba(0, 0, 0, 0.50); /* 50% dim */\n backdrop-filter: saturate(100%) blur(2px);\n z-index: 10000; /* above app UI */\n opacity: 0;\n pointer-events: none;\n transition: opacity 160ms ease-out;\n margin: 0 !important; /* Reset any inherited margins */\n width: auto !important; /* Reset any inherited width */\n height: auto !important; /* Reset any inherited height */\n touch-action: none; /* iOS overscroll prevention */\n }\n .myio-modal-overlay[aria-hidden="false"] {\n opacity: 1;\n pointer-events: auto;\n }\n\n /* === Card container ====================================================== */\n .myio-modal-root {\n position: fixed !important; /* Force fixed positioning relative to viewport */\n top: 0 !important;\n left: 0 !important;\n right: 0 !important;\n bottom: 0 !important;\n display: flex !important;\n align-items: center !important;\n justify-content: center !important;\n z-index: 10001; /* above overlay */\n opacity: 0;\n pointer-events: none;\n transition: opacity 160ms ease-out;\n padding: 20px; /* Add padding for better mobile positioning */\n margin: 0 !important; /* Reset any inherited margins */\n width: auto !important; /* Reset any inherited width */\n height: auto !important; /* Reset any inherited height */\n touch-action: none; /* iOS overscroll prevention */\n }\n .myio-modal-root[aria-hidden="false"] {\n opacity: 1;\n pointer-events: auto;\n }\n\n /* === Card ================================================================ */\n .myio-modal-card {\n width: min(960px, 94vw);\n max-height: calc(100vh - 40px); /* Account for padding */\n overflow: auto;\n background: #fff;\n border-radius: 16px;\n box-shadow: 0 24px 64px rgba(0,0,0,.28);\n transform: translateY(-12px) scale(0.98);\n transition: transform 160ms ease-out;\n }\n .myio-modal-root[aria-hidden="false"] .myio-modal-card {\n transform: translateY(0) scale(1);\n }\n\n /* Reduced motion support */\n @media (prefers-reduced-motion: reduce) {\n .myio-modal-overlay,\n .myio-modal-root,\n .myio-modal-card {\n transition: none !important;\n transform: none !important;\n }\n .myio-modal-root[aria-hidden="false"] .myio-modal-card {\n transform: none !important;\n }\n }\n\n /* Header stays purple (MYIO palette) and sticky */\n .myio-modal-card header.myio-header {\n position: sticky;\n top: 0;\n z-index: 1;\n padding: 16px 20px;\n background: var(--myio-purple-600, #4A148C);\n color: #fff;\n border-radius: 16px 16px 0 0;\n display: flex;\n align-items: center;\n gap: 12px;\n }\n\n .myio-modal-card footer.myio-footer {\n position: sticky;\n bottom: 0;\n background: #fff;\n border-top: 1px solid var(--myio-border, #E5E7EB);\n padding: 12px 20px;\n border-radius: 0 0 16px 16px;\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n }\n\n /* Prevent accidental stacking-context issues inside transformed parents */\n .myio-modal-portal {\n position: relative;\n z-index: 10000;\n }\n\n /* Optional: body lock when open */\n .body--myio-modal-open {\n overflow: hidden;\n overscroll-behavior: contain;\n }\n\n .myio-header {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 16px 20px;\n color: #fff;\n background: #4A148C;\n position: sticky;\n top: 0;\n z-index: 1;\n border-radius: 16px 16px 0 0;\n }\n\n .myio-header h2 {\n font-size: 18px;\n font-weight: 700;\n margin: 0;\n flex: 1;\n }\n\n .myio-counters {\n display: flex;\n align-items: center;\n gap: 8px;\n opacity: 0.95;\n }\n\n .myio-header .close {\n background: transparent;\n color: #fff;\n font-size: 20px;\n border: none;\n cursor: pointer;\n padding: 4px 8px;\n border-radius: 4px;\n }\n\n .myio-header .close:hover {\n background: rgba(255,255,255,0.1);\n }\n\n .myio-toolbar {\n display: flex;\n gap: 12px;\n align-items: center;\n padding: 12px 20px;\n flex-wrap: wrap;\n border-bottom: 1px solid #E5E7EB;\n }\n\n .myio-toolbar .actions {\n display: flex;\n gap: 8px;\n }\n\n .myio-toolbar .search {\n margin-left: auto;\n flex: 1 1 320px;\n max-width: 400px;\n }\n\n .search-input-wrapper {\n position: relative;\n display: flex;\n align-items: center;\n }\n\n .search-icon {\n position: absolute;\n left: 12px;\n z-index: 1;\n opacity: 0.5;\n }\n\n .myio-toolbar .search input {\n width: 100%;\n height: 40px;\n padding: 0 40px;\n border-radius: 12px;\n border: 1px solid #E5E7EB;\n font-size: 14px;\n }\n\n .clear-search {\n position: absolute;\n right: 8px;\n background: transparent;\n border: none;\n cursor: pointer;\n padding: 4px;\n opacity: 0.5;\n }\n\n .clear-search:hover {\n opacity: 1;\n }\n\n .myio-list {\n display: grid;\n grid-template-columns: repeat(2, minmax(0,1fr));\n gap: 12px;\n padding: 16px 20px;\n max-height: 300px;\n overflow-y: auto;\n }\n\n @media (max-width: 768px) {\n .myio-list {\n grid-template-columns: 1fr;\n }\n }\n\n .chip {\n display: flex;\n align-items: center;\n gap: 10px;\n height: 44px;\n border-radius: 12px;\n border: 2px solid #4A148C;\n background: #fff;\n padding: 0 14px;\n cursor: pointer;\n font-size: 14px;\n transition: all 0.2s ease;\n }\n\n .chip:hover {\n background: #EDE7F3;\n }\n\n .chip.selected {\n background: #EDE7F3;\n border-color: #4A148C;\n }\n\n .chip .checkbox {\n width: 22px;\n height: 22px;\n border-radius: 6px;\n border: 2px solid #4A148C;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 14px;\n font-weight: bold;\n color: #4A148C;\n }\n\n .chip.selected .checkbox {\n background: #4A148C;\n color: white;\n }\n\n .myio-sorting {\n padding: 16px 20px;\n border-top: 1px solid #E5E7EB;\n }\n\n .myio-sorting h3 {\n margin: 0 0 12px 0;\n font-size: 16px;\n font-weight: 600;\n color: #1F2937;\n }\n\n .radio-group {\n display: flex;\n flex-wrap: wrap;\n gap: 16px;\n margin-bottom: 8px;\n }\n\n .myio-sorting label {\n display: flex;\n gap: 8px;\n align-items: center;\n cursor: pointer;\n font-size: 14px;\n }\n\n .myio-sorting input[type="radio"] {\n margin: 0;\n }\n\n .hint {\n font-size: 12px;\n color: #6B7280;\n margin: 8px 0 0 0;\n font-style: italic;\n }\n\n .myio-footer {\n position: sticky;\n bottom: 0;\n background: #fff;\n border-top: 1px solid #E5E7EB;\n padding: 12px 20px;\n border-radius: 0 0 16px 16px;\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n }\n\n .btn {\n border: none;\n border-radius: 8px;\n padding: 10px 16px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n }\n\n .btn.primary {\n background: #4A148C;\n color: #fff;\n }\n\n .btn.primary:hover {\n background: #3A0E5C;\n }\n\n .btn.secondary {\n background: #fff;\n border: 1px solid #D1D5DB;\n color: #374151;\n }\n\n .btn.secondary:hover {\n background: #F9FAFB;\n }\n\n .btn.ghost {\n background: transparent;\n border: 1px solid #D1D5DB;\n color: #374151;\n padding: 8px 12px;\n font-size: 13px;\n }\n\n .btn.ghost:hover {\n background: #F3F4F6;\n }\n\n .empty-state {\n text-align: center;\n padding: 40px 20px;\n color: #6B7280;\n }\n `;document.head.appendChild(style)}attachEventListeners(){if(!this.dom)return;const{overlay:overlay,root:root,searchInput:searchInput,clearSearchBtn:clearSearchBtn,sortRadios:sortRadios}=this.dom;overlay.addEventListener("click",()=>this.close());root.querySelector("[data-close]")?.addEventListener("click",()=>this.close());root.addEventListener("keydown",e=>{if(e.key==="Escape"){this.close()}});searchInput.addEventListener("input",e=>{const query=e.target.value;this.debounceSearch(query);if(query){clearSearchBtn.hidden=false}else{clearSearchBtn.hidden=true}});clearSearchBtn.addEventListener("click",()=>{searchInput.value="";clearSearchBtn.hidden=true;this.updateQuery("")});root.querySelector("[data-select-all]")?.addEventListener("click",()=>{this.selectAllVisible()});root.querySelector("[data-clear]")?.addEventListener("click",()=>{this.clearSelection()});sortRadios.forEach(radio=>{radio.addEventListener("change",()=>{if(radio.checked){this.setSort(radio.value)}})});root.querySelector("[data-apply]")?.addEventListener("click",()=>{this.apply()});root.querySelector("[data-reset]")?.addEventListener("click",()=>{this.reset()})}debounceSearch(query){if(this.searchTimeout){clearTimeout(this.searchTimeout)}this.searchTimeout=window.setTimeout(()=>{this.updateQuery(query)},200)}updateQuery(query){this.state.query=query;this.renderList()}selectAllVisible(){const visibleIds=this.getVisibleItems();visibleIds.forEach(id=>this.state.selected.add(id));this.updateCounters();this.renderList();this.emit("myio:filter:select_all",{scope:this.state.query?"visible":"all"})}clearSelection(){const visibleIds=this.getVisibleItems();const queryActive=this.state.query.length>0;if(queryActive){visibleIds.forEach(id=>this.state.selected.delete(id))}else{this.state.selected.clear()}this.updateCounters();this.renderList();this.emit("myio:filter:clear",{scope:queryActive?"visible":"all"})}apply(){const startTime=performance.now();this.props.onApply({selected:Array.from(this.state.selected),sort:this.state.sort});this.emit("myio:filter:apply",{selectedCount:this.state.selected.size,sort:this.state.sort,queryLength:this.state.query.length,durationMs:performance.now()-startTime});if(this.props.autoCloseOnApply!==false){this.close()}}reset(){this.state.selected=new Set(this.initialState.selected);this.state.sort=this.initialState.sort;this.state.query="";if(this.dom){this.dom.searchInput.value="";this.dom.clearSearchBtn.hidden=true;this.dom.sortRadios.forEach(radio=>{radio.checked=radio.value===this.state.sort})}this.updateCounters();this.renderList()}getVisibleItems(){const sorted=this.sortItems([...this.itemsById.values()],this.state.sort);if(!this.state.query){return sorted.map(x=>x.id)}const query=this.normalize(this.state.query);return sorted.filter(x=>this.normalize(x.label).includes(query)).map(x=>x.id)}normalize(s){return s.normalize("NFD").replace(/\p{Diacritic}/gu,"").toLowerCase().trim()}sortItems(list,mode){const byLabelAsc=(a,b)=>a.label.localeCompare(b.label,void 0,{sensitivity:"base"})||(a.id>b.id?1:-1);const byLabelDesc=(a,b)=>-byLabelAsc(a,b);const num=x=>x==null?Number.NEGATIVE_INFINITY:x;switch(mode){case"CONSUMPTION_DESC":return[...list].sort((a,b)=>num(b.consumption)-num(a.consumption)||byLabelAsc(a,b));case"CONSUMPTION_ASC":return[...list].sort((a,b)=>num(a.consumption)-num(b.consumption)||byLabelAsc(a,b));case"ALPHA_ASC":return[...list].sort(byLabelAsc);case"ALPHA_DESC":return[...list].sort(byLabelDesc);default:return[...list]}}updateCounters(){if(!this.dom)return;this.dom.totalCounter.textContent=this.state.allIds.length.toString();this.dom.selectedCounter.textContent=this.state.selected.size.toString()}renderList(){if(!this.dom)return;const visibleIds=this.getVisibleItems();if(visibleIds.length===0){this.dom.listContainer.innerHTML=`\n <div class="empty-state">\n ${this.state.query?"Nenhuma loja encontrada com o filtro aplicado.":"Nenhuma loja disponível."}\n </div>\n `;return}this.dom.listContainer.innerHTML=visibleIds.map(id=>this.renderChip(id)).join("");this.dom.listContainer.querySelectorAll(".chip").forEach(chip=>{chip.addEventListener("click",()=>{const id=chip.getAttribute("data-id");this.toggleItem(id)})})}renderChip(id){const item=this.itemsById.get(id);const isSelected=this.state.selected.has(id);return`\n <button role="option" aria-selected="${isSelected}"\n class="chip ${isSelected?"selected":""}"\n data-id="${id}">\n <span class="checkbox" aria-hidden="true">${isSelected?"✓":""}</span>\n <span class="label" title="${item.label}">${item.label}</span>\n </button>\n `}toggleItem(id){if(this.state.selected.has(id)){this.state.selected.delete(id)}else{this.state.selected.add(id)}this.updateCounters();this.renderList()}emit(eventType,detail){if(this.dom){const event=new CustomEvent(eventType,{detail:detail});this.dom.card.dispatchEvent(event)}}};function attachFilterOrderingModal(props){const modal=new FilterOrderingModal(props);return{open:()=>modal.open(),close:()=>modal.close(),getState:()=>modal.getState(),setSelection:ids=>modal.setSelection(ids),setSort:sort=>modal.setSort(sort),destroy:()=>modal.destroy()}}var DOMAIN_CONFIG2={energy:{endpoint:"energy",unit:"kWh",label:"Consumption (kWh)",totalLabel:"Total kWh"},water:{endpoint:"water",unit:"m³",label:"Consumo (m³)",totalLabel:"Total m³"}};var AllReportModal=class{constructor(params){this.params=params;this.authClient=new AuthClient({clientId:params.api.clientId,clientSecret:params.api.clientSecret,base:params.api.dataApiBaseUrl});const domain=params.domain||"energy";this.domainConfig=DOMAIN_CONFIG2[domain];this.debugEnabled=params.debug===1;this.debugLog("🚀 AllReportModal initialized",{customerId:params.customerId,itemsListLength:params.itemsList?.length||0,itemsList:params.itemsList,debugEnabled:this.debugEnabled,debugParam:params.debug,apiConfig:{hasIngestionToken:!!params.api.ingestionToken,dataApiBaseUrl:params.api.dataApiBaseUrl}})}modal;authClient;data=[];isLoading=false;eventHandlers={};dateRangePicker=null;currentPage=1;itemsPerPage=10;sortField="consumption";sortDirection="desc";searchFilter="";filterModal=null;selectedStoreIds=new Set;currentSortMode="CONSUMPTION_DESC";debugEnabled;domainConfig;debugLog(message,data){if(this.debugEnabled){console.log(`[AllReportModal DEBUG] ${message}`,data||"")}}normalizeId(v){return(v||"").toString().normalize("NFKC").toUpperCase().replace(/\s+/g,"").replace(/[^A-Z0-9]/g,"")}resolveStoreIdentifierFromApi(item){if(item?.assetName){return item.assetName}const name=item?.name||"";if(!name)return null;const tokens=name.trim().split(/\s+/);const last=tokens[tokens.length-1]||"";if(/[A-Za-z0-9]{3,}/.test(last)){return last}const maybe=tokens.find(t=>/[A-Za-z0-9]{3,}/.test(t));return maybe||null}pickConsumption(item){const fields=["total_value","totalValue","consumption","value","total","energy","kwh"];for(const f of fields){if(item?.[f]!==void 0&&item?.[f]!==null){const n=typeof item[f]==="string"?parseFloat(item[f].replace(",",".")):Number(item[f]);if(!Number.isNaN(n))return n}}return 0}show(){this.debugLog("🎭 Modal show() called - creating modal UI");this.modal=createModal({title:`Relatório Geral - Todas as Lojas${this.debugEnabled?" [DEBUG MODE]":""}`,width:"85vw",height:"90vh",theme:this.params.ui?.theme||"light"});this.renderContent();this.modal.on("close",()=>{this.debugLog("🚪 Modal closing - cleaning up resources");if(this.dateRangePicker){this.dateRangePicker.destroy();this.dateRangePicker=null}if(this.filterModal){this.filterModal.destroy();this.filterModal=null}this.authClient.clearCache();this.emit("close")});this.debugLog("✅ Modal created and ready to use");return{close:()=>this.modal.close(),on:(event,handler)=>this.on(event,handler)}}renderContent(){const content=document.createElement("div");content.innerHTML=`\n <div class="myio-modal-scope">\n <div style="margin-bottom: 16px;">\n <div style="display: flex; gap: 16px; align-items: end; margin-bottom: 16px; flex-wrap: wrap;">\n <div class="myio-form-group" style="margin-bottom: 0;">\n <label class="myio-label" for="date-range">Período</label>\n <input type="text" id="date-range" class="myio-input" readonly placeholder="Selecione o período" style="width: 300px;">\n </div>\n <button id="load-btn" class="myio-btn myio-btn-primary">\n <span class="myio-spinner" id="load-spinner" style="display: none;"></span>\n Carregar\n </button>\n <button id="export-btn" class="myio-btn myio-btn-secondary" disabled>\n Exportar CSV\n </button>\n <button id="filter-btn" class="myio-btn myio-btn-secondary" style="background: var(--myio-brand-700); color: white;">\n 🔍 Filtros & Ordenação\n </button>\n <div class="myio-form-group" style="margin-bottom: 0; margin-left: auto;">\n <label class="myio-label" for="search-input">Busca rápida</label>\n <input type="text" id="search-input" class="myio-input" placeholder="Digite para filtrar..." style="width: 200px;">\n </div>\n </div>\n </div>\n\n <div id="error-container" style="display: none; background: #ffebee; color: #c62828; padding: 12px; border-radius: 6px; margin-bottom: 16px;">\n </div>\n\n <div id="summary-container" style="display: none; margin-bottom: 16px;">\n </div>\n\n <div id="table-container">\n <div style="text-align: center; padding: 40px; color: var(--myio-text-muted);">\n Selecione um período e clique em "Carregar" para visualizar os dados de todas as lojas.\n </div>\n </div>\n\n <div id="pagination-container" style="display: none; margin-top: 16px; text-align: center;">\n </div>\n </div>\n `;this.modal.setContent(content);this.setupEventListeners()}async setupEventListeners(){const loadBtn=document.getElementById("load-btn");const exportBtn=document.getElementById("export-btn");const filterBtn=document.getElementById("filter-btn");const dateRangeInput=document.getElementById("date-range");const searchInput=document.getElementById("search-input");loadBtn?.addEventListener("click",()=>this.loadData());exportBtn?.addEventListener("click",()=>this.exportCSV());filterBtn?.addEventListener("click",()=>this.openFilterModal());if(searchInput){searchInput.addEventListener("input",e=>{this.searchFilter=e.target.value.toLowerCase();this.currentPage=1;this.renderTable()});searchInput.addEventListener("keyup",e=>{this.searchFilter=e.target.value.toLowerCase();this.currentPage=1;this.renderTable()})}try{this.dateRangePicker=await attach(dateRangeInput,{presetStart:this.getDefaultStartDate(),presetEnd:this.getDefaultEndDate(),maxRangeDays:31,parentEl:this.modal.element,onApply:({startISO:startISO,endISO:endISO})=>{this.hideError();this.debugLog("Date range selected:",{startISO:startISO,endISO:endISO})}})}catch(error){this.debugLog("DateRangePicker initialization failed, using fallback:",error)}}async loadData(){if(this.isLoading)return;this.debugLog("📊 Starting loadData process");const loadBtn=document.getElementById("load-btn");const exportBtn=document.getElementById("export-btn");const spinner=document.getElementById("load-spinner");if(!this.dateRangePicker){this.showError("Seletor de data não inicializado");return}this.isLoading=true;loadBtn.disabled=true;exportBtn.disabled=true;spinner.style.display="inline-block";try{const{startISO:startISO,endISO:endISO}=this.dateRangePicker.getDates();this.debugLog("📅 Date range selected",{startISO:startISO,endISO:endISO});if(!startISO||!endISO){this.showError("Selecione um período válido");return}const startDate=startISO.split("T")[0];const endDate=endISO.split("T")[0];this.debugLog("🌐 Fetching customer totals from API...");const customerTotalsData=await this.fetchCustomerTotals(startISO,endISO);this.debugLog("✅ API response received",customerTotalsData);this.debugLog("🔄 Processing API response...");this.data=this.mapCustomerTotalsResponse(customerTotalsData);this.debugLog("✅ Data mapping completed",{mappedDataLength:this.data.length,mappedData:this.data,totalConsumption:this.calculateTotalConsumption()});this.selectedStoreIds=new Set(this.data.map(store=>this.generateStoreId(store.identifier)));this.debugLog("🎯 Store IDs initialized",{selectedStoreIdsSize:this.selectedStoreIds.size,selectedStoreIds:Array.from(this.selectedStoreIds)});this.currentPage=1;this.debugLog("🎨 Rendering UI components...");this.renderSummary();this.renderTable();exportBtn.disabled=false;this.debugLog("🎉 Load process completed successfully");this.emit("loaded",{date:{start:startDate,end:endDate},stores:this.data.length,totalConsumption:this.calculateTotalConsumption()})}catch(error){this.debugLog("❌ Error in loadData",error);this.showError("Erro ao carregar dados: "+error.message);this.debugLog("Error loading data:",error);this.emit("error",{message:error.message,context:"loadData"})}finally{this.isLoading=false;loadBtn.disabled=false;spinner.style.display="none"}}getFilteredData(){let filtered=this.data;if(this.selectedStoreIds.size>0){filtered=this.data.filter(store=>{const storeId=this.generateStoreId(store.identifier);return this.selectedStoreIds.has(storeId)})}if(this.searchFilter){filtered=filtered.filter(store=>{const name=(store.name||"").toString().toLowerCase();const identifier=(store.identifier||"").toString().toLowerCase();return name.includes(this.searchFilter)||identifier.includes(this.searchFilter)})}return filtered.sort((a,b)=>{const aVal=a[this.sortField];const bVal=b[this.sortField];if(typeof aVal==="string"&&typeof bVal==="string"){return this.sortDirection==="asc"?aVal.localeCompare(bVal):bVal.localeCompare(aVal)}else{return this.sortDirection==="asc"?aVal-bVal:bVal-aVal}})}getPaginatedData(){return this.getFilteredData()}renderSummary(){const container=document.getElementById("summary-container");if(!container)return;const totalConsumption=this.calculateTotalConsumption();const storeCount=Math.max(1,this.data.length);container.innerHTML=`\n <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px; padding: 16px; background: var(--myio-bg); border-radius: 6px;">\n <div style="text-align: center;">\n <div style="font-size: 24px; font-weight: bold; color: var(--myio-primary);">${storeCount}</div>\n <div style="color: var(--myio-text-muted);">Lojas</div>\n </div>\n <div style="text-align: center;">\n <div style="font-size: 24px; font-weight: bold; color: var(--myio-primary);">${fmtPt(totalConsumption)}</div>\n <div style="color: var(--myio-text-muted);">${this.domainConfig.totalLabel}</div>\n </div>\n <div style="text-align: center;">\n <div style="font-size: 24px; font-weight: bold; color: var(--myio-primary);">${fmtPt(totalConsumption/storeCount)}</div>\n <div style="color: var(--myio-text-muted);">Média por Loja</div>\n </div>\n </div>\n `;container.style.display="block"}renderTable(){const container=document.getElementById("table-container");if(!container)return;const paginatedData=this.getPaginatedData();this.getFilteredData();if(paginatedData.length===0){container.innerHTML=`\n <div style="text-align: center; padding: 40px; color: var(--myio-text-muted);">\n ${this.searchFilter?"Nenhuma loja encontrada com o filtro aplicado.":"Nenhum dado encontrado."}\n </div>\n `;return}container.innerHTML=`\n <div style="max-height: 500px; overflow-y: auto; border: 1px solid var(--myio-border); border-radius: 6px;">\n <style>\n @media (max-width: 768px) {\n .myio-table-mobile {\n display: block !important;\n }\n .myio-table-mobile thead,\n .myio-table-mobile tbody,\n .myio-table-mobile th,\n .myio-table-mobile td,\n .myio-table-mobile tr {\n display: block !important;\n }\n .myio-table-mobile thead tr {\n position: absolute !important;\n top: -9999px !important;\n left: -9999px !important;\n }\n .myio-table-mobile tbody tr {\n border: 1px solid var(--myio-border) !important;\n border-radius: 8px !important;\n margin-bottom: 16px !important;\n padding: 16px !important;\n background: white !important;\n }\n .myio-table-mobile tbody td {\n border: none !important;\n padding: 8px 0 !important;\n position: relative !important;\n }\n .myio-table-mobile tbody td:before {\n content: attr(data-label) ": " !important;\n font-weight: bold !important;\n display: inline-block !important;\n width: 120px !important;\n color: var(--myio-text-muted) !important;\n }\n }\n </style>\n <table class="myio-table myio-table-mobile" style="table-layout: fixed; width: 100%;">\n <thead style="position: sticky; top: 0; background: var(--myio-bg); z-index: 1;">\n <tr>\n <th style="cursor: pointer; width: 25%;" data-sort="identifier">\n Identifier\n <span style="margin-left: 4px; opacity: ${this.getSortOpacity("identifier")};">${this.getSortIcon("identifier")}</span>\n </th>\n <th style="cursor: pointer; width: 45%;" data-sort="name">\n Name\n <span style="margin-left: 4px; opacity: ${this.getSortOpacity("name")};">${this.getSortIcon("name")}</span>\n </th>\n <th style="cursor: pointer; text-align: right; width: 30%;" data-sort="consumption">\n ${this.domainConfig.label}\n <span style="margin-left: 4px; opacity: ${this.getSortOpacity("consumption")};">${this.getSortIcon("consumption")}</span>\n </th>\n </tr>\n </thead>\n <tbody>\n ${paginatedData.map(row=>`\n <tr>\n <td data-label="Identifier" style="font-family: monospace; font-weight: bold; text-transform: uppercase;">${row.identifier}</td>\n <td data-label="Name"><strong>${row.name}</strong></td>\n <td data-label="${this.domainConfig.label}" style="text-align: right; font-weight: bold;">${row.consumption.toFixed(2)}</td>\n </tr>\n `).join("")}\n </tbody>\n </table>\n </div>\n `;this.setupTableSorting()}getSortIcon(field){if(this.sortField!==field)return"↕";return this.sortDirection==="asc"?"↑":"↓"}getSortOpacity(field){return this.sortField===field?"1":"0.5"}setupTableSorting(){const headers=document.querySelectorAll("[data-sort]");headers.forEach(header=>{header.addEventListener("click",()=>{const sortKey=header.getAttribute("data-sort");if(this.sortField===sortKey){this.sortDirection=this.sortDirection==="asc"?"desc":"asc"}else{this.sortField=sortKey;this.sortDirection=sortKey==="identifier"||sortKey==="name"?"asc":"desc"}this.currentPage=1;this.renderTable()})})}renderPagination(){return}calculateTotalConsumption(){return this.data.reduce((sum,row)=>sum+row.consumption,0)}openFilterModal(){if(!this.filterModal){this.filterModal=attachFilterOrderingModal({title:"Filtros & Ordenação - Lojas",items:this.convertToStoreItems(),initialSelected:Array.from(this.selectedStoreIds),initialSort:this.currentSortMode,onApply:({selected:selected,sort:sort})=>{this.applyFiltersAndSort(selected,sort)},onClose:()=>{}})}else{this.filterModal.setSelection(Array.from(this.selectedStoreIds));this.filterModal.setSort(this.currentSortMode)}this.filterModal.open()}convertToStoreItems(){const labelCounts=new Map;this.data.forEach(store=>{const count=labelCounts.get(store.name)||0;labelCounts.set(store.name,count+1)});return this.data.map(store=>{const isDuplicate=labelCounts.get(store.name)>1;const label=isDuplicate?`${store.name} <span style="font-size: 7px; font-style: italic; color: #666;">(${store.identifier})</span>`:store.name;return{id:this.generateStoreId(store.identifier),identifier:store.identifier,label:label,consumption:store.consumption}})}generateStoreId(storeName){const name=(storeName||"SEM-ID").toString();return name.toLowerCase().replace(/\s+/g,"-").replace(/[^a-z0-9-]/g,"")}applyFiltersAndSort(selectedIds,sortMode){this.selectedStoreIds=new Set(selectedIds);this.currentSortMode=sortMode;switch(sortMode){case"CONSUMPTION_DESC":this.sortField="consumption";this.sortDirection="desc";break;case"CONSUMPTION_ASC":this.sortField="consumption";this.sortDirection="asc";break;case"ALPHA_ASC":this.sortField="name";this.sortDirection="asc";break;case"ALPHA_DESC":this.sortField="name";this.sortDirection="desc";break}this.currentPage=1;this.renderSummary();this.renderTable();const filterBtn=document.getElementById("filter-btn");if(filterBtn&&selectedIds.length>0&&selectedIds.length<this.data.length){filterBtn.innerHTML=`🔍 Filtros & Ordenação (${selectedIds.length})`;filterBtn.style.background="var(--myio-brand-600)"}else{filterBtn.innerHTML="🔍 Filtros & Ordenação";filterBtn.style.background="var(--myio-brand-700)"}}exportCSV(){const sortedData=[...this.data].sort((a,b)=>b.consumption-a.consumption);const csvData=[["Identificador","Nome",`Consumo (${this.domainConfig.unit})`],...sortedData.map(row=>[row.identifier,row.name,row.consumption.toFixed(2)])];const csvContent=toCsv(csvData);this.downloadCSV(csvContent,`relatorio-geral-lojas-${(new Date).toISOString().split("T")[0]}.csv`)}async fetchCustomerTotals(startISO,endISO){if(this.params.fetcher){const token2=this.params.api.ingestionToken||await this.authClient.getBearer();return await this.params.fetcher({baseUrl:this.params.api.dataApiBaseUrl||"https://api.data.apps.myio-bas.com",token:token2,customerId:this.params.customerId,startISO:startISO,endISO:endISO})}const token=this.params.api.ingestionToken;if(!token){throw new Error("ingestionToken is required for Data API calls to data.apps.myio-bas.com")}const baseUrl=this.params.api.dataApiBaseUrl||"https://api.data.apps.myio-bas.com";const startTime=encodeURIComponent(startISO);const endTime=encodeURIComponent(endISO);const endpoint=this.domainConfig.endpoint;const url=`${baseUrl}/api/v1/telemetry/customers/${this.params.customerId}/${endpoint}/devices/totals?startTime=${startTime}&endTime=${endTime}`;this.debugLog("[AllReportModal] Fetching customer totals:",{url:url,customerId:this.params.customerId,domain:this.params.domain||"energy"});const response=await fetch(url,{method:"GET",headers:{Authorization:`Bearer ${token}`,"Content-Type":"application/json"}});if(!response.ok){throw new Error(`Erro na API: ${response.status} ${response.statusText}`)}const data=await response.json();this.debugLog("[AllReportModal] Customer totals response:",data);return data}mapCustomerTotalsResponse(apiResponse){this.debugLog("🔍 Starting mapCustomerTotalsResponse",{apiResponse:apiResponse});const apiArray=Array.isArray(apiResponse?.data)?apiResponse.data:Array.isArray(apiResponse)?apiResponse:[];this.debugLog("[AllReportModal] NEW MAPPING - API array length:",apiArray.length);this.debugLog("[AllReportModal] NEW MAPPING - ItemsList length:",this.params.itemsList.length);this.debugLog("📋 API data array extracted",{isDataProperty:!!apiResponse?.data,isDirectArray:Array.isArray(apiResponse),apiArrayLength:apiArray.length,firstFewItems:apiArray.slice(0,3)});if(!apiArray.length){this.debugLog("⚠️ Empty API array, returning empty result");this.debugLog("[AllReportModal] Empty/invalid API response:",apiResponse);return[]}const sumByApiId=new Map;let apiItemsWithoutId=0;let totalApiConsumption=0;this.debugLog("🔨 Building ID index from API data...");for(const[index,item]of apiArray.entries()){const consumption=this.pickConsumption(item);totalApiConsumption+=consumption;if(index<3){this.debugLog(`[AllReportModal] NEW MAPPING - API item ${index}:`,{id:item?.id,name:item?.name,assetName:item?.assetName,total_value:item?.total_value,extractedConsumption:consumption})}this.debugLog(`📊 Processing API item ${index}`,{item:item,extractedConsumption:consumption,hasId:!!item?.id});if(item?.id){const id=String(item.id);const previousSum=sumByApiId.get(id)||0;sumByApiId.set(id,previousSum+consumption);this.debugLog(`✅ Added to ID index: ${id} = ${previousSum} + ${consumption} = ${previousSum+consumption}`)}else{apiItemsWithoutId++;this.debugLog(`❌ API item without ID:`,item)}}this.debugLog("[AllReportModal] NEW MAPPING - Total API consumption:",totalApiConsumption);this.debugLog("[AllReportModal] NEW MAPPING - Unique API IDs:",sumByApiId.size);this.debugLog("📊 ID index built",{sumByApiIdSize:sumByApiId.size,sumByApiIdEntries:Array.from(sumByApiId.entries()),apiItemsWithoutId:apiItemsWithoutId});let matchedById=0,matchedBySubstring=0;let totalMappedConsumption=0;this.debugLog("🎯 Starting itemsList mapping...");const rows=this.params.itemsList.map((listItem,index)=>{this.debugLog(`🔍 Processing listItem ${index}`,listItem);let consumption=sumByApiId.get(listItem.id)??0;this.debugLog(`🎯 Primary ID match for ${listItem.id}: ${consumption}`);if(index<3){this.debugLog(`[AllReportModal] NEW MAPPING - ItemsList item ${index}:`,{id:listItem.id,identifier:listItem.identifier,label:listItem.label,idMatchConsumption:consumption})}if(consumption>0){matchedById++;this.debugLog(`✅ Matched by ID: ${listItem.id} -> ${consumption}`)}else{this.debugLog(`🔄 No ID match, trying substring fallback for identifier: ${listItem.identifier}`);for(const[apiIndex,apiItem]of apiArray.entries()){const assetName=apiItem?.assetName||"";const name=apiItem?.name||"";const assetNameMatch=assetName.includes(listItem.identifier);const nameMatch=name.includes(listItem.identifier);if(assetNameMatch||nameMatch){const itemConsumption=this.pickConsumption(apiItem);consumption+=itemConsumption;if(index<3){this.debugLog(`[AllReportModal] NEW MAPPING - Substring match for ${listItem.identifier}:`,{apiItemName:name,apiItemAssetName:assetName,itemConsumption:itemConsumption,totalConsumption:consumption})}this.debugLog(`✅ Substring match found in API item ${apiIndex}`,{listItemIdentifier:listItem.identifier,apiItemAssetName:assetName,apiItemName:name,assetNameMatch:assetNameMatch,nameMatch:nameMatch,itemConsumption:itemConsumption,totalConsumption:consumption})}}if(consumption>0){matchedBySubstring++;this.debugLog(`✅ Matched by substring: ${listItem.identifier} -> ${consumption}`)}else{this.debugLog(`❌ No match found for: ${listItem.identifier}`)}}const result={identifier:listItem.identifier,name:listItem.label,consumption:Math.round(consumption*100)/100};totalMappedConsumption+=result.consumption;this.debugLog(`📝 Final row for ${listItem.identifier}:`,result);return result});const stats={apiItems:apiArray.length,uniqueApiIds:sumByApiId.size,itemsInList:this.params.itemsList.length,matchedById:matchedById,matchedBySubstring:matchedBySubstring,unmatched:this.params.itemsList.length-matchedById-matchedBySubstring,apiItemsWithoutId:apiItemsWithoutId,totalApiConsumption:totalApiConsumption,totalMappedConsumption:totalMappedConsumption};this.debugLog("[AllReportModal] NEW MAPPING - Final stats:",stats);this.debugLog("📊 Final mapping stats:",stats);this.debugLog("[AllReportModal] Mapping stats:",stats);return rows}parseConsumptionValue(item){const possibleFields=["total_value","totalValue","consumption","value","total","energy","kwh"];for(const field of possibleFields){if(item[field]!==void 0&&item[field]!==null){const value=typeof item[field]==="string"?parseFloat(item[field].replace(",",".")):Number(item[field]);if(!isNaN(value)){return Math.round(value*100)/100}}}this.debugLog("[AllReportModal] No valid consumption value found in item:",item);return 0}downloadCSV(content,filename){const BOM="\ufeff";const csvWithBOM=BOM+content;const blob=new Blob([csvWithBOM],{type:"text/csv;charset=utf-8;"});const link=document.createElement("a");const url=URL.createObjectURL(blob);link.setAttribute("href",url);link.setAttribute("download",filename);link.style.visibility="hidden";document.body.appendChild(link);link.click();document.body.removeChild(link);URL.revokeObjectURL(url)}getDefaultStartDate(){const date=new Date;date.setDate(1);return date.toISOString().split("T")[0]}getDefaultEndDate(){return(new Date).toISOString().split("T")[0]}showError(message){const container=document.getElementById("error-container");if(container){container.textContent=message;container.style.display="block"}}hideError(){const container=document.getElementById("error-container");if(container){container.style.display="none"}}on(event,handler){if(!this.eventHandlers[event]){this.eventHandlers[event]=[]}this.eventHandlers[event].push(handler)}emit(event,payload){if(this.eventHandlers[event]){this.eventHandlers[event].forEach(handler=>handler())}}};function openDashboardPopupAllReport(params){const modal=new AllReportModal(params);return modal.show()}function openDashboardPopup(params){console.log("Opening settings modal with params:",params);return{close:()=>{console.log("Closing settings modal")},on:(event,handler)=>{console.log(`Registering ${event} handler for settings modal`)}}}var WaterTankModalView=class{config;overlay=null;modal=null;i18n;constructor(config){this.config=config;this.i18n=this.getI18n()}getI18n(){const defaults={title:"Water Tank",loading:"Loading...",error:"Error loading data",noData:"No data available",exportCsv:"Export CSV",close:"Close",currentLevel:"Current Level",averageLevel:"Average Level",minLevel:"Minimum Level",maxLevel:"Maximum Level",dateRange:"Date Range",deviceInfo:"Device Information",levelChart:"Water Level History (m.c.a)",percentUnit:"%",status:{critical:"Critical",low:"Low",medium:"Medium",good:"Good",full:"Full"}};return{...defaults,...this.config.params.i18n}}getLevelStatus(level){if(level<20){return{status:"critical",color:"#e74c3c",label:this.i18n.status.critical}}else if(level<40){return{status:"low",color:"#e67e22",label:this.i18n.status.low}}else if(level<70){return{status:"medium",color:"#f39c12",label:this.i18n.status.medium}}else if(level<90){return{status:"good",color:"#27ae60",label:this.i18n.status.good}}else{return{status:"full",color:"#3498db",label:this.i18n.status.full}}}getTankImageUrl(percentage){if(percentage>=70){return"https://dashboard.myio-bas.com/api/images/public/3t6WVhMQJFsrKA8bSZmrngDsNPkZV7fq"}else if(percentage>=40){return"https://dashboard.myio-bas.com/api/images/public/4UBbShfXCVWR9wcw6IzVMNran4x1EW5n"}else if(percentage>=20){return"https://dashboard.myio-bas.com/api/images/public/aB9nX28F54fBBQs1Ht8jKUdYAMcq9QSm"}else{return"https://dashboard.myio-bas.com/api/images/public/qLdwhV4qw295poSCa7HinpnmXoN7dAPO"}}formatDate(ts,includeTime=true){const date=new Date(ts);const dateStr=date.toLocaleDateString("pt-BR");if(includeTime){const timeStr=date.toLocaleTimeString("pt-BR");return`${dateStr} ${timeStr}`}return dateStr}formatDateForInput(ts){const date=new Date(ts);return date.toISOString().split("T")[0]}render(){const{params:params}=this.config;this.overlay=document.createElement("div");this.overlay.className="myio-water-tank-modal-overlay";this.overlay.style.cssText=`\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: ${params.zIndex||1e4};\n opacity: 0;\n transition: opacity 0.3s ease;\n `;this.modal=document.createElement("div");this.modal.className="myio-water-tank-modal";this.modal.style.cssText=`\n background: white;\n border-radius: 12px;\n box-shadow: 0 4px 24px rgba(0, 0, 0, 0.15);\n width: ${params.ui?.width||700}px;\n max-width: 95vw;\n max-height: 90vh;\n display: flex;\n flex-direction: column;\n transform: scale(0.9);\n transition: transform 0.3s ease;\n `;this.modal.innerHTML=`\n ${this.renderHeader()}\n ${this.renderBody()}\n `;this.overlay.appendChild(this.modal);document.body.appendChild(this.overlay);this.attachEventListeners()}renderHeader(){const{context:context,params:params}=this.config;const title=params.ui?.title||`${this.i18n.title} - ${context.device.label}`;return`\n <div class="myio-water-tank-modal-header" style="\n padding: 20px 24px;\n border-bottom: 1px solid #e0e0e0;\n display: flex;\n align-items: center;\n justify-content: space-between;\n ">\n <h2 style="\n margin: 0;\n font-size: 20px;\n font-weight: 600;\n color: #2c3e50;\n ">${title}</h2>\n <button class="myio-water-tank-modal-close" style="\n background: none;\n border: none;\n font-size: 24px;\n color: #7f8c8d;\n cursor: pointer;\n padding: 0;\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 4px;\n transition: background 0.2s ease;\n " title="${this.i18n.close}">\n ×\n </button>\n </div>\n `}renderBody(){return`\n <div class="myio-water-tank-modal-body" style="\n padding: 24px;\n overflow-y: auto;\n flex: 1;\n ">\n ${this.renderDateRangePicker()}\n ${this.renderTankVisualization()}\n ${this.renderChart()}\n </div>\n ${this.renderFooter()}\n `}renderDateRangePicker(){const{params:params}=this.config;const startDate=this.formatDateForInput(params.startTs);const endDate=this.formatDateForInput(params.endTs);return`\n <div style="\n background: #f8f9fa;\n border: 1px solid #e0e0e0;\n border-radius: 8px;\n padding: 16px;\n margin-bottom: 20px;\n display: flex;\n align-items: center;\n gap: 16px;\n flex-wrap: wrap;\n ">\n <div style="display: flex; align-items: center; gap: 8px;">\n <label style="font-size: 14px; font-weight: 500; color: #2c3e50;">From:</label>\n <input type="date" id="myio-water-tank-start-date" value="${startDate}" style="\n padding: 8px 12px;\n border: 1px solid #ddd;\n border-radius: 6px;\n font-size: 14px;\n color: #2c3e50;\n cursor: pointer;\n "/>\n </div>\n <div style="display: flex; align-items: center; gap: 8px;">\n <label style="font-size: 14px; font-weight: 500; color: #2c3e50;">To:</label>\n <input type="date" id="myio-water-tank-end-date" value="${endDate}" style="\n padding: 8px 12px;\n border: 1px solid #ddd;\n border-radius: 6px;\n font-size: 14px;\n color: #2c3e50;\n cursor: pointer;\n "/>\n </div>\n <button id="myio-water-tank-apply-dates" style="\n background: linear-gradient(135deg, #3498db 0%, #2980b9 100%);\n color: white;\n border: none;\n padding: 8px 20px;\n border-radius: 6px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n ">\n Apply\n </button>\n </div>\n `}renderTankVisualization(){const{data:data,context:context}=this.config;let percentage=0;const percentagePoints=data.telemetry.filter(p=>p.key==="water_percentage");if(percentagePoints.length>0){const latestPercentage=percentagePoints[percentagePoints.length-1].value;percentage=latestPercentage<=1?latestPercentage*100:latestPercentage}else if(context.device.currentLevel!==void 0){const level=context.device.currentLevel;percentage=level<=1?level*100:level}const levelStatus=this.getLevelStatus(percentage);const tankImageUrl=this.getTankImageUrl(percentage);return`\n <div style="\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 32px;\n padding: 24px;\n background: linear-gradient(135deg, ${levelStatus.color}10 0%, ${levelStatus.color}05 100%);\n border: 1px solid ${levelStatus.color}30;\n border-radius: 12px;\n margin-bottom: 24px;\n ">\n <div style="\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 12px;\n ">\n <img src="${tankImageUrl}" alt="Water Tank" style="\n width: 120px;\n height: auto;\n filter: drop-shadow(0 4px 8px rgba(0,0,0,0.1));\n "/>\n <div style="\n background: ${levelStatus.color};\n color: white;\n padding: 4px 12px;\n border-radius: 20px;\n font-size: 12px;\n font-weight: 600;\n text-transform: uppercase;\n ">${levelStatus.label}</div>\n </div>\n\n <div style="\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 8px;\n ">\n <div style="\n font-size: 48px;\n font-weight: 700;\n color: ${levelStatus.color};\n line-height: 1;\n ">${percentage.toFixed(1)}%</div>\n <div style="\n font-size: 14px;\n color: #7f8c8d;\n font-weight: 500;\n ">${this.i18n.currentLevel}</div>\n </div>\n </div>\n `}renderChart(){const{data:data}=this.config;const waterLevelPoints=data.telemetry.filter(p=>p.key==="water_level"||p.key==="waterLevel"||p.key==="nivel"||p.key==="level");if(waterLevelPoints.length===0){return`\n <div style="\n background: #f8f9fa;\n border: 1px solid #e0e0e0;\n border-radius: 8px;\n padding: 48px;\n text-align: center;\n ">\n <div style="font-size: 48px; margin-bottom: 16px; opacity: 0.3;">📊</div>\n <div style="color: #7f8c8d; font-size: 16px;">${this.i18n.noData}</div>\n <div style="color: #bdc3c7; font-size: 13px; margin-top: 8px;">\n No water_level (m.c.a) data available for this period\n </div>\n </div>\n `}const firstTs=waterLevelPoints[0]?.ts;const lastTs=waterLevelPoints[waterLevelPoints.length-1]?.ts;return`\n <div style="\n background: white;\n border: 1px solid #e0e0e0;\n border-radius: 8px;\n padding: 20px;\n ">\n <h3 style="\n margin: 0 0 16px 0;\n font-size: 16px;\n font-weight: 600;\n color: #2c3e50;\n ">${this.i18n.levelChart}</h3>\n <canvas id="myio-water-tank-chart" style="width: 100%; height: 280px;"></canvas>\n ${firstTs&&lastTs?`\n <div style="\n margin-top: 12px;\n font-size: 12px;\n color: #7f8c8d;\n text-align: center;\n ">\n ${this.formatDate(firstTs,false)} — ${this.formatDate(lastTs,false)}\n (${waterLevelPoints.length} readings)\n </div>\n `:""}\n </div>\n `}renderFooter(){const{params:params}=this.config;if(!params.ui?.showExport){return""}return`\n <div class="myio-water-tank-modal-footer" style="\n padding: 16px 24px;\n border-top: 1px solid #e0e0e0;\n display: flex;\n justify-content: flex-end;\n gap: 12px;\n ">\n <button class="myio-water-tank-export-btn" style="\n background: #3498db;\n color: white;\n border: none;\n padding: 10px 20px;\n border-radius: 6px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: background 0.2s ease;\n ">\n ${this.i18n.exportCsv}\n </button>\n </div>\n `}attachEventListeners(){if(!this.overlay||!this.modal)return;const closeBtn=this.modal.querySelector(".myio-water-tank-modal-close");if(closeBtn){closeBtn.addEventListener("click",()=>this.config.onClose())}const exportBtn=this.modal.querySelector(".myio-water-tank-export-btn");if(exportBtn){exportBtn.addEventListener("click",()=>this.config.onExport())}const applyDatesBtn=this.modal.querySelector("#myio-water-tank-apply-dates");if(applyDatesBtn){applyDatesBtn.addEventListener("click",()=>this.handleDateRangeChange())}this.overlay.addEventListener("click",e=>{if(e.target===this.overlay){this.config.onClose()}});if(this.config.params.closeOnEsc){this.handleEscapeKey=this.handleEscapeKey.bind(this);document.addEventListener("keydown",this.handleEscapeKey)}requestAnimationFrame(()=>{this.renderCanvasChart()})}handleDateRangeChange(){if(!this.modal)return;const startInput=this.modal.querySelector("#myio-water-tank-start-date");const endInput=this.modal.querySelector("#myio-water-tank-end-date");if(startInput&&endInput){const startTs=new Date(startInput.value).setHours(0,0,0,0);const endTs=new Date(endInput.value).setHours(23,59,59,999);if(startTs>=endTs){alert("Start date must be before end date");return}console.log("[WaterTankModalView] Date range changed:",{startTs:startTs,endTs:endTs,startDate:new Date(startTs).toISOString(),endDate:new Date(endTs).toISOString()});if(this.config.onDateRangeChange){this.config.onDateRangeChange(startTs,endTs)}}}handleEscapeKey(e){if(e.key==="Escape"){this.config.onClose()}}renderCanvasChart(){const canvas=document.getElementById("myio-water-tank-chart");if(!canvas)return;const{data:data}=this.config;const points=data.telemetry.filter(p=>p.key==="water_level"||p.key==="waterLevel"||p.key==="nivel"||p.key==="level");if(points.length<2)return;const ctx=canvas.getContext("2d");if(!ctx)return;const rect=canvas.getBoundingClientRect();canvas.width=rect.width*window.devicePixelRatio;canvas.height=280*window.devicePixelRatio;ctx.scale(window.devicePixelRatio,window.devicePixelRatio);const width=rect.width;const height=280;const padding={top:20,right:20,bottom:40,left:60};ctx.clearRect(0,0,width,height);const values=points.map(p=>p.value);const minValue=Math.min(...values);const maxValue=Math.max(...values);const valueRange=maxValue-minValue||1;const valuePadding=valueRange*.1;const chartMinY=minValue-valuePadding;const chartMaxY=maxValue+valuePadding;const chartRangeY=chartMaxY-chartMinY;ctx.fillStyle="#fafafa";ctx.fillRect(padding.left,padding.top,width-padding.left-padding.right,height-padding.top-padding.bottom);ctx.strokeStyle="#e8e8e8";ctx.lineWidth=1;ctx.fillStyle="#666";ctx.font="11px Arial";ctx.textAlign="right";const ySteps=5;for(let i=0;i<=ySteps;i++){const y=padding.top+(height-padding.top-padding.bottom)*(i/ySteps);const value=chartMaxY-chartRangeY*i/ySteps;ctx.beginPath();ctx.moveTo(padding.left,y);ctx.lineTo(width-padding.right,y);ctx.stroke();ctx.fillText(`${value.toFixed(2)}`,padding.left-8,y+4)}ctx.save();ctx.translate(15,height/2);ctx.rotate(-Math.PI/2);ctx.textAlign="center";ctx.fillStyle="#666";ctx.font="12px Arial";ctx.fillText("m.c.a",0,0);ctx.restore();ctx.strokeStyle="#ccc";ctx.lineWidth=1;ctx.beginPath();ctx.moveTo(padding.left,padding.top);ctx.lineTo(padding.left,height-padding.bottom);ctx.lineTo(width-padding.right,height-padding.bottom);ctx.stroke();const chartWidth=width-padding.left-padding.right;const chartHeight=height-padding.top-padding.bottom;const xScale=chartWidth/(points.length-1);ctx.beginPath();ctx.moveTo(padding.left,height-padding.bottom);points.forEach((point,index)=>{const x=padding.left+index*xScale;const y=padding.top+chartHeight-(point.value-chartMinY)/chartRangeY*chartHeight;ctx.lineTo(x,y)});ctx.lineTo(padding.left+(points.length-1)*xScale,height-padding.bottom);ctx.closePath();const gradient=ctx.createLinearGradient(0,padding.top,0,height-padding.bottom);gradient.addColorStop(0,"rgba(52, 152, 219, 0.3)");gradient.addColorStop(1,"rgba(52, 152, 219, 0.05)");ctx.fillStyle=gradient;ctx.fill();ctx.strokeStyle="#3498db";ctx.lineWidth=2;ctx.lineJoin="round";ctx.lineCap="round";ctx.beginPath();points.forEach((point,index)=>{const x=padding.left+index*xScale;const y=padding.top+chartHeight-(point.value-chartMinY)/chartRangeY*chartHeight;if(index===0){ctx.moveTo(x,y)}else{ctx.lineTo(x,y)}});ctx.stroke();if(points.length<=50){ctx.fillStyle="#3498db";points.forEach((point,index)=>{const x=padding.left+index*xScale;const y=padding.top+chartHeight-(point.value-chartMinY)/chartRangeY*chartHeight;ctx.beginPath();ctx.arc(x,y,3,0,2*Math.PI);ctx.fill()})}ctx.fillStyle="#888";ctx.font="10px Arial";ctx.textAlign="center";const xLabelCount=Math.min(6,points.length);const xLabelStep=Math.floor(points.length/xLabelCount);for(let i=0;i<points.length;i+=xLabelStep){const x=padding.left+i*xScale;const date=new Date(points[i].ts);const label=`${date.getDate()}/${date.getMonth()+1}`;ctx.fillText(label,x,height-padding.bottom+16)}if(points.length>1){const lastX=padding.left+(points.length-1)*xScale;const lastDate=new Date(points[points.length-1].ts);const lastLabel=`${lastDate.getDate()}/${lastDate.getMonth()+1}`;ctx.fillText(lastLabel,lastX,height-padding.bottom+16)}}updateData(data){this.config.data=data;if(this.modal){const bodyEl=this.modal.querySelector(".myio-water-tank-modal-body");if(bodyEl){bodyEl.innerHTML=`\n ${this.renderDateRangePicker()}\n ${this.renderTankVisualization()}\n ${this.renderChart()}\n `;const applyDatesBtn=this.modal.querySelector("#myio-water-tank-apply-dates");if(applyDatesBtn){applyDatesBtn.addEventListener("click",()=>this.handleDateRangeChange())}requestAnimationFrame(()=>{this.renderCanvasChart()})}}}show(){if(!this.overlay||!this.modal)return;requestAnimationFrame(()=>{if(this.overlay&&this.modal){this.overlay.style.opacity="1";this.modal.style.transform="scale(1)"}})}destroy(){if(this.config.params.closeOnEsc){document.removeEventListener("keydown",this.handleEscapeKey)}if(this.overlay&&this.modal){this.overlay.style.opacity="0";this.modal.style.transform="scale(0.9)";setTimeout(()=>{if(this.overlay){document.body.removeChild(this.overlay);this.overlay=null;this.modal=null}},300)}}};var WaterTankModal=class{view=null;options;context;data=null;constructor(options){this.options=this.normalizeOptions(options);this.context=this.buildContext(this.options)}normalizeOptions(options){return{...options,tbApiHost:options.tbApiHost||window.location.origin,timezone:options.timezone||"America/Sao_Paulo",theme:options.theme||"light",closeOnEsc:options.closeOnEsc!==false,zIndex:options.zIndex||1e4,telemetryKeys:options.telemetryKeys||["waterLevel","nivel","level"],aggregation:options.aggregation||"NONE",limit:options.limit||1e3,ui:{title:options.ui?.title||`Water Tank - ${options.label||options.deviceId}`,width:options.ui?.width||900,height:options.ui?.height||600,showExport:options.ui?.showExport!==false,showLevelIndicator:options.ui?.showLevelIndicator!==false}}}buildContext(options){return{device:{id:options.deviceId,label:options.label||options.deviceId,type:options.deviceType,currentLevel:options.currentLevel},metadata:{slaveId:options.slaveId,centralId:options.centralId,ingestionId:options.ingestionId},timeRange:{startTs:options.startTs,endTs:options.endTs,timezone:options.timezone}}}async fetchTelemetryData(){const config={tbApiHost:this.options.tbApiHost,tbJwtToken:this.options.tbJwtToken,deviceId:this.options.deviceId,keys:this.options.telemetryKeys,startTs:this.options.startTs,endTs:this.options.endTs,aggregation:this.options.aggregation,limit:this.options.limit};console.log("[WaterTankModal] Fetching telemetry data:",{deviceId:config.deviceId,keys:config.keys,timeRange:{start:new Date(config.startTs).toISOString(),end:new Date(config.endTs).toISOString()}});try{const keysParam=config.keys.join(",");const url=`${config.tbApiHost}/api/plugins/telemetry/DEVICE/${config.deviceId}/values/timeseries?keys=${keysParam}&startTs=${config.startTs}&endTs=${config.endTs}&limit=${config.limit}`;if(config.aggregation&&config.aggregation!=="NONE"){}const response=await fetch(url,{method:"GET",headers:{"Content-Type":"application/json","X-Authorization":`Bearer ${config.tbJwtToken}`}});if(!response.ok){if(response.status===401){throw this.createError("AUTH_ERROR","Authentication failed. Please login again.")}if(response.status===404){throw this.createError("NO_DATA","Device not found or no telemetry data available.")}throw this.createError("NETWORK_ERROR",`Failed to fetch data: ${response.statusText}`)}const rawData=await response.json();console.log("[WaterTankModal] Raw telemetry response:",rawData);const telemetryData=this.transformTelemetryData(rawData,config.keys);const summary=this.calculateSummary(telemetryData);const result={deviceId:config.deviceId,telemetry:telemetryData,summary:summary,metadata:{keys:config.keys,aggregation:config.aggregation||"NONE",limit:config.limit||1e3}};console.log("[WaterTankModal] Processed telemetry data:",{pointCount:result.telemetry.length,summary:result.summary});return result}catch(error){console.error("[WaterTankModal] Error fetching telemetry:",error);if(error instanceof Error&&"code"in error){throw error}throw this.createError("NETWORK_ERROR",`Failed to fetch telemetry data: ${error instanceof Error?error.message:"Unknown error"}`,error)}}transformTelemetryData(rawData,keys){const allPoints=[];for(const key of keys){if(rawData[key]&&Array.isArray(rawData[key])){const keyPoints=rawData[key].map(point=>({ts:point.ts,value:typeof point.value==="string"?parseFloat(point.value):point.value,key:key}));allPoints.push(...keyPoints)}}allPoints.sort((a,b)=>a.ts-b.ts);const uniquePoints=[];const seenTimestamps=new Set;for(const point of allPoints){if(!seenTimestamps.has(point.ts)){seenTimestamps.add(point.ts);uniquePoints.push(point)}}return uniquePoints}calculateSummary(telemetry){if(telemetry.length===0){return{currentLevel:this.options.currentLevel,avgLevel:0,minLevel:0,maxLevel:0,totalReadings:0}}const values=telemetry.map(p=>p.value);const sum=values.reduce((acc,val)=>acc+val,0);const avg=sum/values.length;const min=Math.min(...values);const max=Math.max(...values);const lastPoint=telemetry[telemetry.length-1];return{currentLevel:lastPoint.value,avgLevel:avg,minLevel:min,maxLevel:max,totalReadings:telemetry.length,firstReadingTs:telemetry[0].ts,lastReadingTs:lastPoint.ts}}createError(code,message,cause){const error={code:code,message:message,cause:cause};switch(code){case"AUTH_ERROR":case"TOKEN_EXPIRED":error.userAction="RE_AUTH";break;case"NETWORK_ERROR":error.userAction="RETRY";break;case"VALIDATION_ERROR":error.userAction="FIX_INPUT";break;default:error.userAction="CONTACT_ADMIN"}return error}async show(){try{console.log("[WaterTankModal] Initializing modal...");this.data=await this.fetchTelemetryData();if(this.options.onDataLoaded){try{this.options.onDataLoaded(this.data)}catch(callbackError){console.warn("[WaterTankModal] onDataLoaded callback error:",callbackError)}}this.view=new WaterTankModalView({context:this.context,params:this.options,data:this.data,onExport:()=>this.handleExport(),onError:error=>this.handleError(error),onClose:()=>this.close(),onDateRangeChange:(startTs,endTs)=>this.handleDateRangeChange(startTs,endTs)});this.view.render();this.view.show();if(this.options.onOpen){try{this.options.onOpen(this.context)}catch(callbackError){console.warn("[WaterTankModal] onOpen callback error:",callbackError)}}console.log("[WaterTankModal] Modal opened successfully");return{close:()=>this.close()}}catch(error){console.error("[WaterTankModal] Failed to show modal:",error);this.handleError(error);throw error}}close(){console.log("[WaterTankModal] Closing modal");if(this.view){this.view.destroy();this.view=null}this.handleClose()}async handleDateRangeChange(startTs,endTs){console.log("[WaterTankModal] Date range changed:",{startTs:startTs,endTs:endTs,startDate:new Date(startTs).toISOString(),endDate:new Date(endTs).toISOString()});this.options.startTs=startTs;this.options.endTs=endTs;this.context.timeRange.startTs=startTs;this.context.timeRange.endTs=endTs;try{console.log("[WaterTankModal] Fetching new data for date range...");this.data=await this.fetchTelemetryData();if(this.view){this.view.updateData(this.data)}if(this.options.onDataLoaded){try{this.options.onDataLoaded(this.data)}catch(callbackError){console.warn("[WaterTankModal] onDataLoaded callback error:",callbackError)}}console.log("[WaterTankModal] Data refreshed successfully")}catch(error){console.error("[WaterTankModal] Failed to fetch data for new date range:",error);this.handleError(error)}}handleExport(){if(!this.data){console.warn("[WaterTankModal] No data to export");return}console.log("[WaterTankModal] Exporting CSV...");try{const headers=["Timestamp","Date","Time","Level (%)","Key"];const rows=this.data.telemetry.map(point=>{const date=new Date(point.ts);return[point.ts.toString(),date.toLocaleDateString("pt-BR"),date.toLocaleTimeString("pt-BR"),point.value.toFixed(2),point.key||"waterLevel"]});const csvContent=[headers.join(","),...rows.map(row=>row.join(","))].join("\n");const blob=new Blob([csvContent],{type:"text/csv;charset=utf-8;"});const link=document.createElement("a");const url=URL.createObjectURL(blob);const filename=`water-tank-${this.options.deviceId}-${Date.now()}.csv`;link.setAttribute("href",url);link.setAttribute("download",filename);link.style.visibility="hidden";document.body.appendChild(link);link.click();document.body.removeChild(link);console.log("[WaterTankModal] CSV exported successfully:",filename)}catch(error){console.error("[WaterTankModal] Export failed:",error);this.handleError(this.createError("UNKNOWN_ERROR","Failed to export CSV"))}}handleError(error){console.error("[WaterTankModal] Error:",error);if(this.options.onError){try{this.options.onError(error)}catch(callbackError){console.warn("[WaterTankModal] onError callback error:",callbackError)}}}handleClose(){if(this.options.onClose){try{this.options.onClose()}catch(callbackError){console.warn("[WaterTankModal] onClose callback error:",callbackError)}}}};async function openDashboardPopupWaterTank(options){try{validateOptions2(options);console.log("[openDashboardPopupWaterTank] Opening water tank modal with options:",{deviceId:options.deviceId,label:options.label,currentLevel:options.currentLevel,timeRange:{start:new Date(options.startTs).toISOString(),end:new Date(options.endTs).toISOString()},telemetryKeys:options.telemetryKeys||["waterLevel","nivel","level"]});const modal=new WaterTankModal(options);const modalHandle=await modal.show();console.log("[openDashboardPopupWaterTank] Water tank modal opened successfully");return modalHandle}catch(error){console.error("[openDashboardPopupWaterTank] Error opening modal:",error);if(options.onError){try{options.onError({code:error instanceof Error&&error.message.includes("validation")?"VALIDATION_ERROR":error instanceof Error&&error.message.includes("auth")?"AUTH_ERROR":"UNKNOWN_ERROR",message:error instanceof Error?error.message:"Unknown error occurred",cause:error})}catch(callbackError){console.warn("[openDashboardPopupWaterTank] onError callback failed:",callbackError)}}throw error}}function validateOptions2(options){const errors=[];if(!options.deviceId||typeof options.deviceId!=="string"){errors.push("deviceId is required and must be a string")}if(!options.tbJwtToken||typeof options.tbJwtToken!=="string"){errors.push("tbJwtToken is required and must be a string")}if(typeof options.startTs!=="number"||isNaN(options.startTs)){errors.push("startTs is required and must be a valid timestamp number")}if(typeof options.endTs!=="number"||isNaN(options.endTs)){errors.push("endTs is required and must be a valid timestamp number")}if(options.startTs&&options.endTs&&options.startTs>=options.endTs){errors.push("startTs must be before endTs")}if(options.currentLevel!==void 0){const level=Number(options.currentLevel);if(isNaN(level)||level<0||level>100){errors.push("currentLevel must be a number between 0 and 100")}}if(options.limit!==void 0){const limit=Number(options.limit);if(isNaN(limit)||limit<1||limit>1e4){errors.push("limit must be a number between 1 and 10000")}}if(options.aggregation!==void 0){const validAggregations=["NONE","MIN","MAX","AVG","SUM","COUNT"];if(!validAggregations.includes(options.aggregation)){errors.push(`aggregation must be one of: ${validAggregations.join(", ")}`)}}if(errors.length>0){throw new Error(`Validation failed:\n- ${errors.join("\n- ")}`)}}var SettingsModalView=class{container;modal;form;config;focusTrapElements=[];originalActiveElement=null;constructor(config){this.config=config;this.createModal()}render(initialData){this.originalActiveElement=document.activeElement;document.body.appendChild(this.container);let formData={...initialData};if(initialData.deviceMapInstaneousPower&&typeof initialData.deviceMapInstaneousPower==="object"){console.log("[SettingsModalView] Configuração salva encontrada (Device Scope). Processando...");const flatLimits=this.parseDeviceSavedLimits(initialData.deviceMapInstaneousPower);formData={...formData,...flatLimits}}this.populateForm(formData);this.attachEventListeners();this.setupAccessibility();this.setupFocusTrap();this.applyTheme();this.fetchLatestConsumptionTelemetry()}close(){this.teardownFocusTrap();if(this.originalActiveElement&&"focus"in this.originalActiveElement){this.originalActiveElement.focus()}if(this.container.parentNode){this.container.parentNode.removeChild(this.container)}}updateMapInstantaneousPower(mapInstantaneousPower){this.config.mapInstantaneousPower=mapInstantaneousPower;console.log("[SettingsModalView] RFC-0080: Updated mapInstantaneousPower config")}showError(message){const errorEl=this.modal.querySelector(".error-message");if(errorEl){errorEl.textContent=message;errorEl.style.display="block";errorEl.setAttribute("role","alert");errorEl.setAttribute("aria-live","polite")}}hideError(){const errorEl=this.modal.querySelector(".error-message");if(errorEl){errorEl.style.display="none";errorEl.removeAttribute("role");errorEl.removeAttribute("aria-live")}}showLoadingState(isLoading){const saveBtn=this.modal.querySelector(".btn-save");const cancelBtn=this.modal.querySelector(".btn-cancel");const formInputs=this.modal.querySelectorAll("input, select, textarea");if(saveBtn){saveBtn.disabled=isLoading;saveBtn.textContent=isLoading?"Salvando...":"Salvar"}if(cancelBtn){cancelBtn.disabled=isLoading}formInputs.forEach(input=>{input.disabled=isLoading})}formatDomainLabel(domain){const MAP={energy:"de energia",water:"de água",temperature:"de temperatura"};return MAP[domain]}getTelemetryLabelByDomain(){const domain=this.config.domain||"energy";const MAP={energy:"de Consumo",water:"de Água",temperature:"de Temperatura"};return MAP[domain]||"de Consumo"}getFormData(){const formData=new FormData(this.form);const data={};for(const[key,value]of formData.entries()){if(typeof value==="string"){if(["maxDailyKwh","maxNightKwh","maxBusinessKwh","minTemperature","maxTemperature","minWaterLevel","maxWaterLevel"].includes(key)){const num=parseFloat(value);if(!isNaN(num)){if(key.includes("Kwh")&&num<0){continue}if(key.includes("WaterLevel")){if(num<0||num>100){continue}}data[key]=num}}else if(value.trim()){data[key]=value.trim()}}}return data}createModal(){this.container=document.createElement("div");this.container.className="myio-settings-modal-overlay";this.container.innerHTML=this.getModalHTML();this.modal=this.container.querySelector(".myio-settings-modal");this.form=this.modal.querySelector("form")}getModalHTML(){const width=typeof this.config.width==="number"?`${this.config.width}px`:this.config.width;return`\n <div class="myio-settings-modal-overlay" role="dialog" aria-modal="true" aria-labelledby="modal-title">\n <div class="myio-settings-modal" style="width: ${width}">\n <div class="modal-header">\n <h3 id="modal-title">Configurações</h3>\n <button type="button" class="close-btn" aria-label="Fechar">×</button>\n </div>\n <div class="modal-body">\n <div class="error-message" style="display: none;" role="alert" aria-live="polite"></div>\n <form novalidate>\n ${this.getFormHTML()}\n </form>\n </div>\n <div class="modal-footer">\n <button type="button" class="btn-cancel">Fechar</button>\n <button type="button" class="btn-save btn-primary">Salvar</button>\n </div>\n </div>\n </div>\n ${this.getModalCSS()}\n `}getFormHTML(){const deviceType=this.config.deviceType;const customerName=this.config.customerName;const hasCustomerName=customerName&&customerName.trim()!=="";return`\n <div class="form-layout">\n ${hasCustomerName?`\n \x3c!-- RFC-0077/0078: Shopping name display with device type icon --\x3e\n <div class="customer-name-container">\n <div class="customer-info-row">\n <div class="device-type-icon-wrapper">\n ${this.getDeviceTypeIcon(deviceType)}\n </div>\n <div class="customer-info-content">\n <div class="customer-name-label">Shopping</div>\n <div class="customer-name-value">\n <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="shopping-icon"><path d="M4 22h16"/><path d="M7 22V4a2 2 0 0 1 2-2h6a2 2 0 0 1 2 2v18"/></svg>\n <span class="customer-name-text">${customerName}</span>\n </div>\n </div>\n </div>\n </div>\n `:""}\n\n \x3c!-- Top Row: Two cards side by side --\x3e\n <div class="form-columns">\n \x3c!-- Left Column: Device Label --\x3e\n <div class="form-column">\n <div class="form-card">\n <h4 class="section-title device-label-title">${this.config.deviceLabel||"NÃO INFORMADO"}</h4>\n\n <div class="form-group">\n <label for="label">Etiqueta</label>\n <input type="text" id="label" name="label" required maxlength="255">\n </div>\n\n <div class="form-group">\n <label for="floor">Andar</label>\n <input type="text" id="floor" name="floor" maxlength="50">\n </div>\n\n <div class="form-group">\n <label for="identifier">Identificador / LUC / SUC</label>\n <input type="text" id="identifier" name="identifier" maxlength="20" readonly>\n </div>\n </div>\n </div>\n\n \x3c!-- Right Column: Alarms --\x3e\n <div class="form-column">\n ${this.getAlarmsHTML(deviceType)}\n </div>\n </div>\n\n \x3c!-- Bottom Row: Connection Info spanning full width --\x3e\n ${this.getConnectionInfoHTML()}\n\n \x3c!-- RFC-0077: Power Limits Configuration (only for energy domain and when deviceType is available) --\x3e\n ${this.config.domain==="energy"&&this.config.deviceType?this.getPowerLimitsHTML():""}\n </div>\n `}getAlarmsHTML(deviceType){switch(deviceType){case"TERMOSTATO":return this.getThermostatAlarmsHTML();case"CAIXA_DAGUA":return this.getWaterTankAlarmsHTML();default:return this.getConsumptionAlarmsHTML()}}getConsumptionAlarmsHTML(){const unit=this.config.domain==="water"?"L":"kWh";return`\n <div class="form-card">\n <h4 class="section-title">Alarmes ${this.formatDomainLabel(this.config.domain)}</h4>\n\n <div class="form-group">\n <label for="maxDailyKwh">Consumo Máximo Diário (${unit})</label>\n <input type="number" id="maxDailyKwh" name="maxDailyKwh" min="0" step="0.1">\n </div>\n\n <div class="form-group">\n <label for="maxNightKwh">Consumo Máximo na Madrugada (0h–06h)</label>\n <input type="number" id="maxNightKwh" name="maxNightKwh" min="0" step="0.1">\n </div>\n\n <div class="form-group">\n <label for="maxBusinessKwh">Consumo Máximo Horário Comercial (09h–22h)</label>\n <input type="number" id="maxBusinessKwh" name="maxBusinessKwh" min="0" step="0.1">\n </div>\n </div>\n `}getThermostatAlarmsHTML(){return`\n <div class="form-card">\n <h4 class="section-title">Alarmes de Temperatura</h4>\n\n <div class="form-group">\n <label for="minTemperature">Temperatura Mínima (°C)</label>\n <input type="number" id="minTemperature" name="minTemperature" step="0.1">\n </div>\n\n <div class="form-group">\n <label for="maxTemperature">Temperatura Máxima (°C)</label>\n <input type="number" id="maxTemperature" name="maxTemperature" step="0.1">\n </div>\n </div>\n `}getWaterTankAlarmsHTML(){return`\n <div class="form-card">\n <h4 class="section-title">Alarmes de Nível</h4>\n\n <div class="form-group">\n <label for="minWaterLevel">Nível Mínimo (%)</label>\n <input type="number" id="minWaterLevel" name="minWaterLevel" min="0" max="100" step="0.1" placeholder="Risco de falta d'água">\n </div>\n\n <div class="form-group">\n <label for="maxWaterLevel">Nível Máximo (%)</label>\n <input type="number" id="maxWaterLevel" name="maxWaterLevel" min="0" max="100" step="0.1" placeholder="Risco de transbordar">\n </div>\n </div>\n `}getDeviceTypeIcon(deviceType){let normalizedType=(deviceType||"").toUpperCase();if(normalizedType==="3F_MEDIDOR"){const deviceProfile=this.config.deviceProfile;if(deviceProfile&&deviceProfile!=="N/D"&&deviceProfile.trim()!==""){normalizedType=deviceProfile.toUpperCase()}}const energyDevices=["COMPRESSOR","VENTILADOR","ESCADA_ROLANTE","ELEVADOR","MOTOR","3F_MEDIDOR","RELOGIO","ENTRADA","SUBESTACAO","BOMBA","CHILLER","AR_CONDICIONADO","HVAC","FANCOIL"];const waterDevices=["HIDROMETRO","CAIXA_DAGUA","TANK"];if(energyDevices.includes(normalizedType)){return`\n <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="device-type-icon energy-icon">\n <path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/>\n </svg>\n `}else if(waterDevices.includes(normalizedType)){return`\n <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="device-type-icon water-icon">\n <path d="M12 2.69l5.66 5.66a8 8 0 1 1-11.31 0z"/>\n </svg>\n `}else{return`\n <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="device-type-icon generic-icon">\n <rect x="4" y="4" width="16" height="16" rx="2" ry="2"/>\n <rect x="9" y="9" width="6" height="6"/>\n <line x1="9" y1="1" x2="9" y2="4"/>\n <line x1="15" y1="1" x2="15" y2="4"/>\n <line x1="9" y1="20" x2="9" y2="23"/>\n <line x1="15" y1="20" x2="15" y2="23"/>\n <line x1="20" y1="9" x2="23" y2="9"/>\n <line x1="20" y1="14" x2="23" y2="14"/>\n <line x1="1" y1="9" x2="4" y2="9"/>\n <line x1="1" y1="14" x2="4" y2="14"/>\n </svg>\n `}}getConsumptionLimits(){const mapPower=this.config.mapInstantaneousPower||{};const limitsByType=mapPower.limitsByInstantaneoustPowerType||[];const consumptionGroup=limitsByType.find(group=>group.telemetryType==="consumption");const targetDeviceType=this.config.deviceType;const itemsByDevice=consumptionGroup?.itemsByDeviceType||[];const deviceSettings=itemsByDevice.find(item=>item.deviceType===targetDeviceType);const limitsList=deviceSettings?.limitsByDeviceStatus||[];const getValues=statusName=>{const statusObj=limitsList.find(l=>l.deviceStatusName===statusName);return statusObj?.limitsValues||{baseValue:"",topValue:""}};return{hasConfig:!!deviceSettings,description:deviceSettings?.description||"Padrão do Sistema",standby:getValues("standBy"),normal:getValues("normal"),alert:getValues("alert"),failure:getValues("failure")}}getPowerLimitsHTML(){const globalData=this.getConsumptionLimits();const fmtRange=val=>{if(val.baseValue===""||val.baseValue===void 0)return"—";return`${val.baseValue} a ${val.topValue} W`};return`\n <div class="form-card power-limits-card">\n <div class="power-limits-header">\n <h4 class="section-title">Configuração de Limites de Telemetrias Instantâneas</h4>\n <div class="power-limits-subtitle">\n Monitoramento de Consumo (W) para: <strong>${this.config.deviceType||"N/D"}</strong>\n </div>\n </div>\n\n <div class="power-limits-controls-row" style="display:flex; gap:20px; align-items: flex-end; margin-bottom: 15px;">\n <div class="telemetry-selector-group" style="flex:1">\n <label for="telemetryType" style="display:block; margin-bottom:5px; font-weight:500;">Tipo de Telemetria</label>\n <select id="telemetryType" name="telemetryType" class="form-select">\n <option value="consumption" selected>Potência Ativa (W)</option>\n </select>\n </div>\n </div>\n\n <div class="global-reference-container">\n <div class="global-ref-header">\n <span>🌐 Referência Global (${globalData.description})</span>\n </div>\n <div class="global-values-grid">\n <div class="global-value-item">\n <span class="g-status">StandBy 🔌</span>\n <span class="g-range">${fmtRange(globalData.standby)}</span>\n </div>\n <div class="global-value-item">\n <span class="g-status">Normal ⚡</span>\n <span class="g-range">${fmtRange(globalData.normal)}</span>\n </div>\n <div class="global-value-item">\n <span class="g-status">Alerta ⚠️</span>\n <span class="g-range">${fmtRange(globalData.alert)}</span>\n </div>\n <div class="global-value-item">\n <span class="g-status">Falha 🚨</span>\n <span class="g-range">${fmtRange(globalData.failure)}</span>\n </div>\n </div>\n </div>\n\n <div class="power-limits-table-wrapper">\n <table class="power-limits-table">\n <thead>\n <tr>\n <th style="width: 30%">Status</th>\n <th style="width: 35%">Mínimo (W)</th>\n <th style="width: 35%">Máximo (W)</th>\n </tr>\n </thead>\n <tbody>\n <tr class="limit-row">\n <td class="status-label"><span class="status-icon">🔌</span> StandBy</td>\n <td>\n <input type="number" \n name="standbyLimitDownConsumption" \n class="limit-input js-limit-input" \n min="0" step="1" \n placeholder="Vazio" \n data-global-value="${globalData.standby.baseValue}">\n </td>\n <td>\n <input type="number" \n name="standbyLimitUpConsumption" \n class="limit-input js-limit-input" \n min="0" step="1" \n placeholder="Vazio" \n data-global-value="${globalData.standby.topValue}">\n </td>\n </tr>\n \n <tr class="limit-row">\n <td class="status-label"><span class="status-icon">⚡</span> Normal</td>\n <td>\n <input type="number" \n name="normalLimitDownConsumption" \n class="limit-input js-limit-input" \n min="0" step="1" \n placeholder="Vazio" \n data-global-value="${globalData.normal.baseValue}">\n </td>\n <td>\n <input type="number" \n name="normalLimitUpConsumption" \n class="limit-input js-limit-input" \n min="0" step="1" \n placeholder="Vazio" \n data-global-value="${globalData.normal.topValue}">\n </td>\n </tr>\n\n <tr class="limit-row">\n <td class="status-label"><span class="status-icon">⚠️</span> Alerta</td>\n <td>\n <input type="number" \n name="alertLimitDownConsumption" \n class="limit-input js-limit-input" \n min="0" step="1" \n placeholder="Vazio" \n data-global-value="${globalData.alert.baseValue}">\n </td>\n <td>\n <input type="number" \n name="alertLimitUpConsumption" \n class="limit-input js-limit-input" \n min="0" step="1" \n placeholder="Vazio" \n data-global-value="${globalData.alert.topValue}">\n </td>\n </tr>\n\n <tr class="limit-row">\n <td class="status-label"><span class="status-icon">🚨</span> Falha</td>\n <td>\n <input type="number" \n name="failureLimitDownConsumption" \n class="limit-input js-limit-input" \n min="0" step="1" \n placeholder="Vazio" \n data-global-value="${globalData.failure.baseValue}">\n </td>\n <td>\n <input type="number" \n name="failureLimitUpConsumption" \n class="limit-input js-limit-input" \n min="0" step="1" \n placeholder="Vazio" \n data-global-value="${globalData.failure.topValue}">\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n\n <div class="power-limits-actions">\n <button type="button" class="btn-copy-global" id="btnCopyFromGlobal">\n ⬇️ Copiar valores da Referência Global\n </button>\n \n <button type="button" class="btn-clear-overrides" id="btnClearInputs">\n 🗑️ Limpar Campos\n </button>\n </div>\n </div>\n `}calculateTimeBetweenDates(data1,data2){if(!(data1 instanceof Date)||!(data2 instanceof Date)){console.error("Entradas inválidas. As duas entradas devem ser objetos Date.");return"Datas inválidas"}const diffMs=Math.abs(data1.getTime()-data2.getTime());const msPorMinuto=1e3*60;const msPorHora=msPorMinuto*60;const msPorDia=msPorHora*24;if(diffMs>=msPorDia){const dias=Math.floor(diffMs/msPorDia);return`${dias} ${dias===1?"dia":"dias"}`}if(diffMs>=msPorHora){const horas=Math.floor(diffMs/msPorHora);return`${horas} ${horas===1?"hora":"horas"}`}const minutos=Math.round(diffMs/msPorMinuto);return`${minutos} ${minutos===1?"minuto":"minutos"}`}getConnectionInfoHTML(){if(!this.config.connectionData){return""}const{centralName:centralName,connectionStatusTime:connectionStatusTime,timeVal:timeVal,deviceStatus:deviceStatus,lastDisconnectTime:lastDisconnectTime}=this.config.connectionData;let disconnectionIntervalFormatted="N/A";if(lastDisconnectTime&&connectionStatusTime){try{const disconnectDate=new Date(lastDisconnectTime);const reconnectDate=new Date(connectionStatusTime);const disconnectFormatted=disconnectDate.toLocaleString("pt-BR",{day:"2-digit",month:"2-digit",year:"numeric",hour:"2-digit",minute:"2-digit",second:"2-digit"});const reconnectFormatted=reconnectDate.toLocaleString("pt-BR",{day:"2-digit",month:"2-digit",year:"numeric",hour:"2-digit",minute:"2-digit",second:"2-digit"});const diffMs=reconnectDate.getTime()-disconnectDate.getTime();const diffSeconds=Math.floor(diffMs/1e3);const diffMinutes=Math.floor(diffSeconds/60);const diffHours=Math.floor(diffMinutes/60);const diffDays=Math.floor(diffHours/24);let durationText="";if(diffDays>0){const remainingHours=diffHours%24;durationText=`${diffDays} dia${diffDays>1?"s":""}${remainingHours>0?` e ${remainingHours} hora${remainingHours>1?"s":""}`:""}`}else if(diffHours>0){const remainingMinutes=diffMinutes%60;durationText=`${diffHours} hora${diffHours>1?"s":""}${remainingMinutes>0?` e ${remainingMinutes} minuto${remainingMinutes>1?"s":""}`:""}`}else if(diffMinutes>0){const remainingSeconds=diffSeconds%60;durationText=`${diffMinutes} minuto${diffMinutes>1?"s":""}${remainingSeconds>0?` e ${remainingSeconds} segundo${remainingSeconds>1?"s":""}`:""}`}else{durationText=`${diffSeconds} segundo${diffSeconds!==1?"s":""}`}disconnectionIntervalFormatted=`${disconnectFormatted} até ${reconnectFormatted} (${durationText})`}catch(e){disconnectionIntervalFormatted="Formato inválido"}}let connectionTimeFormatted="—";let timeSinceLastConnection="";let isCurrentlyConnected=false;if(connectionStatusTime){try{const connectDate=new Date(connectionStatusTime);const disconnectDate=lastDisconnectTime?new Date(lastDisconnectTime):null;if(!disconnectDate||connectDate.getTime()>disconnectDate.getTime()){isCurrentlyConnected=true;connectionTimeFormatted=connectDate.toLocaleString("pt-BR",{day:"2-digit",month:"2-digit",year:"numeric",hour:"2-digit",minute:"2-digit"});const now=new Date;const diffMs=now.getTime()-connectDate.getTime();const diffMinutes=Math.floor(diffMs/(1e3*60));const diffHours=Math.floor(diffMinutes/60);const diffDays=Math.floor(diffHours/24);const remainingMinutes=diffMinutes%60;if(diffDays>0){const remainingHours=diffHours%24;timeSinceLastConnection=`(${diffDays}d:${remainingHours.toString().padStart(2,"0")}hs:${remainingMinutes.toString().padStart(2,"0")}mins atrás)`}else if(diffHours>0){timeSinceLastConnection=`(${diffHours}hs:${remainingMinutes.toString().padStart(2,"0")}mins atrás)`}else if(diffMinutes>0){timeSinceLastConnection=`(${diffMinutes}mins atrás)`}else{timeSinceLastConnection="(agora)"}}}catch(e){connectionTimeFormatted="—"}}let telemetryTimeFormatted="N/A";let timeSinceLastTelemetry="";if(timeVal){try{const telemetryDate=new Date(timeVal);telemetryTimeFormatted=telemetryDate.toLocaleString("pt-BR",{day:"2-digit",month:"2-digit",year:"numeric",hour:"2-digit",minute:"2-digit"});const now=new Date;const diffMs=now.getTime()-telemetryDate.getTime();const diffMinutes=Math.floor(diffMs/(1e3*60));const diffHours=Math.floor(diffMinutes/60);const diffDays=Math.floor(diffHours/24);if(diffDays>0){timeSinceLastTelemetry=`(${diffDays}d atrás)`}else if(diffHours>0){timeSinceLastTelemetry=`(${diffHours}h atrás)`}else if(diffMinutes>0){timeSinceLastTelemetry=`(${diffMinutes}min atrás)`}else{timeSinceLastTelemetry="(agora)"}}catch(e){telemetryTimeFormatted="Formato inválido"}}const statusMap={ok:{text:"ONLINE",color:"#22c55e"},alert:{text:"Atenção",color:"#f59e0b"},fail:{text:"OFFLINE",color:"#ef4444"},not_installed:{text:"Não instalado",color:"#94a3b8"},unknown:{text:"Sem informação",color:"#94a3b8"}};const statusInfo=statusMap[mapDeviceStatusToCardStatus(deviceStatus)]||{text:"Desconhecido",color:"#6b7280"};return`\n <div class="form-card info-card-wide">\n <h4 class="section-title">\n <svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" style="vertical-align: text-bottom; margin-right: 6px;">\n <path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z"/>\n <path d="m8.93 6.588-2.29.287-.082.38.45.083c.294.07.352.176.288.469l-.738 3.468c-.194.897.105 1.319.808 1.319.545 0 1.178-.252 1.465-.598l.088-.416c-.2.176-.492.246-.686.246-.275 0-.375-.193-.304-.533L8.93 6.588zM9 4.5a1 1 0 1 1-2 0 1 1 0 0 1 2 0z"/>\n </svg>\n Informações de Conexão\n </h4>\n\n <div class="info-grid">\n <div class="info-row">\n <span class="info-label">Central:</span>\n <span class="info-value">${centralName||"N/A"}${this.config.customerName?` (${this.config.customerName})`:""}</span>\n </div>\n\n <div class="info-row">\n <span class="info-label">Status:</span>\n <span class="info-value" style="color: ${statusInfo.color}; font-weight: 600;">\n ${statusInfo.text}\n </span>\n </div>\n\n <div class="info-row">\n <span class="info-label">Conectado desde:</span>\n <span class="info-value">\n ${connectionTimeFormatted}\n ${timeSinceLastConnection?`<span class="time-since">${timeSinceLastConnection}</span>`:""}\n </span>\n </div>\n\n <div class="info-row">\n <span class="info-label">Último check status:</span>\n <span class="info-value">\n ${telemetryTimeFormatted}\n ${timeSinceLastTelemetry?`<span class="time-since">${timeSinceLastTelemetry}</span>`:""}\n </span>\n </div>\n <div class="info-row full-width">\n <span class="info-label">Último intervalo desconectado:</span>\n <span class="info-value disconnect-interval">\n ${disconnectionIntervalFormatted}\n </span>\n </div>\n <div class="info-row full-width">\n <span class="info-label">Última Telemetria ${this.getTelemetryLabelByDomain()}:</span>\n <span class="info-value" id="lastConsumptionTelemetry">\n <span class="loading-text">Carregando...</span>\n </span>\n </div>\n </div>\n </div>\n `}getModalCSS(){return`\n <style>\n .myio-settings-modal-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10000;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n }\n \n .myio-settings-modal {\n background: white;\n border-radius: 8px;\n box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);\n max-width: 95vw;\n max-height: 90vh;\n width: 1000px;\n overflow: hidden;\n display: flex;\n flex-direction: column;\n }\n \n .modal-header {\n background: #3e1a7d;\n color: white;\n padding: 20px 24px;\n display: flex;\n justify-content: space-between;\n align-items: center;\n }\n \n .modal-header h3 {\n margin: 0;\n font-size: 18px;\n font-weight: 600;\n color: white;\n }\n \n .close-btn {\n background: none;\n border: none;\n font-size: 24px;\n cursor: pointer;\n color: white;\n padding: 0;\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 4px;\n }\n \n .close-btn:hover {\n background: rgba(255, 255, 255, 0.1);\n }\n \n .modal-body {\n padding: 24px;\n overflow-y: auto;\n flex: 1;\n background: #f8f9fa;\n }\n \n .error-message {\n background: #fee;\n border: 1px solid #fcc;\n color: #c33;\n padding: 12px;\n border-radius: 4px;\n margin-bottom: 16px;\n font-size: 14px;\n }\n \n .form-layout {\n display: flex;\n flex-direction: column;\n gap: 20px;\n }\n\n /* RFC-0077/0078: Customer name display styles with device type icon */\n .customer-name-container {\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n border-radius: 8px;\n padding: 16px 20px;\n box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);\n }\n\n .customer-info-row {\n display: flex;\n align-items: center;\n gap: 16px;\n }\n\n .device-type-icon-wrapper {\n flex-shrink: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 48px;\n height: 48px;\n background: rgba(255, 255, 255, 0.15);\n border-radius: 12px;\n backdrop-filter: blur(4px);\n }\n\n .device-type-icon {\n stroke: white;\n opacity: 0.95;\n }\n\n .device-type-icon.energy-icon {\n stroke: #ffd700;\n }\n\n .device-type-icon.water-icon {\n stroke: #00bfff;\n }\n\n .device-type-icon.generic-icon {\n stroke: white;\n }\n\n .customer-info-content {\n flex: 1;\n min-width: 0;\n }\n\n .customer-name-label {\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: rgba(255, 255, 255, 0.75);\n margin-bottom: 4px;\n }\n\n .customer-name-value {\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .shopping-icon {\n stroke: rgba(255, 255, 255, 0.9);\n flex-shrink: 0;\n }\n\n .customer-name-text {\n font-size: 16px;\n font-weight: 600;\n color: white;\n line-height: 1.2;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n /* RFC-0077: Device label with monospace font */\n .device-label-title {\n font-family: 'Courier New', Courier, monospace;\n font-size: 15px;\n letter-spacing: 0.5px;\n }\n\n .form-columns {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 20px;\n }\n\n .form-column {\n display: flex;\n flex-direction: column;\n }\n\n .form-card {\n background: white;\n border-radius: 8px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n padding: 20px;\n height: fit-content;\n }\n\n .section-title {\n margin: 0 0 20px 0;\n font-size: 16px;\n font-weight: 600;\n color: #3e1a7d;\n }\n \n .form-group {\n display: flex;\n flex-direction: column;\n margin-bottom: 16px;\n }\n \n .form-group:last-child {\n margin-bottom: 0;\n }\n \n .form-group label {\n font-weight: 500;\n margin-bottom: 6px;\n color: #333;\n font-size: 14px;\n }\n \n .form-group input {\n width: 100%;\n padding: 10px 12px;\n border: 1px solid #ddd;\n border-radius: 6px;\n font-size: 14px;\n transition: border-color 0.2s, box-shadow 0.2s;\n box-sizing: border-box;\n }\n \n .form-group input:focus {\n outline: none;\n border-color: #3e1a7d;\n box-shadow: 0 0 0 2px rgba(62, 26, 125, 0.25);\n }\n \n .form-group input:invalid {\n border-color: #dc3545;\n }\n \n .form-group input[readonly] {\n background-color: #f8f9fa;\n color: #6c757d;\n cursor: not-allowed;\n }\n \n .modal-footer {\n padding: 16px 24px;\n border-top: 1px solid #e0e0e0;\n display: flex;\n justify-content: flex-end;\n gap: 12px;\n background: white;\n }\n \n .modal-footer button {\n padding: 10px 20px;\n border: none;\n border-radius: 6px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s;\n }\n \n .btn-cancel {\n background: #6c757d;\n color: white;\n }\n \n .btn-cancel:hover:not(:disabled) {\n background: #545b62;\n }\n \n .btn-primary {\n background: #3e1a7d;\n color: white;\n }\n \n .btn-primary:hover:not(:disabled) {\n background: #2d1458;\n }\n \n .modal-footer button:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n .global-reference-container {\n background-color: #f8f9fa;\n border: 1px dashed #cbd5e1;\n border-radius: 6px;\n padding: 12px 16px;\n margin-top: 8px;\n margin-bottom: 16px;\n }\n\n .global-ref-header {\n font-size: 12px;\n font-weight: 700;\n color: #64748b;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n margin-bottom: 8px;\n display: flex;\n align-items: center;\n gap: 6px;\n }\n\n .global-values-grid {\n display: grid;\n grid-template-columns: repeat(4, 1fr); /* 4 Colunas para os 4 status */\n gap: 12px;\n }\n\n .global-value-item {\n background: white;\n border: 1px solid #e2e8f0;\n border-radius: 4px;\n padding: 6px 8px;\n display: flex;\n flex-direction: column;\n align-items: center;\n text-align: center;\n }\n\n .g-status {\n font-size: 11px;\n font-weight: 600;\n color: #475569;\n margin-bottom: 2px;\n }\n\n .g-range {\n font-family: 'Courier New', monospace;\n font-size: 13px;\n font-weight: 700;\n color: #3e1a7d;\n }\n\n /* Responsividade para telas pequenas */\n @media (max-width: 768px) {\n .global-values-grid {\n grid-template-columns: 1fr 1fr; /* 2 colunas em mobile */\n }\n }\n \n /* Responsive design */\n @media (max-width: 1700px) {\n .myio-settings-modal {\n width: 95vw !important;\n }\n }\n \n @media (max-width: 1024px) {\n .myio-settings-modal {\n width: 90vw !important;\n }\n \n .form-columns {\n gap: 16px;\n }\n \n .form-card {\n padding: 16px;\n }\n }\n \n @media (max-width: 768px) {\n .myio-settings-modal {\n width: 95vw !important;\n margin: 10px;\n }\n\n .form-columns {\n grid-template-columns: 1fr;\n gap: 16px;\n }\n\n .info-grid {\n grid-template-columns: 1fr;\n gap: 12px;\n }\n\n .modal-header, .modal-body, .modal-footer {\n padding-left: 16px;\n padding-right: 16px;\n }\n\n .form-card {\n padding: 16px;\n }\n }\n \n /* Scrollbar styling for modal body */\n .modal-body::-webkit-scrollbar {\n width: 6px;\n }\n \n .modal-body::-webkit-scrollbar-track {\n background: #f1f1f1;\n border-radius: 3px;\n }\n \n .modal-body::-webkit-scrollbar-thumb {\n background: #c1c1c1;\n border-radius: 3px;\n }\n \n .modal-body::-webkit-scrollbar-thumb:hover {\n background: #a8a8a8;\n }\n\n /* Connection Info Card Styles - Wide layout spanning 2 columns */\n .info-card-wide {\n margin-top: 20px;\n background: linear-gradient(135deg, #f8fafc 0%, #f0f9ff 100%);\n border: 1px solid #e0e7ff;\n grid-column: 1 / -1; /* Span all columns */\n }\n\n .info-card-wide .section-title {\n color: #2563eb;\n display: flex;\n align-items: center;\n margin-bottom: 12px;\n }\n\n .info-grid {\n display: grid;\n grid-template-columns: repeat(2, 1fr);\n gap: 12px 24px;\n }\n\n .info-row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 8px 12px;\n background: rgba(255, 255, 255, 0.6);\n border-radius: 6px;\n border: 1px solid rgba(0, 0, 0, 0.05);\n }\n\n .info-label {\n font-weight: 600;\n color: #475569;\n font-size: 13px;\n flex-shrink: 0;\n }\n\n .info-value {\n text-align: right;\n color: #1e293b;\n font-size: 13px;\n word-break: break-word;\n margin-left: 12px;\n }\n\n .time-since {\n display: inline-block;\n margin-left: 6px;\n color: #64748b;\n font-size: 12px;\n font-style: italic;\n }\n\n .disconnect-interval {\n font-size: 12px;\n line-height: 1.4;\n }\n\n .info-row.full-width {\n grid-column: 1 / -1; /* Span all columns */\n }\n\n .loading-text {\n color: #64748b;\n font-style: italic;\n }\n\n .consumption-value-display {\n font-weight: 600;\n color: #3e1a7d;\n }\n\n .consumption-date {\n color: #475569;\n }\n\n .telemetry-error {\n color: #dc2626;\n font-style: italic;\n }\n\n .telemetry-no-data {\n color: #94a3b8;\n font-style: italic;\n }\n\n /* RFC-0078: Power Limits Configuration Styles */\n .power-limits-card {\n grid-column: 1 / -1; /* Span full width */\n margin-top: 20px;\n }\n\n .power-limits-header {\n margin-bottom: 20px;\n }\n\n .power-limits-subtitle {\n font-size: 13px;\n color: #6c757d;\n margin-top: 8px;\n }\n\n /* RFC-0078: Telemetry Type Selector Styles */\n .telemetry-selector {\n margin-bottom: 16px;\n }\n\n .telemetry-selector label {\n display: block;\n font-weight: 500;\n margin-bottom: 6px;\n color: #333;\n font-size: 14px;\n }\n\n .form-select {\n width: 100%;\n padding: 10px 12px;\n border: 1px solid #ddd;\n border-radius: 6px;\n font-size: 14px;\n background-color: white;\n cursor: pointer;\n transition: border-color 0.2s, box-shadow 0.2s;\n }\n\n .form-select:focus {\n outline: none;\n border-color: #3e1a7d;\n box-shadow: 0 0 0 2px rgba(62, 26, 125, 0.25);\n }\n\n .form-select:hover {\n border-color: #3e1a7d;\n }\n\n .power-limits-table-wrapper {\n overflow-x: auto;\n margin-bottom: 16px;\n }\n\n .power-limits-table {\n width: 100%;\n border-collapse: collapse;\n font-size: 14px;\n }\n\n .power-limits-table thead {\n background: #f8f9fa;\n }\n\n .power-limits-table th {\n padding: 12px;\n text-align: left;\n font-weight: 600;\n color: #495057;\n border-bottom: 2px solid #dee2e6;\n }\n\n .power-limits-table td {\n padding: 10px 12px;\n border-bottom: 1px solid #e9ecef;\n }\n\n .limit-row:hover {\n background: #f8f9fa;\n }\n\n .status-label {\n display: flex;\n align-items: center;\n gap: 8px;\n font-weight: 500;\n }\n\n .status-icon {\n font-size: 18px;\n }\n\n .limit-input {\n width: 100%;\n padding: 8px 10px;\n border: 1px solid #ced4da;\n border-radius: 4px;\n font-size: 14px;\n transition: border-color 0.2s, box-shadow 0.2s;\n }\n\n .limit-input:focus {\n outline: none;\n border-color: #3e1a7d;\n box-shadow: 0 0 0 2px rgba(62, 26, 125, 0.15);\n }\n\n .power-limits-source-info {\n display: flex;\n align-items: center;\n gap: 12px;\n margin-bottom: 16px;\n padding: 12px 16px;\n background: #f8f9fa;\n border-radius: 8px;\n border: 1px solid #dee2e6;\n }\n\n .source-label {\n font-weight: 600;\n color: #495057;\n font-size: 14px;\n }\n\n .source-badge {\n display: inline-block;\n padding: 4px 10px;\n border-radius: 12px;\n font-size: 12px;\n font-weight: 600;\n background: #e9ecef;\n color: #495057;\n }\n\n .source-badge.global-source {\n background: #3e1a7d;\n color: #fff;\n padding: 6px 14px;\n font-size: 13px;\n }\n\n .source-badge.source-device {\n background: #cfe2ff;\n color: #084298;\n }\n\n .source-badge.source-global {\n background: #d1e7dd;\n color: #0f5132;\n }\n\n .source-badge.source-hardcoded {\n background: #f8d7da;\n color: #842029;\n }\n\n .power-limits-actions {\n display: flex;\n gap: 12px;\n margin-bottom: 16px;\n }\n\n .btn-copy-global,\n .btn-clear-overrides {\n padding: 10px 16px;\n border: none;\n border-radius: 6px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s;\n }\n\n .btn-copy-global {\n background: #198754;\n color: white;\n }\n\n .btn-copy-global:hover {\n background: #157347;\n }\n\n .btn-clear-overrides {\n background: #0d6efd;\n color: white;\n }\n\n .btn-clear-overrides:hover {\n background: #0b5ed7;\n }\n\n .btn-view-json {\n background: #6c757d;\n color: white;\n }\n\n .btn-view-json:hover {\n background: #5a6268;\n }\n\n /* RFC-0078: JSON Preview Panel Styles */\n .json-preview-panel {\n background: #1e1e1e;\n border-radius: 6px;\n margin-bottom: 16px;\n overflow: hidden;\n }\n\n .json-preview-header {\n background: #2d2d2d;\n padding: 10px 16px;\n display: flex;\n justify-content: space-between;\n align-items: center;\n }\n\n .json-preview-header h5 {\n margin: 0;\n color: #e0e0e0;\n font-size: 14px;\n font-weight: 600;\n }\n\n .btn-close-json {\n background: none;\n border: none;\n color: #999;\n cursor: pointer;\n font-size: 16px;\n padding: 4px 8px;\n border-radius: 4px;\n }\n\n .btn-close-json:hover {\n background: rgba(255, 255, 255, 0.1);\n color: #fff;\n }\n\n .json-content {\n margin: 0;\n padding: 16px;\n color: #d4d4d4;\n font-family: 'Courier New', Courier, monospace;\n font-size: 12px;\n line-height: 1.5;\n overflow-x: auto;\n max-height: 300px;\n overflow-y: auto;\n }\n\n .power-limits-legend {\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 12px;\n background: #f8f9fa;\n border-radius: 6px;\n border-left: 3px solid #3e1a7d;\n }\n\n .legend-item {\n display: flex;\n align-items: center;\n gap: 10px;\n }\n\n .legend-text {\n font-size: 13px;\n color: #495057;\n }\n\n @media (max-width: 768px) {\n .power-limits-table {\n font-size: 12px;\n }\n\n .power-limits-table th,\n .power-limits-table td {\n padding: 8px;\n }\n\n .limit-input {\n padding: 6px 8px;\n font-size: 12px;\n }\n\n .power-limits-actions {\n flex-direction: column;\n }\n\n .btn-copy-global,\n .btn-clear-overrides {\n width: 100%;\n }\n }\n </style>\n `}populateForm(data){for(const[key,value]of Object.entries(data)){const input=this.form.querySelector(`[name="${key}"]`);if(input&&value!==void 0&&value!==null){input.value=String(value)}}}setupAccessibility(){const firstInput=this.modal.querySelector("input");if(firstInput){setTimeout(()=>firstInput.focus(),100)}this.modal.setAttribute("aria-labelledby","modal-title")}setupFocusTrap(){this.focusTrapElements=Array.from(this.modal.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'));this.modal.addEventListener("keydown",this.handleKeyDown.bind(this))}teardownFocusTrap(){this.modal.removeEventListener("keydown",this.handleKeyDown.bind(this))}handleKeyDown(event){if(event.key==="Escape"&&this.config.closeOnBackdrop!==false){event.preventDefault();this.config.onClose();return}if(event.key==="Tab"){const firstElement=this.focusTrapElements[0];const lastElement=this.focusTrapElements[this.focusTrapElements.length-1];if(event.shiftKey){if(document.activeElement===firstElement){event.preventDefault();lastElement.focus()}}else{if(document.activeElement===lastElement){event.preventDefault();firstElement.focus()}}}}parseDeviceSavedLimits(deviceJson){const extracted={};try{if(!deviceJson||!deviceJson.limitsByInstantaneoustPowerType)return extracted;const consumptionGroup=deviceJson.limitsByInstantaneoustPowerType.find(g=>g.telemetryType==="consumption");const deviceItem=consumptionGroup?.itemsByDeviceType?.[0];if(!deviceItem?.limitsByDeviceStatus)return extracted;const mapPrefix={standBy:"standby",normal:"normal",alert:"alert",failure:"failure"};deviceItem.limitsByDeviceStatus.forEach(status=>{const prefix=mapPrefix[status.deviceStatusName];if(prefix&&status.limitsValues){const{baseValue:baseValue,topValue:topValue}=status.limitsValues;if(baseValue!==null&&baseValue!==void 0){extracted[`${prefix}LimitDownConsumption`]=baseValue}if(topValue!==null&&topValue!==void 0){extracted[`${prefix}LimitUpConsumption`]=topValue}}})}catch(e){console.warn("[SettingsModalView] Erro ao processar deviceMapInstaneousPower:",e)}return extracted}attachEventListeners(){this.form.addEventListener("submit",event=>{event.preventDefault();this.hideError();const formData=this.getFormData();this.config.onSave(formData)});const closeBtn=this.modal.querySelector(".close-btn");if(closeBtn){closeBtn.addEventListener("click",event=>{event.preventDefault();event.stopPropagation();this.config.onClose()})}const cancelBtn=this.modal.querySelector(".btn-cancel");if(cancelBtn){cancelBtn.addEventListener("click",event=>{event.preventDefault();event.stopPropagation();this.config.onClose()})}const saveBtn=this.modal.querySelector(".btn-save");if(saveBtn){saveBtn.addEventListener("click",event=>{event.preventDefault();event.stopPropagation();this.hideError();const formData=this.getFormData();this.config.onSave(formData)})}const btnCopy=this.modal.querySelector("#btnCopyFromGlobal");if(btnCopy){btnCopy.addEventListener("click",e=>{e.preventDefault();e.stopPropagation();const inputs=this.modal.querySelectorAll(".js-limit-input");inputs.forEach(el=>{const input=el;const globalVal=input.getAttribute("data-global-value");if(globalVal!==null&&globalVal!==""&&globalVal!=="undefined"){input.value=globalVal;input.dispatchEvent(new Event("input",{bubbles:true}))}})})}const btnClear=this.modal.querySelector("#btnClearInputs");if(btnClear){btnClear.addEventListener("click",e=>{e.preventDefault();e.stopPropagation();const inputs=this.modal.querySelectorAll(".js-limit-input");inputs.forEach(el=>{const input=el;input.value="";input.dispatchEvent(new Event("input",{bubbles:true}))})})}this.container.addEventListener("click",event=>{const target=event.target;if(target.classList.contains("myio-settings-modal-overlay")&&this.config.closeOnBackdrop!==false){this.config.onClose()}});this.form.addEventListener("input",this.handleInputValidation.bind(this))}handleInputValidation(event){const input=event.target;input.classList.remove("is-invalid");if(input.name==="guid"&&input.value){const guidPattern=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;if(!guidPattern.test(input.value)){input.classList.add("is-invalid");input.setCustomValidity("Invalid GUID format")}else{input.setCustomValidity("")}}if(input.type==="number"&&input.value){const num=parseFloat(input.value);if(isNaN(num)||num<0){input.classList.add("is-invalid");input.setCustomValidity("Must be a positive number")}else{input.setCustomValidity("")}}}applyTheme(){if(this.config.themeTokens){const style=document.createElement("style");let css="";for(const[property,value]of Object.entries(this.config.themeTokens)){css+=`--myio-${property}: ${value};\n`}style.textContent=`.myio-settings-modal { ${css} }`;this.container.appendChild(style)}}getI18nText(key,defaultText){return this.config.i18n?.t(key,defaultText)||defaultText}async fetchLatestConsumptionTelemetry(){const telemetryElement=this.modal.querySelector("#lastConsumptionTelemetry");if(!telemetryElement)return;const deviceId=this.config.deviceId;const jwtToken=this.config.jwtToken;const domain=this.config.domain||"energy";if(!deviceId||!jwtToken){telemetryElement.innerHTML='<span class="telemetry-error">N/A</span>';return}const telemetryConfigByDomain={energy:{key:"consumption",unit:"kW",label:"Consumo",formatter:v=>(v/1e3).toFixed(2)},temperature:{key:"temperature",unit:"°C",label:"Temperatura",formatter:v=>v.toFixed(1)},water:{key:"pulses",unit:"L",label:"Pulsos",formatter:v=>v.toFixed(0)}};const telemetryConfig=telemetryConfigByDomain[domain]||telemetryConfigByDomain.energy;try{const endTs=Date.now();const startTs=endTs-24*60*60*1e3;const url=`/api/plugins/telemetry/DEVICE/${deviceId}/values/timeseries?keys=${telemetryConfig.key}&startTs=${startTs}&endTs=${endTs}&limit=1&orderBy=DESC`;const response=await fetch(url,{headers:{"X-Authorization":`Bearer ${jwtToken}`,"Content-Type":"application/json"}});if(!response.ok){throw new Error(`HTTP ${response.status}`)}const data=await response.json();const telemetryData=data[telemetryConfig.key];if(telemetryData&&telemetryData.length>0){const latestPoint=telemetryData[0];const rawValue=parseFloat(latestPoint.value);const timestamp=latestPoint.ts;const formattedValue=telemetryConfig.formatter(rawValue);const date=new Date(timestamp);const formattedDate=date.toLocaleString("pt-BR",{day:"2-digit",month:"2-digit",year:"numeric",hour:"2-digit",minute:"2-digit",second:"2-digit"});const now=new Date;const diffMs=now.getTime()-date.getTime();const diffMinutes=Math.floor(diffMs/(1e3*60));const diffHours=Math.floor(diffMinutes/60);const remainingMinutes=diffMinutes%60;let timeSince="";if(diffHours>0){timeSince=`(${diffHours}hs:${remainingMinutes.toString().padStart(2,"0")}mins atrás)`}else if(diffMinutes>0){timeSince=`(${diffMinutes}mins atrás)`}else{timeSince="(agora)"}telemetryElement.innerHTML=`\n <span class="consumption-value-display">${formattedValue} ${telemetryConfig.unit}</span>\n <span class="consumption-date">- ${formattedDate}</span>\n <span class="time-since">${timeSince}</span>\n `}else{telemetryElement.innerHTML='<span class="telemetry-no-data">Sem dados</span>'}}catch(error){console.error("[SettingsModal] Failed to fetch telemetry:",error);telemetryElement.innerHTML='<span class="telemetry-error">Erro ao carregar</span>'}}};var DefaultSettingsPersister=class{jwtToken;tbBaseUrl;deviceType;deviceProfile;existingMapInstantaneousPower;constructor(jwtToken,apiConfig){this.jwtToken=jwtToken;this.tbBaseUrl=apiConfig?.tbBaseUrl||window.location.origin;this.deviceType=apiConfig?.deviceType||"ELEVADOR";this.deviceProfile=apiConfig?.deviceProfile||null;this.existingMapInstantaneousPower=apiConfig?.mapInstantaneousPower||null}getEffectiveDeviceType(){const normalizedType=(this.deviceType||"").toUpperCase();if(normalizedType==="3F_MEDIDOR"){const profile=(this.deviceProfile||"").toUpperCase();if(profile&&profile!=="N/D"&&profile.trim()!==""){console.log(`[SettingsPersister] RFC-0086: Resolved 3F_MEDIDOR → ${profile}`);return profile}}return normalizedType||"ELEVADOR"}async saveEntityLabel(deviceId,label){try{const getRes=await fetch(`${this.tbBaseUrl}/api/device/${deviceId}`,{headers:{"X-Authorization":`Bearer ${this.jwtToken}`}});if(!getRes.ok){throw this.createHttpError(getRes.status,await getRes.text().catch(()=>""))}const device=await getRes.json();const postRes=await fetch(`${this.tbBaseUrl}/api/device`,{method:"POST",headers:{"X-Authorization":`Bearer ${this.jwtToken}`,"Content-Type":"application/json"},body:JSON.stringify({...device,label:this.sanitizeLabel(label)})});if(!postRes.ok){throw this.createHttpError(postRes.status,await postRes.text().catch(()=>""))}return{ok:true}}catch(error){console.error("[SettingsPersister] Entity label save failed:",error);return{ok:false,error:this.mapError(error)}}}async saveServerScopeAttributes(deviceId,attributes){try{const payload={...attributes};const effectiveDeviceType=this.getEffectiveDeviceType();const deviceJson=this.buildDevicePowerJson(payload,effectiveDeviceType);if(deviceJson){payload.deviceMapInstaneousPower=deviceJson}const flatKeysToRemove=["telemetryType","standbyLimitDownConsumption","standbyLimitUpConsumption","normalLimitDownConsumption","normalLimitUpConsumption","alertLimitDownConsumption","alertLimitUpConsumption","failureLimitDownConsumption","failureLimitUpConsumption"];flatKeysToRemove.forEach(key=>delete payload[key]);console.log("[SettingsPersister] Saving Server Scope Attributes:",payload);const res=await fetch(`${this.tbBaseUrl}/api/plugins/telemetry/DEVICE/${deviceId}/attributes/SERVER_SCOPE`,{method:"POST",headers:{"X-Authorization":`Bearer ${this.jwtToken}`,"Content-Type":"application/json"},body:JSON.stringify(payload)});if(!res.ok){throw this.createHttpError(res.status,await res.text().catch(()=>""))}return{ok:true,updatedKeys:Object.keys(payload)}}catch(error){console.error("[SettingsPersister] Attributes save failed:",error);return{ok:false,error:this.mapError(error)}}}buildDevicePowerJson(formData,deviceType){const statuses=["standby","normal","alert","failure"];const hasPowerData=statuses.some(status=>formData[`${status}LimitDownConsumption`]!==void 0&&formData[`${status}LimitDownConsumption`]!==""||formData[`${status}LimitUpConsumption`]!==void 0&&formData[`${status}LimitUpConsumption`]!=="");if(!hasPowerData)return null;const statusMap={standby:"standBy",normal:"normal",alert:"alert",failure:"failure"};const limitsList=[];statuses.forEach(statusKey=>{const down=formData[`${statusKey}LimitDownConsumption`];const up=formData[`${statusKey}LimitUpConsumption`];if(down!==void 0&&down!==""||up!==void 0&&up!==""){limitsList.push({deviceStatusName:statusMap[statusKey],limitsValues:{baseValue:down!==""&&down!==void 0?Number(down):null,topValue:up!==""&&up!==void 0?Number(up):null}})}});if(limitsList.length===0)return null;return{version:"1.0.0",limitsByInstantaneoustPowerType:[{telemetryType:"consumption",itemsByDeviceType:[{deviceType:deviceType,name:`deviceMapInstaneousPower${this.formatDeviceTypeName(deviceType)}`,description:"Override manual configurado via Dashboard",limitsByDeviceStatus:limitsList}]}]}}formatDeviceTypeName(deviceType){if(!deviceType)return"";return deviceType.charAt(0).toUpperCase()+deviceType.slice(1).toLowerCase()}buildMapInstantaneousPower(formData){const effectiveDeviceType=this.getEffectiveDeviceType();const telemetryType=String(formData.telemetryType||"consumption");const result={version:"1.0.0",limitsByInstantaneoustPowerType:[{telemetryType:telemetryType,itemsByDeviceType:[{deviceType:effectiveDeviceType,name:`mapInstantaneousPower${this.formatDeviceTypeName(effectiveDeviceType)}`,description:formData.identifier?`Limites customizados para ${formData.identifier}`:`Limites de potência customizados para ${effectiveDeviceType}`,limitsByDeviceStatus:[{deviceStatusName:"standBy",limitsValues:{baseValue:Number(formData.standbyLimitDownConsumption)||0,topValue:Number(formData.standbyLimitUpConsumption)||0}},{deviceStatusName:"normal",limitsValues:{baseValue:Number(formData.normalLimitDownConsumption)||0,topValue:Number(formData.normalLimitUpConsumption)||0}},{deviceStatusName:"alert",limitsValues:{baseValue:Number(formData.alertLimitDownConsumption)||0,topValue:Number(formData.alertLimitUpConsumption)||0}},{deviceStatusName:"failure",limitsValues:{baseValue:Number(formData.failureLimitDownConsumption)||0,topValue:Number(formData.failureLimitUpConsumption)||0}}]}]}]};console.log(`[SettingsPersister] RFC-0086: Built mapInstantaneousPower for deviceType=${effectiveDeviceType}:`,result);return result}sanitizeLabel(label){return label.trim().slice(0,255).replace(/[\x00-\x1F\x7F]/g,"")}createHttpError(status,body){const error=new Error(`HTTP ${status}: ${body}`);error.status=status;error.body=body;return error}mapError(error){const status=error.status;if(status===400){return{code:"VALIDATION_ERROR",message:"Invalid input data",userAction:"FIX_INPUT",cause:error}}if(status===401){return{code:"TOKEN_EXPIRED",message:"Authentication token has expired",userAction:"RE_AUTH",cause:error}}if(status===403){return{code:"AUTH_ERROR",message:"Insufficient permissions",userAction:"RE_AUTH",cause:error}}if(status===404){return{code:"NETWORK_ERROR",message:"Device not found",userAction:"CONTACT_ADMIN",cause:error}}if(status===409){return{code:"VALIDATION_ERROR",message:"Concurrent modification detected",userAction:"RETRY",cause:error}}if(status>=500){return{code:"NETWORK_ERROR",message:"Server error occurred",userAction:"RETRY",cause:error}}return{code:"UNKNOWN_ERROR",message:error.message||"Unknown error occurred",userAction:"CONTACT_ADMIN",cause:error}}};var DefaultSettingsFetcher=class{jwtToken;tbBaseUrl;constructor(jwtToken,apiConfig){this.jwtToken=jwtToken;this.tbBaseUrl=apiConfig?.tbBaseUrl||window.location.origin}async fetchCurrentSettings(deviceId,jwtToken,scope="SERVER_SCOPE"){try{const[entityResult,attributesResult]=await Promise.allSettled([this.fetchDeviceEntity(deviceId),this.fetchDeviceAttributes(deviceId,scope)]);const result={};if(entityResult.status==="fulfilled"){result.entity=entityResult.value}else{console.warn("[SettingsFetcher] Failed to fetch device entity:",entityResult.reason)}if(attributesResult.status==="fulfilled"){result.attributes=attributesResult.value}else{console.warn("[SettingsFetcher] Failed to fetch device attributes:",attributesResult.reason)}return result}catch(error){console.error("[SettingsFetcher] Failed to fetch current settings:",error);return{}}}async fetchDeviceEntity(deviceId){const response=await fetch(`${this.tbBaseUrl}/api/device/${deviceId}`,{headers:{"X-Authorization":`Bearer ${this.jwtToken}`}});if(!response.ok){throw new Error(`Failed to fetch device entity: ${response.status} ${response.statusText}`)}const device=await response.json();return{label:device.label||device.name||""}}async fetchDeviceAttributes(deviceId,scope){const response=await fetch(`${this.tbBaseUrl}/api/plugins/telemetry/DEVICE/${deviceId}/values/attributes/${scope}`,{headers:{"X-Authorization":`Bearer ${this.jwtToken}`}});if(!response.ok){throw new Error(`Failed to fetch device attributes: ${response.status} ${response.statusText}`)}const attributesArray=await response.json();const attributes={};for(const attr of attributesArray){if(attr.key&&attr.value!==void 0&&attr.value!==null&&attr.value!==""){if(attr.key==="floor"){attributes.floor=attr.value}else if(attr.key==="identifier"){attributes.identifier=attr.value}else if(attr.key==="mapInstantaneousPower"){attributes.mapInstantaneousPower=attr.value}else if(attr.key==="deviceMapInstaneousPower"){attributes.deviceMapInstaneousPower=attr.value}}}return attributes}static mergeWithSeed(fetchedData,seedData){const merged={};if(seedData){Object.assign(merged,seedData)}if(fetchedData.entity?.label){merged.label=fetchedData.entity.label}if(fetchedData.attributes){Object.assign(merged,fetchedData.attributes)}return merged}static sanitizeFetchedData(data){const sanitized={};const stringFields=["label","floor","identifier"];for(const field of stringFields){if(data[field]&&typeof data[field]==="string"){sanitized[field]=data[field].trim()}}const numericFields=["maxDailyKwh","maxNightKwh","maxBusinessKwh"];for(const field of numericFields){if(data[field]!==void 0&&data[field]!==null){const num=Number(data[field]);if(!isNaN(num)&&num>=0){sanitized[field]=num}}}const objectFields=["mapInstantaneousPower","deviceMapInstaneousPower"];for(const field of objectFields){if(data[field]&&typeof data[field]==="object"){sanitized[field]=data[field]}}return sanitized}};var SettingsController=class{view;persister;fetcher;params;constructor(params){this.params=params;this.validateParams();const apiConfigWithDeviceInfo={...params.api,deviceType:params.deviceType,deviceProfile:params.deviceProfile,mapInstantaneousPower:params.mapInstantaneousPower};this.persister=params.persister||new DefaultSettingsPersister(params.jwtToken,apiConfigWithDeviceInfo);this.fetcher=params.fetcher||new DefaultSettingsFetcher(params.jwtToken,params.api);this.view=new SettingsModalView({title:params.ui?.title||`Settings - ${params.label||params.deviceId}`,width:params.ui?.width||600,theme:"light",closeOnBackdrop:params.ui?.closeOnBackdrop!==false,domain:params.domain||"energy",deviceType:params.deviceType,deviceProfile:params.deviceProfile,customerName:params.customerName,customerId:params.customerId,deviceId:params.deviceId,jwtToken:params.jwtToken,themeTokens:params.ui?.themeTokens,i18n:params.ui?.i18n,deviceLabel:params.label,connectionData:params.connectionData,onSave:this.handleSave.bind(this),onClose:this.handleClose.bind(this),mapInstantaneousPower:params.mapInstantaneousPower,deviceMapInstaneousPower:params.deviceMapInstaneousPower})}async show(){console.info("[SettingsModal] Opening modal",{deviceId:this.params.deviceId,deviceType:this.params.deviceType,deviceProfile:this.params.deviceProfile});this.emitEvent("modal_opened");if(!this.params.mapInstantaneousPower&&this.params.customerId){try{const globalMap=await this.fetchGlobalMapInstantaneousPower();if(globalMap){this.params.mapInstantaneousPower=globalMap;console.log("[SettingsModal] RFC-0080: Loaded GLOBAL mapInstantaneousPower from CUSTOMER");this.persister=new DefaultSettingsPersister(this.params.jwtToken,{...this.params.api,deviceType:this.params.deviceType,deviceProfile:this.params.deviceProfile,mapInstantaneousPower:globalMap});this.view.updateMapInstantaneousPower(globalMap)}}catch(error){console.warn("[SettingsModal] RFC-0080: Failed to fetch GLOBAL mapInstantaneousPower:",error)}}let initialData=this.params.seed||{};if(!this.params.seed){try{const fetchedData=await this.fetcher.fetchCurrentSettings(this.params.deviceId,this.params.jwtToken,this.params.scope||"SERVER_SCOPE");initialData=DefaultSettingsFetcher.mergeWithSeed(fetchedData,this.params.seed);initialData=DefaultSettingsFetcher.sanitizeFetchedData(initialData)}catch(error){console.warn("[SettingsModal] Failed to fetch current settings:",error);if(this.params.onError){this.params.onError({code:"NETWORK_ERROR",message:"Failed to load current settings",cause:error})}}}this.view.render(initialData)}async fetchGlobalMapInstantaneousPower(){const customerId=this.params.customerId;const jwtToken=this.params.jwtToken;if(!customerId||!jwtToken){console.warn("[SettingsModal] RFC-0080: Cannot fetch GLOBAL - missing customerId or token");return null}try{const tbBaseUrl=this.params.api?.tbBaseUrl||window.location.origin;const url=`${tbBaseUrl}/api/plugins/telemetry/CUSTOMER/${customerId}/values/attributes/SERVER_SCOPE?keys=mapInstantaneousPower`;console.log("[SettingsModal] RFC-0080: Fetching GLOBAL from:",url);const response=await fetch(url,{headers:{"X-Authorization":`Bearer ${jwtToken}`,"Content-Type":"application/json"}});if(!response.ok){throw new Error(`HTTP ${response.status}`)}const attrs=await response.json();const powerLimitsAttr=attrs.find(a=>a.key==="mapInstantaneousPower");if(!powerLimitsAttr){console.log("[SettingsModal] RFC-0080: No GLOBAL mapInstantaneousPower found on CUSTOMER");return null}const value=typeof powerLimitsAttr.value==="string"?JSON.parse(powerLimitsAttr.value):powerLimitsAttr.value;console.log("[SettingsModal] RFC-0080: Loaded GLOBAL mapInstantaneousPower:",value);return value}catch(error){console.error("[SettingsModal] RFC-0080: Failed to fetch GLOBAL mapInstantaneousPower:",error);return null}}validateParams(){if(!this.params.jwtToken){throw new Error("jwtToken is required for settings persistence")}if(!this.params.deviceId){throw new Error("deviceId is required")}}async handleSave(formData){console.info("[SettingsModal] Save initiated",{deviceId:this.params.deviceId,formData:formData});this.emitEvent("save_started",{formData:formData});this.view.showLoadingState(true);try{const result=await this.saveSettings(formData);if(result.ok){console.info("[SettingsModal] Settings saved successfully",result);this.emitEvent("save_completed",{result:result});if(this.params.onSaved){this.params.onSaved(result)}setTimeout(()=>{this.view.close()},500)}else{console.error("[SettingsModal] Save failed:",result);this.emitEvent("save_failed",{result:result});const errorMessage=this.getErrorMessage(result);this.view.showError(errorMessage);if(this.params.onError){this.params.onError({code:"VALIDATION_ERROR",message:errorMessage,cause:result})}}}catch(error){console.error("[SettingsModal] Save error:",error);this.emitEvent("save_failed",{error:error.message});const errorMessage="Network error occurred while saving settings";this.view.showError(errorMessage);if(this.params.onError){this.params.onError({code:"NETWORK_ERROR",message:errorMessage,cause:error})}}finally{this.view.showLoadingState(false)}}async saveSettings(formData){const result={ok:true,timestamp:(new Date).toISOString()};if(formData.label){try{const labelResult=await this.persister.saveEntityLabel(this.params.deviceId,formData.label);result.entity={ok:labelResult.ok,updated:labelResult.ok?["label"]:void 0,error:labelResult.error?{code:labelResult.error.code,message:labelResult.error.message,cause:labelResult.error.cause}:void 0};if(!labelResult.ok){result.ok=false}}catch(error){result.entity={ok:false,error:{code:"UNKNOWN_ERROR",message:error.message||"Failed to save device label",cause:error}};result.ok=false}}const attributes=this.extractAttributes(formData);if(Object.keys(attributes).length>0){try{const attributesResult=await this.persister.saveServerScopeAttributes(this.params.deviceId,attributes);result.serverScope={ok:attributesResult.ok,updatedKeys:attributesResult.updatedKeys,error:attributesResult.error?{code:attributesResult.error.code,message:attributesResult.error.message,cause:attributesResult.error.cause}:void 0};if(!attributesResult.ok){result.ok=false}}catch(error){result.serverScope={ok:false,error:{code:"UNKNOWN_ERROR",message:error.message||"Failed to save device attributes",cause:error}};result.ok=false}}return result}extractAttributes(formData){const attributes={};for(const[key,value]of Object.entries(formData)){if(key!=="label"&&value!==void 0&&value!==null&&value!==""){attributes[key]=value}}return attributes}getErrorMessage(result){const errors=[];if(result.entity?.error){errors.push(`Device label: ${result.entity.error.message}`)}if(result.serverScope?.error){errors.push(`Settings: ${result.serverScope.error.message}`)}return errors.length>0?errors.join("; "):"Failed to save settings"}handleClose(){console.info("[SettingsModal] Modal closed");this.emitEvent("modal_closed");if(this.params.onClose){this.params.onClose()}}emitEvent(type,data){if(this.params.onEvent){const event={type:type,deviceId:this.params.deviceId,timestamp:(new Date).toISOString(),data:data};try{this.params.onEvent(event)}catch(error){console.warn("[SettingsModal] Event handler error:",error)}}}validateFormData(formData){const errors=[];if(formData.guid){const guidPattern=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;if(!guidPattern.test(formData.guid)){errors.push("GUID must be in valid UUID format")}}const numericFields=["maxDailyKwh","maxNightKwh","maxBusinessKwh","minTemperature","maxTemperature","minWaterLevel","maxWaterLevel"];for(const field of numericFields){if(formData[field]!==void 0){const num=Number(formData[field]);if(isNaN(num)||field.includes("Kwh")&&num<0){errors.push(`${field} must be a valid number`)}if(field.includes("WaterLevel")){if(num<0||num>100){errors.push(`${field} must be between 0 and 100`)}}}}if(formData.minTemperature!==void 0&&formData.maxTemperature!==void 0){const minTemp=Number(formData.minTemperature);const maxTemp=Number(formData.maxTemperature);if(!isNaN(minTemp)&&!isNaN(maxTemp)&&minTemp>=maxTemp){errors.push("Minimum temperature must be less than maximum temperature")}}if(formData.minWaterLevel!==void 0&&formData.maxWaterLevel!==void 0){const minLevel=Number(formData.minWaterLevel);const maxLevel=Number(formData.maxWaterLevel);if(!isNaN(minLevel)&&!isNaN(maxLevel)&&minLevel>=maxLevel){errors.push("Minimum water level must be less than maximum water level")}}if(formData.label&&formData.label.length>255){errors.push("Device label must be 255 characters or less")}if(formData.floor&&formData.floor.length>50){errors.push("Floor must be 50 characters or less")}if(formData.storeNumber&&formData.storeNumber.length>20){errors.push("Store number must be 20 characters or less")}return{valid:errors.length===0,errors:errors}}closeModal(){this.view.close()}getCurrentFormData(){return this.view.getFormData()}};async function openDashboardPopupSettings(params){if(!params.jwtToken){throw new Error("jwtToken is required for settings persistence")}if(!params.deviceId){throw new Error("deviceId is required")}console.info("[openDashboardPopupSettings] Initializing settings modal",{deviceId:params.deviceId,ingestionId:params.ingestionId,hasJwtToken:!!params.jwtToken,hasSeedData:!!params.seed,apiConfig:params.api?Object.keys(params.api):void 0});const controller=new SettingsController(params);try{await controller.show()}catch(error){console.error("[openDashboardPopupSettings] Error:",error);if(params.onError){params.onError({code:"UNKNOWN_ERROR",message:error.message||"Unknown error occurred while opening settings modal",cause:error})}throw error}}async function createDateRangePicker2(input,options={}){const defaultOptions={maxRangeDays:31,...options};return await DateRangePickerJQ.attach(input,defaultOptions)}var PREMIUM_STYLES=`\n .myio-daterange-wrapper {\n font-family: 'Roboto', Arial, sans-serif;\n background: #f9f9f9;\n padding: 20px;\n border-radius: 8px;\n margin-bottom: 20px;\n box-shadow: 0 2px 4px rgba(0,0,0,0.05);\n transition: all 0.2s ease;\n }\n \n .myio-daterange-wrapper:hover {\n box-shadow: 0 4px 8px rgba(0,0,0,0.1);\n }\n \n .myio-daterange-label {\n display: block;\n margin-bottom: 8px;\n font-weight: 500;\n color: #333;\n font-size: 14px;\n line-height: 1.4;\n }\n \n .myio-daterange-input {\n width: 100%;\n max-width: 300px;\n padding: 8px 12px;\n border: 1px solid #ddd;\n border-radius: 4px;\n background: white;\n font-size: 14px;\n color: #333;\n cursor: pointer;\n transition: all 0.2s ease;\n font-family: inherit;\n line-height: 1.4;\n }\n \n .myio-daterange-input:hover {\n border-color: #4A148C;\n box-shadow: 0 0 0 2px rgba(74, 20, 140, 0.1);\n }\n \n .myio-daterange-input:focus {\n outline: none;\n border-color: #4A148C;\n box-shadow: 0 0 0 3px rgba(74, 20, 140, 0.2);\n }\n \n .myio-daterange-input::placeholder {\n color: #999;\n opacity: 1;\n }\n \n .myio-daterange-helper {\n font-size: 12px;\n color: #666;\n margin-top: 4px;\n line-height: 1.3;\n transition: color 0.2s ease;\n }\n \n .myio-daterange-helper.success {\n color: #28a745;\n font-weight: 500;\n }\n \n .myio-daterange-helper.error {\n color: #dc3545;\n font-weight: 500;\n }\n \n /* Responsive design */\n @media (max-width: 768px) {\n .myio-daterange-wrapper {\n padding: 16px;\n margin-bottom: 16px;\n }\n \n .myio-daterange-input {\n max-width: 100%;\n font-size: 16px; /* Prevents zoom on iOS */\n }\n }\n \n /* High contrast mode support */\n @media (prefers-contrast: high) {\n .myio-daterange-wrapper {\n border: 2px solid #000;\n }\n \n .myio-daterange-input {\n border: 2px solid #000;\n }\n }\n \n /* Reduced motion support */\n @media (prefers-reduced-motion: reduce) {\n .myio-daterange-wrapper,\n .myio-daterange-input,\n .myio-daterange-helper {\n transition: none;\n }\n }\n`;function injectPremiumStyles(){const styleId="myio-daterange-premium-styles";if(document.getElementById(styleId)){return}const styleEl=document.createElement("style");styleEl.id=styleId;styleEl.textContent=PREMIUM_STYLES;document.head.appendChild(styleEl);console.log("[MyIO] Premium date range styles injected")}function validateId(id,context){if(!id||typeof id!=="string"){throw new Error(`[createInputDateRangePickerInsideDIV] ${context} must be a non-empty string`)}if(!/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(id)){throw new Error(`[createInputDateRangePickerInsideDIV] ${context} '${id}' is not a valid HTML ID`)}}async function createInputDateRangePickerInsideDIV(params){const{containerId:containerId,inputId:inputId,label:label="Período de Datas",placeholder:placeholder="Clique para selecionar período",pickerOptions:pickerOptions={},classNames:classNames={},injectStyles:injectStyles=true,showHelper:showHelper=true}=params;validateId(containerId,"containerId");validateId(inputId,"inputId");const container=document.getElementById(containerId);if(!container){throw new Error(`[createInputDateRangePickerInsideDIV] Container '#${containerId}' not found`)}if(injectStyles){injectPremiumStyles()}let inputEl=document.getElementById(inputId);if(inputEl&&inputEl.tagName.toLowerCase()!=="input"){throw new Error(`[createInputDateRangePickerInsideDIV] Element '#${inputId}' exists but is not an input element`)}const wrapper=document.createElement("div");wrapper.className=classNames.wrapper||"myio-daterange-wrapper";wrapper.setAttribute("data-myio-component","daterange-input");wrapper.setAttribute("data-version","1.0.0");let labelEl=null;if(label){labelEl=document.createElement("label");labelEl.className=classNames.label||"myio-daterange-label";labelEl.textContent=label;labelEl.setAttribute("for",inputId);wrapper.appendChild(labelEl)}if(!inputEl){inputEl=document.createElement("input");inputEl.type="text";inputEl.id=inputId;inputEl.name=inputId}inputEl.className=classNames.input||"myio-daterange-input";inputEl.readOnly=true;inputEl.placeholder=placeholder;inputEl.autocomplete="off";inputEl.setAttribute("aria-label",label||"Date range selector");if(showHelper){inputEl.setAttribute("aria-describedby",`${inputId}-helper`)}let helperEl=null;if(showHelper){helperEl=document.createElement("div");helperEl.id=`${inputId}-helper`;helperEl.className=classNames.helper||"myio-daterange-helper";helperEl.setAttribute("aria-live","polite");helperEl.style.display="flex";helperEl.style.alignItems="center"}wrapper.appendChild(inputEl);if(helperEl){wrapper.appendChild(helperEl)}const existingWrapper=container.querySelector('[data-myio-component="daterange-input"]');if(existingWrapper){console.warn(`[createInputDateRangePickerInsideDIV] Replacing existing daterange input in container '#${containerId}'`);existingWrapper.remove()}container.appendChild(wrapper);const enhancedOptions={maxRangeDays:31,onApply:result=>{if(helperEl){const startDate=new Date(result.startISO);const endDate=new Date(result.endISO);const days=Math.ceil((endDate.getTime()-startDate.getTime())/(1e3*60*60*24))+1;helperEl.textContent=`Período selecionado: ${days} dia${days!==1?"s":""}`;helperEl.className=(classNames.helper||"myio-daterange-helper")+" success";setTimeout(()=>{if(helperEl){helperEl.className=classNames.helper||"myio-daterange-helper"}},3e3)}if(pickerOptions.onApply){pickerOptions.onApply(result)}},...pickerOptions};let picker;try{picker=await createDateRangePicker2(inputEl,enhancedOptions);console.log(`[createInputDateRangePickerInsideDIV] Successfully initialized for input '#${inputId}'`)}catch(error){wrapper.remove();throw new Error(`[createInputDateRangePickerInsideDIV] Failed to initialize date picker: ${error.message}`)}const controller={input:inputEl,container:container,wrapper:wrapper,picker:picker,getDisplayValue:()=>inputEl.value,getDates:()=>picker.getDates(),setDates:(startISO,endISO)=>{try{picker.setDates(startISO,endISO)}catch(error){console.error(`[createInputDateRangePickerInsideDIV] Error setting dates:`,error);throw error}},setHelperText:(text,type="default")=>{if(helperEl){helperEl.textContent=text;const baseClass=classNames.helper||"myio-daterange-helper";helperEl.className=type==="default"?baseClass:`${baseClass} ${type}`}},destroy:()=>{try{picker.destroy();console.log(`[createInputDateRangePickerInsideDIV] Date picker destroyed for input '#${inputId}'`)}catch(error){console.warn(`[createInputDateRangePickerInsideDIV] Error destroying picker:`,error)}try{wrapper.remove();console.log(`[createInputDateRangePickerInsideDIV] Wrapper removed for input '#${inputId}'`)}catch(error){console.warn(`[createInputDateRangePickerInsideDIV] Error removing wrapper:`,error)}}};return controller}function openGoalsPanel(params){if(typeof window==="undefined"||typeof document==="undefined"){throw new Error("GoalsPanel requires browser environment")}const{customerId:customerId,token:token,api:api={},data:data=null,shoppingList:shoppingList=[],onSave:onSave=null,onClose:onClose=null,styles:styles={},locale:locale="pt-BR"}=params;if(!customerId){throw new Error("customerId is required")}if(!token&&!data){throw new Error("token is required when not using mock data")}const theme={primaryColor:styles.primaryColor||"#4A148C",accentColor:styles.accentColor||"#FFC107",successColor:styles.successColor||"#28a745",errorColor:styles.errorColor||"#dc3545",warningColor:styles.warningColor||"#fd7e14",borderRadius:styles.borderRadius||"8px",fontFamily:styles.fontFamily||"'Roboto', Arial, sans-serif",zIndex:styles.zIndex||1e4};const i18n=locale==="en-US"?getEnglishStrings():getPortugueseStrings();let modalState={currentTab:"shopping",currentYear:(new Date).getFullYear(),selectedShoppingId:shoppingList.length>0?shoppingList[0].value:null,goalsData:data||null,isDirty:false,isSaving:false,validationErrors:[]};const instance={close:()=>closeModal(),getState:()=>({...modalState}),setYear:year=>setCurrentYear(year),refresh:()=>loadGoalsData()};initializeModal();return instance;function initializeModal(){if(data){modalState.goalsData=data;renderModal4()}else{renderModal4();loadGoalsData()}}function renderModal4(){const existing=document.getElementById("myio-goals-panel-modal");if(existing){existing.remove()}const modal=document.createElement("div");modal.id="myio-goals-panel-modal";modal.className="myio-goals-modal-overlay";modal.setAttribute("role","dialog");modal.setAttribute("aria-modal","true");modal.setAttribute("aria-labelledby","goals-modal-title");modal.innerHTML=generateModalHTML();document.body.appendChild(modal);injectStyles();attachEventListeners();trapFocus(modal);renderTabContent()}function generateModalHTML(){return`\n <div class="myio-goals-modal-backdrop" aria-hidden="true"></div>\n <div class="myio-goals-modal-container">\n <div class="myio-goals-modal-card">\n \x3c!-- Header --\x3e\n <div class="myio-goals-modal-header">\n <div class="myio-goals-header-content">\n <div class="myio-goals-header-icon">\n <svg width="24" height="24" viewBox="0 0 24 24" fill="none">\n <path d="M12 2L15.09 8.26L22 9.27L17 14.14L18.18 21.02L12 17.77L5.82 21.02L7 14.14L2 9.27L8.91 8.26L12 2Z"\n fill="currentColor" stroke="currentColor" stroke-width="1.5"/>\n </svg>\n </div>\n <h2 id="goals-modal-title" class="myio-goals-modal-title">${i18n.modalTitle}</h2>\n </div>\n <button class="myio-goals-close-btn" aria-label="${i18n.close}" data-action="close">\n <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\n <path d="M6 18L18 6M6 6l12 12"/>\n </svg>\n </button>\n </div>\n\n \x3c!-- Year Selector --\x3e\n <div class="myio-goals-year-selector">\n <button class="myio-goals-year-btn" data-action="prev-year" aria-label="${i18n.previousYear}">\n <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\n <path d="M15 18l-6-6 6-6"/>\n </svg>\n </button>\n <div class="myio-goals-year-display">${modalState.currentYear}</div>\n <button class="myio-goals-year-btn" data-action="next-year" aria-label="${i18n.nextYear}">\n <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\n <path d="M9 18l6-6-6-6"/>\n </svg>\n </button>\n </div>\n\n \x3c!-- Tabs --\x3e\n <div class="myio-goals-tabs" role="tablist">\n <button class="myio-goals-tab ${modalState.currentTab==="shopping"?"active":""}"\n role="tab"\n aria-selected="${modalState.currentTab==="shopping"}"\n aria-controls="shopping-panel"\n data-tab="shopping">\n ${i18n.shoppingTab}\n </button>\n <button class="myio-goals-tab ${modalState.currentTab==="assets"?"active":""}"\n role="tab"\n aria-selected="${modalState.currentTab==="assets"}"\n aria-controls="assets-panel"\n data-tab="assets">\n ${i18n.assetsTab}\n </button>\n </div>\n\n \x3c!-- Tab Content --\x3e\n <div class="myio-goals-content">\n <div id="tab-content-area"></div>\n </div>\n\n \x3c!-- Validation Errors --\x3e\n <div id="validation-errors" class="myio-goals-errors" style="display: none;"></div>\n\n \x3c!-- Footer --\x3e\n <div class="myio-goals-modal-footer">\n <div class="myio-goals-meta-info">\n <small id="last-update-info"></small>\n </div>\n <div class="myio-goals-footer-actions">\n <button class="myio-goals-btn myio-goals-btn-secondary" data-action="cancel">\n ${i18n.cancel}\n </button>\n <button class="myio-goals-btn myio-goals-btn-primary" data-action="save" ${modalState.isSaving?"disabled":""}>\n ${modalState.isSaving?i18n.saving:i18n.save}\n </button>\n </div>\n </div>\n </div>\n </div>\n `}function renderTabContent(){const container=document.getElementById("tab-content-area");if(!container)return;if(modalState.currentTab==="shopping"){container.innerHTML=generateShoppingTabHTML();attachShoppingTabListeners()}else{container.innerHTML=generateAssetsTabHTML();attachAssetsTabListeners()}updateLastUpdateInfo()}function generateShoppingTabHTML(){const yearData=getYearData(modalState.currentYear);const annual=yearData?.annual||{total:0,unit:"kWh"};const monthly=yearData?.monthly||{};const monthlySum=Object.values(monthly).reduce((sum,val)=>sum+parseFloat(val||0),0);const progress=annual.total>0?monthlySum/annual.total*100:0;return`\n <div class="myio-goals-shopping-panel" role="tabpanel" id="shopping-panel">\n \x3c!-- Shopping Selector --\x3e\n ${shoppingList.length>0?`\n <div class="myio-goals-section">\n <div class="myio-goals-shopping-selector">\n <label for="shopping-select" class="myio-goals-shopping-label">\n <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\n <path d="M3 9l9-7 9 7v11a2 2 0 01-2 2H5a2 2 0 01-2-2z"/>\n <path d="M9 22V12h6v10"/>\n </svg>\n ${i18n.selectShopping}\n </label>\n <select id="shopping-select" class="myio-goals-input myio-goals-shopping-select">\n ${shoppingList.map(shopping=>`\n <option value="${shopping.value}" ${modalState.selectedShoppingId===shopping.value?"selected":""}>\n ${shopping.name}\n </option>\n `).join("")}\n </select>\n </div>\n </div>\n `:""}\n\n \x3c!-- Annual Goal Section --\x3e\n <div class="myio-goals-section">\n <h3 class="myio-goals-section-title">${i18n.annualGoal}</h3>\n <div class="myio-goals-form-row">\n <div class="myio-goals-form-group">\n <label for="unit-select">${i18n.unit}</label>\n <select id="unit-select" class="myio-goals-input">\n <option value="kWh" ${annual.unit==="kWh"?"selected":""}>kWh</option>\n <option value="m3" ${annual.unit==="m3"?"selected":""}>m³</option>\n </select>\n </div>\n <div class="myio-goals-form-group myio-goals-form-group-large">\n <label for="annual-total">${i18n.annualTotal}</label>\n <input type="number"\n id="annual-total"\n class="myio-goals-input"\n value="${annual.total}"\n min="0"\n step="0.01"\n placeholder="0.00">\n </div>\n </div>\n </div>\n\n \x3c!-- Monthly Distribution Section --\x3e\n <div class="myio-goals-section">\n <div class="myio-goals-section-header">\n <h3 class="myio-goals-section-title">${i18n.monthlyDistribution}</h3>\n <button class="myio-goals-btn-link" data-action="auto-fill">\n ${i18n.autoFill}\n </button>\n </div>\n\n \x3c!-- Progress Bar --\x3e\n <div class="myio-goals-progress-bar">\n <div class="myio-goals-progress-fill" style="width: ${Math.min(progress,100)}%"></div>\n </div>\n <div class="myio-goals-progress-text">\n <span>${formatNumber3(monthlySum,locale)} ${annual.unit}</span>\n <span>${formatNumber3(annual.total,locale)} ${annual.unit}</span>\n </div>\n\n \x3c!-- Monthly Grid --\x3e\n <div class="myio-goals-monthly-grid">\n ${generateMonthlyInputsHTML(monthly,annual.unit)}\n </div>\n </div>\n </div>\n `}function generateMonthlyInputsHTML(monthly,unit){const months=[{key:"01",label:i18n.jan},{key:"02",label:i18n.feb},{key:"03",label:i18n.mar},{key:"04",label:i18n.apr},{key:"05",label:i18n.may},{key:"06",label:i18n.jun},{key:"07",label:i18n.jul},{key:"08",label:i18n.aug},{key:"09",label:i18n.sep},{key:"10",label:i18n.oct},{key:"11",label:i18n.nov},{key:"12",label:i18n.dec}];return months.map(month=>`\n <div class="myio-goals-month-input">\n <label for="month-${month.key}">${month.label}</label>\n <input type="number"\n id="month-${month.key}"\n class="myio-goals-input myio-goals-input-small"\n data-month="${month.key}"\n value="${monthly[month.key]||""}"\n min="0"\n step="0.01"\n placeholder="0">\n <span class="myio-goals-unit">${unit}</span>\n </div>\n `).join("")}function generateAssetsTabHTML(){const yearData=getYearData(modalState.currentYear);const assets=yearData?.assets||{};return`\n <div class="myio-goals-assets-panel" role="tabpanel" id="assets-panel">\n <div class="myio-goals-assets-header">\n <input type="text"\n id="asset-search"\n class="myio-goals-input"\n placeholder="${i18n.searchAssets}"\n aria-label="${i18n.searchAssets}">\n <button class="myio-goals-btn myio-goals-btn-primary" data-action="add-asset">\n + ${i18n.addAsset}\n </button>\n </div>\n\n <div class="myio-goals-assets-list">\n ${Object.keys(assets).length>0?generateAssetItemsHTML(assets):`<div class="myio-goals-empty-state">${i18n.noAssets}</div>`}\n </div>\n </div>\n `}function generateAssetItemsHTML(assets){return Object.entries(assets).map(([assetId,assetData])=>`\n <div class="myio-goals-asset-item" data-asset-id="${assetId}">\n <div class="myio-goals-asset-header" data-action="toggle-asset">\n <div class="myio-goals-asset-title">\n <svg class="myio-goals-asset-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\n <path d="M9 18l6-6-6-6"/>\n </svg>\n <span>${assetData.label||assetId}</span>\n </div>\n <div class="myio-goals-asset-total">\n ${formatNumber3(assetData.annual?.total||0,locale)} ${assetData.annual?.unit||"kWh"}\n </div>\n <button class="myio-goals-btn-icon" data-action="delete-asset" data-asset-id="${assetId}" aria-label="${i18n.deleteAsset}">\n <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\n <path d="M3 6h18M19 6v14a2 2 0 01-2 2H7a2 2 0 01-2-2V6m3 0V4a2 2 0 012-2h4a2 2 0 012 2v2"/>\n </svg>\n </button>\n </div>\n <div class="myio-goals-asset-content" style="display: none;">\n ${generateAssetDetailHTML(assetId,assetData)}\n </div>\n </div>\n `).join("")}function generateAssetDetailHTML(assetId,assetData){const annual=assetData.annual||{total:0,unit:"kWh"};const monthly=assetData.monthly||{};return`\n <div class="myio-goals-asset-detail">\n <div class="myio-goals-form-row">\n <div class="myio-goals-form-group">\n <label for="asset-${assetId}-unit">${i18n.unit}</label>\n <select id="asset-${assetId}-unit" class="myio-goals-input" data-asset-id="${assetId}">\n <option value="kWh" ${annual.unit==="kWh"?"selected":""}>kWh</option>\n <option value="m3" ${annual.unit==="m3"?"selected":""}>m³</option>\n </select>\n </div>\n <div class="myio-goals-form-group myio-goals-form-group-large">\n <label for="asset-${assetId}-total">${i18n.annualTotal}</label>\n <input type="number"\n id="asset-${assetId}-total"\n class="myio-goals-input"\n data-asset-id="${assetId}"\n value="${annual.total}"\n min="0"\n step="0.01">\n </div>\n </div>\n\n <div class="myio-goals-monthly-grid">\n ${generateAssetMonthlyInputsHTML(assetId,monthly,annual.unit)}\n </div>\n </div>\n `}function generateAssetMonthlyInputsHTML(assetId,monthly,unit){const months=[{key:"01",label:i18n.jan},{key:"02",label:i18n.feb},{key:"03",label:i18n.mar},{key:"04",label:i18n.apr},{key:"05",label:i18n.may},{key:"06",label:i18n.jun},{key:"07",label:i18n.jul},{key:"08",label:i18n.aug},{key:"09",label:i18n.sep},{key:"10",label:i18n.oct},{key:"11",label:i18n.nov},{key:"12",label:i18n.dec}];return months.map(month=>`\n <div class="myio-goals-month-input">\n <label for="asset-${assetId}-month-${month.key}">${month.label}</label>\n <input type="number"\n id="asset-${assetId}-month-${month.key}"\n class="myio-goals-input myio-goals-input-small"\n data-asset-id="${assetId}"\n data-month="${month.key}"\n value="${monthly[month.key]||""}"\n min="0"\n step="0.01"\n placeholder="0">\n <span class="myio-goals-unit">${unit}</span>\n </div>\n `).join("")}function attachEventListeners(){const modal=document.getElementById("myio-goals-panel-modal");if(!modal)return;modal.addEventListener("click",e=>{const action=e.target.closest("[data-action]")?.dataset.action;if(action==="close"||action==="cancel"){if(modalState.isDirty){if(confirm(i18n.unsavedChanges)){closeModal()}}else{closeModal()}}else if(action==="save"){handleSave()}else if(action==="prev-year"){setCurrentYear(modalState.currentYear-1)}else if(action==="next-year"){setCurrentYear(modalState.currentYear+1)}});modal.addEventListener("click",e=>{const tab=e.target.closest("[data-tab]");if(tab){switchTab(tab.dataset.tab)}});modal.querySelector(".myio-goals-modal-backdrop")?.addEventListener("click",()=>{if(modalState.isDirty){if(confirm(i18n.unsavedChanges)){closeModal()}}else{closeModal()}});const handleEscape=e=>{if(e.key==="Escape"){if(modalState.isDirty){if(confirm(i18n.unsavedChanges)){closeModal()}}else{closeModal()}}};document.addEventListener("keydown",handleEscape);modal._escapeHandler=handleEscape}function attachShoppingTabListeners(){const container=document.getElementById("tab-content-area");if(!container)return;container.querySelector("#shopping-select")?.addEventListener("change",e=>{modalState.selectedShoppingId=e.target.value;modalState.isDirty=true;loadGoalsDataForShopping(e.target.value)});container.querySelector("#annual-total")?.addEventListener("input",e=>{modalState.isDirty=true;updateProgressBar()});container.querySelector("#unit-select")?.addEventListener("change",e=>{modalState.isDirty=true;updateMonthlyUnits(e.target.value)});container.querySelectorAll("[data-month]").forEach(input=>{input.addEventListener("input",()=>{modalState.isDirty=true;updateProgressBar()})});container.querySelector('[data-action="auto-fill"]')?.addEventListener("click",()=>{autoFillMonthly()})}function attachAssetsTabListeners(){const container=document.getElementById("tab-content-area");if(!container)return;container.addEventListener("click",e=>{const toggleBtn=e.target.closest('[data-action="toggle-asset"]');if(toggleBtn){const assetItem=toggleBtn.closest(".myio-goals-asset-item");const content=assetItem?.querySelector(".myio-goals-asset-content");const icon=assetItem?.querySelector(".myio-goals-asset-icon");if(content){const isHidden=content.style.display==="none";content.style.display=isHidden?"block":"none";if(icon){icon.style.transform=isHidden?"rotate(90deg)":"rotate(0deg)"}}}const deleteBtn=e.target.closest('[data-action="delete-asset"]');if(deleteBtn){const assetId=deleteBtn.dataset.assetId;if(confirm(i18n.confirmDeleteAsset)){deleteAsset(assetId)}}const addBtn=e.target.closest('[data-action="add-asset"]');if(addBtn){showAddAssetDialog()}});container.addEventListener("input",e=>{if(e.target.dataset.assetId){modalState.isDirty=true}})}function getYearData(year){if(!modalState.goalsData?.years)return null;return modalState.goalsData.years[year.toString()]}function setYearData(year,data2){if(!modalState.goalsData){modalState.goalsData={version:1,history:[],years:{}}}if(!modalState.goalsData.years){modalState.goalsData.years={}}modalState.goalsData.years[year.toString()]=data2}function loadGoalsData(){if(!modalState.goalsData){modalState.goalsData={version:1,history:[],years:{}};setYearData(modalState.currentYear,{annual:{total:0,unit:"kWh"},monthly:{},assets:{},metaTag:`${(new Date).toISOString()}|user`})}renderTabContent()}function loadGoalsDataForShopping(shoppingId){console.log("[GoalsPanel] Loading goals for shopping:",shoppingId);renderTabContent()}async function handleSave(){const errors=validateGoalsData();if(errors.length>0){displayValidationErrors(errors);return}modalState.isSaving=true;updateSaveButton();try{const goalsData=collectGoalsDataFromInputs();if(!modalState.goalsData.version){modalState.goalsData.version=1}else{modalState.goalsData.version++}if(!modalState.goalsData.history){modalState.goalsData.history=[]}modalState.goalsData.history.unshift({tag:`${(new Date).toISOString()}|user`,reason:"Manual update from Goals Panel",diff:{year:modalState.currentYear,changed:["manual_update"]}});setYearData(modalState.currentYear,goalsData);if(onSave){await onSave(modalState.goalsData)}modalState.isDirty=false;showSuccessMessage(i18n.saveSuccess);setTimeout(()=>{closeModal()},1500)}catch(error){console.error("Error saving goals:",error);displayValidationErrors([i18n.saveError+": "+error.message])}finally{modalState.isSaving=false;updateSaveButton()}}function collectGoalsDataFromInputs(){if(modalState.currentTab==="shopping"){return collectShoppingData()}else{return collectAssetsData()}}function collectShoppingData(){const unitSelect=document.getElementById("unit-select");const annualTotal=document.getElementById("annual-total");const unit=unitSelect?.value||"kWh";const total=parseFloat(annualTotal?.value||0);const monthly={};for(let i=1;i<=12;i++){const monthKey=i.toString().padStart(2,"0");const input=document.getElementById(`month-${monthKey}`);if(input&&input.value){monthly[monthKey]=parseFloat(input.value)}}const yearData=getYearData(modalState.currentYear)||{assets:{}};return{annual:{total:total,unit:unit},monthly:monthly,assets:yearData.assets||{},metaTag:`${(new Date).toISOString()}|user`}}function collectAssetsData(){const yearData=getYearData(modalState.currentYear);return yearData||{annual:{total:0,unit:"kWh"},monthly:{},assets:{},metaTag:`${(new Date).toISOString()}|user`}}function validateGoalsData(){const errors=[];if(modalState.currentTab==="shopping"){const annualTotal=parseFloat(document.getElementById("annual-total")?.value||0);if(annualTotal<0){errors.push(i18n.errorNegativeAnnual)}let monthlySum=0;for(let i=1;i<=12;i++){const monthKey=i.toString().padStart(2,"0");const input=document.getElementById(`month-${monthKey}`);const value=parseFloat(input?.value||0);if(value<0){errors.push(`${i18n.errorNegativeMonth} ${monthKey}`)}monthlySum+=value}if(monthlySum>annualTotal&&annualTotal>0){errors.push(`${i18n.errorMonthlyExceedsAnnual} (${formatNumber3(monthlySum,locale)} > ${formatNumber3(annualTotal,locale)})`)}}return errors}function displayValidationErrors(errors){const container=document.getElementById("validation-errors");if(!container)return;if(errors.length===0){container.style.display="none";return}container.innerHTML=`\n <div class="myio-goals-error-header">\n <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\n <circle cx="12" cy="12" r="10"/>\n <line x1="12" y1="8" x2="12" y2="12"/>\n <line x1="12" y1="16" x2="12.01" y2="16"/>\n </svg>\n ${i18n.validationErrors}\n </div>\n <ul class="myio-goals-error-list">\n ${errors.map(err=>`<li>${err}</li>`).join("")}\n </ul>\n `;container.style.display="block";container.scrollIntoView({behavior:"smooth",block:"nearest"})}function showSuccessMessage(message){const container=document.getElementById("validation-errors");if(!container)return;container.innerHTML=`\n <div class="myio-goals-success-message">\n <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\n <path d="M20 6L9 17l-5-5"/>\n </svg>\n ${message}\n </div>\n `;container.style.display="block"}function switchTab(tab){if(modalState.currentTab===tab)return;modalState.currentTab=tab;const modal=document.getElementById("myio-goals-panel-modal");modal.querySelectorAll(".myio-goals-tab").forEach(btn=>{const isActive=btn.dataset.tab===tab;btn.classList.toggle("active",isActive);btn.setAttribute("aria-selected",isActive.toString())});renderTabContent()}function setCurrentYear(year){modalState.currentYear=year;const yearDisplay=document.querySelector(".myio-goals-year-display");if(yearDisplay){yearDisplay.textContent=year}if(!getYearData(year)){setYearData(year,{annual:{total:0,unit:"kWh"},monthly:{},assets:{},metaTag:`${(new Date).toISOString()}|user`})}renderTabContent()}function updateProgressBar(){const annualTotal=parseFloat(document.getElementById("annual-total")?.value||0);let monthlySum=0;for(let i=1;i<=12;i++){const monthKey=i.toString().padStart(2,"0");const input=document.getElementById(`month-${monthKey}`);monthlySum+=parseFloat(input?.value||0)}const progress=annualTotal>0?monthlySum/annualTotal*100:0;const progressFill=document.querySelector(".myio-goals-progress-fill");const progressTexts=document.querySelectorAll(".myio-goals-progress-text span");if(progressFill){progressFill.style.width=Math.min(progress,100)+"%";if(progress>100){progressFill.style.background=theme.errorColor}else if(progress>95){progressFill.style.background=theme.warningColor}else{progressFill.style.background=theme.successColor}}if(progressTexts.length===2){const unit=document.getElementById("unit-select")?.value||"kWh";progressTexts[0].textContent=`${formatNumber3(monthlySum,locale)} ${unit}`;progressTexts[1].textContent=`${formatNumber3(annualTotal,locale)} ${unit}`}}function updateMonthlyUnits(unit){document.querySelectorAll(".myio-goals-unit").forEach(span=>{span.textContent=unit})}function autoFillMonthly(){const annualTotal=parseFloat(document.getElementById("annual-total")?.value||0);if(annualTotal<=0)return;const monthlyValue=Math.round(annualTotal/12*100)/100;for(let i=1;i<=12;i++){const monthKey=i.toString().padStart(2,"0");const input=document.getElementById(`month-${monthKey}`);if(input){input.value=monthlyValue}}modalState.isDirty=true;updateProgressBar()}function deleteAsset(assetId){const yearData=getYearData(modalState.currentYear);if(yearData?.assets?.[assetId]){delete yearData.assets[assetId];modalState.isDirty=true;renderTabContent()}}function showAddAssetDialog(){const assetLabel=prompt(i18n.enterAssetName);if(!assetLabel)return;const yearData=getYearData(modalState.currentYear);if(!yearData.assets)yearData.assets={};const assetId=`asset-${Date.now()}`;yearData.assets[assetId]={label:assetLabel,annual:{total:0,unit:"kWh"},monthly:{}};modalState.isDirty=true;renderTabContent()}function updateLastUpdateInfo(){const infoElement=document.getElementById("last-update-info");if(!infoElement)return;const yearData=getYearData(modalState.currentYear);if(yearData?.metaTag){const[timestamp,author]=yearData.metaTag.split("|");const date=new Date(timestamp);infoElement.textContent=`${i18n.lastUpdate}: ${date.toLocaleString(locale)} - ${author}`}else{infoElement.textContent=""}}function updateSaveButton(){const saveBtn=document.querySelector('[data-action="save"]');if(saveBtn){saveBtn.disabled=modalState.isSaving;saveBtn.textContent=modalState.isSaving?i18n.saving:i18n.save}}function closeModal(){const modal=document.getElementById("myio-goals-panel-modal");if(!modal)return;if(modal._escapeHandler){document.removeEventListener("keydown",modal._escapeHandler)}modal.remove();if(onClose){onClose()}}function trapFocus(modal){const focusableElements=modal.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');if(focusableElements.length===0)return;const firstElement=focusableElements[0];const lastElement=focusableElements[focusableElements.length-1];const handleTab=e=>{if(e.key!=="Tab")return;if(e.shiftKey){if(document.activeElement===firstElement){e.preventDefault();lastElement.focus()}}else{if(document.activeElement===lastElement){e.preventDefault();firstElement.focus()}}};modal.addEventListener("keydown",handleTab);firstElement.focus()}function formatNumber3(value,locale2){return new Intl.NumberFormat(locale2,{minimumFractionDigits:0,maximumFractionDigits:2}).format(value)}function injectStyles(){const styleId="myio-goals-panel-styles";if(document.getElementById(styleId))return;const style=document.createElement("style");style.id=styleId;style.textContent=`\n .myio-goals-modal-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n z-index: ${theme.zIndex};\n display: flex;\n align-items: center;\n justify-content: center;\n font-family: ${theme.fontFamily};\n }\n\n .myio-goals-modal-backdrop {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n backdrop-filter: blur(4px);\n }\n\n .myio-goals-modal-container {\n position: relative;\n width: 90%;\n max-width: 1000px;\n max-height: 90vh;\n display: flex;\n flex-direction: column;\n }\n\n .myio-goals-modal-card {\n background: white;\n border-radius: ${theme.borderRadius};\n box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);\n display: flex;\n flex-direction: column;\n max-height: 90vh;\n overflow: hidden;\n }\n\n .myio-goals-modal-header {\n background: ${theme.primaryColor};\n color: white;\n padding: 20px 24px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n flex-shrink: 0;\n }\n\n .myio-goals-header-content {\n display: flex;\n align-items: center;\n gap: 12px;\n }\n\n .myio-goals-header-icon {\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .myio-goals-modal-title {\n margin: 0;\n font-size: 20px;\n font-weight: 600;\n }\n\n .myio-goals-close-btn {\n background: transparent;\n border: none;\n color: white;\n cursor: pointer;\n padding: 8px;\n border-radius: 4px;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: background 0.2s;\n }\n\n .myio-goals-close-btn:hover {\n background: rgba(255, 255, 255, 0.1);\n }\n\n .myio-goals-year-selector {\n padding: 16px 24px;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 24px;\n background: #f8f9fa;\n border-bottom: 1px solid #dee2e6;\n }\n\n .myio-goals-year-btn {\n background: white;\n border: 1px solid #dee2e6;\n border-radius: 50%;\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n transition: all 0.2s;\n }\n\n .myio-goals-year-btn:hover {\n background: ${theme.primaryColor};\n color: white;\n border-color: ${theme.primaryColor};\n }\n\n .myio-goals-year-display {\n font-size: 24px;\n font-weight: 700;\n color: ${theme.primaryColor};\n min-width: 80px;\n text-align: center;\n }\n\n .myio-goals-tabs {\n display: flex;\n border-bottom: 1px solid #dee2e6;\n background: white;\n flex-shrink: 0;\n }\n\n .myio-goals-tab {\n flex: 1;\n padding: 16px 24px;\n background: transparent;\n border: none;\n border-bottom: 3px solid transparent;\n cursor: pointer;\n font-size: 15px;\n font-weight: 500;\n color: #6c757d;\n transition: all 0.2s;\n }\n\n .myio-goals-tab:hover {\n background: #f8f9fa;\n color: ${theme.primaryColor};\n }\n\n .myio-goals-tab.active {\n color: ${theme.primaryColor};\n border-bottom-color: ${theme.primaryColor};\n }\n\n .myio-goals-content {\n flex: 1;\n overflow-y: auto;\n padding: 24px;\n }\n\n .myio-goals-section {\n margin-bottom: 32px;\n }\n\n .myio-goals-section-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 16px;\n }\n\n .myio-goals-section-title {\n font-size: 16px;\n font-weight: 600;\n color: #212529;\n margin: 0 0 16px 0;\n }\n\n .myio-goals-shopping-selector {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 20px;\n background: linear-gradient(135deg, ${theme.primaryColor}15 0%, ${theme.primaryColor}05 100%);\n border: 2px solid ${theme.primaryColor}30;\n border-radius: ${theme.borderRadius};\n margin-bottom: 24px;\n }\n\n .myio-goals-shopping-label {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 15px;\n font-weight: 600;\n color: ${theme.primaryColor};\n margin: 0;\n }\n\n .myio-goals-shopping-label svg {\n flex-shrink: 0;\n }\n\n .myio-goals-shopping-select {\n font-size: 15px;\n font-weight: 500;\n padding: 12px 16px;\n border: 2px solid ${theme.primaryColor};\n background: white;\n color: ${theme.primaryColor};\n cursor: pointer;\n }\n\n .myio-goals-shopping-select:hover {\n background: ${theme.primaryColor}10;\n }\n\n .myio-goals-shopping-select:focus {\n border-color: ${theme.primaryColor};\n box-shadow: 0 0 0 4px ${theme.primaryColor}20;\n }\n\n .myio-goals-btn-link {\n background: transparent;\n border: none;\n color: ${theme.primaryColor};\n cursor: pointer;\n font-size: 14px;\n font-weight: 500;\n padding: 4px 8px;\n border-radius: 4px;\n transition: background 0.2s;\n }\n\n .myio-goals-btn-link:hover {\n background: rgba(74, 20, 140, 0.1);\n }\n\n .myio-goals-form-row {\n display: flex;\n gap: 16px;\n margin-bottom: 16px;\n }\n\n .myio-goals-form-group {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 8px;\n }\n\n .myio-goals-form-group-large {\n flex: 2;\n }\n\n .myio-goals-form-group label {\n font-size: 14px;\n font-weight: 500;\n color: #495057;\n }\n\n .myio-goals-input {\n padding: 10px 12px;\n border: 1px solid #ced4da;\n border-radius: 6px;\n font-size: 14px;\n transition: border-color 0.2s;\n }\n\n .myio-goals-input:focus {\n outline: none;\n border-color: ${theme.primaryColor};\n box-shadow: 0 0 0 3px rgba(74, 20, 140, 0.1);\n }\n\n .myio-goals-progress-bar {\n width: 100%;\n height: 8px;\n background: #e9ecef;\n border-radius: 4px;\n overflow: hidden;\n margin-bottom: 8px;\n }\n\n .myio-goals-progress-fill {\n height: 100%;\n background: ${theme.successColor};\n transition: width 0.3s, background 0.3s;\n }\n\n .myio-goals-progress-text {\n display: flex;\n justify-content: space-between;\n font-size: 14px;\n color: #6c757d;\n margin-bottom: 16px;\n }\n\n .myio-goals-monthly-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));\n gap: 16px;\n }\n\n .myio-goals-month-input {\n display: flex;\n flex-direction: column;\n gap: 6px;\n }\n\n .myio-goals-month-input label {\n font-size: 13px;\n font-weight: 500;\n color: #495057;\n }\n\n .myio-goals-input-small {\n padding: 8px 10px;\n font-size: 13px;\n }\n\n .myio-goals-unit {\n font-size: 12px;\n color: #6c757d;\n margin-top: -4px;\n }\n\n .myio-goals-assets-header {\n display: flex;\n gap: 16px;\n margin-bottom: 24px;\n }\n\n .myio-goals-assets-header .myio-goals-input {\n flex: 1;\n }\n\n .myio-goals-assets-list {\n display: flex;\n flex-direction: column;\n gap: 12px;\n }\n\n .myio-goals-asset-item {\n border: 1px solid #dee2e6;\n border-radius: ${theme.borderRadius};\n overflow: hidden;\n }\n\n .myio-goals-asset-header {\n padding: 16px;\n background: #f8f9fa;\n display: flex;\n align-items: center;\n justify-content: space-between;\n cursor: pointer;\n transition: background 0.2s;\n }\n\n .myio-goals-asset-header:hover {\n background: #e9ecef;\n }\n\n .myio-goals-asset-title {\n display: flex;\n align-items: center;\n gap: 8px;\n font-weight: 500;\n color: #212529;\n }\n\n .myio-goals-asset-icon {\n transition: transform 0.3s;\n }\n\n .myio-goals-asset-total {\n font-size: 14px;\n color: #6c757d;\n }\n\n .myio-goals-btn-icon {\n background: transparent;\n border: none;\n color: ${theme.errorColor};\n cursor: pointer;\n padding: 6px;\n border-radius: 4px;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: background 0.2s;\n }\n\n .myio-goals-btn-icon:hover {\n background: rgba(220, 53, 69, 0.1);\n }\n\n .myio-goals-asset-content {\n padding: 16px;\n border-top: 1px solid #dee2e6;\n }\n\n .myio-goals-asset-detail {\n /* Inherits styles from shopping tab */\n }\n\n .myio-goals-empty-state {\n text-align: center;\n padding: 48px 24px;\n color: #6c757d;\n font-size: 15px;\n }\n\n .myio-goals-errors {\n margin: 0 24px;\n padding: 16px;\n background: #fff3cd;\n border: 1px solid #ffc107;\n border-radius: ${theme.borderRadius};\n margin-bottom: 16px;\n }\n\n .myio-goals-error-header {\n display: flex;\n align-items: center;\n gap: 8px;\n font-weight: 600;\n color: #856404;\n margin-bottom: 12px;\n }\n\n .myio-goals-error-list {\n margin: 0;\n padding-left: 24px;\n color: #856404;\n }\n\n .myio-goals-error-list li {\n margin-bottom: 4px;\n }\n\n .myio-goals-success-message {\n display: flex;\n align-items: center;\n gap: 8px;\n color: ${theme.successColor};\n font-weight: 500;\n }\n\n .myio-goals-modal-footer {\n padding: 16px 24px;\n background: #f8f9fa;\n border-top: 1px solid #dee2e6;\n display: flex;\n align-items: center;\n justify-content: space-between;\n flex-shrink: 0;\n }\n\n .myio-goals-meta-info {\n font-size: 12px;\n color: #6c757d;\n }\n\n .myio-goals-footer-actions {\n display: flex;\n gap: 12px;\n }\n\n .myio-goals-btn {\n padding: 10px 20px;\n border: none;\n border-radius: 6px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s;\n }\n\n .myio-goals-btn-primary {\n background: ${theme.primaryColor};\n color: white;\n }\n\n .myio-goals-btn-primary:hover:not(:disabled) {\n background: #5c1ba1;\n transform: translateY(-1px);\n box-shadow: 0 4px 12px rgba(74, 20, 140, 0.3);\n }\n\n .myio-goals-btn-primary:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n .myio-goals-btn-secondary {\n background: white;\n color: #495057;\n border: 1px solid #ced4da;\n }\n\n .myio-goals-btn-secondary:hover {\n background: #f8f9fa;\n }\n\n @media (max-width: 768px) {\n .myio-goals-modal-container {\n width: 95%;\n max-height: 95vh;\n }\n\n .myio-goals-monthly-grid {\n grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));\n gap: 12px;\n }\n\n .myio-goals-form-row {\n flex-direction: column;\n }\n }\n `;document.head.appendChild(style)}function getPortugueseStrings(){return{modalTitle:"Setup de Metas de Consumo",close:"Fechar",previousYear:"Ano anterior",nextYear:"Próximo ano",shoppingTab:"Shopping (Anual/Mensal)",assetsTab:"Por Asset",selectShopping:"Selecione o Shopping",annualGoal:"Meta Anual",unit:"Unidade",annualTotal:"Total Anual",monthlyDistribution:"Distribuição Mensal",autoFill:"Preencher Proporcionalmente",searchAssets:"Buscar assets...",addAsset:"Adicionar Asset",noAssets:'Nenhum asset configurado. Clique em "Adicionar Asset" para começar.',deleteAsset:"Remover asset",save:"Salvar",saving:"Salvando...",cancel:"Cancelar",lastUpdate:"Última atualização",jan:"Jan",feb:"Fev",mar:"Mar",apr:"Abr",may:"Mai",jun:"Jun",jul:"Jul",aug:"Ago",sep:"Set",oct:"Out",nov:"Nov",dec:"Dez",unsavedChanges:"Você tem alterações não salvas. Deseja sair mesmo assim?",confirmDeleteAsset:"Deseja realmente remover este asset?",enterAssetName:"Digite o nome do asset:",validationErrors:"Erros de validação",errorNegativeAnnual:"A meta anual não pode ser negativa",errorNegativeMonth:"O valor mensal não pode ser negativo para o mês",errorMonthlyExceedsAnnual:"A soma das metas mensais excede a meta anual",saveSuccess:"Metas salvas com sucesso!",saveError:"Erro ao salvar metas"}}function getEnglishStrings(){return{modalTitle:"Consumption Goals Setup",close:"Close",previousYear:"Previous year",nextYear:"Next year",shoppingTab:"Shopping (Annual/Monthly)",assetsTab:"By Asset",selectShopping:"Select Shopping Center",annualGoal:"Annual Goal",unit:"Unit",annualTotal:"Annual Total",monthlyDistribution:"Monthly Distribution",autoFill:"Auto Fill Proportionally",searchAssets:"Search assets...",addAsset:"Add Asset",noAssets:'No assets configured. Click "Add Asset" to get started.',deleteAsset:"Remove asset",save:"Save",saving:"Saving...",cancel:"Cancel",lastUpdate:"Last update",jan:"Jan",feb:"Feb",mar:"Mar",apr:"Apr",may:"May",jun:"Jun",jul:"Jul",aug:"Aug",sep:"Sep",oct:"Oct",nov:"Nov",dec:"Dec",unsavedChanges:"You have unsaved changes. Do you want to exit anyway?",confirmDeleteAsset:"Do you really want to remove this asset?",enterAssetName:"Enter asset name:",validationErrors:"Validation errors",errorNegativeAnnual:"Annual goal cannot be negative",errorNegativeMonth:"Monthly value cannot be negative for month",errorMonthlyExceedsAnnual:"Monthly sum exceeds annual goal",saveSuccess:"Goals saved successfully!",saveError:"Error saving goals"}}}var DAY_PERIODS=[{id:"madrugada",label:"Madrugada (00h-06h)",startHour:0,endHour:6},{id:"manha",label:"Manhã (06h-12h)",startHour:6,endHour:12},{id:"tarde",label:"Tarde (12h-18h)",startHour:12,endHour:18},{id:"noite",label:"Noite (18h-24h)",startHour:18,endHour:24}];var DEFAULT_CLAMP_RANGE={min:15,max:40};function getTodaySoFar(){const now=new Date;const startOfDay=new Date(now.getFullYear(),now.getMonth(),now.getDate(),0,0,0,0);return{startTs:startOfDay.getTime(),endTs:now.getTime()}}var CHART_COLORS=["#1976d2","#FF6B6B","#4CAF50","#FF9800","#9C27B0","#00BCD4","#E91E63","#795548"];async function fetchTemperatureData(token,deviceId,startTs,endTs){const url=`/api/plugins/telemetry/DEVICE/${deviceId}/values/timeseries?keys=temperature&startTs=${encodeURIComponent(startTs)}&endTs=${encodeURIComponent(endTs)}&limit=50000&agg=NONE`;const response=await fetch(url,{headers:{"X-Authorization":`Bearer ${token}`,"Content-Type":"application/json"}});if(!response.ok){throw new Error(`Failed to fetch temperature data: ${response.status}`)}const data=await response.json();return data?.temperature||[]}function clampTemperature(value,range=DEFAULT_CLAMP_RANGE){const num=Number(value||0);if(num<range.min)return range.min;if(num>range.max)return range.max;return num}function calculateStats(data,clampRange=DEFAULT_CLAMP_RANGE){if(data.length===0){return{avg:0,min:0,max:0,count:0}}const values=data.map(item=>clampTemperature(item.value,clampRange));const sum=values.reduce((acc,v)=>acc+v,0);return{avg:sum/values.length,min:Math.min(...values),max:Math.max(...values),count:values.length}}function interpolateTemperature(data,options){const{intervalMinutes:intervalMinutes,startTs:startTs,endTs:endTs,clampRange:clampRange=DEFAULT_CLAMP_RANGE}=options;const intervalMs=intervalMinutes*60*1e3;if(data.length===0){return[]}const sortedData=[...data].sort((a,b)=>a.ts-b.ts);const result=[];let lastKnownValue=clampTemperature(sortedData[0].value,clampRange);let dataIndex=0;for(let ts=startTs;ts<=endTs;ts+=intervalMs){while(dataIndex<sortedData.length-1&&sortedData[dataIndex+1].ts<=ts){dataIndex++}const currentData=sortedData[dataIndex];if(currentData&&Math.abs(currentData.ts-ts)<intervalMs){lastKnownValue=clampTemperature(currentData.value,clampRange)}result.push({ts:ts,value:lastKnownValue})}return result}function aggregateByDay(data,clampRange=DEFAULT_CLAMP_RANGE){if(data.length===0){return[]}const dayMap=new Map;data.forEach(item=>{const date=new Date(item.ts);const dateKey=date.toISOString().split("T")[0];if(!dayMap.has(dateKey)){dayMap.set(dateKey,[])}dayMap.get(dateKey).push(item)});const result=[];dayMap.forEach((dayData,dateKey)=>{const values=dayData.map(item=>clampTemperature(item.value,clampRange));const sum=values.reduce((acc,v)=>acc+v,0);result.push({date:dateKey,dateTs:new Date(dateKey).getTime(),avg:sum/values.length,min:Math.min(...values),max:Math.max(...values),count:values.length})});return result.sort((a,b)=>a.dateTs-b.dateTs)}function filterByDayPeriods(data,selectedPeriods){if(selectedPeriods.length===0||selectedPeriods.length===DAY_PERIODS.length){return data}return data.filter(item=>{const date=new Date(item.ts);const hour=date.getHours();return selectedPeriods.some(periodId=>{const period=DAY_PERIODS.find(p=>p.id===periodId);if(!period)return false;return hour>=period.startHour&&hour<period.endHour})})}function getSelectedPeriodsLabel(selectedPeriods){if(selectedPeriods.length===0||selectedPeriods.length===DAY_PERIODS.length){return"Todos os períodos"}if(selectedPeriods.length===1){const period=DAY_PERIODS.find(p=>p.id===selectedPeriods[0]);return period?.label||""}return`${selectedPeriods.length} períodos selecionados`}function formatTemperature2(value,decimals=1){return`${value.toFixed(decimals)}°C`}function exportTemperatureCSV(data,deviceLabel,stats,startDate,endDate){if(data.length===0){console.warn("No data to export");return}const BOM="\ufeff";let csvContent=BOM;csvContent+=`Relatório de Temperatura - ${deviceLabel}\n`;csvContent+=`Período: ${startDate} até ${endDate}\n`;csvContent+=`Média: ${formatTemperature2(stats.avg)}\n`;csvContent+=`Mínima: ${formatTemperature2(stats.min)}\n`;csvContent+=`Máxima: ${formatTemperature2(stats.max)}\n`;csvContent+=`Total de leituras: ${stats.count}\n`;csvContent+="\n";csvContent+="Data/Hora,Temperatura (°C)\n";data.forEach(item=>{const date=new Date(item.ts).toLocaleString("pt-BR");const temp=Number(item.value).toFixed(2);csvContent+=`"${date}",${temp}\n`});const blob=new Blob([csvContent],{type:"text/csv;charset=utf-8;"});const url=URL.createObjectURL(blob);const link=document.createElement("a");link.href=url;link.download=`temperatura_${deviceLabel.replace(/\s+/g,"_")}_${startDate}_${endDate}.csv`;document.body.appendChild(link);link.click();document.body.removeChild(link);URL.revokeObjectURL(url)}var DARK_THEME={background:"rgba(0, 0, 0, 0.85)",surface:"#1a1f28",text:"#ffffff",textMuted:"rgba(255, 255, 255, 0.7)",border:"rgba(255, 255, 255, 0.1)",primary:"#1976d2",success:"#4CAF50",warning:"#FF9800",danger:"#f44336",chartLine:"#1976d2",chartGrid:"rgba(255, 255, 255, 0.1)"};var LIGHT_THEME={background:"rgba(0, 0, 0, 0.6)",surface:"#ffffff",text:"#333333",textMuted:"#666666",border:"#e0e0e0",primary:"#1976d2",success:"#4CAF50",warning:"#FF9800",danger:"#f44336",chartLine:"#1976d2",chartGrid:"#e0e0e0"};function getThemeColors(theme){return theme==="dark"?DARK_THEME:LIGHT_THEME}async function openTemperatureModal(params){const modalId=`myio-temp-modal-${Date.now()}`;const defaultDateRange=getTodaySoFar();const startTs=params.startDate?new Date(params.startDate).getTime():defaultDateRange.startTs;const endTs=params.endDate?new Date(params.endDate).getTime():defaultDateRange.endTs;const state={token:params.token,deviceId:params.deviceId,label:params.label||"Sensor de Temperatura",currentTemperature:params.currentTemperature??null,temperatureMin:params.temperatureMin??null,temperatureMax:params.temperatureMax??null,temperatureStatus:params.temperatureStatus??null,startTs:startTs,endTs:endTs,granularity:params.granularity||"hour",theme:params.theme||"light",clampRange:params.clampRange||DEFAULT_CLAMP_RANGE,locale:params.locale||"pt-BR",data:[],stats:{avg:0,min:0,max:0,count:0},isLoading:true,dateRangePicker:null,selectedPeriods:["madrugada","manha","tarde","noite"]};const savedGranularity=localStorage.getItem("myio-temp-modal-granularity");const savedTheme=localStorage.getItem("myio-temp-modal-theme");if(savedGranularity)state.granularity=savedGranularity;if(savedTheme)state.theme=savedTheme;const modalContainer=document.createElement("div");modalContainer.id=modalId;document.body.appendChild(modalContainer);renderModal(modalContainer,state,modalId);try{state.data=await fetchTemperatureData(state.token,state.deviceId,state.startTs,state.endTs);state.stats=calculateStats(state.data,state.clampRange);state.isLoading=false;renderModal(modalContainer,state,modalId);drawChart(modalId,state)}catch(error){console.error("[TemperatureModal] Error fetching data:",error);state.isLoading=false;renderModal(modalContainer,state,modalId,error)}await setupEventListeners(modalContainer,state,modalId,params.onClose);return{destroy:()=>{modalContainer.remove();params.onClose?.()},updateData:async(startDate,endDate,granularity)=>{state.startTs=new Date(startDate).getTime();state.endTs=new Date(endDate).getTime();if(granularity)state.granularity=granularity;state.isLoading=true;renderModal(modalContainer,state,modalId);try{state.data=await fetchTemperatureData(state.token,state.deviceId,state.startTs,state.endTs);state.stats=calculateStats(state.data,state.clampRange);state.isLoading=false;renderModal(modalContainer,state,modalId);drawChart(modalId,state)}catch(error){console.error("[TemperatureModal] Error updating data:",error);state.isLoading=false;renderModal(modalContainer,state,modalId,error)}}}}function renderModal(container,state,modalId,error){const colors=getThemeColors(state.theme);const startDateStr=new Date(state.startTs).toLocaleDateString(state.locale);const endDateStr=new Date(state.endTs).toLocaleDateString(state.locale);const statusText=state.temperatureStatus==="ok"?"Dentro da faixa":state.temperatureStatus==="above"?"Acima do limite":state.temperatureStatus==="below"?"Abaixo do limite":"N/A";const statusColor=state.temperatureStatus==="ok"?colors.success:state.temperatureStatus==="above"?colors.danger:state.temperatureStatus==="below"?colors.primary:colors.textMuted;const rangeText=state.temperatureMin!==null&&state.temperatureMax!==null?`${state.temperatureMin}°C - ${state.temperatureMax}°C`:"Não definida";new Date(state.startTs).toISOString().slice(0,16);new Date(state.endTs).toISOString().slice(0,16);const isMaximized=container.__isMaximized||false;const contentMaxWidth=isMaximized?"100%":"900px";const contentMaxHeight=isMaximized?"100vh":"95vh";const contentBorderRadius=isMaximized?"0":"10px";container.innerHTML=`\n <div class="myio-temp-modal-overlay myio-modal-scope" style="\n position: fixed; top: 0; left: 0; width: 100%; height: 100%;\n background: rgba(0, 0, 0, 0.5); z-index: 9998;\n display: flex; justify-content: center; align-items: center;\n backdrop-filter: blur(2px);\n ">\n <div class="myio-temp-modal-content" style="\n background: ${colors.surface}; border-radius: ${contentBorderRadius};\n max-width: ${contentMaxWidth}; width: ${isMaximized?"100%":"95%"};\n max-height: ${contentMaxHeight}; height: ${isMaximized?"100%":"auto"};\n overflow: hidden; display: flex; flex-direction: column;\n box-shadow: 0 4px 12px rgba(0,0,0,0.15);\n font-family: 'Roboto', Arial, sans-serif;\n ">\n \x3c!-- Header - MyIO Premium Style --\x3e\n <div style="\n padding: 4px 8px; display: flex; align-items: center; justify-content: space-between;\n background: #3e1a7d; color: white; border-radius: ${isMaximized?"0":"10px 10px 0 0"};\n min-height: 20px;\n ">\n <h2 style="margin: 6px; font-size: 18px; font-weight: 600; color: white; line-height: 2;">\n 🌡️ ${state.label} - Histórico de Temperatura\n </h2>\n <div style="display: flex; gap: 4px; align-items: center;">\n \x3c!-- Theme Toggle --\x3e\n <button id="${modalId}-theme-toggle" title="Alternar tema" style="\n background: none; border: none; font-size: 16px; cursor: pointer;\n padding: 4px 8px; border-radius: 6px; color: rgba(255,255,255,0.8);\n transition: background-color 0.2s;\n ">${state.theme==="dark"?"☀️":"🌙"}</button>\n \x3c!-- Maximize Button --\x3e\n <button id="${modalId}-maximize" title="${isMaximized?"Restaurar":"Maximizar"}" style="\n background: none; border: none; font-size: 16px; cursor: pointer;\n padding: 4px 8px; border-radius: 6px; color: rgba(255,255,255,0.8);\n transition: background-color 0.2s;\n ">${isMaximized?"🗗":"🗖"}</button>\n \x3c!-- Close Button --\x3e\n <button id="${modalId}-close" title="Fechar" style="\n background: none; border: none; font-size: 20px; cursor: pointer;\n padding: 4px 8px; border-radius: 6px; color: rgba(255,255,255,0.8);\n transition: background-color 0.2s;\n ">×</button>\n </div>\n </div>\n\n \x3c!-- Body --\x3e\n <div style="flex: 1; overflow-y: auto; padding: 16px;">\n\n \x3c!-- Controls Row --\x3e\n <div style="\n display: flex; gap: 16px; flex-wrap: wrap; align-items: flex-end;\n margin-bottom: 16px; padding: 16px; background: ${state.theme==="dark"?"rgba(255,255,255,0.05)":"#f7f7f7"};\n border-radius: 6px; border: 1px solid ${colors.border};\n ">\n \x3c!-- Granularity Select --\x3e\n <div>\n <label style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500; display: block; margin-bottom: 4px;">\n Granularidade\n </label>\n <select id="${modalId}-granularity" style="\n padding: 8px 12px; border: 1px solid ${colors.border}; border-radius: 6px;\n font-size: 14px; color: ${colors.text}; background: ${colors.surface};\n cursor: pointer; min-width: 130px;\n ">\n <option value="hour" ${state.granularity==="hour"?"selected":""}>Hora (30 min)</option>\n <option value="day" ${state.granularity==="day"?"selected":""}>Dia (média)</option>\n </select>\n </div>\n \x3c!-- Day Period Filter (Multiselect) --\x3e\n <div style="position: relative;">\n <label style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500; display: block; margin-bottom: 4px;">\n Períodos do Dia\n </label>\n <button id="${modalId}-period-btn" type="button" style="\n padding: 8px 12px; border: 1px solid ${colors.border}; border-radius: 6px;\n font-size: 14px; color: ${colors.text}; background: ${colors.surface};\n cursor: pointer; min-width: 180px; text-align: left;\n display: flex; align-items: center; justify-content: space-between; gap: 8px;\n ">\n <span>${getSelectedPeriodsLabel(state.selectedPeriods)}</span>\n <span style="font-size: 10px;">▼</span>\n </button>\n <div id="${modalId}-period-dropdown" style="\n display: none; position: absolute; top: 100%; left: 0; z-index: 1000;\n background: ${colors.surface}; border: 1px solid ${colors.border};\n border-radius: 6px; box-shadow: 0 4px 12px rgba(0,0,0,0.15);\n min-width: 200px; margin-top: 4px; padding: 8px 0;\n ">\n ${DAY_PERIODS.map(period=>`\n <label style="\n display: flex; align-items: center; gap: 8px; padding: 8px 12px;\n cursor: pointer; font-size: 13px; color: ${colors.text};\n " onmouseover="this.style.background='${state.theme==="dark"?"rgba(255,255,255,0.1)":"#f0f0f0"}'"\n onmouseout="this.style.background='transparent'">\n <input type="checkbox"\n name="${modalId}-period"\n value="${period.id}"\n ${state.selectedPeriods.includes(period.id)?"checked":""}\n style="width: 16px; height: 16px; cursor: pointer; accent-color: #3e1a7d;">\n ${period.label}\n </label>\n `).join("")}\n <div style="border-top: 1px solid ${colors.border}; margin-top: 8px; padding-top: 8px;">\n <button id="${modalId}-period-select-all" type="button" style="\n width: calc(100% - 16px); margin: 0 8px 4px; padding: 6px;\n background: ${state.theme==="dark"?"rgba(255,255,255,0.1)":"#f0f0f0"};\n border: none; border-radius: 4px; cursor: pointer;\n font-size: 12px; color: ${colors.text};\n ">Selecionar Todos</button>\n <button id="${modalId}-period-clear" type="button" style="\n width: calc(100% - 16px); margin: 0 8px; padding: 6px;\n background: ${state.theme==="dark"?"rgba(255,255,255,0.1)":"#f0f0f0"};\n border: none; border-radius: 4px; cursor: pointer;\n font-size: 12px; color: ${colors.text};\n ">Limpar Seleção</button>\n </div>\n </div>\n </div>\n \x3c!-- Date Range Picker --\x3e\n <div style="flex: 1; min-width: 220px;">\n <label style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500; display: block; margin-bottom: 4px;">\n Período\n </label>\n <input type="text" id="${modalId}-date-range" readonly placeholder="Selecione o período..." style="\n padding: 8px 12px; border: 1px solid ${colors.border}; border-radius: 6px;\n font-size: 14px; color: ${colors.text}; background: ${colors.surface};\n width: 100%; cursor: pointer; box-sizing: border-box;\n "/>\n </div>\n \x3c!-- Query Button --\x3e\n <button id="${modalId}-query" style="\n background: #3e1a7d; color: white; border: none;\n padding: 8px 16px; border-radius: 6px; cursor: pointer;\n font-size: 14px; font-weight: 500; height: 38px;\n display: flex; align-items: center; gap: 8px;\n font-family: 'Roboto', Arial, sans-serif;\n " ${state.isLoading?"disabled":""}>\n ${state.isLoading?'<span style="animation: spin 1s linear infinite; display: inline-block;">↻</span> Carregando...':"Carregar"}\n </button>\n </div>\n\n \x3c!-- Stats Cards --\x3e\n <div style="\n display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));\n gap: 12px; margin-bottom: 16px;\n ">\n \x3c!-- Current Temperature --\x3e\n <div style="\n padding: 16px; background: ${state.theme==="dark"?"rgba(255,255,255,0.05)":"#fafafa"};\n border-radius: 12px; border: 1px solid ${colors.border};\n ">\n <span style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500;">Temperatura Atual</span>\n <div style="font-weight: 700; font-size: 24px; color: ${statusColor}; margin-top: 4px;">\n ${state.currentTemperature!==null?formatTemperature2(state.currentTemperature):"N/A"}\n </div>\n <div style="font-size: 11px; color: ${statusColor}; margin-top: 2px;">${statusText}</div>\n </div>\n \x3c!-- Average --\x3e\n <div style="\n padding: 16px; background: ${state.theme==="dark"?"rgba(255,255,255,0.05)":"#fafafa"};\n border-radius: 12px; border: 1px solid ${colors.border};\n ">\n <span style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500;">Média do Período</span>\n <div id="${modalId}-avg" style="font-weight: 600; font-size: 20px; color: ${colors.text}; margin-top: 4px;">\n ${state.stats.count>0?formatTemperature2(state.stats.avg):"N/A"}\n </div>\n <div style="font-size: 11px; color: ${colors.textMuted}; margin-top: 2px;">${startDateStr} - ${endDateStr}</div>\n </div>\n \x3c!-- Min/Max --\x3e\n <div style="\n padding: 16px; background: ${state.theme==="dark"?"rgba(255,255,255,0.05)":"#fafafa"};\n border-radius: 12px; border: 1px solid ${colors.border};\n ">\n <span style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500;">Min / Max</span>\n <div id="${modalId}-minmax" style="font-weight: 600; font-size: 20px; color: ${colors.text}; margin-top: 4px;">\n ${state.stats.count>0?`${formatTemperature2(state.stats.min)} / ${formatTemperature2(state.stats.max)}`:"N/A"}\n </div>\n <div style="font-size: 11px; color: ${colors.textMuted}; margin-top: 2px;">${state.stats.count} leituras</div>\n </div>\n \x3c!-- Ideal Range --\x3e\n <div style="\n padding: 16px; background: ${state.theme==="dark"?"rgba(255,255,255,0.05)":"#fafafa"};\n border-radius: 12px; border: 1px solid ${colors.border};\n ">\n <span style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500;">Faixa Ideal</span>\n <div style="font-weight: 600; font-size: 20px; color: ${colors.success}; margin-top: 4px;">\n ${rangeText}\n </div>\n </div>\n </div>\n\n \x3c!-- Chart Container --\x3e\n <div style="margin-bottom: 20px;">\n <h3 style="margin: 0 0 12px 0; font-size: 14px; color: ${colors.textMuted}; font-weight: 500;">\n Histórico de Temperatura\n </h3>\n <div id="${modalId}-chart" style="\n height: 320px; background: ${state.theme==="dark"?"rgba(255,255,255,0.03)":"#fafafa"};\n border-radius: 12px; display: flex; justify-content: center; align-items: center;\n border: 1px solid ${colors.border}; position: relative;\n ">\n ${state.isLoading?`<div style="text-align: center; color: ${colors.textMuted};">\n <div style="animation: spin 1s linear infinite; font-size: 32px; margin-bottom: 8px;">↻</div>\n <div>Carregando dados...</div>\n </div>`:error?`<div style="text-align: center; color: ${colors.danger};">\n <div style="font-size: 32px; margin-bottom: 8px;">⚠️</div>\n <div>Erro ao carregar dados</div>\n <div style="font-size: 12px; margin-top: 4px;">${error.message}</div>\n </div>`:state.data.length===0?`<div style="text-align: center; color: ${colors.textMuted};">\n <div style="font-size: 32px; margin-bottom: 8px;">📭</div>\n <div>Sem dados para o período selecionado</div>\n </div>`:`<canvas id="${modalId}-canvas" style="width: 100%; height: 100%;"></canvas>`}\n </div>\n </div>\n\n \x3c!-- Actions --\x3e\n <div style="display: flex; justify-content: flex-end; gap: 12px;">\n <button id="${modalId}-export" style="\n background: ${state.theme==="dark"?"rgba(255,255,255,0.1)":"#f7f7f7"};\n color: ${colors.text}; border: 1px solid ${colors.border};\n padding: 8px 16px; border-radius: 6px; cursor: pointer;\n font-size: 14px; display: flex; align-items: center; gap: 8px;\n font-family: 'Roboto', Arial, sans-serif;\n " ${state.data.length===0?"disabled":""}>\n 📥 Exportar CSV\n </button>\n <button id="${modalId}-close-btn" style="\n background: #3e1a7d; color: white; border: none;\n padding: 8px 16px; border-radius: 6px; cursor: pointer;\n font-size: 14px; font-weight: 500;\n font-family: 'Roboto', Arial, sans-serif;\n ">\n Fechar\n </button>\n </div>\n </div>\x3c!-- End Body --\x3e\n </div>\n </div>\n <style>\n @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }\n #${modalId} select:focus, #${modalId} input:focus {\n outline: 2px solid #3e1a7d;\n outline-offset: 2px;\n }\n #${modalId} button:hover:not(:disabled) {\n opacity: 0.9;\n }\n #${modalId} button:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n #${modalId} .myio-temp-modal-content > div:first-child button:hover {\n background: rgba(255, 255, 255, 0.1) !important;\n color: white !important;\n }\n\n /* DateRangePicker styles */\n ${CSS_TOKENS}\n ${DATERANGEPICKER_STYLES}\n\n /* Fix DateRangePicker buttons alignment */\n .myio-modal-scope .daterangepicker .drp-buttons {\n display: flex;\n justify-content: flex-end;\n align-items: center;\n gap: 8px;\n }\n .myio-modal-scope .daterangepicker .drp-buttons .btn {\n display: inline-block;\n margin-left: 0;\n }\n </style>\n `}function drawChart(modalId,state){const chartContainer=document.getElementById(`${modalId}-chart`);const canvas=document.getElementById(`${modalId}-canvas`);if(!chartContainer||!canvas||state.data.length===0)return;const ctx=canvas.getContext("2d");if(!ctx)return;const colors=getThemeColors(state.theme);const filteredData=filterByDayPeriods(state.data,state.selectedPeriods);if(filteredData.length===0){canvas.width=chartContainer.clientWidth;canvas.height=chartContainer.clientHeight;ctx.fillStyle=colors.textMuted;ctx.font="14px Roboto, Arial, sans-serif";ctx.textAlign="center";ctx.fillText("Nenhum dado para os períodos selecionados",canvas.width/2,canvas.height/2);return}let chartData;if(state.granularity==="hour"){const interpolated=interpolateTemperature(filteredData,{intervalMinutes:30,startTs:state.startTs,endTs:state.endTs,clampRange:state.clampRange});const filteredInterpolated=filterByDayPeriods(interpolated,state.selectedPeriods);chartData=filteredInterpolated.map(item=>({x:item.ts,y:Number(item.value),screenX:0,screenY:0}))}else{const daily=aggregateByDay(filteredData,state.clampRange);chartData=daily.map(item=>({x:item.dateTs,y:item.avg,screenX:0,screenY:0,label:item.date}))}if(chartData.length===0)return;const width=chartContainer.clientWidth-2;const height=320;canvas.width=width;canvas.height=height;const paddingLeft=60;const paddingRight=20;const paddingTop=20;const paddingBottom=55;const isPeriodsFiltered=state.selectedPeriods.length<4&&state.selectedPeriods.length>0;const values=chartData.map(d=>d.y);const dataMin=Math.min(...values);const dataMax=Math.max(...values);const thresholdMin=state.temperatureMin!==null?state.temperatureMin:dataMin;const thresholdMax=state.temperatureMax!==null?state.temperatureMax:dataMax;const minY=Math.min(dataMin,thresholdMin)-1;const maxY=Math.max(dataMax,thresholdMax)+1;const chartWidth=width-paddingLeft-paddingRight;const chartHeight=height-paddingTop-paddingBottom;const scaleY=chartHeight/(maxY-minY||1);if(isPeriodsFiltered){const pointSpacing=chartWidth/Math.max(1,chartData.length-1);chartData.forEach((point,index)=>{point.screenX=paddingLeft+index*pointSpacing;point.screenY=height-paddingBottom-(point.y-minY)*scaleY})}else{const minX=chartData[0].x;const maxX=chartData[chartData.length-1].x;const timeRange=maxX-minX||1;const scaleX=chartWidth/timeRange;chartData.forEach(point=>{point.screenX=paddingLeft+(point.x-minX)*scaleX;point.screenY=height-paddingBottom-(point.y-minY)*scaleY})}ctx.clearRect(0,0,width,height);ctx.strokeStyle=colors.chartGrid;ctx.lineWidth=1;for(let i=0;i<=4;i++){const y=paddingTop+chartHeight*i/4;ctx.beginPath();ctx.moveTo(paddingLeft,y);ctx.lineTo(width-paddingRight,y);ctx.stroke()}if(state.temperatureMin!==null&&state.temperatureMax!==null){const rangeMinY=height-paddingBottom-(state.temperatureMin-minY)*scaleY;const rangeMaxY=height-paddingBottom-(state.temperatureMax-minY)*scaleY;ctx.fillStyle="rgba(76, 175, 80, 0.1)";ctx.fillRect(paddingLeft,rangeMaxY,chartWidth,rangeMinY-rangeMaxY);ctx.strokeStyle=colors.success;ctx.setLineDash([5,5]);ctx.beginPath();ctx.moveTo(paddingLeft,rangeMinY);ctx.lineTo(width-paddingRight,rangeMinY);ctx.moveTo(paddingLeft,rangeMaxY);ctx.lineTo(width-paddingRight,rangeMaxY);ctx.stroke();ctx.setLineDash([])}ctx.strokeStyle=colors.chartLine;ctx.lineWidth=2;ctx.beginPath();chartData.forEach((point,i)=>{if(i===0)ctx.moveTo(point.screenX,point.screenY);else ctx.lineTo(point.screenX,point.screenY)});ctx.stroke();ctx.fillStyle=colors.chartLine;chartData.forEach(point=>{ctx.beginPath();ctx.arc(point.screenX,point.screenY,4,0,Math.PI*2);ctx.fill()});ctx.fillStyle=colors.textMuted;ctx.font="11px system-ui, sans-serif";ctx.textAlign="right";for(let i=0;i<=4;i++){const val=minY+(maxY-minY)*(4-i)/4;const y=paddingTop+chartHeight*i/4;ctx.fillText(val.toFixed(1)+"°C",paddingLeft-8,y+4)}ctx.textAlign="center";const numLabels=Math.min(8,chartData.length);const labelInterval=Math.max(1,Math.floor(chartData.length/numLabels));for(let i=0;i<chartData.length;i+=labelInterval){const point=chartData[i];const date=new Date(point.x);let label;if(state.granularity==="hour"){label=date.toLocaleTimeString(state.locale,{hour:"2-digit",minute:"2-digit"})}else{label=date.toLocaleDateString(state.locale,{day:"2-digit",month:"2-digit"})}ctx.strokeStyle=colors.chartGrid;ctx.lineWidth=1;ctx.beginPath();ctx.moveTo(point.screenX,paddingTop);ctx.lineTo(point.screenX,height-paddingBottom);ctx.stroke();ctx.fillStyle=colors.textMuted;ctx.fillText(label,point.screenX,height-paddingBottom+18)}ctx.strokeStyle=colors.border;ctx.lineWidth=1;ctx.beginPath();ctx.moveTo(paddingLeft,paddingTop);ctx.lineTo(paddingLeft,height-paddingBottom);ctx.lineTo(width-paddingRight,height-paddingBottom);ctx.stroke();setupChartTooltip(canvas,chartContainer,chartData,state,colors)}function setupChartTooltip(canvas,container,chartData,state,colors){const existingTooltip=container.querySelector(".myio-chart-tooltip");if(existingTooltip)existingTooltip.remove();const tooltip=document.createElement("div");tooltip.className="myio-chart-tooltip";tooltip.style.cssText=`\n position: absolute;\n background: ${state.theme==="dark"?"rgba(30, 30, 40, 0.95)":"rgba(255, 255, 255, 0.98)"};\n border: 1px solid ${colors.border};\n border-radius: 8px;\n padding: 10px 14px;\n font-size: 13px;\n color: ${colors.text};\n pointer-events: none;\n opacity: 0;\n transition: opacity 0.15s;\n z-index: 1000;\n box-shadow: 0 4px 12px rgba(0,0,0,0.15);\n min-width: 140px;\n `;container.appendChild(tooltip);const findNearestPoint=(mouseX,mouseY)=>{const threshold=20;let nearest=null;let minDist=Infinity;for(const point of chartData){const dist=Math.sqrt(Math.pow(mouseX-point.screenX,2)+Math.pow(mouseY-point.screenY,2));if(dist<minDist&&dist<threshold){minDist=dist;nearest=point}}return nearest};canvas.addEventListener("mousemove",e=>{const rect=canvas.getBoundingClientRect();const mouseX=e.clientX-rect.left;const mouseY=e.clientY-rect.top;const point=findNearestPoint(mouseX,mouseY);if(point){const date=new Date(point.x);let dateStr;if(state.granularity==="hour"){dateStr=date.toLocaleDateString(state.locale,{day:"2-digit",month:"2-digit",year:"numeric"})+" "+date.toLocaleTimeString(state.locale,{hour:"2-digit",minute:"2-digit"})}else{dateStr=date.toLocaleDateString(state.locale,{day:"2-digit",month:"2-digit",year:"numeric"})}tooltip.innerHTML=`\n <div style="font-weight: 600; margin-bottom: 6px; color: ${colors.primary};">\n ${formatTemperature2(point.y)}\n </div>\n <div style="font-size: 11px; color: ${colors.textMuted};">\n 📅 ${dateStr}\n </div>\n `;let tooltipX=point.screenX+15;let tooltipY=point.screenY-15;const tooltipRect=tooltip.getBoundingClientRect();const containerRect=container.getBoundingClientRect();if(tooltipX+tooltipRect.width>containerRect.width-10){tooltipX=point.screenX-tooltipRect.width-15}if(tooltipY<10){tooltipY=point.screenY+15}tooltip.style.left=`${tooltipX}px`;tooltip.style.top=`${tooltipY}px`;tooltip.style.opacity="1";canvas.style.cursor="pointer"}else{tooltip.style.opacity="0";canvas.style.cursor="default"}});canvas.addEventListener("mouseleave",()=>{tooltip.style.opacity="0";canvas.style.cursor="default"})}async function setupEventListeners(container,state,modalId,onClose){const closeModal=()=>{container.remove();onClose?.()};container.querySelector(".myio-temp-modal-overlay")?.addEventListener("click",e=>{if(e.target===e.currentTarget)closeModal()});document.getElementById(`${modalId}-close`)?.addEventListener("click",closeModal);document.getElementById(`${modalId}-close-btn`)?.addEventListener("click",closeModal);const dateRangeInput=document.getElementById(`${modalId}-date-range`);if(dateRangeInput&&!state.dateRangePicker){try{state.dateRangePicker=await createDateRangePicker2(dateRangeInput,{presetStart:new Date(state.startTs).toISOString(),presetEnd:new Date(state.endTs).toISOString(),includeTime:true,timePrecision:"minute",maxRangeDays:90,locale:state.locale,parentEl:container.querySelector(".myio-temp-modal-content"),onApply:result=>{state.startTs=new Date(result.startISO).getTime();state.endTs=new Date(result.endISO).getTime();console.log("[TemperatureModal] Date range applied:",result)}})}catch(error){console.warn("[TemperatureModal] DateRangePicker initialization failed:",error)}}document.getElementById(`${modalId}-theme-toggle`)?.addEventListener("click",async()=>{state.theme=state.theme==="dark"?"light":"dark";localStorage.setItem("myio-temp-modal-theme",state.theme);state.dateRangePicker=null;renderModal(container,state,modalId);if(state.data.length>0)drawChart(modalId,state);await setupEventListeners(container,state,modalId,onClose)});document.getElementById(`${modalId}-maximize`)?.addEventListener("click",async()=>{container.__isMaximized=!container.__isMaximized;state.dateRangePicker=null;renderModal(container,state,modalId);if(state.data.length>0)drawChart(modalId,state);await setupEventListeners(container,state,modalId,onClose)});const periodBtn=document.getElementById(`${modalId}-period-btn`);const periodDropdown=document.getElementById(`${modalId}-period-dropdown`);periodBtn?.addEventListener("click",e=>{e.stopPropagation();if(periodDropdown){periodDropdown.style.display=periodDropdown.style.display==="none"?"block":"none"}});document.addEventListener("click",e=>{if(periodDropdown&&!periodDropdown.contains(e.target)&&e.target!==periodBtn){periodDropdown.style.display="none"}});const periodCheckboxes=document.querySelectorAll(`input[name="${modalId}-period"]`);periodCheckboxes.forEach(checkbox=>{checkbox.addEventListener("change",()=>{const checked=Array.from(periodCheckboxes).filter(cb=>cb.checked).map(cb=>cb.value);state.selectedPeriods=checked;const btnLabel=periodBtn?.querySelector("span:first-child");if(btnLabel){btnLabel.textContent=getSelectedPeriodsLabel(state.selectedPeriods)}if(state.data.length>0)drawChart(modalId,state)})});document.getElementById(`${modalId}-period-select-all`)?.addEventListener("click",()=>{periodCheckboxes.forEach(cb=>{cb.checked=true});state.selectedPeriods=["madrugada","manha","tarde","noite"];const btnLabel=periodBtn?.querySelector("span:first-child");if(btnLabel){btnLabel.textContent=getSelectedPeriodsLabel(state.selectedPeriods)}if(state.data.length>0)drawChart(modalId,state)});document.getElementById(`${modalId}-period-clear`)?.addEventListener("click",()=>{periodCheckboxes.forEach(cb=>{cb.checked=false});state.selectedPeriods=[];const btnLabel=periodBtn?.querySelector("span:first-child");if(btnLabel){btnLabel.textContent=getSelectedPeriodsLabel(state.selectedPeriods)}if(state.data.length>0)drawChart(modalId,state)});document.getElementById(`${modalId}-granularity`)?.addEventListener("change",e=>{state.granularity=e.target.value;localStorage.setItem("myio-temp-modal-granularity",state.granularity);if(state.data.length>0)drawChart(modalId,state)});document.getElementById(`${modalId}-query`)?.addEventListener("click",async()=>{if(state.startTs>=state.endTs){alert("Por favor, selecione um período válido");return}state.isLoading=true;state.dateRangePicker=null;renderModal(container,state,modalId);try{state.data=await fetchTemperatureData(state.token,state.deviceId,state.startTs,state.endTs);state.stats=calculateStats(state.data,state.clampRange);state.isLoading=false;renderModal(container,state,modalId);drawChart(modalId,state);await setupEventListeners(container,state,modalId,onClose)}catch(error){console.error("[TemperatureModal] Error fetching data:",error);state.isLoading=false;renderModal(container,state,modalId,error);await setupEventListeners(container,state,modalId,onClose)}});document.getElementById(`${modalId}-export`)?.addEventListener("click",()=>{if(state.data.length===0)return;const startDateStr=new Date(state.startTs).toLocaleDateString(state.locale).replace(/\//g,"-");const endDateStr=new Date(state.endTs).toLocaleDateString(state.locale).replace(/\//g,"-");exportTemperatureCSV(state.data,state.label,state.stats,startDateStr,endDateStr)})}async function openTemperatureComparisonModal(params){const modalId=`myio-temp-comparison-modal-${Date.now()}`;const defaultDateRange=getTodaySoFar();const startTs=params.startDate?new Date(params.startDate).getTime():defaultDateRange.startTs;const endTs=params.endDate?new Date(params.endDate).getTime():defaultDateRange.endTs;const state={token:params.token,devices:params.devices,startTs:startTs,endTs:endTs,granularity:params.granularity||"hour",theme:params.theme||"dark",clampRange:params.clampRange||DEFAULT_CLAMP_RANGE,locale:params.locale||"pt-BR",deviceData:[],isLoading:true,dateRangePicker:null,selectedPeriods:["madrugada","manha","tarde","noite"],temperatureMin:params.temperatureMin??null,temperatureMax:params.temperatureMax??null};const savedGranularity=localStorage.getItem("myio-temp-comparison-granularity");const savedTheme=localStorage.getItem("myio-temp-comparison-theme");if(savedGranularity)state.granularity=savedGranularity;if(savedTheme)state.theme=savedTheme;const modalContainer=document.createElement("div");modalContainer.id=modalId;document.body.appendChild(modalContainer);renderModal2(modalContainer,state,modalId);await fetchAllDevicesData(state);renderModal2(modalContainer,state,modalId);drawComparisonChart(modalId,state);await setupEventListeners2(modalContainer,state,modalId,params.onClose);return{destroy:()=>{modalContainer.remove();params.onClose?.()},updateData:async(startDate,endDate,granularity)=>{state.startTs=new Date(startDate).getTime();state.endTs=new Date(endDate).getTime();if(granularity)state.granularity=granularity;state.isLoading=true;renderModal2(modalContainer,state,modalId);await fetchAllDevicesData(state);renderModal2(modalContainer,state,modalId);drawComparisonChart(modalId,state);setupEventListeners2(modalContainer,state,modalId,params.onClose)}}}async function fetchAllDevicesData(state){state.isLoading=true;state.deviceData=[];try{const results=await Promise.all(state.devices.map(async(device,index)=>{const deviceId=device.tbId||device.id;try{const data=await fetchTemperatureData(state.token,deviceId,state.startTs,state.endTs);const stats=calculateStats(data,state.clampRange);return{device:device,data:data,stats:stats,color:CHART_COLORS[index%CHART_COLORS.length]}}catch(error){console.error(`[TemperatureComparisonModal] Error fetching data for ${device.label}:`,error);return{device:device,data:[],stats:{avg:0,min:0,max:0,count:0},color:CHART_COLORS[index%CHART_COLORS.length]}}}));state.deviceData=results}catch(error){console.error("[TemperatureComparisonModal] Error fetching data:",error)}state.isLoading=false}function renderModal2(container,state,modalId){const colors=getThemeColors(state.theme);new Date(state.startTs).toLocaleDateString(state.locale);new Date(state.endTs).toLocaleDateString(state.locale);new Date(state.startTs).toISOString().slice(0,16);new Date(state.endTs).toISOString().slice(0,16);const legendHTML=state.deviceData.map(dd=>`\n <div style="display: flex; align-items: center; gap: 8px; padding: 8px 12px;\n background: ${state.theme==="dark"?"rgba(255,255,255,0.05)":"rgba(0,0,0,0.03)"};\n border-radius: 8px;">\n <span style="width: 12px; height: 12px; border-radius: 50%; background: ${dd.color};"></span>\n <span style="color: ${colors.text}; font-size: 13px;">${dd.device.label}</span>\n <span style="color: ${colors.textMuted}; font-size: 11px; margin-left: auto;">\n ${dd.stats.count>0?formatTemperature2(dd.stats.avg):"N/A"}\n </span>\n </div>\n `).join("");const statsHTML=state.deviceData.map(dd=>`\n <div style="\n padding: 12px; background: ${state.theme==="dark"?"rgba(255,255,255,0.05)":"#fafafa"};\n border-radius: 10px; border-left: 4px solid ${dd.color};\n min-width: 150px;\n ">\n <div style="font-weight: 600; color: ${colors.text}; font-size: 13px; margin-bottom: 8px;">\n ${dd.device.label}\n </div>\n <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 4px; font-size: 11px;">\n <span style="color: ${colors.textMuted};">Média:</span>\n <span style="color: ${colors.text}; font-weight: 500;">\n ${dd.stats.count>0?formatTemperature2(dd.stats.avg):"N/A"}\n </span>\n <span style="color: ${colors.textMuted};">Min:</span>\n <span style="color: ${colors.text};">\n ${dd.stats.count>0?formatTemperature2(dd.stats.min):"N/A"}\n </span>\n <span style="color: ${colors.textMuted};">Max:</span>\n <span style="color: ${colors.text};">\n ${dd.stats.count>0?formatTemperature2(dd.stats.max):"N/A"}\n </span>\n <span style="color: ${colors.textMuted};">Leituras:</span>\n <span style="color: ${colors.text};">${dd.stats.count}</span>\n </div>\n </div>\n `).join("");const isMaximized=container.__isMaximized||false;const contentMaxWidth=isMaximized?"100%":"1100px";const contentMaxHeight=isMaximized?"100vh":"95vh";const contentBorderRadius=isMaximized?"0":"10px";container.innerHTML=`\n <div class="myio-temp-comparison-overlay" style="\n position: fixed; top: 0; left: 0; width: 100%; height: 100%;\n background: rgba(0, 0, 0, 0.5); z-index: 9998;\n display: flex; justify-content: center; align-items: center;\n backdrop-filter: blur(2px);\n ">\n <div class="myio-temp-comparison-content" style="\n background: ${colors.surface}; border-radius: ${contentBorderRadius};\n max-width: ${contentMaxWidth}; width: ${isMaximized?"100%":"95%"};\n max-height: ${contentMaxHeight}; height: ${isMaximized?"100%":"auto"};\n overflow: hidden; display: flex; flex-direction: column;\n box-shadow: 0 4px 12px rgba(0,0,0,0.15);\n font-family: 'Roboto', Arial, sans-serif;\n ">\n \x3c!-- Header - MyIO Premium Style --\x3e\n <div style="\n padding: 4px 8px; display: flex; align-items: center; justify-content: space-between;\n background: #3e1a7d; color: white; border-radius: ${isMaximized?"0":"10px 10px 0 0"};\n min-height: 20px;\n ">\n <h2 style="margin: 6px; font-size: 18px; font-weight: 600; color: white; line-height: 2;">\n 🌡️ Comparação de Temperatura - ${state.devices.length} sensores\n </h2>\n <div style="display: flex; gap: 4px; align-items: center;">\n \x3c!-- Theme Toggle --\x3e\n <button id="${modalId}-theme-toggle" title="Alternar tema" style="\n background: none; border: none; font-size: 16px; cursor: pointer;\n padding: 4px 8px; border-radius: 6px; color: rgba(255,255,255,0.8);\n transition: background-color 0.2s;\n ">${state.theme==="dark"?"☀️":"🌙"}</button>\n \x3c!-- Maximize Button --\x3e\n <button id="${modalId}-maximize" title="${isMaximized?"Restaurar":"Maximizar"}" style="\n background: none; border: none; font-size: 16px; cursor: pointer;\n padding: 4px 8px; border-radius: 6px; color: rgba(255,255,255,0.8);\n transition: background-color 0.2s;\n ">${isMaximized?"🗗":"🗖"}</button>\n \x3c!-- Close Button --\x3e\n <button id="${modalId}-close" title="Fechar" style="\n background: none; border: none; font-size: 20px; cursor: pointer;\n padding: 4px 8px; border-radius: 6px; color: rgba(255,255,255,0.8);\n transition: background-color 0.2s;\n ">×</button>\n </div>\n </div>\n\n \x3c!-- Body --\x3e\n <div style="flex: 1; overflow-y: auto; padding: 16px;">\n\n \x3c!-- Controls Row --\x3e\n <div style="\n display: flex; gap: 16px; flex-wrap: wrap; align-items: flex-end;\n margin-bottom: 16px; padding: 16px;\n background: ${state.theme==="dark"?"rgba(255,255,255,0.05)":"#f7f7f7"};\n border-radius: 6px; border: 1px solid ${colors.border};\n ">\n \x3c!-- Granularity Select --\x3e\n <div>\n <label style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500; display: block; margin-bottom: 4px;">\n Granularidade\n </label>\n <select id="${modalId}-granularity" style="\n padding: 8px 12px; border: 1px solid ${colors.border}; border-radius: 6px;\n font-size: 14px; color: ${colors.text}; background: ${colors.surface};\n cursor: pointer; min-width: 130px;\n ">\n <option value="hour" ${state.granularity==="hour"?"selected":""}>Hora (30 min)</option>\n <option value="day" ${state.granularity==="day"?"selected":""}>Dia (média)</option>\n </select>\n </div>\n \x3c!-- Day Period Filter (Multiselect) --\x3e\n <div style="position: relative;">\n <label style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500; display: block; margin-bottom: 4px;">\n Períodos do Dia\n </label>\n <button id="${modalId}-period-btn" type="button" style="\n padding: 8px 12px; border: 1px solid ${colors.border}; border-radius: 6px;\n font-size: 14px; color: ${colors.text}; background: ${colors.surface};\n cursor: pointer; min-width: 180px; text-align: left;\n display: flex; align-items: center; justify-content: space-between; gap: 8px;\n ">\n <span>${getSelectedPeriodsLabel(state.selectedPeriods)}</span>\n <span style="font-size: 10px;">▼</span>\n </button>\n <div id="${modalId}-period-dropdown" style="\n display: none; position: absolute; top: 100%; left: 0; z-index: 1000;\n background: ${colors.surface}; border: 1px solid ${colors.border};\n border-radius: 6px; box-shadow: 0 4px 12px rgba(0,0,0,0.15);\n min-width: 200px; margin-top: 4px; padding: 8px 0;\n ">\n ${DAY_PERIODS.map(period=>`\n <label style="\n display: flex; align-items: center; gap: 8px; padding: 8px 12px;\n cursor: pointer; font-size: 13px; color: ${colors.text};\n " onmouseover="this.style.background='${state.theme==="dark"?"rgba(255,255,255,0.1)":"#f0f0f0"}'"\n onmouseout="this.style.background='transparent'">\n <input type="checkbox"\n name="${modalId}-period"\n value="${period.id}"\n ${state.selectedPeriods.includes(period.id)?"checked":""}\n style="width: 16px; height: 16px; cursor: pointer; accent-color: #3e1a7d;">\n ${period.label}\n </label>\n `).join("")}\n <div style="border-top: 1px solid ${colors.border}; margin-top: 8px; padding-top: 8px;">\n <button id="${modalId}-period-select-all" type="button" style="\n width: calc(100% - 16px); margin: 0 8px 4px; padding: 6px;\n background: ${state.theme==="dark"?"rgba(255,255,255,0.1)":"#f0f0f0"};\n border: none; border-radius: 4px; cursor: pointer;\n font-size: 12px; color: ${colors.text};\n ">Selecionar Todos</button>\n <button id="${modalId}-period-clear" type="button" style="\n width: calc(100% - 16px); margin: 0 8px; padding: 6px;\n background: ${state.theme==="dark"?"rgba(255,255,255,0.1)":"#f0f0f0"};\n border: none; border-radius: 4px; cursor: pointer;\n font-size: 12px; color: ${colors.text};\n ">Limpar Seleção</button>\n </div>\n </div>\n </div>\n \x3c!-- Date Range Picker --\x3e\n <div style="flex: 1; min-width: 220px;">\n <label style="color: ${colors.textMuted}; font-size: 12px; font-weight: 500; display: block; margin-bottom: 4px;">\n Período\n </label>\n <input type="text" id="${modalId}-date-range" readonly placeholder="Selecione o período..." style="\n padding: 8px 12px; border: 1px solid ${colors.border}; border-radius: 6px;\n font-size: 14px; color: ${colors.text}; background: ${colors.surface};\n width: 100%; cursor: pointer; box-sizing: border-box;\n "/>\n </div>\n \x3c!-- Query Button --\x3e\n <button id="${modalId}-query" style="\n background: #3e1a7d; color: white; border: none;\n padding: 8px 16px; border-radius: 6px; cursor: pointer;\n font-size: 14px; font-weight: 500; height: 38px;\n display: flex; align-items: center; gap: 8px;\n font-family: 'Roboto', Arial, sans-serif;\n " ${state.isLoading?"disabled":""}>\n ${state.isLoading?'<span style="animation: spin 1s linear infinite; display: inline-block;">↻</span> Carregando...':"Carregar"}\n </button>\n </div>\n\n \x3c!-- Legend --\x3e\n <div style="\n display: flex; flex-wrap: wrap; gap: 10px;\n margin-bottom: 20px;\n ">\n ${legendHTML}\n </div>\n\n \x3c!-- Chart Container --\x3e\n <div style="margin-bottom: 24px;">\n <div id="${modalId}-chart" style="\n height: 380px;\n background: ${state.theme==="dark"?"rgba(255,255,255,0.03)":"#fafafa"};\n border-radius: 14px; display: flex; justify-content: center; align-items: center;\n border: 1px solid ${colors.border}; position: relative;\n ">\n ${state.isLoading?`<div style="text-align: center; color: ${colors.textMuted};">\n <div style="animation: spin 1s linear infinite; font-size: 36px; margin-bottom: 12px;">↻</div>\n <div style="font-size: 15px;">Carregando dados de ${state.devices.length} sensores...</div>\n </div>`:state.deviceData.every(dd=>dd.data.length===0)?`<div style="text-align: center; color: ${colors.textMuted};">\n <div style="font-size: 48px; margin-bottom: 12px;">📭</div>\n <div style="font-size: 16px;">Sem dados para o período selecionado</div>\n </div>`:`<canvas id="${modalId}-canvas" style="width: 100%; height: 100%;"></canvas>`}\n </div>\n </div>\n\n \x3c!-- Stats Cards --\x3e\n <div style="\n display: flex; flex-wrap: wrap; gap: 12px;\n margin-bottom: 24px;\n ">\n ${statsHTML}\n </div>\n\n \x3c!-- Actions --\x3e\n <div style="display: flex; justify-content: flex-end; gap: 12px;">\n <button id="${modalId}-export" style="\n background: ${state.theme==="dark"?"rgba(255,255,255,0.1)":"#f7f7f7"};\n color: ${colors.text}; border: 1px solid ${colors.border};\n padding: 8px 16px; border-radius: 6px; cursor: pointer;\n font-size: 14px; display: flex; align-items: center; gap: 8px;\n font-family: 'Roboto', Arial, sans-serif;\n " ${state.deviceData.every(dd=>dd.data.length===0)?"disabled":""}>\n 📥 Exportar CSV\n </button>\n <button id="${modalId}-close-btn" style="\n background: #3e1a7d; color: white; border: none;\n padding: 8px 16px; border-radius: 6px; cursor: pointer;\n font-size: 14px; font-weight: 500;\n font-family: 'Roboto', Arial, sans-serif;\n ">\n Fechar\n </button>\n </div>\n </div>\x3c!-- End Body --\x3e\n </div>\n </div>\n <style>\n @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }\n #${modalId} select:focus, #${modalId} input:focus {\n outline: 2px solid #3e1a7d;\n outline-offset: 2px;\n }\n #${modalId} button:hover:not(:disabled) {\n opacity: 0.9;\n }\n #${modalId} button:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n #${modalId} .myio-temp-comparison-content > div:first-child button:hover {\n background: rgba(255, 255, 255, 0.1) !important;\n color: white !important;\n }\n </style>\n `}function drawComparisonChart(modalId,state){const chartContainer=document.getElementById(`${modalId}-chart`);const canvas=document.getElementById(`${modalId}-canvas`);if(!chartContainer||!canvas)return;const hasData=state.deviceData.some(dd=>dd.data.length>0);if(!hasData)return;const ctx=canvas.getContext("2d");if(!ctx)return;const colors=getThemeColors(state.theme);const width=chartContainer.clientWidth-2;const height=380;canvas.width=width;canvas.height=height;const paddingLeft=65;const paddingRight=25;const paddingTop=25;const paddingBottom=55;ctx.clearRect(0,0,width,height);const processedData=[];state.deviceData.forEach(dd=>{if(dd.data.length===0)return;const filteredData=filterByDayPeriods(dd.data,state.selectedPeriods);if(filteredData.length===0)return;let points;if(state.granularity==="hour"){const interpolated=interpolateTemperature(filteredData,{intervalMinutes:30,startTs:state.startTs,endTs:state.endTs,clampRange:state.clampRange});const filteredInterpolated=filterByDayPeriods(interpolated,state.selectedPeriods);points=filteredInterpolated.map(item=>({x:item.ts,y:Number(item.value),screenX:0,screenY:0,deviceLabel:dd.device.label,deviceColor:dd.color}))}else{const daily=aggregateByDay(filteredData,state.clampRange);points=daily.map(item=>({x:item.dateTs,y:item.avg,screenX:0,screenY:0,deviceLabel:dd.device.label,deviceColor:dd.color}))}if(points.length>0){processedData.push({device:dd,points:points})}});if(processedData.length===0){ctx.fillStyle=colors.textMuted;ctx.font="14px Roboto, Arial, sans-serif";ctx.textAlign="center";ctx.fillText("Nenhum dado para os períodos selecionados",width/2,height/2);return}const isPeriodsFiltered=state.selectedPeriods.length<4&&state.selectedPeriods.length>0;let dataMinY=Infinity;let dataMaxY=-Infinity;processedData.forEach(({points:points})=>{points.forEach(point=>{if(point.y<dataMinY)dataMinY=point.y;if(point.y>dataMaxY)dataMaxY=point.y})});const rangeMap=new Map;state.deviceData.forEach((dd,index)=>{const device=dd.device;const min=device.temperatureMin;const max=device.temperatureMax;if(min!==void 0&&min!==null&&max!==void 0&&max!==null){const key=`${min}-${max}`;if(!rangeMap.has(key)){rangeMap.set(key,{min:min,max:max,customerName:device.customerName||"",color:CHART_COLORS[index%CHART_COLORS.length],deviceLabels:[device.label]})}else{rangeMap.get(key).deviceLabels.push(device.label)}}});if(rangeMap.size===0&&state.temperatureMin!==null&&state.temperatureMax!==null){rangeMap.set("global",{min:state.temperatureMin,max:state.temperatureMax,customerName:"Global",color:colors.success,deviceLabels:[]})}const temperatureRanges=Array.from(rangeMap.values());let thresholdMinY=dataMinY;let thresholdMaxY=dataMaxY;temperatureRanges.forEach(range=>{if(range.min<thresholdMinY)thresholdMinY=range.min;if(range.max>thresholdMaxY)thresholdMaxY=range.max});const globalMinY=Math.floor(Math.min(dataMinY,thresholdMinY))-1;const globalMaxY=Math.ceil(Math.max(dataMaxY,thresholdMaxY))+1;const chartWidth=width-paddingLeft-paddingRight;const chartHeight=height-paddingTop-paddingBottom;const scaleY=chartHeight/(globalMaxY-globalMinY||1);if(isPeriodsFiltered){const maxPoints=Math.max(...processedData.map(({points:points})=>points.length));const pointSpacing=chartWidth/Math.max(1,maxPoints-1);processedData.forEach(({points:points})=>{points.forEach((point,index)=>{point.screenX=paddingLeft+index*pointSpacing;point.screenY=height-paddingBottom-(point.y-globalMinY)*scaleY})})}else{let globalMinX=Infinity;let globalMaxX=-Infinity;processedData.forEach(({points:points})=>{points.forEach(point=>{if(point.x<globalMinX)globalMinX=point.x;if(point.x>globalMaxX)globalMaxX=point.x})});const timeRange=globalMaxX-globalMinX||1;const scaleX=chartWidth/timeRange;processedData.forEach(({points:points})=>{points.forEach(point=>{point.screenX=paddingLeft+(point.x-globalMinX)*scaleX;point.screenY=height-paddingBottom-(point.y-globalMinY)*scaleY})})}ctx.strokeStyle=colors.chartGrid;ctx.lineWidth=1;for(let i=0;i<=5;i++){const y=paddingTop+chartHeight*i/5;ctx.beginPath();ctx.moveTo(paddingLeft,y);ctx.lineTo(width-paddingRight,y);ctx.stroke()}const rangeColors=[{fill:"rgba(76, 175, 80, 0.12)",stroke:"#4CAF50"},{fill:"rgba(33, 150, 243, 0.12)",stroke:"#2196F3"},{fill:"rgba(255, 152, 0, 0.12)",stroke:"#FF9800"},{fill:"rgba(156, 39, 176, 0.12)",stroke:"#9C27B0"}];temperatureRanges.forEach((range,index)=>{const rangeMinY=height-paddingBottom-(range.min-globalMinY)*scaleY;const rangeMaxY=height-paddingBottom-(range.max-globalMinY)*scaleY;const colorSet=rangeColors[index%rangeColors.length];ctx.fillStyle=colorSet.fill;ctx.fillRect(paddingLeft,rangeMaxY,chartWidth,rangeMinY-rangeMaxY);ctx.strokeStyle=colorSet.stroke;ctx.lineWidth=1.5;ctx.setLineDash([6,4]);ctx.beginPath();ctx.moveTo(paddingLeft,rangeMinY);ctx.lineTo(width-paddingRight,rangeMinY);ctx.moveTo(paddingLeft,rangeMaxY);ctx.lineTo(width-paddingRight,rangeMaxY);ctx.stroke();ctx.setLineDash([]);if(temperatureRanges.length>1||range.customerName){ctx.fillStyle=colorSet.stroke;ctx.font="10px system-ui, sans-serif";ctx.textAlign="left";const labelY=(rangeMinY+rangeMaxY)/2;const labelText=range.customerName||`${range.min}°-${range.max}°C`;ctx.fillText(labelText,width-paddingRight+5,labelY+3)}});processedData.forEach(({device:device,points:points})=>{ctx.strokeStyle=device.color;ctx.lineWidth=2.5;ctx.beginPath();points.forEach((point,i)=>{if(i===0)ctx.moveTo(point.screenX,point.screenY);else ctx.lineTo(point.screenX,point.screenY)});ctx.stroke();ctx.fillStyle=device.color;points.forEach(point=>{ctx.beginPath();ctx.arc(point.screenX,point.screenY,4,0,Math.PI*2);ctx.fill()})});ctx.fillStyle=colors.textMuted;ctx.font="12px system-ui, sans-serif";ctx.textAlign="right";for(let i=0;i<=5;i++){const val=globalMinY+(globalMaxY-globalMinY)*(5-i)/5;const y=paddingTop+chartHeight*i/5;ctx.fillText(val.toFixed(1)+"°C",paddingLeft-10,y+4)}ctx.textAlign="center";const xAxisPoints=processedData[0]?.points||[];const numLabels=Math.min(8,xAxisPoints.length);const labelInterval=Math.max(1,Math.floor(xAxisPoints.length/numLabels));for(let i=0;i<xAxisPoints.length;i+=labelInterval){const point=xAxisPoints[i];const date=new Date(point.x);let label;if(state.granularity==="hour"){label=date.toLocaleTimeString(state.locale,{hour:"2-digit",minute:"2-digit"})}else{label=date.toLocaleDateString(state.locale,{day:"2-digit",month:"2-digit"})}ctx.strokeStyle=colors.chartGrid;ctx.lineWidth=1;ctx.beginPath();ctx.moveTo(point.screenX,paddingTop);ctx.lineTo(point.screenX,height-paddingBottom);ctx.stroke();ctx.fillStyle=colors.textMuted;ctx.fillText(label,point.screenX,height-paddingBottom+18)}ctx.strokeStyle=colors.border;ctx.lineWidth=1;ctx.beginPath();ctx.moveTo(paddingLeft,paddingTop);ctx.lineTo(paddingLeft,height-paddingBottom);ctx.lineTo(width-paddingRight,height-paddingBottom);ctx.stroke();const allChartPoints=processedData.flatMap(pd=>pd.points);setupComparisonChartTooltip(canvas,chartContainer,allChartPoints,state,colors)}function setupComparisonChartTooltip(canvas,container,chartData,state,colors){const existingTooltip=container.querySelector(".myio-chart-tooltip");if(existingTooltip)existingTooltip.remove();const tooltip=document.createElement("div");tooltip.className="myio-chart-tooltip";tooltip.style.cssText=`\n position: absolute;\n background: ${state.theme==="dark"?"rgba(30, 30, 40, 0.95)":"rgba(255, 255, 255, 0.98)"};\n border: 1px solid ${colors.border};\n border-radius: 8px;\n padding: 10px 14px;\n font-size: 13px;\n color: ${colors.text};\n pointer-events: none;\n opacity: 0;\n transition: opacity 0.15s;\n z-index: 1000;\n box-shadow: 0 4px 12px rgba(0,0,0,0.15);\n min-width: 160px;\n `;container.appendChild(tooltip);const findNearestPoint=(mouseX,mouseY)=>{const threshold=20;let nearest=null;let minDist=Infinity;for(const point of chartData){const dist=Math.sqrt(Math.pow(mouseX-point.screenX,2)+Math.pow(mouseY-point.screenY,2));if(dist<minDist&&dist<threshold){minDist=dist;nearest=point}}return nearest};canvas.addEventListener("mousemove",e=>{const rect=canvas.getBoundingClientRect();const mouseX=e.clientX-rect.left;const mouseY=e.clientY-rect.top;const point=findNearestPoint(mouseX,mouseY);if(point){const date=new Date(point.x);let dateStr;if(state.granularity==="hour"){dateStr=date.toLocaleDateString(state.locale,{day:"2-digit",month:"2-digit",year:"numeric"})+" "+date.toLocaleTimeString(state.locale,{hour:"2-digit",minute:"2-digit"})}else{dateStr=date.toLocaleDateString(state.locale,{day:"2-digit",month:"2-digit",year:"numeric"})}tooltip.innerHTML=`\n <div style="display: flex; align-items: center; gap: 8px; margin-bottom: 6px;">\n <span style="width: 10px; height: 10px; border-radius: 50%; background: ${point.deviceColor};"></span>\n <span style="font-weight: 600;">${point.deviceLabel}</span>\n </div>\n <div style="font-weight: 600; font-size: 16px; color: ${point.deviceColor}; margin-bottom: 4px;">\n ${formatTemperature2(point.y)}\n </div>\n <div style="font-size: 11px; color: ${colors.textMuted};">\n 📅 ${dateStr}\n </div>\n `;let tooltipX=point.screenX+15;let tooltipY=point.screenY-15;const tooltipRect=tooltip.getBoundingClientRect();const containerRect=container.getBoundingClientRect();if(tooltipX+tooltipRect.width>containerRect.width-10){tooltipX=point.screenX-tooltipRect.width-15}if(tooltipY<10){tooltipY=point.screenY+15}tooltip.style.left=`${tooltipX}px`;tooltip.style.top=`${tooltipY}px`;tooltip.style.opacity="1";canvas.style.cursor="pointer"}else{tooltip.style.opacity="0";canvas.style.cursor="default"}});canvas.addEventListener("mouseleave",()=>{tooltip.style.opacity="0";canvas.style.cursor="default"})}async function setupEventListeners2(container,state,modalId,onClose){const closeModal=()=>{container.remove();onClose?.()};container.querySelector(".myio-temp-comparison-overlay")?.addEventListener("click",e=>{if(e.target===e.currentTarget)closeModal()});document.getElementById(`${modalId}-close`)?.addEventListener("click",closeModal);document.getElementById(`${modalId}-close-btn`)?.addEventListener("click",closeModal);const dateRangeInput=document.getElementById(`${modalId}-date-range`);if(dateRangeInput&&!state.dateRangePicker){try{state.dateRangePicker=await createDateRangePicker2(dateRangeInput,{presetStart:new Date(state.startTs).toISOString(),presetEnd:new Date(state.endTs).toISOString(),includeTime:true,timePrecision:"minute",maxRangeDays:90,locale:state.locale,parentEl:container.querySelector(".myio-temp-comparison-content"),onApply:result=>{state.startTs=new Date(result.startISO).getTime();state.endTs=new Date(result.endISO).getTime();console.log("[TemperatureComparisonModal] Date range applied:",result)}})}catch(error){console.warn("[TemperatureComparisonModal] DateRangePicker initialization failed:",error)}}document.getElementById(`${modalId}-theme-toggle`)?.addEventListener("click",async()=>{state.theme=state.theme==="dark"?"light":"dark";localStorage.setItem("myio-temp-comparison-theme",state.theme);state.dateRangePicker=null;renderModal2(container,state,modalId);if(state.deviceData.some(dd=>dd.data.length>0)){drawComparisonChart(modalId,state)}await setupEventListeners2(container,state,modalId,onClose)});document.getElementById(`${modalId}-maximize`)?.addEventListener("click",async()=>{container.__isMaximized=!container.__isMaximized;state.dateRangePicker=null;renderModal2(container,state,modalId);if(state.deviceData.some(dd=>dd.data.length>0)){drawComparisonChart(modalId,state)}await setupEventListeners2(container,state,modalId,onClose)});const periodBtn=document.getElementById(`${modalId}-period-btn`);const periodDropdown=document.getElementById(`${modalId}-period-dropdown`);periodBtn?.addEventListener("click",e=>{e.stopPropagation();if(periodDropdown){periodDropdown.style.display=periodDropdown.style.display==="none"?"block":"none"}});document.addEventListener("click",e=>{if(periodDropdown&&!periodDropdown.contains(e.target)&&e.target!==periodBtn){periodDropdown.style.display="none"}});const periodCheckboxes=document.querySelectorAll(`input[name="${modalId}-period"]`);periodCheckboxes.forEach(checkbox=>{checkbox.addEventListener("change",()=>{const checked=Array.from(periodCheckboxes).filter(cb=>cb.checked).map(cb=>cb.value);state.selectedPeriods=checked;const btnLabel=periodBtn?.querySelector("span:first-child");if(btnLabel){btnLabel.textContent=getSelectedPeriodsLabel(state.selectedPeriods)}if(state.deviceData.some(dd=>dd.data.length>0)){drawComparisonChart(modalId,state)}})});document.getElementById(`${modalId}-period-select-all`)?.addEventListener("click",()=>{periodCheckboxes.forEach(cb=>{cb.checked=true});state.selectedPeriods=["madrugada","manha","tarde","noite"];const btnLabel=periodBtn?.querySelector("span:first-child");if(btnLabel){btnLabel.textContent=getSelectedPeriodsLabel(state.selectedPeriods)}if(state.deviceData.some(dd=>dd.data.length>0)){drawComparisonChart(modalId,state)}});document.getElementById(`${modalId}-period-clear`)?.addEventListener("click",()=>{periodCheckboxes.forEach(cb=>{cb.checked=false});state.selectedPeriods=[];const btnLabel=periodBtn?.querySelector("span:first-child");if(btnLabel){btnLabel.textContent=getSelectedPeriodsLabel(state.selectedPeriods)}if(state.deviceData.some(dd=>dd.data.length>0)){drawComparisonChart(modalId,state)}});document.getElementById(`${modalId}-granularity`)?.addEventListener("change",e=>{state.granularity=e.target.value;localStorage.setItem("myio-temp-comparison-granularity",state.granularity);if(state.deviceData.some(dd=>dd.data.length>0)){drawComparisonChart(modalId,state)}});document.getElementById(`${modalId}-query`)?.addEventListener("click",async()=>{if(state.startTs>=state.endTs){alert("Por favor, selecione um período válido");return}state.isLoading=true;state.dateRangePicker=null;renderModal2(container,state,modalId);await fetchAllDevicesData(state);renderModal2(container,state,modalId);drawComparisonChart(modalId,state);await setupEventListeners2(container,state,modalId,onClose)});document.getElementById(`${modalId}-export`)?.addEventListener("click",()=>{if(state.deviceData.every(dd=>dd.data.length===0))return;exportComparisonCSV(state)})}function exportComparisonCSV(state){const startDateStr=new Date(state.startTs).toLocaleDateString(state.locale).replace(/\//g,"-");const endDateStr=new Date(state.endTs).toLocaleDateString(state.locale).replace(/\//g,"-");const BOM="\ufeff";let csvContent=BOM;csvContent+=`Comparação de Temperatura\n`;csvContent+=`Período: ${startDateStr} até ${endDateStr}\n`;csvContent+=`Sensores: ${state.devices.map(d=>d.label).join(", ")}\n`;csvContent+="\n";csvContent+="Estatísticas por Sensor:\n";csvContent+="Sensor,Média (°C),Min (°C),Max (°C),Leituras\n";state.deviceData.forEach(dd=>{csvContent+=`"${dd.device.label}",${dd.stats.avg.toFixed(2)},${dd.stats.min.toFixed(2)},${dd.stats.max.toFixed(2)},${dd.stats.count}\n`});csvContent+="\n";csvContent+="Dados Detalhados:\n";csvContent+="Data/Hora,Sensor,Temperatura (°C)\n";state.deviceData.forEach(dd=>{dd.data.forEach(item=>{const date=new Date(item.ts).toLocaleString(state.locale);const temp=Number(item.value).toFixed(2);csvContent+=`"${date}","${dd.device.label}",${temp}\n`})});const blob=new Blob([csvContent],{type:"text/csv;charset=utf-8;"});const url=URL.createObjectURL(blob);const link=document.createElement("a");link.href=url;link.download=`comparacao_temperatura_${startDateStr}_${endDateStr}.csv`;document.body.appendChild(link);link.click();document.body.removeChild(link);URL.revokeObjectURL(url)}var DARK_THEME2={modalBg:"linear-gradient(180deg, #1e1e2e 0%, #151521 100%)",headerBg:"#3e1a7d",textPrimary:"#ffffff",textSecondary:"rgba(255, 255, 255, 0.7)",textMuted:"rgba(255, 255, 255, 0.5)",inputBg:"rgba(255, 255, 255, 0.08)",inputBorder:"rgba(255, 255, 255, 0.2)",inputText:"#ffffff",buttonPrimary:"#3e1a7d",buttonPrimaryHover:"#5a2da8",buttonSecondary:"rgba(255, 255, 255, 0.1)",success:"#4CAF50",error:"#f44336",overlay:"rgba(0, 0, 0, 0.85)"};var LIGHT_THEME2={modalBg:"#ffffff",headerBg:"#3e1a7d",textPrimary:"#1a1a2e",textSecondary:"rgba(0, 0, 0, 0.7)",textMuted:"rgba(0, 0, 0, 0.5)",inputBg:"#f5f5f5",inputBorder:"rgba(0, 0, 0, 0.2)",inputText:"#1a1a2e",buttonPrimary:"#3e1a7d",buttonPrimaryHover:"#5a2da8",buttonSecondary:"rgba(0, 0, 0, 0.05)",success:"#4CAF50",error:"#f44336",overlay:"rgba(0, 0, 0, 0.5)"};function getColors(theme){return theme==="dark"?DARK_THEME2:LIGHT_THEME2}async function fetchCustomerAttributes(customerId,token){const url=`/api/plugins/telemetry/CUSTOMER/${customerId}/values/attributes/SERVER_SCOPE`;const response=await fetch(url,{method:"GET",headers:{"Content-Type":"application/json","X-Authorization":`Bearer ${token}`}});if(!response.ok){if(response.status===404||response.status===400){return{minTemperature:null,maxTemperature:null}}throw new Error(`Failed to fetch attributes: ${response.status}`)}const attributes=await response.json();let minTemperature=null;let maxTemperature=null;if(Array.isArray(attributes)){for(const attr of attributes){if(attr.key==="minTemperature"){minTemperature=Number(attr.value)}else if(attr.key==="maxTemperature"){maxTemperature=Number(attr.value)}}}return{minTemperature:minTemperature,maxTemperature:maxTemperature}}async function saveCustomerAttributes(customerId,token,minTemperature,maxTemperature){const url=`/api/plugins/telemetry/CUSTOMER/${customerId}/SERVER_SCOPE`;const attributes={minTemperature:minTemperature,maxTemperature:maxTemperature};const response=await fetch(url,{method:"POST",headers:{"Content-Type":"application/json","X-Authorization":`Bearer ${token}`},body:JSON.stringify(attributes)});if(!response.ok){throw new Error(`Failed to save attributes: ${response.status}`)}}function renderModal3(container,state,modalId,onClose,onSave){const colors=getColors(state.theme);const minValue=state.minTemperature!==null?state.minTemperature:"";const maxValue=state.maxTemperature!==null?state.maxTemperature:"";container.innerHTML=`\n <style>\n @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }\n @keyframes slideIn { from { transform: translateY(-20px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }\n @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }\n\n #${modalId} {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background: ${colors.overlay};\n z-index: 10000;\n display: flex;\n align-items: center;\n justify-content: center;\n animation: fadeIn 0.2s ease-out;\n }\n\n #${modalId} .modal-content {\n background: ${colors.modalBg};\n border-radius: 16px;\n width: 90%;\n max-width: 480px;\n box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);\n border: 1px solid rgba(255, 255, 255, 0.1);\n animation: slideIn 0.3s ease-out;\n overflow: hidden;\n }\n\n #${modalId} .modal-header {\n background: ${colors.headerBg};\n padding: 20px 24px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n }\n\n #${modalId} .modal-title {\n margin: 0;\n font-size: 18px;\n font-weight: 600;\n color: #fff;\n font-family: 'Roboto', sans-serif;\n display: flex;\n align-items: center;\n gap: 10px;\n }\n\n #${modalId} .close-btn {\n width: 32px;\n height: 32px;\n background: rgba(255, 255, 255, 0.1);\n border: 1px solid rgba(255, 255, 255, 0.2);\n border-radius: 8px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.2s;\n color: #fff;\n font-size: 18px;\n }\n\n #${modalId} .close-btn:hover {\n background: rgba(255, 68, 68, 0.25);\n border-color: rgba(255, 68, 68, 0.5);\n }\n\n #${modalId} .modal-body {\n padding: 24px;\n }\n\n #${modalId} .customer-info {\n margin-bottom: 24px;\n padding: 12px 16px;\n background: ${colors.inputBg};\n border-radius: 8px;\n border: 1px solid ${colors.inputBorder};\n }\n\n #${modalId} .customer-label {\n font-size: 12px;\n color: ${colors.textMuted};\n margin-bottom: 4px;\n }\n\n #${modalId} .customer-name {\n font-size: 16px;\n font-weight: 500;\n color: ${colors.textPrimary};\n }\n\n #${modalId} .form-group {\n margin-bottom: 20px;\n }\n\n #${modalId} .form-label {\n display: block;\n font-size: 14px;\n font-weight: 500;\n color: ${colors.textSecondary};\n margin-bottom: 8px;\n }\n\n #${modalId} .form-input {\n width: 100%;\n padding: 12px 16px;\n font-size: 16px;\n background: ${colors.inputBg};\n border: 1px solid ${colors.inputBorder};\n border-radius: 8px;\n color: ${colors.inputText};\n outline: none;\n transition: border-color 0.2s;\n box-sizing: border-box;\n }\n\n #${modalId} .form-input:focus {\n border-color: ${colors.buttonPrimary};\n }\n\n #${modalId} .form-input::placeholder {\n color: ${colors.textMuted};\n }\n\n #${modalId} .form-hint {\n font-size: 12px;\n color: ${colors.textMuted};\n margin-top: 6px;\n }\n\n #${modalId} .temperature-range {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 16px;\n }\n\n #${modalId} .range-preview {\n margin-top: 20px;\n padding: 16px;\n background: rgba(76, 175, 80, 0.1);\n border: 1px dashed ${colors.success};\n border-radius: 8px;\n text-align: center;\n }\n\n #${modalId} .range-preview-label {\n font-size: 12px;\n color: ${colors.textMuted};\n margin-bottom: 8px;\n }\n\n #${modalId} .range-preview-value {\n font-size: 24px;\n font-weight: 600;\n color: ${colors.success};\n }\n\n #${modalId} .modal-footer {\n padding: 16px 24px;\n border-top: 1px solid ${colors.inputBorder};\n display: flex;\n justify-content: flex-end;\n gap: 12px;\n }\n\n #${modalId} .btn {\n padding: 10px 24px;\n font-size: 14px;\n font-weight: 500;\n border-radius: 8px;\n cursor: pointer;\n transition: all 0.2s;\n border: none;\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n #${modalId} .btn-secondary {\n background: ${colors.buttonSecondary};\n color: ${colors.textSecondary};\n border: 1px solid ${colors.inputBorder};\n }\n\n #${modalId} .btn-secondary:hover {\n background: ${colors.inputBg};\n }\n\n #${modalId} .btn-primary {\n background: ${colors.buttonPrimary};\n color: #fff;\n }\n\n #${modalId} .btn-primary:hover {\n background: ${colors.buttonPrimaryHover};\n }\n\n #${modalId} .btn-primary:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n #${modalId} .spinner {\n width: 16px;\n height: 16px;\n border: 2px solid rgba(255,255,255,0.3);\n border-top-color: #fff;\n border-radius: 50%;\n animation: spin 1s linear infinite;\n }\n\n #${modalId} .message {\n padding: 12px 16px;\n border-radius: 8px;\n margin-bottom: 16px;\n font-size: 14px;\n }\n\n #${modalId} .message-error {\n background: rgba(244, 67, 54, 0.1);\n border: 1px solid ${colors.error};\n color: ${colors.error};\n }\n\n #${modalId} .message-success {\n background: rgba(76, 175, 80, 0.1);\n border: 1px solid ${colors.success};\n color: ${colors.success};\n }\n\n #${modalId} .loading-overlay {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 48px;\n color: ${colors.textSecondary};\n }\n\n #${modalId} .loading-spinner {\n width: 40px;\n height: 40px;\n border: 3px solid ${colors.inputBorder};\n border-top-color: ${colors.buttonPrimary};\n border-radius: 50%;\n animation: spin 1s linear infinite;\n margin-bottom: 16px;\n }\n </style>\n\n <div id="${modalId}" class="modal-overlay">\n <div class="modal-content">\n <div class="modal-header">\n <h2 class="modal-title">\n <span>🌡️</span>\n Configurar Temperatura\n </h2>\n <button class="close-btn" id="${modalId}-close">×</button>\n </div>\n\n <div class="modal-body">\n ${state.isLoading?`\n <div class="loading-overlay">\n <div class="loading-spinner"></div>\n <div>Carregando configurações...</div>\n </div>\n `:`\n ${state.error?`\n <div class="message message-error">${state.error}</div>\n `:""}\n\n ${state.successMessage?`\n <div class="message message-success">${state.successMessage}</div>\n `:""}\n\n <div class="customer-info">\n <div class="customer-label">Shopping / Cliente</div>\n <div class="customer-name">${state.customerName||"Não identificado"}</div>\n </div>\n\n <div class="form-group">\n <label class="form-label">Faixa de Temperatura Ideal</label>\n <div class="temperature-range">\n <div>\n <input\n type="number"\n id="${modalId}-min"\n class="form-input"\n placeholder="Mínima"\n value="${minValue}"\n step="0.5"\n min="0"\n max="50"\n />\n <div class="form-hint">Temperatura mínima (°C)</div>\n </div>\n <div>\n <input\n type="number"\n id="${modalId}-max"\n class="form-input"\n placeholder="Máxima"\n value="${maxValue}"\n step="0.5"\n min="0"\n max="50"\n />\n <div class="form-hint">Temperatura máxima (°C)</div>\n </div>\n </div>\n </div>\n\n <div class="range-preview" id="${modalId}-preview">\n <div class="range-preview-label">Faixa configurada</div>\n <div class="range-preview-value" id="${modalId}-preview-value">\n ${minValue&&maxValue?`${minValue}°C - ${maxValue}°C`:"Não definida"}\n </div>\n </div>\n `}\n </div>\n\n ${!state.isLoading?`\n <div class="modal-footer">\n <button class="btn btn-secondary" id="${modalId}-cancel">Cancelar</button>\n <button class="btn btn-primary" id="${modalId}-save" ${state.isSaving?"disabled":""}>\n ${state.isSaving?'<div class="spinner"></div> Salvando...':"Salvar"}\n </button>\n </div>\n `:""}\n </div>\n </div>\n `;const closeBtn=document.getElementById(`${modalId}-close`);const cancelBtn=document.getElementById(`${modalId}-cancel`);const saveBtn=document.getElementById(`${modalId}-save`);const minInput=document.getElementById(`${modalId}-min`);const maxInput=document.getElementById(`${modalId}-max`);const previewValue=document.getElementById(`${modalId}-preview-value`);const overlay=document.getElementById(modalId);closeBtn?.addEventListener("click",onClose);cancelBtn?.addEventListener("click",onClose);overlay?.addEventListener("click",e=>{if(e.target===overlay)onClose()});const updatePreview=()=>{if(previewValue&&minInput&&maxInput){const min=minInput.value;const max=maxInput.value;if(min&&max){previewValue.textContent=`${min}°C - ${max}°C`}else{previewValue.textContent="Não definida"}}};minInput?.addEventListener("input",updatePreview);maxInput?.addEventListener("input",updatePreview);saveBtn?.addEventListener("click",async()=>{if(!minInput||!maxInput)return;const min=parseFloat(minInput.value);const max=parseFloat(maxInput.value);if(isNaN(min)||isNaN(max)){state.error="Por favor, preencha ambos os valores.";renderModal3(container,state,modalId,onClose,onSave);return}if(min>=max){state.error="A temperatura mínima deve ser menor que a máxima.";renderModal3(container,state,modalId,onClose,onSave);return}if(min<0||max>50){state.error="Os valores devem estar entre 0°C e 50°C.";renderModal3(container,state,modalId,onClose,onSave);return}await onSave(min,max)})}function openTemperatureSettingsModal(params){const modalId=`myio-temp-settings-${Date.now()}`;const state={customerId:params.customerId,customerName:params.customerName||"",token:params.token,theme:params.theme||"dark",minTemperature:null,maxTemperature:null,isLoading:true,isSaving:false,error:null,successMessage:null};const container=document.createElement("div");container.id=`${modalId}-container`;document.body.appendChild(container);const destroy=()=>{container.remove();params.onClose?.()};const handleSave=async(min,max)=>{state.isSaving=true;state.error=null;state.successMessage=null;renderModal3(container,state,modalId,destroy,handleSave);try{await saveCustomerAttributes(state.customerId,state.token,min,max);state.minTemperature=min;state.maxTemperature=max;state.isSaving=false;state.successMessage="Configurações salvas com sucesso!";renderModal3(container,state,modalId,destroy,handleSave);params.onSave?.({minTemperature:min,maxTemperature:max});setTimeout(()=>{destroy()},1500)}catch(error){state.isSaving=false;state.error=`Erro ao salvar: ${error.message}`;renderModal3(container,state,modalId,destroy,handleSave)}};renderModal3(container,state,modalId,destroy,handleSave);fetchCustomerAttributes(state.customerId,state.token).then(({minTemperature:minTemperature,maxTemperature:maxTemperature})=>{state.minTemperature=minTemperature;state.maxTemperature=maxTemperature;state.isLoading=false;renderModal3(container,state,modalId,destroy,handleSave)}).catch(error=>{state.isLoading=false;state.error=`Erro ao carregar: ${error.message}`;renderModal3(container,state,modalId,destroy,handleSave)});return{destroy:destroy}}var DEFAULT_BG_COLOR="#3e1a7d";var DEFAULT_TEXT_COLOR="white";var DEFAULT_BORDER_RADIUS="10px 10px 0 0";var EXPORT_FORMAT_LABELS={csv:"CSV",xls:"Excel (XLS)",pdf:"PDF"};var EXPORT_FORMAT_ICONS={csv:"📄",xls:"📊",pdf:"📑"};function createModalHeader(config){let currentTheme=config.theme||"light";let currentIsMaximized=config.isMaximized||false;let currentTitle=config.title;let themeBtn=null;let maximizeBtn=null;let closeBtn=null;let exportBtn=null;let exportDropdown=null;const cleanupHandlers=[];const handleThemeClick=()=>{currentTheme=currentTheme==="light"?"dark":"light";config.onThemeToggle?.(currentTheme);updateButtonIcons()};const handleMaximizeClick=()=>{currentIsMaximized=!currentIsMaximized;config.onMaximize?.(currentIsMaximized);updateButtonIcons()};const handleCloseClick=()=>{config.onClose?.()};const handleExportClick=format=>{config.onExport?.(format);if(exportDropdown){exportDropdown.style.display="none"}};function updateButtonIcons(){if(themeBtn){themeBtn.textContent=currentTheme==="dark"?"☀️":"🌙";themeBtn.title=currentTheme==="dark"?"Modo claro":"Modo escuro"}if(maximizeBtn){maximizeBtn.textContent=currentIsMaximized?"🗗":"🗖";maximizeBtn.title=currentIsMaximized?"Restaurar":"Maximizar"}}function getButtonStyle(){return`\n background: none;\n border: none;\n font-size: 16px;\n cursor: pointer;\n padding: 4px 8px;\n border-radius: 6px;\n color: rgba(255, 255, 255, 0.8);\n transition: background-color 0.2s, color 0.2s;\n `.replace(/\s+/g," ").trim()}function renderExportDropdown(){const formats=config.exportFormats||[];if(formats.length===0)return"";if(formats.length===1){return`\n <button id="${config.id}-export" title="Exportar ${EXPORT_FORMAT_LABELS[formats[0]]}" style="${getButtonStyle()}">\n 📥\n </button>\n `}return`\n <div style="position: relative; display: inline-block;">\n <button id="${config.id}-export-btn" title="Exportar" style="${getButtonStyle()}">\n 📥\n </button>\n <div id="${config.id}-export-dropdown" style="\n display: none;\n position: absolute;\n top: 100%;\n right: 0;\n background: white;\n border-radius: 6px;\n box-shadow: 0 4px 12px rgba(0,0,0,0.15);\n min-width: 140px;\n z-index: 10001;\n margin-top: 4px;\n overflow: hidden;\n ">\n ${formats.map(format=>`\n <button\n id="${config.id}-export-${format}"\n class="myio-export-option"\n data-format="${format}"\n style="\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n padding: 10px 14px;\n border: none;\n background: white;\n cursor: pointer;\n font-size: 13px;\n color: #333;\n text-align: left;\n transition: background-color 0.2s;\n "\n >\n ${EXPORT_FORMAT_ICONS[format]} ${EXPORT_FORMAT_LABELS[format]}\n </button>\n `).join("")}\n </div>\n </div>\n `}const instance={render(){const bgColor=config.backgroundColor||DEFAULT_BG_COLOR;const textColor=config.textColor||DEFAULT_TEXT_COLOR;const borderRadius=currentIsMaximized?"0":config.borderRadius||DEFAULT_BORDER_RADIUS;const showTheme=config.showThemeToggle!==false;const showMax=config.showMaximize!==false;const showClose=config.showClose!==false;const showExport=config.exportFormats&&config.exportFormats.length>0;const iconHtml=config.icon?`<span style="margin-right: 8px;">${config.icon}</span>`:"";const buttonStyle=getButtonStyle();return`\n <div class="myio-modal-header" style="\n padding: 4px 8px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n background: ${bgColor};\n color: ${textColor};\n border-radius: ${borderRadius};\n min-height: 20px;\n font-family: 'Roboto', Arial, sans-serif;\n ">\n <h2 id="${config.id}-header-title" style="\n margin: 6px;\n font-size: 18px;\n font-weight: 600;\n color: ${textColor};\n line-height: 2;\n display: flex;\n align-items: center;\n ">\n ${iconHtml}${currentTitle}\n </h2>\n <div style="display: flex; gap: 4px; align-items: center;">\n ${showExport?renderExportDropdown():""}\n ${showTheme?`\n <button id="${config.id}-theme-toggle" title="${currentTheme==="dark"?"Modo claro":"Modo escuro"}" style="${buttonStyle}">\n ${currentTheme==="dark"?"☀️":"🌙"}\n </button>\n `:""}\n ${showMax?`\n <button id="${config.id}-maximize" title="${currentIsMaximized?"Restaurar":"Maximizar"}" style="${buttonStyle}">\n ${currentIsMaximized?"🗗":"🗖"}\n </button>\n `:""}\n ${showClose?`\n <button id="${config.id}-close" title="Fechar" style="${buttonStyle}; font-size: 20px;">\n ×\n </button>\n `:""}\n </div>\n </div>\n `},attachListeners(){themeBtn=document.getElementById(`${config.id}-theme-toggle`);maximizeBtn=document.getElementById(`${config.id}-maximize`);closeBtn=document.getElementById(`${config.id}-close`);const singleExportBtn=document.getElementById(`${config.id}-export`);if(singleExportBtn&&config.exportFormats?.length===1){exportBtn=singleExportBtn;const format=config.exportFormats[0];const clickHandler=()=>handleExportClick(format);exportBtn.addEventListener("click",clickHandler);cleanupHandlers.push(()=>exportBtn?.removeEventListener("click",clickHandler));const enterHandler=()=>{exportBtn.style.backgroundColor="rgba(255, 255, 255, 0.2)"};const leaveHandler=()=>{exportBtn.style.backgroundColor="transparent"};exportBtn.addEventListener("mouseenter",enterHandler);exportBtn.addEventListener("mouseleave",leaveHandler);cleanupHandlers.push(()=>{exportBtn?.removeEventListener("mouseenter",enterHandler);exportBtn?.removeEventListener("mouseleave",leaveHandler)})}const exportDropdownBtn=document.getElementById(`${config.id}-export-btn`);exportDropdown=document.getElementById(`${config.id}-export-dropdown`);if(exportDropdownBtn&&exportDropdown){exportBtn=exportDropdownBtn;const toggleHandler=e=>{e.stopPropagation();if(exportDropdown){exportDropdown.style.display=exportDropdown.style.display==="none"?"block":"none"}};exportDropdownBtn.addEventListener("click",toggleHandler);cleanupHandlers.push(()=>exportDropdownBtn.removeEventListener("click",toggleHandler));const outsideClickHandler=e=>{if(exportDropdown&&!exportDropdown.contains(e.target)&&e.target!==exportDropdownBtn){exportDropdown.style.display="none"}};document.addEventListener("click",outsideClickHandler);cleanupHandlers.push(()=>document.removeEventListener("click",outsideClickHandler));config.exportFormats?.forEach(format=>{const btn=document.getElementById(`${config.id}-export-${format}`);if(btn){const clickHandler=()=>handleExportClick(format);btn.addEventListener("click",clickHandler);cleanupHandlers.push(()=>btn.removeEventListener("click",clickHandler));const enterHandler2=()=>{btn.style.backgroundColor="#f0f0f0"};const leaveHandler2=()=>{btn.style.backgroundColor="white"};btn.addEventListener("mouseenter",enterHandler2);btn.addEventListener("mouseleave",leaveHandler2);cleanupHandlers.push(()=>{btn.removeEventListener("mouseenter",enterHandler2);btn.removeEventListener("mouseleave",leaveHandler2)})}});const enterHandler=()=>{exportDropdownBtn.style.backgroundColor="rgba(255, 255, 255, 0.2)"};const leaveHandler=()=>{exportDropdownBtn.style.backgroundColor="transparent"};exportDropdownBtn.addEventListener("mouseenter",enterHandler);exportDropdownBtn.addEventListener("mouseleave",leaveHandler);cleanupHandlers.push(()=>{exportDropdownBtn.removeEventListener("mouseenter",enterHandler);exportDropdownBtn.removeEventListener("mouseleave",leaveHandler)})}if(themeBtn&&config.showThemeToggle!==false){themeBtn.addEventListener("click",handleThemeClick);cleanupHandlers.push(()=>themeBtn?.removeEventListener("click",handleThemeClick));const enterHandler=()=>{themeBtn.style.backgroundColor="rgba(255, 255, 255, 0.2)"};const leaveHandler=()=>{themeBtn.style.backgroundColor="transparent"};themeBtn.addEventListener("mouseenter",enterHandler);themeBtn.addEventListener("mouseleave",leaveHandler);cleanupHandlers.push(()=>{themeBtn?.removeEventListener("mouseenter",enterHandler);themeBtn?.removeEventListener("mouseleave",leaveHandler)})}if(maximizeBtn&&config.showMaximize!==false){maximizeBtn.addEventListener("click",handleMaximizeClick);cleanupHandlers.push(()=>maximizeBtn?.removeEventListener("click",handleMaximizeClick));const enterHandler=()=>{maximizeBtn.style.backgroundColor="rgba(255, 255, 255, 0.2)"};const leaveHandler=()=>{maximizeBtn.style.backgroundColor="transparent"};maximizeBtn.addEventListener("mouseenter",enterHandler);maximizeBtn.addEventListener("mouseleave",leaveHandler);cleanupHandlers.push(()=>{maximizeBtn?.removeEventListener("mouseenter",enterHandler);maximizeBtn?.removeEventListener("mouseleave",leaveHandler)})}if(closeBtn&&config.showClose!==false){closeBtn.addEventListener("click",handleCloseClick);cleanupHandlers.push(()=>closeBtn?.removeEventListener("click",handleCloseClick));const enterHandler=()=>{closeBtn.style.backgroundColor="rgba(255, 255, 255, 0.2)"};const leaveHandler=()=>{closeBtn.style.backgroundColor="transparent"};closeBtn.addEventListener("mouseenter",enterHandler);closeBtn.addEventListener("mouseleave",leaveHandler);cleanupHandlers.push(()=>{closeBtn?.removeEventListener("mouseenter",enterHandler);closeBtn?.removeEventListener("mouseleave",leaveHandler)})}},update(updates){if(updates.theme!==void 0){currentTheme=updates.theme;updateButtonIcons()}if(updates.isMaximized!==void 0){currentIsMaximized=updates.isMaximized;updateButtonIcons()}if(updates.title!==void 0){currentTitle=updates.title;const titleEl=document.getElementById(`${config.id}-header-title`);if(titleEl){const iconHtml=config.icon?`<span style="margin-right: 8px;">${config.icon}</span>`:"";titleEl.innerHTML=`${iconHtml}${currentTitle}`}}},getState(){return{theme:currentTheme,isMaximized:currentIsMaximized}},destroy(){cleanupHandlers.forEach(handler=>handler());cleanupHandlers.length=0;themeBtn=null;maximizeBtn=null;closeBtn=null;exportBtn=null;exportDropdown=null}};return instance}function getModalHeaderStyles(){return`\n .myio-modal-header button:hover {\n background-color: rgba(255, 255, 255, 0.2) !important;\n }\n .myio-modal-header button:active {\n background-color: rgba(255, 255, 255, 0.3) !important;\n }\n .myio-export-option:hover {\n background-color: #f0f0f0 !important;\n }\n `}var DEFAULT_COLORS={energy:{primary:"#2563eb",background:"rgba(37, 99, 235, 0.1)",gradient:["#f0fdf4","#dcfce7"],pointBackground:"#2563eb",pointBorder:"#ffffff"},water:{primary:"#0288d1",background:"rgba(2, 136, 209, 0.1)",gradient:["#f0f9ff","#bae6fd"],pointBackground:"#0288d1",pointBorder:"#ffffff"},gas:{primary:"#ea580c",background:"rgba(234, 88, 12, 0.1)",gradient:["#fff7ed","#fed7aa"],pointBackground:"#ea580c",pointBorder:"#ffffff"},temperature:{primary:"#dc2626",background:"rgba(220, 38, 38, 0.1)",gradient:["#fef2f2","#fecaca"],pointBackground:"#dc2626",pointBorder:"#ffffff"}};var THEME_COLORS={light:{chartBackground:"#ffffff",text:"#1f2937",textMuted:"#6b7280",grid:"rgba(0, 0, 0, 0.1)",border:"#e5e7eb",tooltipBackground:"#ffffff",tooltipText:"#1f2937"},dark:{chartBackground:"#1f2937",text:"#f9fafb",textMuted:"#9ca3af",grid:"rgba(255, 255, 255, 0.1)",border:"#374151",tooltipBackground:"#374151",tooltipText:"#f9fafb"}};var DEFAULT_CONFIG={defaultPeriod:7,defaultChartType:"line",defaultVizMode:"total",defaultTheme:"light",cacheTTL:3e5,decimalPlaces:1,lineTension:.4,pointRadius:4,borderWidth:2,fill:true,showLegend:false,enableExport:true};function createConsumption7DaysChart(config){let chartInstance=null;let cachedData=null;let currentPeriod=config.defaultPeriod??DEFAULT_CONFIG.defaultPeriod;let currentChartType=config.defaultChartType??DEFAULT_CONFIG.defaultChartType;let currentVizMode=config.defaultVizMode??DEFAULT_CONFIG.defaultVizMode;let currentTheme=config.theme??DEFAULT_CONFIG.defaultTheme;let currentIdealRange=config.idealRange??null;let isRendered=false;let autoRefreshTimer=null;const colors={...DEFAULT_COLORS[config.domain]??DEFAULT_COLORS.energy,...config.colors};function $id(id){if(config.$container&&config.$container[0]){return config.$container[0].querySelector(`#${id}`)}return document.getElementById(id)}function log(level,...args){const prefix=`[${config.domain.toUpperCase()}]`;console[level](prefix,...args)}function calculateYAxisMax(values){const maxValue=Math.max(...values,0);if(config.domain==="temperature"){const tempConfig=config.temperatureConfig;if(tempConfig?.clampRange){return tempConfig.clampRange.max}const maxWithThreshold=Math.max(maxValue,tempConfig?.maxThreshold?.value??0,tempConfig?.idealRange?.max??0);return Math.ceil(maxWithThreshold+5)}if(maxValue===0){return config.thresholdForLargeUnit?config.thresholdForLargeUnit/2:500}let roundTo;if(config.thresholdForLargeUnit&&maxValue>=config.thresholdForLargeUnit){roundTo=config.thresholdForLargeUnit/10}else if(maxValue>=1e3){roundTo=100}else if(maxValue>=100){roundTo=50}else if(maxValue>=10){roundTo=10}else{roundTo=5}return Math.ceil(maxValue*1.1/roundTo)*roundTo}function calculateYAxisMin(values){if(config.domain!=="temperature"){return 0}const tempConfig=config.temperatureConfig;if(tempConfig?.clampRange){return tempConfig.clampRange.min}const minValue=Math.min(...values);const minWithThreshold=Math.min(minValue,tempConfig?.minThreshold?.value??minValue,tempConfig?.idealRange?.min??minValue);return Math.floor(minWithThreshold-5)}function buildTemperatureAnnotations(){const tempConfig=config.temperatureConfig;if(!tempConfig||config.domain!=="temperature"){return{}}const annotations={};const createLineAnnotation=(line,id)=>{const borderDash=line.lineStyle==="dashed"?[6,6]:line.lineStyle==="dotted"?[2,2]:[];return{type:"line",yMin:line.value,yMax:line.value,borderColor:line.color,borderWidth:line.lineWidth??2,borderDash:borderDash,label:{display:true,content:line.label,position:"end",backgroundColor:line.color,color:"#fff",font:{size:10,weight:"bold"},padding:{x:4,y:2}}}};if(tempConfig.minThreshold){annotations["minThreshold"]=createLineAnnotation(tempConfig.minThreshold)}if(tempConfig.maxThreshold){annotations["maxThreshold"]=createLineAnnotation(tempConfig.maxThreshold)}if(tempConfig.idealRange){annotations["idealRange"]={type:"box",yMin:tempConfig.idealRange.min,yMax:tempConfig.idealRange.max,backgroundColor:tempConfig.idealRange.color,borderWidth:0,label:tempConfig.idealRange.label?{display:true,content:tempConfig.idealRange.label,position:{x:"start",y:"center"},color:"#666",font:{size:10}}:void 0}}return annotations}function buildIdealRangeAnnotation(){if(!currentIdealRange){return{}}const{min:min,max:max,enabled:enabled=true}=currentIdealRange;if(!enabled||min===0&&max===0||min>=max){return{}}const defaultColors={temperature:{bg:"rgba(34, 197, 94, 0.15)",border:"rgba(34, 197, 94, 0.4)"},energy:{bg:"rgba(37, 99, 235, 0.1)",border:"rgba(37, 99, 235, 0.3)"},water:{bg:"rgba(2, 136, 209, 0.1)",border:"rgba(2, 136, 209, 0.3)"},gas:{bg:"rgba(234, 88, 12, 0.1)",border:"rgba(234, 88, 12, 0.3)"}};const domainDefaults=defaultColors[config.domain]||defaultColors.energy;return{idealRangeBox:{type:"box",yMin:min,yMax:max,backgroundColor:currentIdealRange.color||domainDefaults.bg,borderColor:currentIdealRange.borderColor||domainDefaults.border,borderWidth:1,label:currentIdealRange.label?{display:true,content:currentIdealRange.label,position:{x:"start",y:"center"},color:"#666",font:{size:10,style:"italic"},backgroundColor:"rgba(255, 255, 255, 0.8)",padding:{x:4,y:2}}:void 0}}}function formatValue(value,includeUnit=true){const decimals=config.decimalPlaces??DEFAULT_CONFIG.decimalPlaces;if(config.unitLarge&&config.thresholdForLargeUnit&&value>=config.thresholdForLargeUnit){const converted=value/config.thresholdForLargeUnit;return includeUnit?`${converted.toFixed(decimals)} ${config.unitLarge}`:converted.toFixed(decimals)}return includeUnit?`${value.toFixed(decimals)} ${config.unit}`:value.toFixed(decimals)}function formatTickValue(value){if(config.unitLarge&&config.thresholdForLargeUnit&&value>=config.thresholdForLargeUnit){return`${(value/config.thresholdForLargeUnit).toFixed(1)}`}return value.toFixed(0)}function buildChartConfig(data){const yAxisMax=calculateYAxisMax(data.dailyTotals);const yAxisMin=calculateYAxisMin(data.dailyTotals);const tension=config.lineTension??DEFAULT_CONFIG.lineTension;const pointRadius=config.pointRadius??DEFAULT_CONFIG.pointRadius;const borderWidth=config.borderWidth??DEFAULT_CONFIG.borderWidth;const fill=config.fill??DEFAULT_CONFIG.fill;const showLegend=config.showLegend??DEFAULT_CONFIG.showLegend;const themeColors=THEME_COLORS[currentTheme];const isTemperature=config.domain==="temperature";let datasets;if(currentVizMode==="separate"&&data.shoppingData&&data.shoppingNames){const shoppingColors=colors.shoppingColors||["#2563eb","#16a34a","#ea580c","#dc2626","#8b5cf6","#0891b2","#65a30d","#d97706","#be185d","#0d9488"];datasets=Object.entries(data.shoppingData).map(([shoppingId,values],index)=>({label:data.shoppingNames?.[shoppingId]||shoppingId,data:values,borderColor:shoppingColors[index%shoppingColors.length],backgroundColor:currentChartType==="line"?`${shoppingColors[index%shoppingColors.length]}20`:shoppingColors[index%shoppingColors.length],fill:currentChartType==="line"&&fill,tension:tension,borderWidth:borderWidth,pointRadius:currentChartType==="line"?pointRadius:0,pointBackgroundColor:shoppingColors[index%shoppingColors.length],pointBorderColor:"#fff",pointBorderWidth:2}))}else{const datasetLabel=isTemperature?`Temperatura (${config.unit})`:`Consumo (${config.unit})`;datasets=[{label:datasetLabel,data:data.dailyTotals,borderColor:colors.primary,backgroundColor:currentChartType==="line"?colors.background:colors.primary,fill:currentChartType==="line"&&fill,tension:tension,borderWidth:borderWidth,pointRadius:currentChartType==="line"?pointRadius:0,pointBackgroundColor:colors.pointBackground||colors.primary,pointBorderColor:colors.pointBorder||"#fff",pointBorderWidth:2,borderRadius:currentChartType==="bar"?4:0}]}const temperatureAnnotations=buildTemperatureAnnotations();const idealRangeAnnotations=buildIdealRangeAnnotation();const allAnnotations={...temperatureAnnotations,...idealRangeAnnotations};const yAxisLabel=config.unitLarge&&config.thresholdForLargeUnit&&yAxisMax>=config.thresholdForLargeUnit?config.unitLarge:config.unit;return{type:currentChartType,data:{labels:data.labels,datasets:datasets},options:{responsive:true,maintainAspectRatio:false,animation:false,plugins:{legend:{display:showLegend||currentVizMode==="separate",position:"bottom",labels:{color:themeColors.text}},tooltip:{backgroundColor:themeColors.tooltipBackground,titleColor:themeColors.tooltipText,bodyColor:themeColors.tooltipText,borderColor:themeColors.border,borderWidth:1,callbacks:{label:function(context){const value=context.parsed.y||0;const label=context.dataset.label||"";return`${label}: ${formatValue(value)}`}}},annotation:Object.keys(allAnnotations).length>0?{annotations:allAnnotations}:void 0},scales:{y:{beginAtZero:!isTemperature,min:yAxisMin,max:yAxisMax,grid:{color:themeColors.grid},title:{display:true,text:yAxisLabel,font:{size:12},color:themeColors.text},ticks:{font:{size:11},color:themeColors.textMuted,callback:function(value){return formatTickValue(value)}}},x:{grid:{color:themeColors.grid},ticks:{font:{size:11},color:themeColors.textMuted}}}}}}function validateChartJs(){if(typeof Chart==="undefined"){log("error","Chart.js not loaded. Cannot initialize chart.");config.onError?.(new Error("Chart.js not loaded"));return false}return true}function validateCanvas(){const canvas=$id(config.containerId);if(!canvas){log("error",`Canvas #${config.containerId} not found`);config.onError?.(new Error(`Canvas #${config.containerId} not found`));return null}return canvas}function setupAutoRefresh(){if(config.autoRefreshInterval&&config.autoRefreshInterval>0){if(autoRefreshTimer){clearInterval(autoRefreshTimer)}autoRefreshTimer=setInterval(async()=>{log("log","Auto-refreshing data...");await instance.refresh(true)},config.autoRefreshInterval)}}function cleanupAutoRefresh(){if(autoRefreshTimer){clearInterval(autoRefreshTimer);autoRefreshTimer=null}}function setupButtonHandlers(){if(config.settingsButtonId&&config.onSettingsClick){const settingsBtn=$id(config.settingsButtonId);if(settingsBtn){settingsBtn.addEventListener("click",()=>{log("log","Settings button clicked");config.onSettingsClick?.()});log("log","Settings button handler attached")}}if(config.maximizeButtonId&&config.onMaximizeClick){const maximizeBtn=$id(config.maximizeButtonId);if(maximizeBtn){maximizeBtn.addEventListener("click",()=>{log("log","Maximize button clicked");config.onMaximizeClick?.()});log("log","Maximize button handler attached")}}const enableExport=config.enableExport??DEFAULT_CONFIG.enableExport;if(enableExport&&config.exportButtonId){const exportBtn=$id(config.exportButtonId);if(exportBtn){exportBtn.addEventListener("click",()=>{log("log","Export button clicked");if(config.onExportCSV&&cachedData){config.onExportCSV(cachedData)}else{instance.exportCSV()}});log("log","Export button handler attached")}}}function generateCSVContent(data){const rows=[];const decimals=config.decimalPlaces??DEFAULT_CONFIG.decimalPlaces;if(currentVizMode==="separate"&&data.shoppingData&&data.shoppingNames){const shoppingHeaders=Object.keys(data.shoppingData).map(id=>data.shoppingNames?.[id]||id);rows.push(["Data",...shoppingHeaders,"Total"].join(";"));data.labels.forEach((label,index)=>{const shoppingValues=Object.keys(data.shoppingData).map(id=>data.shoppingData[id][index].toFixed(decimals));rows.push([label,...shoppingValues,data.dailyTotals[index].toFixed(decimals)].join(";"))})}else{rows.push(["Data",`Consumo (${config.unit})`].join(";"));data.labels.forEach((label,index)=>{rows.push([label,data.dailyTotals[index].toFixed(decimals)].join(";"))})}const total=data.dailyTotals.reduce((sum,v)=>sum+v,0);const avg=total/data.dailyTotals.length;rows.push("");rows.push(["Total",total.toFixed(decimals)].join(";"));rows.push(["Média",avg.toFixed(decimals)].join(";"));return rows.join("\n")}function downloadCSV(content,filename){const BOM="\ufeff";const blob=new Blob([BOM+content],{type:"text/csv;charset=utf-8"});const url=URL.createObjectURL(blob);const link=document.createElement("a");link.href=url;link.download=`${filename}.csv`;document.body.appendChild(link);link.click();document.body.removeChild(link);URL.revokeObjectURL(url);log("log",`CSV exported: ${filename}.csv`)}function updateTitle(){if(config.titleElementId){const titleEl=$id(config.titleElementId);if(titleEl){if(currentPeriod===0){titleEl.textContent=`Consumo - Período Personalizado`}else{titleEl.textContent=`Consumo dos últimos ${currentPeriod} dias`}}}}const instance={async render(){log("log","Rendering chart...");if(!validateChartJs())return;const canvas=validateCanvas();if(!canvas)return;try{log("log",`Fetching ${currentPeriod} days of data...`);cachedData=await config.fetchData(currentPeriod);cachedData.fetchTimestamp=Date.now();if(config.onBeforeRender){cachedData=config.onBeforeRender(cachedData)}if(chartInstance){chartInstance.destroy();chartInstance=null}const ctx=canvas.getContext("2d");const chartConfig=buildChartConfig(cachedData);chartInstance=new Chart(ctx,chartConfig);isRendered=true;const yAxisMax=calculateYAxisMax(cachedData.dailyTotals);log("log",`Chart initialized with yAxisMax: ${yAxisMax}`);config.onDataLoaded?.(cachedData);config.onAfterRender?.(chartInstance);setupButtonHandlers();updateTitle();setupAutoRefresh()}catch(error){log("error","Failed to render chart:",error);config.onError?.(error instanceof Error?error:new Error(String(error)))}},async update(data){if(data){cachedData=data;cachedData.fetchTimestamp=Date.now()}if(!chartInstance||!cachedData){log("warn","Cannot update: chart not initialized or no data");return}let renderData=cachedData;if(config.onBeforeRender){renderData=config.onBeforeRender(cachedData)}const chartConfig=buildChartConfig(renderData);chartInstance.data=chartConfig.data;chartInstance.options=chartConfig.options;chartInstance.update("none");log("log","Chart updated")},setChartType(type){if(currentChartType===type)return;log("log",`Changing chart type to: ${type}`);currentChartType=type;if(cachedData&&chartInstance){const canvas=validateCanvas();if(canvas){chartInstance.destroy();const ctx=canvas.getContext("2d");chartInstance=new Chart(ctx,buildChartConfig(cachedData))}}},setVizMode(mode){if(currentVizMode===mode)return;log("log",`Changing viz mode to: ${mode}`);currentVizMode=mode;if(cachedData){this.update()}},async setPeriod(days){if(currentPeriod===days)return;log("log",`Changing period to: ${days} days`);currentPeriod=days;updateTitle();await this.refresh(true)},async refresh(forceRefresh=false){if(!forceRefresh&&cachedData?.fetchTimestamp){const age=Date.now()-cachedData.fetchTimestamp;const ttl=config.cacheTTL??DEFAULT_CONFIG.cacheTTL;if(age<ttl){log("log",`Using cached data (age: ${Math.round(age/1e3)}s)`);return}}log("log","Refreshing data...");await this.render()},destroy(){log("log","Destroying chart...");cleanupAutoRefresh();if(chartInstance){chartInstance.destroy();chartInstance=null}cachedData=null;isRendered=false},getChartInstance(){return chartInstance},getCachedData(){return cachedData},getState(){return{period:currentPeriod,chartType:currentChartType,vizMode:currentVizMode,theme:currentTheme,isRendered:isRendered}},exportCSV(filename){if(!cachedData){log("warn","Cannot export: no data available");return}const defaultFilename=config.exportFilename||`${config.domain}-consumo-${(new Date).toISOString().slice(0,10)}`;const csvContent=generateCSVContent(cachedData);downloadCSV(csvContent,filename||defaultFilename)},setTheme(theme){if(currentTheme===theme)return;log("log",`Changing theme to: ${theme}`);currentTheme=theme;if(cachedData&&chartInstance){const canvas=validateCanvas();if(canvas){chartInstance.destroy();const ctx=canvas.getContext("2d");chartInstance=new Chart(ctx,buildChartConfig(cachedData))}}},setIdealRange(range){const rangeChanged=JSON.stringify(currentIdealRange)!==JSON.stringify(range);if(!rangeChanged)return;if(range){log("log",`Setting ideal range: ${range.min} - ${range.max}`)}else{log("log","Clearing ideal range")}currentIdealRange=range;if(cachedData&&chartInstance){const canvas=validateCanvas();if(canvas){chartInstance.destroy();const ctx=canvas.getContext("2d");chartInstance=new Chart(ctx,buildChartConfig(cachedData))}}},getIdealRange(){return currentIdealRange}};return instance}var DOMAIN_CONFIG3={energy:{name:"Energia",icon:"⚡"},water:{name:"Água",icon:"💧"},gas:{name:"Gás",icon:"🔥"},temperature:{name:"Temperatura",icon:"🌡️"}};function createConsumptionModal(config){const modalId=`myio-consumption-modal-${Date.now()}`;let modalElement=null;let chartInstance=null;let headerInstance=null;let currentTheme=config.theme??"light";let currentChartType=config.defaultChartType??"line";let currentVizMode=config.defaultVizMode??"total";let isMaximized=false;const domainCfg=DOMAIN_CONFIG3[config.domain]||{name:config.domain,icon:"📊"};const title=config.title||`${domainCfg.name} - Histórico de Consumo`;function getThemeColors3(){return THEME_COLORS[currentTheme]}const consolidadoIcon=`<svg viewBox="0 0 16 16" fill="currentColor" style="width:14px;height:14px;pointer-events:none"><rect x="3" y="3" width="10" height="10" rx="2"/></svg>`;const porShoppingIcon=`<svg viewBox="0 0 16 16" fill="currentColor" style="width:14px;height:14px;pointer-events:none"><rect x="1" y="1" width="6" height="6" rx="1"/><rect x="9" y="1" width="6" height="6" rx="1"/><rect x="1" y="9" width="6" height="6" rx="1"/><rect x="9" y="9" width="6" height="6" rx="1"/></svg>`;const lineChartIcon=`<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:14px;height:14px;pointer-events:none"><polyline points="2,12 5,7 9,9 14,3"/></svg>`;const barChartIcon=`<svg viewBox="0 0 16 16" fill="currentColor" style="width:14px;height:14px;pointer-events:none"><rect x="1" y="9" width="3" height="6" rx="0.5"/><rect x="6" y="5" width="3" height="10" rx="0.5"/><rect x="11" y="7" width="3" height="8" rx="0.5"/></svg>`;const showSettingsButton=config.showSettingsButton??true;function renderModal4(){const colors=getThemeColors3();const exportFormats=config.exportFormats||["csv"];headerInstance=createModalHeader({id:modalId,title:title,icon:domainCfg.icon,theme:currentTheme,isMaximized:isMaximized,exportFormats:exportFormats,onExport:format=>{if(config.onExport){config.onExport(format)}else{if(format==="csv"){chartInstance?.exportCSV()}else{console.warn(`[ConsumptionModal] Export format "${format}" requires custom onExport handler`)}}},onThemeToggle:theme=>{currentTheme=theme;chartInstance?.setTheme(currentTheme);updateModal()},onMaximize:maximized=>{isMaximized=maximized;updateModal()},onClose:()=>{instance.close()}});const btnBaseStyle=`\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n padding: 6px 12px;\n border: none;\n border-radius: 6px;\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s;\n white-space: nowrap;\n `.replace(/\s+/g," ").trim();const tabBgColor=currentTheme==="dark"?"#4b5563":"#e5e7eb";const activeColor="#3e1a7d";const inactiveTextColor=colors.text;return`\n <style>\n .myio-modal-tab-btn {\n ${btnBaseStyle}\n }\n .myio-modal-tab-btn:hover {\n opacity: 0.85;\n }\n .myio-modal-tab-btn.active {\n background: ${activeColor} !important;\n color: white !important;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);\n }\n .myio-modal-tab-btn svg {\n pointer-events: none;\n }\n .myio-modal-settings-btn {\n background: transparent;\n border: 1px solid ${colors.border};\n font-size: 16px;\n cursor: pointer;\n padding: 6px 10px;\n border-radius: 6px;\n transition: all 0.2s;\n color: ${colors.text};\n }\n .myio-modal-settings-btn:hover {\n background: ${activeColor};\n border-color: ${activeColor};\n color: white;\n }\n </style>\n <div class="myio-consumption-modal-overlay" style="\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background: rgba(0, 0, 0, 0.5);\n backdrop-filter: blur(2px);\n z-index: 99998;\n display: flex;\n justify-content: center;\n align-items: center;\n ">\n <div class="myio-consumption-modal-content" style="\n background: ${colors.chartBackground};\n border-radius: ${isMaximized?"0":"10px"};\n width: ${isMaximized?"100%":"95%"};\n max-width: ${isMaximized?"100%":"1200px"};\n height: ${isMaximized?"100%":"85vh"};\n display: flex;\n flex-direction: column;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n overflow: hidden;\n ">\n \x3c!-- MyIO Premium Header (using ModalHeader component) --\x3e\n ${headerInstance.render()}\n\n \x3c!-- Controls Bar --\x3e\n <div class="myio-consumption-modal-controls" style="\n display: flex;\n gap: 12px;\n padding: 12px 16px;\n background: ${currentTheme==="dark"?"#374151":"#f7f7f7"};\n border-bottom: 1px solid ${colors.border};\n align-items: center;\n flex-wrap: wrap;\n ">\n \x3c!-- Settings Button --\x3e\n ${showSettingsButton?`\n <button id="${modalId}-settings-btn" class="myio-modal-settings-btn" title="Configurações">⚙️</button>\n `:""}\n\n \x3c!-- Viz Mode Tabs --\x3e\n <div style="display: flex; gap: 2px; background: ${tabBgColor}; border-radius: 8px; padding: 3px;">\n <button id="${modalId}-viz-total" class="myio-modal-tab-btn ${currentVizMode==="total"?"active":""}"\n data-viz="total" title="Consolidado"\n style="background: ${currentVizMode==="total"?activeColor:"transparent"}; color: ${currentVizMode==="total"?"white":inactiveTextColor};">\n ${consolidadoIcon}\n </button>\n <button id="${modalId}-viz-separate" class="myio-modal-tab-btn ${currentVizMode==="separate"?"active":""}"\n data-viz="separate" title="Por Shopping"\n style="background: ${currentVizMode==="separate"?activeColor:"transparent"}; color: ${currentVizMode==="separate"?"white":inactiveTextColor};">\n ${porShoppingIcon}\n </button>\n </div>\n\n \x3c!-- Chart Type Tabs --\x3e\n <div style="display: flex; gap: 2px; background: ${tabBgColor}; border-radius: 8px; padding: 3px;">\n <button id="${modalId}-type-line" class="myio-modal-tab-btn ${currentChartType==="line"?"active":""}"\n data-type="line" title="Gráfico de Linhas"\n style="background: ${currentChartType==="line"?activeColor:"transparent"}; color: ${currentChartType==="line"?"white":inactiveTextColor};">\n ${lineChartIcon}\n </button>\n <button id="${modalId}-type-bar" class="myio-modal-tab-btn ${currentChartType==="bar"?"active":""}"\n data-type="bar" title="Gráfico de Barras"\n style="background: ${currentChartType==="bar"?activeColor:"transparent"}; color: ${currentChartType==="bar"?"white":inactiveTextColor};">\n ${barChartIcon}\n </button>\n </div>\n </div>\n\n \x3c!-- Chart Container --\x3e\n <div style="\n flex: 1;\n padding: 16px;\n min-height: 0;\n position: relative;\n background: ${colors.chartBackground};\n ">\n <canvas id="${modalId}-chart" style="width: 100%; height: 100%;"></canvas>\n </div>\n </div>\n </div>\n `}function setupListeners(){if(!modalElement)return;headerInstance?.attachListeners();if(showSettingsButton){document.getElementById(`${modalId}-settings-btn`)?.addEventListener("click",()=>{config.onSettingsClick?.()})}document.getElementById(`${modalId}-viz-total`)?.addEventListener("click",()=>{currentVizMode="total";chartInstance?.setVizMode("total");updateControlStyles()});document.getElementById(`${modalId}-viz-separate`)?.addEventListener("click",()=>{currentVizMode="separate";chartInstance?.setVizMode("separate");updateControlStyles()});document.getElementById(`${modalId}-type-line`)?.addEventListener("click",()=>{currentChartType="line";chartInstance?.setChartType("line");updateControlStyles()});document.getElementById(`${modalId}-type-bar`)?.addEventListener("click",()=>{currentChartType="bar";chartInstance?.setChartType("bar");updateControlStyles()});modalElement.querySelector(".myio-consumption-modal-overlay")?.addEventListener("click",e=>{if(e.target.classList.contains("myio-consumption-modal-overlay")){instance.close()}});const handleKeydown=e=>{if(e.key==="Escape"){instance.close()}};document.addEventListener("keydown",handleKeydown);modalElement.__handleKeydown=handleKeydown}function updateControlStyles(){const colors=getThemeColors3();const activeColor="#3e1a7d";const vizTotalBtn=document.getElementById(`${modalId}-viz-total`);const vizSeparateBtn=document.getElementById(`${modalId}-viz-separate`);if(vizTotalBtn){vizTotalBtn.classList.toggle("active",currentVizMode==="total");vizTotalBtn.style.background=currentVizMode==="total"?activeColor:"transparent";vizTotalBtn.style.color=currentVizMode==="total"?"white":colors.text}if(vizSeparateBtn){vizSeparateBtn.classList.toggle("active",currentVizMode==="separate");vizSeparateBtn.style.background=currentVizMode==="separate"?activeColor:"transparent";vizSeparateBtn.style.color=currentVizMode==="separate"?"white":colors.text}const typeLineBtn=document.getElementById(`${modalId}-type-line`);const typeBarBtn=document.getElementById(`${modalId}-type-bar`);if(typeLineBtn){typeLineBtn.classList.toggle("active",currentChartType==="line");typeLineBtn.style.background=currentChartType==="line"?activeColor:"transparent";typeLineBtn.style.color=currentChartType==="line"?"white":colors.text}if(typeBarBtn){typeBarBtn.classList.toggle("active",currentChartType==="bar");typeBarBtn.style.background=currentChartType==="bar"?activeColor:"transparent";typeBarBtn.style.color=currentChartType==="bar"?"white":colors.text}}function updateModal(){if(!modalElement)return;const cachedData=chartInstance?.getCachedData();headerInstance?.destroy();chartInstance?.destroy();modalElement.innerHTML=renderModal4();setupListeners();if(cachedData){chartInstance=createConsumption7DaysChart({...config,containerId:`${modalId}-chart`,theme:currentTheme,defaultChartType:currentChartType,defaultVizMode:currentVizMode});chartInstance.update(cachedData)}}const instance={async open(){modalElement=document.createElement("div");modalElement.id=modalId;modalElement.innerHTML=renderModal4();const container=config.container||document.body;container.appendChild(modalElement);setupListeners();chartInstance=createConsumption7DaysChart({...config,containerId:`${modalId}-chart`,theme:currentTheme,defaultChartType:currentChartType,defaultVizMode:currentVizMode});if(config.initialData){chartInstance.update(config.initialData)}else{await chartInstance.render()}},close(){if(modalElement){const handleKeydown=modalElement.__handleKeydown;if(handleKeydown){document.removeEventListener("keydown",handleKeydown)}headerInstance?.destroy();headerInstance=null;chartInstance?.destroy();chartInstance=null;modalElement.remove();modalElement=null;config.onClose?.()}},getChart(){return chartInstance},destroy(){instance.close()}};return instance}var DOMAIN_CONFIG4={energy:{name:"Energia",icon:"⚡",color:"#6c2fbf",colors:["#2563eb","#16a34a","#8b5cf6","#ea580c","#dc2626"]},water:{name:"Água",icon:"💧",color:"#0288d1",colors:["#0288d1","#06b6d4","#0891b2","#22d3ee","#67e8f9"]},gas:{name:"Gás",icon:"🔥",color:"#ea580c",colors:["#ea580c","#f97316","#fb923c","#fdba74","#fed7aa"]},temperature:{name:"Temperatura",icon:"🌡️",color:"#e65100",colors:["#dc2626","#059669","#0ea5e9","#f59e0b","#8b5cf6"]}};function getWidgetStyles(theme,primaryColor){const colors=THEME_COLORS[theme];return`\n .myio-chart-widget {\n font-family: Inter, system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif;\n background: ${colors.chartBackground};\n border: 1px solid ${colors.border};\n border-radius: 16px;\n overflow: hidden;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);\n }\n\n .myio-chart-widget.dark {\n background: ${THEME_COLORS.dark.chartBackground};\n border-color: ${THEME_COLORS.dark.border};\n }\n\n .myio-chart-widget-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 16px 20px;\n border-bottom: 1px solid ${colors.border};\n flex-wrap: wrap;\n gap: 12px;\n }\n\n .myio-chart-widget-title-group {\n display: flex;\n align-items: center;\n gap: 10px;\n }\n\n .myio-chart-widget-title {\n margin: 0;\n font-size: 14px;\n font-weight: 600;\n color: ${colors.text};\n }\n\n .myio-chart-widget-tabs {\n display: flex;\n gap: 2px;\n background: ${theme==="dark"?"#374151":"#f3f4f6"};\n padding: 3px;\n border-radius: 8px;\n }\n\n .myio-chart-widget-tab {\n padding: 6px 14px;\n font-size: 12px;\n font-weight: 500;\n border: none;\n background: transparent;\n color: ${colors.textMuted};\n cursor: pointer;\n border-radius: 6px;\n transition: all 0.2s;\n white-space: nowrap;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 4px;\n }\n\n .myio-chart-widget-tab.icon-only {\n padding: 6px 10px;\n }\n\n .myio-chart-widget-tab svg {\n width: 16px;\n height: 16px;\n pointer-events: none;\n }\n\n .myio-chart-widget-tab:hover {\n color: ${colors.text};\n background: ${theme==="dark"?"rgba(255,255,255,0.1)":"rgba(0,0,0,0.05)"};\n }\n\n .myio-chart-widget-tab.active {\n background: ${primaryColor};\n color: white;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);\n }\n\n .myio-chart-widget-btn {\n background: transparent;\n border: 1px solid ${colors.border};\n font-size: 16px;\n cursor: pointer;\n padding: 6px 10px;\n border-radius: 6px;\n transition: all 0.2s;\n color: ${colors.text};\n }\n\n .myio-chart-widget-btn:hover {\n background: ${primaryColor};\n border-color: ${primaryColor};\n color: white;\n }\n\n .myio-chart-widget-body {\n position: relative;\n padding: 16px 20px;\n }\n\n .myio-chart-widget-canvas-container {\n position: relative;\n width: 100%;\n }\n\n .myio-chart-widget-loading {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(255, 255, 255, 0.9);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10;\n border-radius: 8px;\n }\n\n .myio-chart-widget.dark .myio-chart-widget-loading {\n background: rgba(31, 41, 55, 0.9);\n }\n\n .myio-chart-widget-spinner {\n width: 32px;\n height: 32px;\n border: 3px solid ${colors.border};\n border-top-color: ${primaryColor};\n border-radius: 50%;\n animation: myio-spin 1s linear infinite;\n }\n\n @keyframes myio-spin {\n to { transform: rotate(360deg); }\n }\n\n .myio-chart-widget-footer {\n display: flex;\n justify-content: space-around;\n padding: 16px 20px;\n border-top: 1px solid ${colors.border};\n gap: 16px;\n flex-wrap: wrap;\n }\n\n .myio-chart-widget-stat {\n display: flex;\n flex-direction: column;\n align-items: center;\n text-align: center;\n min-width: 100px;\n }\n\n .myio-chart-widget-stat-label {\n font-size: 11px;\n font-weight: 500;\n color: ${colors.textMuted};\n text-transform: uppercase;\n letter-spacing: 0.5px;\n margin-bottom: 4px;\n }\n\n .myio-chart-widget-stat-value {\n font-size: 20px;\n font-weight: 700;\n color: ${colors.text};\n }\n\n .myio-chart-widget-stat-value.primary {\n color: ${primaryColor};\n }\n\n .myio-chart-widget-stat-sub {\n font-size: 11px;\n color: ${colors.textMuted};\n margin-top: 2px;\n }\n\n /* Settings Modal Overlay */\n .myio-settings-overlay {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.6);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 99999;\n backdrop-filter: blur(4px);\n }\n\n .myio-settings-overlay.hidden {\n display: none;\n }\n\n .myio-settings-card {\n background: ${colors.chartBackground};\n border-radius: 10px;\n width: 90%;\n max-width: 860px;\n max-height: 90vh;\n display: flex;\n flex-direction: column;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);\n overflow: hidden;\n }\n\n .myio-settings-card .myio-modal-header {\n border-radius: 10px 10px 0 0;\n }\n\n .myio-settings-body {\n padding: 20px;\n overflow-y: auto;\n display: flex;\n flex-direction: column;\n gap: 12px;\n }\n\n .myio-settings-section {\n background: ${theme==="dark"?"rgba(255,255,255,0.05)":"#f8fafc"};\n border-radius: 10px;\n padding: 16px;\n border: 1px solid ${theme==="dark"?"rgba(255,255,255,0.1)":"#e2e8f0"};\n }\n\n .myio-settings-section-label {\n font-size: 13px;\n font-weight: 600;\n color: ${colors.text};\n margin-bottom: 12px;\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .myio-settings-row {\n display: flex;\n gap: 16px;\n flex-wrap: wrap;\n align-items: flex-end;\n }\n\n .myio-settings-field {\n display: flex;\n flex-direction: column;\n gap: 6px;\n flex: 1;\n min-width: 120px;\n }\n\n .myio-settings-field-label {\n font-size: 12px;\n font-weight: 500;\n color: ${colors.textMuted};\n }\n\n .myio-settings-input,\n .myio-settings-select {\n padding: 8px 12px;\n border: 1px solid ${colors.border};\n border-radius: 8px;\n font-size: 12px;\n background: ${colors.chartBackground};\n color: ${colors.text};\n width: 100%;\n }\n\n .myio-settings-input:focus,\n .myio-settings-select:focus {\n outline: 2px solid ${primaryColor};\n outline-offset: 1px;\n }\n\n .myio-settings-tabs {\n display: flex;\n gap: 2px;\n background: ${theme==="dark"?"#374151":"#e5e7eb"};\n padding: 3px;\n border-radius: 8px;\n }\n\n .myio-settings-tab {\n flex: 1;\n padding: 8px 12px;\n font-size: 12px;\n font-weight: 500;\n border: none;\n background: transparent;\n color: ${colors.textMuted};\n cursor: pointer;\n border-radius: 6px;\n transition: all 0.2s;\n white-space: nowrap;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n }\n\n .myio-settings-tab:hover {\n color: ${colors.text};\n background: ${theme==="dark"?"rgba(255,255,255,0.1)":"rgba(0,0,0,0.05)"};\n }\n\n .myio-settings-tab.active {\n background: ${primaryColor};\n color: white;\n }\n\n .myio-settings-footer {\n display: flex;\n gap: 12px;\n justify-content: flex-end;\n padding: 16px 20px;\n border-top: 1px solid ${colors.border};\n background: ${theme==="dark"?"rgba(0,0,0,0.2)":"#fafafa"};\n }\n\n .myio-settings-btn {\n padding: 10px 20px;\n border-radius: 8px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s;\n }\n\n .myio-settings-btn-secondary {\n background: transparent;\n border: 1px solid ${colors.border};\n color: ${colors.text};\n }\n\n .myio-settings-btn-secondary:hover {\n background: ${theme==="dark"?"rgba(255,255,255,0.1)":"#f3f4f6"};\n }\n\n .myio-settings-btn-primary {\n background: ${primaryColor};\n border: none;\n color: white;\n }\n\n .myio-settings-btn-primary:hover {\n filter: brightness(1.1);\n }\n\n .myio-settings-hint {\n font-size: 11px;\n color: ${colors.textMuted};\n font-weight: normal;\n }\n\n .myio-settings-context-group {\n margin-bottom: 12px;\n }\n\n .myio-settings-context-group:last-child {\n margin-bottom: 0;\n }\n\n .myio-settings-section + .myio-settings-section {\n margin-top: 12px;\n }\n\n /* Dropdown styles */\n .myio-settings-dropdown-container {\n position: relative;\n }\n\n .myio-settings-dropdown-btn {\n padding: 10px 14px;\n border: 1px solid ${colors.border};\n border-radius: 8px;\n font-size: 14px;\n background: ${colors.chartBackground};\n color: ${colors.text};\n cursor: pointer;\n min-width: 180px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n width: 100%;\n }\n\n .myio-settings-dropdown-btn:hover {\n border-color: ${primaryColor};\n }\n\n .myio-settings-dropdown-arrow {\n font-size: 10px;\n color: ${colors.textMuted};\n }\n\n .myio-settings-dropdown {\n position: absolute;\n top: calc(100% + 4px);\n left: 0;\n z-index: 100001;\n background: ${colors.chartBackground};\n border: 1px solid ${colors.border};\n border-radius: 8px;\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);\n min-width: 220px;\n padding: 8px 0;\n }\n\n .myio-settings-dropdown.hidden {\n display: none;\n }\n\n .myio-settings-dropdown-option {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 14px;\n cursor: pointer;\n font-size: 13px;\n color: ${colors.text};\n transition: background 0.15s;\n }\n\n .myio-settings-dropdown-option:hover {\n background: ${theme==="dark"?"rgba(255,255,255,0.1)":"#f3f4f6"};\n }\n\n .myio-settings-dropdown-option input {\n width: 16px;\n height: 16px;\n cursor: pointer;\n accent-color: ${primaryColor};\n }\n\n .myio-settings-dropdown-actions {\n border-top: 1px solid ${colors.border};\n margin-top: 8px;\n padding: 8px;\n display: flex;\n flex-direction: column;\n gap: 6px;\n }\n\n .myio-settings-dropdown-actions button {\n width: 100%;\n padding: 8px;\n background: ${theme==="dark"?"rgba(255,255,255,0.1)":"#f3f4f6"};\n border: none;\n border-radius: 6px;\n cursor: pointer;\n font-size: 12px;\n color: ${colors.text};\n transition: background 0.15s;\n }\n\n .myio-settings-dropdown-actions button:hover {\n background: ${theme==="dark"?"rgba(255,255,255,0.15)":"#e5e7eb"};\n }\n\n /* Suggestion icon styles */\n .myio-settings-section-label span[id$="-settings-suggestion"] {\n transition: opacity 0.2s, transform 0.2s;\n }\n\n .myio-settings-section-label span[id$="-settings-suggestion"]:hover {\n opacity: 1 !important;\n transform: scale(1.2);\n }\n\n /* DateRangePicker styles (CSS tokens + premium styling) */\n ${CSS_TOKENS}\n ${DATERANGEPICKER_STYLES}\n\n /* Fix DateRangePicker buttons alignment */\n .myio-modal-scope .daterangepicker .drp-buttons {\n display: flex;\n justify-content: flex-end;\n align-items: center;\n gap: 8px;\n }\n\n .myio-modal-scope .daterangepicker .drp-buttons .btn {\n display: inline-block;\n margin-left: 0;\n }\n `}function createConsumptionChartWidget(config){const widgetId=`myio-widget-${Date.now()}-${Math.random().toString(36).substr(2,9)}`;let containerElement=null;let chartInstance=null;let styleElement=null;let settingsModalElement=null;let settingsHeaderInstance=null;let dateRangePickerInstance=null;let currentTheme=config.theme??"light";let currentChartType=config.defaultChartType??"line";let currentVizMode=config.defaultVizMode??"total";let currentPeriod=config.defaultPeriod??7;let currentIdealRange=config.idealRange??null;let tempPeriod=currentPeriod;let tempChartType=currentChartType;let tempVizMode=currentVizMode;let tempTheme=currentTheme;let tempIdealRange=currentIdealRange;let tempStartDate=null;let tempEndDate=null;let currentSuggestion=null;const domainCfg=DOMAIN_CONFIG4[config.domain]||DOMAIN_CONFIG4.energy;const primaryColor=config.colors?.primary||domainCfg.color;const domainColors=config.colors?.shoppingColors||domainCfg.colors;const showSettingsButton=config.showSettingsButton??true;const showMaximizeButton=config.showMaximizeButton??true;const showVizModeTabs=config.showVizModeTabs??true;const showChartTypeTabs=config.showChartTypeTabs??true;const chartHeight=typeof config.chartHeight==="number"?`${config.chartHeight}px`:config.chartHeight??"300px";function getTitle(){if(config.title)return config.title;const domainName=config.domain==="temperature"?"Temperatura":"Consumo";return`${domainName} dos últimos ${currentPeriod} dias`}function renderHTML(){const consolidadoIcon=`<svg viewBox="0 0 16 16" fill="currentColor"><rect x="3" y="3" width="10" height="10" rx="2"/></svg>`;const porShoppingIcon=`<svg viewBox="0 0 16 16" fill="currentColor"><rect x="1" y="1" width="6" height="6" rx="1"/><rect x="9" y="1" width="6" height="6" rx="1"/><rect x="1" y="9" width="6" height="6" rx="1"/><rect x="9" y="9" width="6" height="6" rx="1"/></svg>`;const lineChartIcon=`<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="2,12 5,7 9,9 14,3"/></svg>`;const barChartIcon=`<svg viewBox="0 0 16 16" fill="currentColor"><rect x="1" y="9" width="3" height="6" rx="0.5"/><rect x="6" y="5" width="3" height="10" rx="0.5"/><rect x="11" y="7" width="3" height="8" rx="0.5"/></svg>`;return`\n <div id="${widgetId}" class="myio-chart-widget ${currentTheme==="dark"?"dark":""} ${config.className||""}">\n <div class="myio-chart-widget-header">\n <div class="myio-chart-widget-title-group">\n ${showSettingsButton?`\n <button id="${widgetId}-settings-btn" class="myio-chart-widget-btn" title="Configurações">⚙️</button>\n `:""}\n <h4 id="${widgetId}-title" class="myio-chart-widget-title">${getTitle()}</h4>\n ${showVizModeTabs?`\n <div class="myio-chart-widget-tabs" id="${widgetId}-viz-tabs">\n <button class="myio-chart-widget-tab icon-only ${currentVizMode==="total"?"active":""}" data-viz="total" title="Consolidado">${consolidadoIcon}</button>\n <button class="myio-chart-widget-tab icon-only ${currentVizMode==="separate"?"active":""}" data-viz="separate" title="Por Shopping">${porShoppingIcon}</button>\n </div>\n `:""}\n ${showChartTypeTabs?`\n <div class="myio-chart-widget-tabs" id="${widgetId}-type-tabs">\n <button class="myio-chart-widget-tab icon-only ${currentChartType==="line"?"active":""}" data-type="line" title="Gráfico de Linhas">${lineChartIcon}</button>\n <button class="myio-chart-widget-tab icon-only ${currentChartType==="bar"?"active":""}" data-type="bar" title="Gráfico de Barras">${barChartIcon}</button>\n </div>\n `:""}\n ${showMaximizeButton?`\n <button id="${widgetId}-maximize-btn" class="myio-chart-widget-btn" title="Maximizar">⛶</button>\n `:""}\n </div>\n </div>\n <div class="myio-chart-widget-body">\n <div id="${widgetId}-loading" class="myio-chart-widget-loading" style="display: none;">\n <div class="myio-chart-widget-spinner"></div>\n </div>\n <div class="myio-chart-widget-canvas-container" style="height: ${chartHeight};">\n <canvas id="${widgetId}-canvas"></canvas>\n </div>\n </div>\n <div class="myio-chart-widget-footer" id="${widgetId}-footer">\n <div class="myio-chart-widget-stat">\n <span class="myio-chart-widget-stat-label">Total Período</span>\n <span id="${widgetId}-stat-total" class="myio-chart-widget-stat-value primary">--</span>\n </div>\n <div class="myio-chart-widget-stat">\n <span class="myio-chart-widget-stat-label">Média Diária</span>\n <span id="${widgetId}-stat-avg" class="myio-chart-widget-stat-value">--</span>\n </div>\n <div class="myio-chart-widget-stat">\n <span class="myio-chart-widget-stat-label">Dia de Pico</span>\n <span id="${widgetId}-stat-peak" class="myio-chart-widget-stat-value">--</span>\n <span id="${widgetId}-stat-peak-date" class="myio-chart-widget-stat-sub"></span>\n </div>\n </div>\n </div>\n `}function renderSettingsModal(){const unit=config.unit??"";const isTemperature=config.domain==="temperature";const consolidadoIcon=`<svg viewBox="0 0 16 16" fill="currentColor" style="width:14px;height:14px;pointer-events:none"><rect x="3" y="3" width="10" height="10" rx="2"/></svg>`;const porShoppingIcon=`<svg viewBox="0 0 16 16" fill="currentColor" style="width:14px;height:14px;pointer-events:none"><rect x="1" y="1" width="6" height="6" rx="1"/><rect x="9" y="1" width="6" height="6" rx="1"/><rect x="1" y="9" width="6" height="6" rx="1"/><rect x="9" y="9" width="6" height="6" rx="1"/></svg>`;const lineChartIcon=`<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:14px;height:14px;pointer-events:none"><polyline points="2,12 5,7 9,9 14,3"/></svg>`;const barChartIcon=`<svg viewBox="0 0 16 16" fill="currentColor" style="width:14px;height:14px;pointer-events:none"><rect x="1" y="9" width="3" height="6" rx="0.5"/><rect x="6" y="5" width="3" height="10" rx="0.5"/><rect x="11" y="7" width="3" height="8" rx="0.5"/></svg>`;settingsHeaderInstance=createModalHeader({id:`${widgetId}-settings`,title:"Configurações",icon:"⚙️",theme:tempTheme,showThemeToggle:false,showMaximize:false,showClose:true,onClose:()=>closeSettingsModal()});return`\n <div id="${widgetId}-settings-overlay" class="myio-settings-overlay myio-modal-scope hidden">\n <div class="myio-settings-card">\n ${settingsHeaderInstance.render()}\n <div class="myio-settings-body">\n \x3c!-- CONTEXT 1: Período e Dados --\x3e\n <div class="myio-settings-context-group">\n \x3c!-- Período --\x3e\n <div class="myio-settings-section">\n <div class="myio-settings-section-label">📅 Período</div>\n <div class="myio-settings-row">\n <div class="myio-settings-field" style="flex: 1;">\n <input type="text" id="${widgetId}-settings-daterange" class="myio-settings-input"\n readonly placeholder="Selecione o período..." style="cursor: pointer;">\n </div>\n </div>\n </div>\n\n \x3c!-- Dados --\x3e\n <div class="myio-settings-section">\n <div class="myio-settings-section-label">📊 Dados</div>\n <div class="myio-settings-row">\n \x3c!-- Granularity Select --\x3e\n <div class="myio-settings-field">\n <label class="myio-settings-field-label">Granularidade</label>\n <select id="${widgetId}-settings-granularity" class="myio-settings-select">\n <option value="1d" selected>📆 Por Dia</option>\n <option value="1h">🕐 Por Hora</option>\n </select>\n </div>\n\n \x3c!-- Weekday Filter --\x3e\n <div class="myio-settings-field">\n <label class="myio-settings-field-label">Dias da Semana</label>\n <div class="myio-settings-dropdown-container">\n <button type="button" id="${widgetId}-settings-weekday-btn" class="myio-settings-dropdown-btn">\n <span id="${widgetId}-settings-weekday-label">Todos os dias</span>\n <span class="myio-settings-dropdown-arrow">▼</span>\n </button>\n <div id="${widgetId}-settings-weekday-dropdown" class="myio-settings-dropdown hidden">\n <label class="myio-settings-dropdown-option">\n <input type="checkbox" name="${widgetId}-weekday" value="dom" checked /> Domingo\n </label>\n <label class="myio-settings-dropdown-option">\n <input type="checkbox" name="${widgetId}-weekday" value="seg" checked /> Segunda-feira\n </label>\n <label class="myio-settings-dropdown-option">\n <input type="checkbox" name="${widgetId}-weekday" value="ter" checked /> Terça-feira\n </label>\n <label class="myio-settings-dropdown-option">\n <input type="checkbox" name="${widgetId}-weekday" value="qua" checked /> Quarta-feira\n </label>\n <label class="myio-settings-dropdown-option">\n <input type="checkbox" name="${widgetId}-weekday" value="qui" checked /> Quinta-feira\n </label>\n <label class="myio-settings-dropdown-option">\n <input type="checkbox" name="${widgetId}-weekday" value="sex" checked /> Sexta-feira\n </label>\n <label class="myio-settings-dropdown-option">\n <input type="checkbox" name="${widgetId}-weekday" value="sab" checked /> Sábado\n </label>\n <div class="myio-settings-dropdown-actions">\n <button type="button" id="${widgetId}-settings-weekday-all">Selecionar Todos</button>\n <button type="button" id="${widgetId}-settings-weekday-clear">Limpar</button>\n </div>\n </div>\n </div>\n </div>\n\n \x3c!-- Day Period Filter (only visible when hourly) --\x3e\n <div class="myio-settings-field" id="${widgetId}-settings-dayperiod-field" style="display: none;">\n <label class="myio-settings-field-label">Períodos do Dia</label>\n <div class="myio-settings-dropdown-container">\n <button type="button" id="${widgetId}-settings-dayperiod-btn" class="myio-settings-dropdown-btn">\n <span id="${widgetId}-settings-dayperiod-label">Todos os períodos</span>\n <span class="myio-settings-dropdown-arrow">▼</span>\n </button>\n <div id="${widgetId}-settings-dayperiod-dropdown" class="myio-settings-dropdown hidden">\n <label class="myio-settings-dropdown-option">\n <input type="checkbox" name="${widgetId}-dayperiod" value="madrugada" checked /> Madrugada (00h-06h)\n </label>\n <label class="myio-settings-dropdown-option">\n <input type="checkbox" name="${widgetId}-dayperiod" value="manha" checked /> Manhã (06h-12h)\n </label>\n <label class="myio-settings-dropdown-option">\n <input type="checkbox" name="${widgetId}-dayperiod" value="tarde" checked /> Tarde (12h-18h)\n </label>\n <label class="myio-settings-dropdown-option">\n <input type="checkbox" name="${widgetId}-dayperiod" value="noite" checked /> Noite (18h-24h)\n </label>\n <div class="myio-settings-dropdown-actions">\n <button type="button" id="${widgetId}-settings-dayperiod-all">Selecionar Todos</button>\n <button type="button" id="${widgetId}-settings-dayperiod-clear">Limpar</button>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n \x3c!-- CONTEXT 2: Faixa Ideal --\x3e\n <div class="myio-settings-context-group">\n <div class="myio-settings-section">\n <div class="myio-settings-section-label">\n 🎯 Faixa Ideal\n <span class="myio-settings-hint" id="${widgetId}-settings-range-hint">(opcional - deixe zerado para não exibir)</span>\n <span\n id="${widgetId}-settings-suggestion"\n title=""\n style="cursor: pointer; font-size: 16px; opacity: 0.7; transition: opacity 0.2s; margin-left: 4px;"\n >💡</span>\n </div>\n <div class="myio-settings-row">\n <div class="myio-settings-field" style="min-width: 100px;">\n <label class="myio-settings-field-label">Mínimo (${unit})</label>\n <input type="number" id="${widgetId}-settings-range-min" class="myio-settings-input"\n value="${tempIdealRange?.min??""}" placeholder="0" step="0.1">\n </div>\n <div class="myio-settings-field" style="min-width: 100px;">\n <label class="myio-settings-field-label">Máximo (${unit})</label>\n <input type="number" id="${widgetId}-settings-range-max" class="myio-settings-input"\n value="${tempIdealRange?.max??""}" placeholder="0" step="0.1">\n </div>\n <div class="myio-settings-field" style="flex: 1;">\n <label class="myio-settings-field-label">Rótulo</label>\n <input type="text" id="${widgetId}-settings-range-label" class="myio-settings-input"\n value="${tempIdealRange?.label??""}" placeholder="${isTemperature?"Faixa Ideal":"Meta de Consumo"}">\n </div>\n </div>\n </div>\n </div>\n\n \x3c!-- CONTEXT 3: Visualização --\x3e\n <div class="myio-settings-context-group">\n <div class="myio-settings-section">\n <div class="myio-settings-section-label">🎨 Visualização</div>\n <div class="myio-settings-row" style="gap: 20px; flex-wrap: wrap;">\n \x3c!-- Chart Type --\x3e\n <div class="myio-settings-field" style="flex: 1; min-width: 180px;">\n <label class="myio-settings-field-label">Tipo de Gráfico</label>\n <div class="myio-settings-tabs" id="${widgetId}-settings-chart-type">\n <button class="myio-settings-tab ${tempChartType==="line"?"active":""}" data-type="line">${lineChartIcon} Linhas</button>\n <button class="myio-settings-tab ${tempChartType==="bar"?"active":""}" data-type="bar">${barChartIcon} Barras</button>\n </div>\n </div>\n\n \x3c!-- Viz Mode --\x3e\n <div class="myio-settings-field" style="flex: 1; min-width: 200px;">\n <label class="myio-settings-field-label">Agrupamento</label>\n <div class="myio-settings-tabs" id="${widgetId}-settings-viz-mode">\n <button class="myio-settings-tab ${tempVizMode==="total"?"active":""}" data-viz="total">${consolidadoIcon} Consolidado</button>\n <button class="myio-settings-tab ${tempVizMode==="separate"?"active":""}" data-viz="separate">${porShoppingIcon} Por Shopping</button>\n </div>\n </div>\n\n \x3c!-- Theme --\x3e\n <div class="myio-settings-field" style="flex: 1; min-width: 160px;">\n <label class="myio-settings-field-label">Tema</label>\n <div class="myio-settings-tabs" id="${widgetId}-settings-theme">\n <button class="myio-settings-tab ${tempTheme==="light"?"active":""}" data-theme="light">☀️ Light</button>\n <button class="myio-settings-tab ${tempTheme==="dark"?"active":""}" data-theme="dark">🌙 Dark</button>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n <div class="myio-settings-footer">\n <button id="${widgetId}-settings-reset" class="myio-settings-btn myio-settings-btn-secondary">Resetar</button>\n <button id="${widgetId}-settings-apply" class="myio-settings-btn myio-settings-btn-primary">Carregar</button>\n </div>\n </div>\n </div>\n `}function injectStyles(){if(styleElement)return;styleElement=document.createElement("style");styleElement.id=`${widgetId}-styles`;styleElement.textContent=getWidgetStyles(currentTheme,primaryColor);document.head.appendChild(styleElement)}function updateStyles(){if(styleElement){styleElement.textContent=getWidgetStyles(currentTheme,primaryColor)}}function setupListeners(){if(showSettingsButton){document.getElementById(`${widgetId}-settings-btn`)?.addEventListener("click",()=>{openSettingsModal();config.onSettingsClick?.()})}if(showMaximizeButton&&config.onMaximizeClick){document.getElementById(`${widgetId}-maximize-btn`)?.addEventListener("click",()=>{config.onMaximizeClick?.()})}if(showVizModeTabs){document.getElementById(`${widgetId}-viz-tabs`)?.addEventListener("click",e=>{const target=e.target;if(target.classList.contains("myio-chart-widget-tab")){const mode=target.dataset.viz;if(mode){instance.setVizMode(mode)}}})}if(showChartTypeTabs){document.getElementById(`${widgetId}-type-tabs`)?.addEventListener("click",e=>{const target=e.target;if(target.classList.contains("myio-chart-widget-tab")){const type=target.dataset.type;if(type){instance.setChartType(type)}}})}}function updateTabStates(){document.querySelectorAll(`#${widgetId}-viz-tabs .myio-chart-widget-tab`).forEach(tab=>{const btn=tab;btn.classList.toggle("active",btn.dataset.viz===currentVizMode)});document.querySelectorAll(`#${widgetId}-type-tabs .myio-chart-widget-tab`).forEach(tab=>{const btn=tab;btn.classList.toggle("active",btn.dataset.type===currentChartType)})}function updateTitle(){const titleEl=document.getElementById(`${widgetId}-title`);if(titleEl){titleEl.textContent=getTitle()}}function setLoading(loading){const loadingEl=document.getElementById(`${widgetId}-loading`);if(loadingEl){loadingEl.style.display=loading?"flex":"none"}}function formatValue(value){const unit=config.unit??"";const unitLarge=config.unitLarge;const threshold=config.thresholdForLargeUnit??1e3;if(unitLarge&&Math.abs(value)>=threshold){return`${(value/threshold).toFixed(2)} ${unitLarge}`}return`${value.toFixed(2)} ${unit}`}function updateFooterStats(data){const totalEl=document.getElementById(`${widgetId}-stat-total`);const avgEl=document.getElementById(`${widgetId}-stat-avg`);const peakEl=document.getElementById(`${widgetId}-stat-peak`);const peakDateEl=document.getElementById(`${widgetId}-stat-peak-date`);if(!data.dailyTotals||data.dailyTotals.length===0){if(totalEl)totalEl.textContent="--";if(avgEl)avgEl.textContent="--";if(peakEl)peakEl.textContent="--";if(peakDateEl)peakDateEl.textContent="";return}const isTemperature=config.domain==="temperature";const totals=data.dailyTotals;const labels=data.labels??[];const total=totals.reduce((a,b)=>a+b,0);const avg=total/totals.length;const peakValue=Math.max(...totals);const peakIndex=totals.indexOf(peakValue);const peakDate=labels[peakIndex]??"";if(totalEl){if(isTemperature){totalEl.textContent=formatValue(avg);const labelEl=totalEl.previousElementSibling;if(labelEl)labelEl.textContent="Média Período"}else{totalEl.textContent=formatValue(total)}}if(avgEl){avgEl.textContent=formatValue(avg)}if(peakEl){peakEl.textContent=formatValue(peakValue)}if(peakDateEl){peakDateEl.textContent=peakDate}}async function openSettingsModal(){tempPeriod=currentPeriod;tempChartType=currentChartType;tempVizMode=currentVizMode;tempTheme=currentTheme;tempIdealRange=currentIdealRange?{...currentIdealRange}:null;dateRangePickerInstance=null;if(!settingsModalElement){settingsModalElement=document.createElement("div");settingsModalElement.innerHTML=renderSettingsModal();document.body.appendChild(settingsModalElement.firstElementChild);settingsModalElement=document.getElementById(`${widgetId}-settings-overlay`);await setupSettingsModalListeners()}else{await setupSettingsModalListeners()}updateSettingsModalValues();updateIdealRangeSuggestionTooltip();settingsModalElement?.classList.remove("hidden")}function closeSettingsModal(){settingsModalElement?.classList.add("hidden");if(dateRangePickerInstance){dateRangePickerInstance.destroy();dateRangePickerInstance=null}}function updateSettingsModalValues(){const periodSelect=document.getElementById(`${widgetId}-settings-period`);if(periodSelect)periodSelect.value=String(tempPeriod);const minInput=document.getElementById(`${widgetId}-settings-range-min`);const maxInput=document.getElementById(`${widgetId}-settings-range-max`);const labelInput=document.getElementById(`${widgetId}-settings-range-label`);if(minInput)minInput.value=tempIdealRange?.min?.toString()??"";if(maxInput)maxInput.value=tempIdealRange?.max?.toString()??"";if(labelInput)labelInput.value=tempIdealRange?.label??"";updateSettingsModalTabs()}function updateSettingsModalTabs(){document.querySelectorAll(`#${widgetId}-settings-chart-type .myio-settings-tab`).forEach(tab=>{const btn=tab;btn.classList.toggle("active",btn.dataset.type===tempChartType)});document.querySelectorAll(`#${widgetId}-settings-viz-mode .myio-settings-tab`).forEach(tab=>{const btn=tab;btn.classList.toggle("active",btn.dataset.viz===tempVizMode)});document.querySelectorAll(`#${widgetId}-settings-theme .myio-settings-tab`).forEach(tab=>{const btn=tab;btn.classList.toggle("active",btn.dataset.theme===tempTheme)})}function updateWeekdayLabel(){const checkboxes=document.querySelectorAll(`input[name="${widgetId}-weekday"]`);const checked=Array.from(checkboxes).filter(cb=>cb.checked);const label=document.getElementById(`${widgetId}-settings-weekday-label`);if(label){if(checked.length===0){label.textContent="Nenhum dia"}else if(checked.length===checkboxes.length){label.textContent="Todos os dias"}else{label.textContent=`${checked.length} dias selecionados`}}}function updateDayPeriodLabel(){const checkboxes=document.querySelectorAll(`input[name="${widgetId}-dayperiod"]`);const checked=Array.from(checkboxes).filter(cb=>cb.checked);const label=document.getElementById(`${widgetId}-settings-dayperiod-label`);if(label){if(checked.length===0){label.textContent="Nenhum período"}else if(checked.length===checkboxes.length){label.textContent="Todos os períodos"}else{label.textContent=`${checked.length} períodos selecionados`}}}function calculateIdealRangeSuggestion(){const data=chartInstance?.getCachedData();if(!data||!data.dailyTotals||data.dailyTotals.length===0){return{min:0,max:0,avg:0}}const total=data.dailyTotals.reduce((a,b)=>a+b,0);const avg=total/data.dailyTotals.length;const min=avg*.85;const max=avg*1.15;return{min:Math.round(min*10)/10,max:Math.round(max*10)/10,avg:Math.round(avg*10)/10}}function updateIdealRangeSuggestionTooltip(){const suggestion=calculateIdealRangeSuggestion();const suggestionEl=document.getElementById(`${widgetId}-settings-suggestion`);const hintEl=document.getElementById(`${widgetId}-settings-range-hint`);const unit=config.unit??"";const isTemperature=config.domain==="temperature";currentSuggestion=suggestion;if(hintEl){if(isTemperature){hintEl.textContent="(valores carregados do cliente)"}else{hintEl.textContent="(opcional - deixe zerado para não exibir)"}}if(suggestionEl){if(suggestion.avg>0){const tooltipText=`Sugestão: ${suggestion.min} - ${suggestion.max} ${unit} (média ±15%). Clique para aplicar.`;suggestionEl.title=tooltipText;suggestionEl.style.display="inline"}else{suggestionEl.style.display="none"}}}function applyIdealRangeSuggestion(){if(!currentSuggestion||currentSuggestion.min===0&¤tSuggestion.max===0){return}const minInput=document.getElementById(`${widgetId}-settings-range-min`);const maxInput=document.getElementById(`${widgetId}-settings-range-max`);const labelInput=document.getElementById(`${widgetId}-settings-range-label`);if(minInput)minInput.value=String(currentSuggestion.min);if(maxInput)maxInput.value=String(currentSuggestion.max);if(labelInput)labelInput.value="Faixa Sugerida";tempIdealRange={min:currentSuggestion.min,max:currentSuggestion.max,label:"Faixa Sugerida"}}async function setupSettingsModalListeners(){settingsHeaderInstance?.attachListeners();const dateRangeInput=document.getElementById(`${widgetId}-settings-daterange`);if(dateRangeInput&&!dateRangePickerInstance){try{const endDate=new Date;const startDate=new Date;startDate.setDate(endDate.getDate()-tempPeriod);const presetStart=tempStartDate||startDate.toISOString();const presetEnd=tempEndDate||endDate.toISOString();dateRangePickerInstance=await createDateRangePicker2(dateRangeInput,{presetStart:presetStart,presetEnd:presetEnd,includeTime:true,timePrecision:"hour",maxRangeDays:90,locale:"pt-BR",parentEl:document.getElementById(`${widgetId}-settings-overlay`),onApply:result=>{tempStartDate=result.startISO;tempEndDate=result.endISO;const start=new Date(result.startISO);const end=new Date(result.endISO);const diffTime=Math.abs(end.getTime()-start.getTime());const diffHours=diffTime/(1e3*60*60);const diffDays=Math.ceil(diffTime/(1e3*60*60*24));tempPeriod=diffDays||7;const granularitySelect=document.getElementById(`${widgetId}-settings-granularity`);if(granularitySelect&&diffHours<=48){granularitySelect.value="1h";const dayPeriodField=document.getElementById(`${widgetId}-settings-dayperiod-field`);if(dayPeriodField)dayPeriodField.style.display="block"}console.log("[ConsumptionChartWidget] Date range applied:",{start:result.startISO,end:result.endISO,hours:diffHours,days:tempPeriod})}})}catch(error){console.warn("[ConsumptionChartWidget] DateRangePicker initialization failed:",error)}}document.getElementById(`${widgetId}-settings-overlay`)?.addEventListener("click",e=>{if(e.target.classList.contains("myio-settings-overlay")){closeSettingsModal()}});document.getElementById(`${widgetId}-settings-granularity`)?.addEventListener("change",e=>{const select=e.target;const dayPeriodField=document.getElementById(`${widgetId}-settings-dayperiod-field`);if(dayPeriodField){dayPeriodField.style.display=select.value==="1h"?"block":"none"}});document.getElementById(`${widgetId}-settings-suggestion`)?.addEventListener("click",()=>{applyIdealRangeSuggestion()});document.getElementById(`${widgetId}-settings-weekday-btn`)?.addEventListener("click",e=>{e.stopPropagation();const dropdown=document.getElementById(`${widgetId}-settings-weekday-dropdown`);dropdown?.classList.toggle("hidden")});document.querySelectorAll(`input[name="${widgetId}-weekday"]`).forEach(cb=>{cb.addEventListener("change",updateWeekdayLabel)});document.getElementById(`${widgetId}-settings-weekday-all`)?.addEventListener("click",()=>{document.querySelectorAll(`input[name="${widgetId}-weekday"]`).forEach(cb=>{cb.checked=true});updateWeekdayLabel()});document.getElementById(`${widgetId}-settings-weekday-clear`)?.addEventListener("click",()=>{document.querySelectorAll(`input[name="${widgetId}-weekday"]`).forEach(cb=>{cb.checked=false});updateWeekdayLabel()});document.getElementById(`${widgetId}-settings-dayperiod-btn`)?.addEventListener("click",e=>{e.stopPropagation();const dropdown=document.getElementById(`${widgetId}-settings-dayperiod-dropdown`);dropdown?.classList.toggle("hidden")});document.querySelectorAll(`input[name="${widgetId}-dayperiod"]`).forEach(cb=>{cb.addEventListener("change",updateDayPeriodLabel)});document.getElementById(`${widgetId}-settings-dayperiod-all`)?.addEventListener("click",()=>{document.querySelectorAll(`input[name="${widgetId}-dayperiod"]`).forEach(cb=>{cb.checked=true});updateDayPeriodLabel()});document.getElementById(`${widgetId}-settings-dayperiod-clear`)?.addEventListener("click",()=>{document.querySelectorAll(`input[name="${widgetId}-dayperiod"]`).forEach(cb=>{cb.checked=false});updateDayPeriodLabel()});document.addEventListener("click",e=>{const target=e.target;const weekdayDropdown=document.getElementById(`${widgetId}-settings-weekday-dropdown`);const dayperiodDropdown=document.getElementById(`${widgetId}-settings-dayperiod-dropdown`);if(weekdayDropdown&&!target.closest(`#${widgetId}-settings-weekday-btn`)&&!target.closest(`#${widgetId}-settings-weekday-dropdown`)){weekdayDropdown.classList.add("hidden")}if(dayperiodDropdown&&!target.closest(`#${widgetId}-settings-dayperiod-btn`)&&!target.closest(`#${widgetId}-settings-dayperiod-dropdown`)){dayperiodDropdown.classList.add("hidden")}});document.getElementById(`${widgetId}-settings-chart-type`)?.addEventListener("click",e=>{const target=e.target;if(target.classList.contains("myio-settings-tab")){tempChartType=target.dataset.type;updateSettingsModalTabs()}});document.getElementById(`${widgetId}-settings-viz-mode`)?.addEventListener("click",e=>{const target=e.target;if(target.classList.contains("myio-settings-tab")){tempVizMode=target.dataset.viz;updateSettingsModalTabs()}});document.getElementById(`${widgetId}-settings-theme`)?.addEventListener("click",e=>{const target=e.target;if(target.classList.contains("myio-settings-tab")){tempTheme=target.dataset.theme;updateSettingsModalTabs()}});document.getElementById(`${widgetId}-settings-reset`)?.addEventListener("click",async()=>{tempPeriod=config.defaultPeriod??7;tempChartType=config.defaultChartType??"line";tempVizMode=config.defaultVizMode??"total";tempTheme=config.theme??"light";tempIdealRange=config.idealRange??null;tempStartDate=null;tempEndDate=null;if(dateRangePickerInstance){dateRangePickerInstance.destroy();dateRangePickerInstance=null}const dateRangeInput2=document.getElementById(`${widgetId}-settings-daterange`);if(dateRangeInput2){const endDate=new Date;const startDate=new Date;startDate.setDate(endDate.getDate()-tempPeriod);try{dateRangePickerInstance=await createDateRangePicker2(dateRangeInput2,{presetStart:startDate.toISOString(),presetEnd:endDate.toISOString(),includeTime:true,timePrecision:"hour",maxRangeDays:90,locale:"pt-BR",parentEl:document.getElementById(`${widgetId}-settings-overlay`),onApply:result=>{tempStartDate=result.startISO;tempEndDate=result.endISO;const start=new Date(result.startISO);const end=new Date(result.endISO);const diffTime=Math.abs(end.getTime()-start.getTime());const diffHours=diffTime/(1e3*60*60);const diffDays=Math.ceil(diffTime/(1e3*60*60*24));tempPeriod=diffDays||7;const granularitySelect2=document.getElementById(`${widgetId}-settings-granularity`);if(granularitySelect2&&diffHours<=48){granularitySelect2.value="1h";const dayPeriodField2=document.getElementById(`${widgetId}-settings-dayperiod-field`);if(dayPeriodField2)dayPeriodField2.style.display="block"}}})}catch(error){console.warn("[ConsumptionChartWidget] DateRangePicker reset failed:",error)}}const granularitySelect=document.getElementById(`${widgetId}-settings-granularity`);if(granularitySelect)granularitySelect.value="1d";const dayPeriodField=document.getElementById(`${widgetId}-settings-dayperiod-field`);if(dayPeriodField)dayPeriodField.style.display="none";document.querySelectorAll(`input[name="${widgetId}-weekday"]`).forEach(cb=>{cb.checked=true});updateWeekdayLabel();document.querySelectorAll(`input[name="${widgetId}-dayperiod"]`).forEach(cb=>{cb.checked=true});updateDayPeriodLabel();updateSettingsModalValues()});document.getElementById(`${widgetId}-settings-apply`)?.addEventListener("click",async()=>{const minInput=document.getElementById(`${widgetId}-settings-range-min`);const maxInput=document.getElementById(`${widgetId}-settings-range-max`);const labelInput=document.getElementById(`${widgetId}-settings-range-label`);const periodSelect=document.getElementById(`${widgetId}-settings-period`);const min=parseFloat(minInput?.value||"0");const max=parseFloat(maxInput?.value||"0");const label=labelInput?.value||"";tempPeriod=parseInt(periodSelect?.value||"7",10);if(min>0||max>0){tempIdealRange={min:min,max:max,label:label}}else{tempIdealRange=null}closeSettingsModal();if(tempTheme!==currentTheme){instance.setTheme(tempTheme)}if(tempChartType!==currentChartType){instance.setChartType(tempChartType)}if(tempVizMode!==currentVizMode){instance.setVizMode(tempVizMode)}if(JSON.stringify(tempIdealRange)!==JSON.stringify(currentIdealRange)){instance.setIdealRange(tempIdealRange)}if(tempPeriod!==currentPeriod){await instance.setPeriod(tempPeriod)}})}const instance={async render(){containerElement=document.getElementById(config.containerId);if(!containerElement){console.error(`[ConsumptionWidget] Container #${config.containerId} not found`);return}injectStyles();containerElement.innerHTML=renderHTML();setupListeners();setLoading(true);chartInstance=createConsumption7DaysChart({...config,containerId:`${widgetId}-canvas`,theme:currentTheme,defaultChartType:currentChartType,defaultVizMode:currentVizMode,defaultPeriod:currentPeriod,idealRange:currentIdealRange,colors:{primary:primaryColor,background:`${primaryColor}20`,shoppingColors:domainColors,...config.colors},onDataLoaded:data=>{setLoading(false);updateFooterStats(data);config.onDataLoaded?.(data)},onError:error=>{setLoading(false);config.onError?.(error)}});await chartInstance.render();setLoading(false)},async refresh(forceRefresh=false){if(!chartInstance)return;setLoading(true);await chartInstance.refresh(forceRefresh);setLoading(false)},setChartType(type){if(currentChartType===type)return;currentChartType=type;chartInstance?.setChartType(type);updateTabStates()},setVizMode(mode){if(currentVizMode===mode)return;currentVizMode=mode;chartInstance?.setVizMode(mode);updateTabStates()},setTheme(theme){if(currentTheme===theme)return;currentTheme=theme;chartInstance?.setTheme(theme);const widget=document.getElementById(widgetId);if(widget){widget.classList.toggle("dark",theme==="dark")}updateStyles()},async setPeriod(days){if(currentPeriod===days)return;currentPeriod=days;updateTitle();setLoading(true);await(chartInstance?.setPeriod(days));setLoading(false)},setIdealRange(range){currentIdealRange=range;chartInstance?.setIdealRange(range)},getChart(){return chartInstance},getChartInstance(){return chartInstance?.getChartInstance?.()??null},getCachedData(){return chartInstance?.getCachedData()??null},exportCSV(filename){chartInstance?.exportCSV(filename)},destroy(){chartInstance?.destroy();chartInstance=null;if(styleElement){styleElement.remove();styleElement=null}settingsHeaderInstance?.destroy();settingsHeaderInstance=null;if(dateRangePickerInstance){dateRangePickerInstance.destroy();dateRangePickerInstance=null}if(settingsModalElement){settingsModalElement.remove();settingsModalElement=null}if(containerElement){containerElement.innerHTML="";containerElement=null}}};return instance}var DEFAULT_COLORS4={primary:"#3e1a7d",secondary:"#6b4c9a",accent:"#00bcd4",background:"#ffffff",text:"#333333",chartColors:["#3e1a7d","#00bcd4","#4caf50","#ff9800","#e91e63","#9c27b0"]};var DOMAIN_ICONS={energy:"⚡",water:"💧",temperature:"🌡️"};var DOMAIN_LABELS={energy:"Energia",water:"Água",temperature:"Temperatura"};var DOMAIN_LABELS_EN={energy:"Energy",water:"Water",temperature:"Temperature"};var DOMAIN_UNITS={energy:"kWh",water:"m³",temperature:"°C"};var CSV_SEPARATORS={"pt-BR":";","en-US":",",default:";"};function formatDateForFilename(date){const pad=n=>n.toString().padStart(2,"0");return`${date.getFullYear()}-${pad(date.getMonth()+1)}-${pad(date.getDate())}-${pad(date.getHours())}-${pad(date.getMinutes())}-${pad(date.getSeconds())}`}function sanitizeFilename(str){return str.replace(/[<>:"/\\|?*]/g,"").replace(/\s+/g,"_").substring(0,50)}function generateFilename(data,config){const timestamp=formatDateForFilename(new Date);const domainLabel=DOMAIN_LABELS_EN[config.domain].toUpperCase();const ext=config.formatExport;let baseName="export";if("device"in data&&data.device){const device=data.device;const label=device.label||device.name||"device";const identifier=device.identifier?`-${device.identifier}`:"";baseName=`${sanitizeFilename(label)}${identifier}`}else if("customer"in data&&data.customer?.customerName){baseName=sanitizeFilename(data.customer.customerName)}else if("groupName"in data){baseName=sanitizeFilename(data.groupName)}return`${baseName}-${domainLabel}-${timestamp}.${ext}`}function normalizeTimestamp(ts){if(ts instanceof Date)return ts;if(typeof ts==="number")return new Date(ts);return new Date(ts)}function calculateStats2(dataPoints){if(dataPoints.length===0){return{min:0,max:0,average:0,sum:0,count:0}}const values=dataPoints.map(d=>d.value);const sum=values.reduce((a,b)=>a+b,0);return{min:Math.min(...values),max:Math.max(...values),average:sum/values.length,sum:sum,count:values.length}}function formatNumber2(value,locale,decimals=2){return new Intl.NumberFormat(locale,{minimumFractionDigits:decimals,maximumFractionDigits:decimals}).format(value)}function formatDate3(date,locale){return new Intl.DateTimeFormat(locale,{year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit"}).format(date)}function generateCSV(data,config){const sep=CSV_SEPARATORS[config.locale]||CSV_SEPARATORS["default"];const rows=[];const escapeCSV=val=>{const str=String(val??"");if(str.includes(sep)||str.includes('"')||str.includes("\n")){return`"${str.replace(/"/g,'""')}"`}return str};const formatNumCSV=val=>formatNumber2(val,config.locale);if("device"in data&&"data"in data&&Array.isArray(data.data)){const deviceData=data;rows.push(["Timestamp",config.domainLabel,`Unit (${config.domainUnit})`]);for(const point of deviceData.data){const ts=normalizeTimestamp(point.timestamp);rows.push([formatDate3(ts,config.locale),formatNumCSV(point.value),point.unit||config.domainUnit])}if(config.includeStats){const stats=calculateStats2(deviceData.data);rows.push([]);rows.push(["Statistics","",""]);rows.push(["Minimum",formatNumCSV(stats.min),config.domainUnit]);rows.push(["Maximum",formatNumCSV(stats.max),config.domainUnit]);rows.push(["Average",formatNumCSV(stats.average),config.domainUnit]);rows.push(["Total",formatNumCSV(stats.sum),config.domainUnit]);rows.push(["Count",String(stats.count),"points"])}}else if("devices"in data&&Array.isArray(data.devices)){const compData=data;const deviceHeaders=compData.devices.map(d=>d.device.label||d.device.name||"Device");rows.push(["Timestamp",...deviceHeaders]);const allTimestamps=new Set;compData.devices.forEach(d=>{d.data.forEach(point=>{allTimestamps.add(normalizeTimestamp(point.timestamp).getTime())})});const sortedTimestamps=Array.from(allTimestamps).sort((a,b)=>a-b);for(const ts of sortedTimestamps){const row=[formatDate3(new Date(ts),config.locale)];for(const device of compData.devices){const point=device.data.find(p=>normalizeTimestamp(p.timestamp).getTime()===ts);row.push(point?formatNumCSV(point.value):"")}rows.push(row)}}return rows.map(row=>row.map(escapeCSV).join(sep)).join("\r\n")}function generateXLSX(data,config){return generateCSV(data,config)}function generatePDFContent(data,config){const{colors:colors,domainIcon:domainIcon,domainLabel:domainLabel,domainUnit:domainUnit,locale:locale,includeStats:includeStats,includeChart:includeChart}=config;let deviceLabel="Export";let customerName="";let identifier="";let dataPoints=[];if("device"in data&&"data"in data){const deviceData=data;deviceLabel=deviceData.device.label||deviceData.device.name||"Device";identifier=deviceData.device.identifier||"";customerName=deviceData.customer?.customerName||"";dataPoints=deviceData.data}const stats=calculateStats2(dataPoints);const tableRows=dataPoints.slice(0,100).map(point=>{const ts=normalizeTimestamp(point.timestamp);return`\n <tr>\n <td style="padding: 8px; border-bottom: 1px solid #eee;">${formatDate3(ts,locale)}</td>\n <td style="padding: 8px; border-bottom: 1px solid #eee; text-align: right;">${formatNumber2(point.value,locale)}</td>\n <td style="padding: 8px; border-bottom: 1px solid #eee;">${point.unit||domainUnit}</td>\n </tr>\n `}).join("");const statsSection=includeStats?`\n <div style="margin-top: 24px; padding: 16px; background: #f5f5f5; border-radius: 8px;">\n <h3 style="margin: 0 0 12px 0; color: ${colors.primary};">Statistics</h3>\n <table style="width: 100%;">\n <tr>\n <td><strong>Minimum:</strong></td>\n <td>${formatNumber2(stats.min,locale)} ${domainUnit}</td>\n <td><strong>Maximum:</strong></td>\n <td>${formatNumber2(stats.max,locale)} ${domainUnit}</td>\n </tr>\n <tr>\n <td><strong>Average:</strong></td>\n <td>${formatNumber2(stats.average,locale)} ${domainUnit}</td>\n <td><strong>Total:</strong></td>\n <td>${formatNumber2(stats.sum,locale)} ${domainUnit}</td>\n </tr>\n </table>\n </div>\n `:"";return`\n <!DOCTYPE html>\n <html>\n <head>\n <meta charset="UTF-8">\n <title>${deviceLabel} - ${domainLabel} Report</title>\n <style>\n body {\n font-family: 'Roboto', Arial, sans-serif;\n margin: 0;\n padding: 24px;\n color: ${colors.text};\n background: ${colors.background};\n }\n .header {\n background: ${colors.primary};\n color: white;\n padding: 20px;\n border-radius: 8px;\n margin-bottom: 24px;\n }\n .header h1 {\n margin: 0;\n font-size: 24px;\n }\n .header .subtitle {\n opacity: 0.9;\n margin-top: 8px;\n }\n .device-info {\n display: flex;\n gap: 16px;\n margin-bottom: 16px;\n padding: 12px;\n background: #f5f5f5;\n border-radius: 8px;\n }\n .device-info span {\n padding: 4px 12px;\n background: ${colors.secondary};\n color: white;\n border-radius: 4px;\n font-size: 14px;\n }\n table {\n width: 100%;\n border-collapse: collapse;\n }\n th {\n background: ${colors.primary};\n color: white;\n padding: 12px 8px;\n text-align: left;\n }\n th:nth-child(2) {\n text-align: right;\n }\n .footer {\n margin-top: 24px;\n padding-top: 16px;\n border-top: 1px solid #eee;\n text-align: center;\n font-size: 12px;\n color: #999;\n }\n @media print {\n body { padding: 0; }\n .header { border-radius: 0; }\n }\n </style>\n </head>\n <body>\n <div class="header">\n <h1>${domainIcon} ${deviceLabel}</h1>\n <div class="subtitle">${domainLabel} Report - Generated ${formatDate3(new Date,locale)}</div>\n </div>\n\n ${customerName?`<div class="customer-name" style="margin-bottom: 16px; font-size: 18px;"><strong>Customer:</strong> ${customerName}</div>`:""}\n\n ${identifier?`\n <div class="device-info">\n <span>ID: ${identifier}</span>\n <span>Domain: ${domainLabel}</span>\n <span>Unit: ${domainUnit}</span>\n </div>\n `:""}\n\n <table>\n <thead>\n <tr>\n <th>Timestamp</th>\n <th>${domainLabel} (${domainUnit})</th>\n <th>Unit</th>\n </tr>\n </thead>\n <tbody>\n ${tableRows}\n ${dataPoints.length>100?`<tr><td colspan="3" style="text-align: center; padding: 16px; color: #999;">... and ${dataPoints.length-100} more rows</td></tr>`:""}\n </tbody>\n </table>\n\n ${statsSection}\n\n <div class="footer">\n <p>${config.footerText||"Generated by MyIO Platform"}</p>\n </div>\n </body>\n </html>\n `}function buildTemplateExport(params){const{domain:domain,formatExport:formatExport,typeExport:typeExport,colorsPallet:colorsPallet,locale:locale="pt-BR",includeChart:includeChart=formatExport==="pdf",includeStats:includeStats=true,headerText:headerText,footerText:footerText}=params;const colors={...DEFAULT_COLORS4,...colorsPallet,chartColors:colorsPallet?.chartColors||DEFAULT_COLORS4.chartColors};return{domain:domain,formatExport:formatExport,typeExport:typeExport,colors:colors,locale:locale,includeChart:includeChart,includeStats:includeStats,headerText:headerText||`${DOMAIN_LABELS[domain]} Report`,footerText:footerText||"Generated by MyIO Platform",domainIcon:DOMAIN_ICONS[domain],domainLabel:DOMAIN_LABELS[domain],domainUnit:DOMAIN_UNITS[domain]}}function myioExportData(data,config,options){const filename=generateFilename(data,config);let allDataPoints=[];if("data"in data&&Array.isArray(data.data)){allDataPoints=data.data}else if("devices"in data&&Array.isArray(data.devices)){allDataPoints=data.devices.flatMap(d=>d.data)}const stats=calculateStats2(allDataPoints);const instance={async export(){try{options?.onProgress?.(10,"Generating content...");let content;let mimeType;let finalFilename=filename;switch(config.formatExport){case"csv":content=generateCSV(data,config);mimeType="text/csv;charset=utf-8;";break;case"xlsx":content=generateXLSX(data,config);mimeType="text/csv;charset=utf-8;";finalFilename=filename.replace(".xlsx",".csv");break;case"pdf":content=generatePDFContent(data,config);mimeType="text/html;charset=utf-8;";finalFilename=filename.replace(".pdf",".html");break;default:throw new Error(`Unsupported format: ${config.formatExport}`)}options?.onProgress?.(80,"Creating file...");const bom=config.formatExport==="csv"?"\ufeff":"";const blob=new Blob([bom+content],{type:mimeType});options?.onProgress?.(100,"Export complete");return{success:true,filename:finalFilename,blob:blob,dataUrl:URL.createObjectURL(blob)}}catch(error){return{success:false,filename:filename,error:error instanceof Error?error.message:"Unknown error"}}},async download(){const result=await this.export();if(!result.success||!result.blob){console.error("Export failed:",result.error);return}const link=document.createElement("a");link.href=URL.createObjectURL(result.blob);link.download=result.filename;link.style.display="none";document.body.appendChild(link);link.click();document.body.removeChild(link);URL.revokeObjectURL(link.href)},async preview(){if(config.formatExport!=="pdf"){return null}const result=await this.export();return result.dataUrl||null},getStats(){return stats},getFilename(){return filename}};if(options?.autoDownload){instance.download()}return instance}var EXPORT_DEFAULT_COLORS=DEFAULT_COLORS4;var EXPORT_DOMAIN_ICONS=DOMAIN_ICONS;var EXPORT_DOMAIN_LABELS=DOMAIN_LABELS;var EXPORT_DOMAIN_UNITS=DOMAIN_UNITS;var DEFAULT_SHOPPING_COLORS=["#3b82f6","#8b5cf6","#f59e0b","#ef4444","#10b981","#06b6d4","#ec4899","#14b8a6","#f97316","#a855f7"];var DEFAULT_ENERGY_GROUP_COLORS={Elevadores:"#3b82f6","Escadas Rolantes":"#8b5cf6","Climatização":"#f59e0b",Climatizacao:"#f59e0b","Outros Equipamentos":"#ef4444",Lojas:"#10b981"};var DEFAULT_WATER_GROUP_COLORS={Lojas:"#10b981","Área Comum":"#0288d1","Area Comum":"#0288d1"};var DEFAULT_GAS_GROUP_COLORS={Cozinha:"#f59e0b",Aquecimento:"#ef4444",Outros:"#94a3b8"};function getDefaultGroupColors(domain){switch(domain.toLowerCase()){case"energy":return DEFAULT_ENERGY_GROUP_COLORS;case"water":return DEFAULT_WATER_GROUP_COLORS;case"gas":return DEFAULT_GAS_GROUP_COLORS;default:return DEFAULT_ENERGY_GROUP_COLORS}}function assignShoppingColors(shoppingIds){const colors={};shoppingIds.forEach((id,index)=>{colors[id]=DEFAULT_SHOPPING_COLORS[index%DEFAULT_SHOPPING_COLORS.length]});return colors}function getShoppingColor(shoppingId,shoppingColors,fallbackIndex=0){if(shoppingColors&&shoppingColors[shoppingId]){return shoppingColors[shoppingId]}if(shoppingColors){const normalizedId=shoppingId.toLowerCase();for(const[key,color]of Object.entries(shoppingColors)){if(key.toLowerCase()===normalizedId||key.toLowerCase().includes(normalizedId)){return color}}}return DEFAULT_SHOPPING_COLORS[Math.abs(fallbackIndex)%DEFAULT_SHOPPING_COLORS.length]}function getGroupColor(groupName,groupColors,domain="energy",fallbackIndex=0){if(groupColors&&groupColors[groupName]){return groupColors[groupName]}const defaultColors=getDefaultGroupColors(domain);if(defaultColors[groupName]){return defaultColors[groupName]}return DEFAULT_SHOPPING_COLORS[Math.abs(fallbackIndex)%DEFAULT_SHOPPING_COLORS.length]}function getThemeColors2(theme){if(theme==="dark"){return{text:"#f3f4f6",secondaryText:"#9ca3af",background:"#111827",cardBackground:"#1f2937",border:"#374151",grid:"#374151"}}return{text:"#1f2937",secondaryText:"#6b7280",background:"#f5f5f5",cardBackground:"#ffffff",border:"#e5e7eb",grid:"#e5e7eb"}}function getHashColor(str){let hash=0;for(let i=0;i<str.length;i++){const char=str.charCodeAt(i);hash=(hash<<5)-hash+char;hash=hash&hash}return DEFAULT_SHOPPING_COLORS[Math.abs(hash)%DEFAULT_SHOPPING_COLORS.length]}var settingsIcon=`<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:16px;height:16px;pointer-events:none"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>`;var maximizeIcon=`<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:16px;height:16px;pointer-events:none"><polyline points="15 3 21 3 21 9"/><polyline points="9 21 3 21 3 15"/><line x1="21" y1="3" x2="14" y2="10"/><line x1="3" y1="21" x2="10" y2="14"/></svg>`;function createDistributionChartWidget(config){const widgetId=`distribution-${config.domain}-${Date.now()}-${Math.random().toString(36).substr(2,9)}`;let chartInstance=null;let currentMode=config.defaultMode||"groups";let currentTheme=config.theme||"light";let currentData=null;let containerElement=null;const title=config.title||getDomainTitle(config.domain);const chartHeight=config.chartHeight||300;const showHeader=config.showHeader!==false;const showModeSelector=config.showModeSelector!==false;const showSettingsButton=config.showSettingsButton??false;const showMaximizeButton=config.showMaximizeButton??false;const decimalPlaces=config.decimalPlaces??2;const modes=config.modes||getDefaultModes(config.domain);const groupColors=config.groupColors||getDefaultGroupColors(config.domain);function getDomainTitle(domain){const titles={energy:"Distribuição de Energia",water:"Distribuição de Água",gas:"Distribuição de Gás",temperature:"Distribuição de Temperatura"};return titles[domain.toLowerCase()]||`Distribuição de ${domain}`}function getDefaultModes(domain){if(domain==="water"){return[{value:"groups",label:"Lojas vs Área Comum"},{value:"stores",label:"Lojas por Shopping"},{value:"common",label:"Área Comum por Shopping"}]}return[{value:"groups",label:"Por Grupos de Equipamentos"},{value:"elevators",label:"Elevadores por Shopping"},{value:"escalators",label:"Escadas Rolantes por Shopping"},{value:"hvac",label:"Climatização por Shopping"},{value:"others",label:"Outros Equipamentos por Shopping"},{value:"stores",label:"Lojas por Shopping"}]}function $id(id){if(config.$container){return config.$container[0].querySelector(`#${id}`)}return document.getElementById(id)}function formatValue(value){if(config.unitLarge&&config.thresholdForLargeUnit&&value>=config.thresholdForLargeUnit){return`${(value/config.thresholdForLargeUnit).toFixed(decimalPlaces)} ${config.unitLarge}`}return`${value.toFixed(decimalPlaces)} ${config.unit}`}function getColor(key,index,isGroupMode2){if(isGroupMode2){return getGroupColor(key,groupColors,config.domain,index)}const shoppingColors=config.getShoppingColors?.();return getShoppingColor(key,shoppingColors,index)}function getColors2(){return getThemeColors2(currentTheme)}function isGroupMode(){return currentMode==="groups"}function renderHTML(){const colors=getColors2();const modeOptions=modes.map(m=>`<option value="${m.value}" ${m.value===currentMode?"selected":""}>${m.label}</option>`).join("");const headerButtons=[];if(showSettingsButton){headerButtons.push(`\n <button id="${widgetId}-settings-btn" class="myio-dist-btn" title="Configurações">\n ${settingsIcon}\n </button>\n `)}if(showMaximizeButton){headerButtons.push(`\n <button id="${widgetId}-maximize-btn" class="myio-dist-btn" title="Expandir">\n ${maximizeIcon}\n </button>\n `)}return`\n <style>\n #${widgetId} {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n }\n #${widgetId} .myio-dist-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 16px;\n flex-wrap: wrap;\n gap: 12px;\n }\n #${widgetId} .myio-dist-title {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n color: ${colors.text};\n }\n #${widgetId} .myio-dist-controls {\n display: flex;\n align-items: center;\n gap: 8px;\n }\n #${widgetId} .myio-dist-label {\n font-size: 12px;\n color: ${colors.secondaryText};\n }\n #${widgetId} .myio-dist-select {\n padding: 6px 10px;\n border-radius: 6px;\n border: 1px solid ${colors.border};\n background: ${colors.cardBackground};\n color: ${colors.text};\n font-size: 12px;\n cursor: pointer;\n min-width: 180px;\n }\n #${widgetId} .myio-dist-select:focus {\n outline: none;\n border-color: #3e1a7d;\n }\n #${widgetId} .myio-dist-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 6px 8px;\n border: 1px solid ${colors.border};\n border-radius: 6px;\n background: transparent;\n color: ${colors.text};\n cursor: pointer;\n transition: all 0.2s;\n }\n #${widgetId} .myio-dist-btn:hover {\n background: #3e1a7d;\n border-color: #3e1a7d;\n color: white;\n }\n #${widgetId} .myio-dist-chart-container {\n position: relative;\n height: ${chartHeight}px;\n }\n #${widgetId} .myio-dist-loading {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: ${colors.cardBackground}ee;\n z-index: 10;\n }\n #${widgetId} .myio-dist-spinner {\n width: 32px;\n height: 32px;\n border: 3px solid ${colors.border};\n border-top-color: #3e1a7d;\n border-radius: 50%;\n animation: myio-dist-spin 0.8s linear infinite;\n }\n @keyframes myio-dist-spin {\n to { transform: rotate(360deg); }\n }\n #${widgetId} .myio-dist-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n color: ${colors.secondaryText};\n font-size: 14px;\n }\n #${widgetId} .myio-dist-empty-icon {\n font-size: 48px;\n margin-bottom: 12px;\n opacity: 0.5;\n }\n </style>\n\n <div id="${widgetId}" class="myio-distribution-widget" style="\n background: ${colors.cardBackground};\n border-radius: 12px;\n padding: 16px;\n height: 100%;\n display: flex;\n flex-direction: column;\n ">\n ${showHeader?`\n <div class="myio-dist-header">\n <h4 class="myio-dist-title">${title}</h4>\n <div class="myio-dist-controls">\n ${showModeSelector&&modes.length>1?`\n <label for="${widgetId}-mode" class="myio-dist-label">Visualizar:</label>\n <select id="${widgetId}-mode" class="myio-dist-select">\n ${modeOptions}\n </select>\n `:""}\n ${headerButtons.join("")}\n </div>\n </div>\n `:""}\n\n <div class="myio-dist-chart-container">\n <canvas id="${widgetId}-canvas"></canvas>\n <div id="${widgetId}-loading" class="myio-dist-loading" style="display: none;">\n <div class="myio-dist-spinner"></div>\n </div>\n </div>\n </div>\n `}function setLoading(loading){const loadingEl=$id(`${widgetId}-loading`);if(loadingEl){loadingEl.style.display=loading?"flex":"none"}}function buildChartData(distribution){const labels=[];const data=[];const backgroundColors=[];const total=Object.values(distribution).reduce((sum,val)=>sum+val,0);const isGroup=isGroupMode();const entries=Object.entries(distribution).filter(([_,value])=>value>0).sort((a,b)=>b[1]-a[1]);entries.forEach(([key,value],index)=>{const percentage=total>0?(value/total*100).toFixed(1):"0";labels.push(`${key} (${formatValue(value)} - ${percentage}%)`);data.push(value);backgroundColors.push(getColor(key,index,isGroup))});return{labels:labels,data:data,backgroundColors:backgroundColors,total:total}}function renderEmptyState(){const container=$id(`${widgetId}-canvas`)?.parentElement;if(container){const emptyEl=document.createElement("div");emptyEl.className="myio-dist-empty";emptyEl.innerHTML=`\n <div class="myio-dist-empty-icon">📊</div>\n <div>Sem dados disponíveis</div>\n `;const canvas=$id(`${widgetId}-canvas`);if(canvas)canvas.style.display="none";const existingEmpty=container.querySelector(".myio-dist-empty");if(!existingEmpty){container.appendChild(emptyEl)}}}function removeEmptyState(){const container=$id(`${widgetId}-canvas`)?.parentElement;if(container){const emptyEl=container.querySelector(".myio-dist-empty");if(emptyEl)emptyEl.remove();const canvas=$id(`${widgetId}-canvas`);if(canvas)canvas.style.display="block"}}async function updateChart(){const canvas=$id(`${widgetId}-canvas`);if(!canvas){console.error(`[${config.domain.toUpperCase()}] Distribution canvas not found`);return}setLoading(true);try{const distribution=await config.fetchDistribution(currentMode);if(!distribution||Object.keys(distribution).length===0){console.warn(`[${config.domain.toUpperCase()}] No distribution data for mode: ${currentMode}`);currentData=null;setLoading(false);renderEmptyState();return}removeEmptyState();currentData=distribution;const{labels:labels,data:data,backgroundColors:backgroundColors,total:total}=buildChartData(distribution);const colors=getColors2();const Chart2=window.Chart;if(!Chart2){throw new Error("Chart.js not loaded")}if(chartInstance){chartInstance.destroy();chartInstance=null}const ctx=canvas.getContext("2d");if(!ctx){throw new Error("Could not get canvas context")}chartInstance=new Chart2(ctx,{type:"bar",data:{labels:labels,datasets:[{label:`Consumo (${config.unit})`,data:data,backgroundColor:backgroundColors}]},options:{responsive:true,maintainAspectRatio:false,indexAxis:"y",animation:false,plugins:{legend:{display:false},tooltip:{callbacks:{label:context=>{const value=context.parsed.x||0;const percentage=total>0?(value/total*100).toFixed(1):"0";return`${formatValue(value)} (${percentage}%)`}}}},scales:{x:{beginAtZero:true,ticks:{callback:value=>formatValue(Number(value)),color:colors.secondaryText,font:{size:11}},grid:{color:colors.grid}},y:{ticks:{font:{size:11},color:colors.text},grid:{display:false}}}}});config.onDataLoaded?.(distribution);console.log(`[${config.domain.toUpperCase()}] Distribution chart updated for mode: ${currentMode}`)}catch(error){console.error(`[${config.domain.toUpperCase()}] Error updating distribution chart:`,error);config.onError?.(error);renderEmptyState()}finally{setLoading(false)}}function setupListeners(){const modeSelect=$id(`${widgetId}-mode`);if(modeSelect){modeSelect.addEventListener("change",async e=>{currentMode=e.target.value;config.onModeChange?.(currentMode);await updateChart()})}if(showSettingsButton){const settingsBtn=$id(`${widgetId}-settings-btn`);if(settingsBtn){settingsBtn.addEventListener("click",()=>{config.onSettingsClick?.()})}}if(showMaximizeButton){const maximizeBtn=$id(`${widgetId}-maximize-btn`);if(maximizeBtn){maximizeBtn.addEventListener("click",()=>{config.onMaximizeClick?.()})}}}function updateThemeStyles(){const colors=getColors2();const widget=$id(widgetId);if(widget){widget.style.background=colors.cardBackground;const title2=widget.querySelector(".myio-dist-title");if(title2)title2.style.color=colors.text;const labels=widget.querySelectorAll(".myio-dist-label");labels.forEach(l=>l.style.color=colors.secondaryText);const select=widget.querySelector(".myio-dist-select");if(select){select.style.background=colors.cardBackground;select.style.color=colors.text;select.style.borderColor=colors.border}const buttons=widget.querySelectorAll(".myio-dist-btn");buttons.forEach(b=>{b.style.color=colors.text;b.style.borderColor=colors.border})}}const instance={async render(){containerElement=$id(config.containerId);if(!containerElement){throw new Error(`Container #${config.containerId} not found`)}containerElement.innerHTML=renderHTML();setupListeners();await updateChart()},async setMode(mode){currentMode=mode;const modeSelect=$id(`${widgetId}-mode`);if(modeSelect){modeSelect.value=mode}config.onModeChange?.(mode);await updateChart()},async refresh(){await updateChart()},setTheme(theme){currentTheme=theme;updateThemeStyles();if(currentData){updateChart()}},destroy(){if(chartInstance){chartInstance.destroy();chartInstance=null}if(containerElement){containerElement.innerHTML=""}currentData=null},getChartInstance:()=>chartInstance,getCurrentMode:()=>currentMode,getCurrentData:()=>currentData};return instance}exports.CHART_COLORS=CHART_COLORS;exports.CONSUMPTION_CHART_COLORS=DEFAULT_COLORS;exports.CONSUMPTION_CHART_DEFAULTS=DEFAULT_CONFIG;exports.CONSUMPTION_THEME_COLORS=THEME_COLORS;exports.ConnectionStatusType=ConnectionStatusType;exports.DEFAULT_CLAMP_RANGE=DEFAULT_CLAMP_RANGE;exports.DEFAULT_ENERGY_GROUP_COLORS=DEFAULT_ENERGY_GROUP_COLORS;exports.DEFAULT_GAS_GROUP_COLORS=DEFAULT_GAS_GROUP_COLORS;exports.DEFAULT_SHOPPING_COLORS=DEFAULT_SHOPPING_COLORS;exports.DEFAULT_WATER_GROUP_COLORS=DEFAULT_WATER_GROUP_COLORS;exports.DeviceStatusType=DeviceStatusType;exports.EXPORT_DEFAULT_COLORS=EXPORT_DEFAULT_COLORS;exports.EXPORT_DOMAIN_ICONS=EXPORT_DOMAIN_ICONS;exports.EXPORT_DOMAIN_LABELS=EXPORT_DOMAIN_LABELS;exports.EXPORT_DOMAIN_UNITS=EXPORT_DOMAIN_UNITS;exports.MyIOChartModal=MyIOChartModal;exports.MyIODraggableCard=MyIODraggableCard;exports.MyIOSelectionStoreClass=MyIOSelectionStoreClass;exports.MyIOToast=MyIOToast;exports.addDetectionContext=addDetectionContext;exports.addNamespace=addNamespace;exports.aggregateByDay=aggregateByDay;exports.assignShoppingColors=assignShoppingColors;exports.averageByDay=averageByDay;exports.buildListItemsThingsboardByUniqueDatasource=buildListItemsThingsboardByUniqueDatasource;exports.buildMyioIngestionAuth=buildMyioIngestionAuth;exports.buildTemplateExport=buildTemplateExport;exports.buildWaterReportCSV=buildWaterReportCSV;exports.buildWaterStoresCSV=buildWaterStoresCSV;exports.calcDeltaPercent=calcDeltaPercent;exports.calculateDeviceStatus=calculateDeviceStatus;exports.calculateDeviceStatusWithRanges=calculateDeviceStatusWithRanges;exports.calculateExportStats=calculateStats2;exports.calculateStats=calculateStats;exports.clampTemperature=clampTemperature;exports.classify=classify;exports.classifyWaterLabel=classifyWaterLabel;exports.classifyWaterLabels=classifyWaterLabels;exports.clearAllAuthCaches=clearAllAuthCaches;exports.connectionStatusIcons=connectionStatusIcons;exports.createConsumption7DaysChart=createConsumption7DaysChart;exports.createConsumptionChartWidget=createConsumptionChartWidget;exports.createConsumptionModal=createConsumptionModal;exports.createDateRangePicker=createDateRangePicker2;exports.createDistributionChartWidget=createDistributionChartWidget;exports.createInputDateRangePickerInsideDIV=createInputDateRangePickerInsideDIV;exports.createModalHeader=createModalHeader;exports.decodePayload=decodePayload;exports.decodePayloadBase64Xor=decodePayloadBase64Xor;exports.detectDeviceType=detectDeviceType;exports.determineInterval=determineInterval;exports.deviceStatusIcons=deviceStatusIcons;exports.exportTemperatureCSV=exportTemperatureCSV;exports.exportToCSV=exportToCSV;exports.exportToCSVAll=exportToCSVAll;exports.extractMyIOCredentials=extractMyIOCredentials;exports.fetchTemperatureData=fetchTemperatureData;exports.fetchThingsboardCustomerAttrsFromStorage=fetchThingsboardCustomerAttrsFromStorage;exports.fetchThingsboardCustomerServerScopeAttrs=fetchThingsboardCustomerServerScopeAttrs;exports.findValue=findValue;exports.findValueWithDefault=findValueWithDefault;exports.fmtPerc=fmtPerc;exports.fmtPercLegacy=fmtPerc2;exports.formatAllInSameUnit=formatAllInSameUnit;exports.formatAllInSameWaterUnit=formatAllInSameWaterUnit;exports.formatDateForInput=formatDateForInput;exports.formatDateToYMD=formatDateToYMD;exports.formatDateWithTimezoneOffset=formatDateWithTimezoneOffset;exports.formatDuration=formatDuration;exports.formatEnergy=formatEnergy;exports.formatNumberReadable=formatNumberReadable;exports.formatRelativeTime=formatRelativeTime;exports.formatTankHeadFromCm=formatTankHeadFromCm;exports.formatTemperature=formatTemperature2;exports.formatWater=formatWater;exports.formatWaterByGroup=formatWaterByGroup;exports.formatWaterVolumeM3=formatWaterVolumeM3;exports.formatarDuracao=formatarDuracao;exports.generateExportFilename=generateFilename;exports.getAuthCacheStats=getAuthCacheStats;exports.getAvailableContexts=getAvailableContexts;exports.getConnectionStatusIcon=getConnectionStatusIcon;exports.getDateRangeArray=getDateRangeArray;exports.getDefaultGroupColors=getDefaultGroupColors;exports.getDeviceStatusIcon=getDeviceStatusIcon;exports.getDeviceStatusInfo=getDeviceStatusInfo;exports.getDistributionThemeColors=getThemeColors2;exports.getGroupColor=getGroupColor;exports.getHashColor=getHashColor;exports.getModalHeaderStyles=getModalHeaderStyles;exports.getSaoPauloISOString=getSaoPauloISOString;exports.getSaoPauloISOStringFixed=getSaoPauloISOStringFixed;exports.getShoppingColor=getShoppingColor;exports.getValueByDatakey=getValueByDatakey;exports.getValueByDatakeyLegacy=getValueByDatakeyLegacy;exports.getWaterCategories=getWaterCategories;exports.groupByDay=groupByDay;exports.interpolateTemperature=interpolateTemperature;exports.isDeviceOffline=isDeviceOffline;exports.isValidConnectionStatus=isValidConnectionStatus;exports.isValidDeviceStatus=isValidDeviceStatus;exports.isWaterCategory=isWaterCategory;exports.mapConnectionStatus=mapConnectionStatus;exports.mapDeviceStatusToCardStatus=mapDeviceStatusToCardStatus;exports.mapDeviceToConnectionStatus=mapDeviceToConnectionStatus;exports.myioExportData=myioExportData;exports.normalizeRecipients=normalizeRecipients;exports.numbers=numbers_exports;exports.openDashboardPopup=openDashboardPopup;exports.openDashboardPopupAllReport=openDashboardPopupAllReport;exports.openDashboardPopupEnergy=openDashboardPopupEnergy;exports.openDashboardPopupReport=openDashboardPopupReport;exports.openDashboardPopupSettings=openDashboardPopupSettings;exports.openDashboardPopupWaterTank=openDashboardPopupWaterTank;exports.openDemandModal=openDemandModal;exports.openGoalsPanel=openGoalsPanel;exports.openRealTimeTelemetryModal=openRealTimeTelemetryModal;exports.openTemperatureComparisonModal=openTemperatureComparisonModal;exports.openTemperatureModal=openTemperatureModal;exports.openTemperatureSettingsModal=openTemperatureSettingsModal;exports.parseInputDateToDate=parseInputDateToDate;exports.renderCardComponent=renderCardComponent;exports.renderCardComponentEnhanced=renderCardComponent2;exports.renderCardComponentHeadOffice=renderCardComponentHeadOffice;exports.renderCardComponentLegacy=renderCardComponentLegacy;exports.renderCardComponentV2=renderCardComponentV2;exports.renderCardComponentV5=renderCardComponent3;exports.renderCardV5=renderCardComponentV5;exports.shouldFlashIcon=shouldFlashIcon;exports.strings=strings_exports;exports.timeWindowFromInputYMD=timeWindowFromInputYMD;exports.toCSV=toCSV;exports.toFixedSafe=toFixedSafe;exports.waterDeviceStatusIcons=waterDeviceStatusIcons});
|