/** Shopify CDN: Minification failed

Line 16:0 Unexpected "("
Line 171:0 Comments in CSS use "/* ... */" instead of "//"
Line 172:0 Comments in CSS use "/* ... */" instead of "//"
Line 181:0 Comments in CSS use "/* ... */" instead of "//"
Line 190:0 Comments in CSS use "/* ... */" instead of "//"
Line 212:0 Comments in CSS use "/* ... */" instead of "//"
Line 213:0 Comments in CSS use "/* ... */" instead of "//"
Line 255:0 Comments in CSS use "/* ... */" instead of "//"
Line 266:0 Comments in CSS use "/* ... */" instead of "//"
Line 395:138 Unterminated string token
... and 66 more hidden warnings

**/
(()=>{try{const r=document.getElementById('lisa-app-vg-native');if(r&&!sessionStorage.getItem('lisa_panel_token')){const t=sessionStorage.getItem('visa-geral:'+r.dataset.worker.replace(/\/$/,'')+':session');if(t)sessionStorage.setItem('lisa_panel_token',t)}}catch{}})();

(()=>{
'use strict';
/* Mini mapa do Rastreamento: usa somente o bundle já pré-carregado pelo painel-lisa6. */
let tries=0,geoPromise=null,renderSeq=0;
const UF_REGION={AC:'Norte',AP:'Norte',AM:'Norte',PA:'Norte',RO:'Norte',RR:'Norte',TO:'Norte',AL:'Nordeste',BA:'Nordeste',CE:'Nordeste',MA:'Nordeste',PB:'Nordeste',PE:'Nordeste',PI:'Nordeste',RN:'Nordeste',SE:'Nordeste',DF:'Centro-Oeste',GO:'Centro-Oeste',MT:'Centro-Oeste',MS:'Centro-Oeste',ES:'Sudeste',MG:'Sudeste',RJ:'Sudeste',SP:'Sudeste',PR:'Sul',RS:'Sul',SC:'Sul'};
const STATE_NAME_UF={ACRE:'AC',ALAGOAS:'AL',AMAPA:'AP',AMAZONAS:'AM',BAHIA:'BA',CEARA:'CE','DISTRITO FEDERAL':'DF','ESPIRITO SANTO':'ES',GOIAS:'GO',MARANHAO:'MA','MATO GROSSO':'MT','MATO GROSSO DO SUL':'MS','MINAS GERAIS':'MG',PARA:'PA',PARAIBA:'PB',PARANA:'PR',PERNAMBUCO:'PE',PIAUI:'PI','RIO DE JANEIRO':'RJ','RIO GRANDE DO NORTE':'RN','RIO GRANDE DO SUL':'RS',RONDONIA:'RO',RORAIMA:'RR','SANTA CATARINA':'SC','SAO PAULO':'SP',SERGIPE:'SE',TOCANTINS:'TO'};
const REGION_COLORS={'Norte':'#16a085','Nordeste':'#f39c3d','Centro-Oeste':'#d6a613','Sudeste':'#377ec8','Sul':'#8a63c7'};
const REGION_SHIFT={'Norte':[0,-4],'Nordeste':[8,-1],'Centro-Oeste':[-1,6],'Sudeste':[7,7],'Sul':[1,7]};
const norm=v=>String(v??'').normalize('NFD').replace(/[\u0300-\u036f]/g,'').toUpperCase().replace(/\s+/g,' ').trim();
const fmt=n=>Number(n||0).toLocaleString('pt-BR');
function featureUf(feature){const p=feature?.properties||{};for(const raw of [p.sigla,p.UF,p.uf,p.abbrev,p.name,p.nome]){const n=norm(raw);if(UF_REGION[n])return n;if(STATE_NAME_UF[n])return STATE_NAME_UF[n]}return ''}
function eachPoint(geometry,cb){if(!geometry)return;const walk=x=>{if(!Array.isArray(x))return;if(typeof x[0]==='number'&&typeof x[1]==='number'){cb(x);return}for(const y of x)walk(y)};walk(geometry.coordinates)}
function geometryPath(geometry,project){if(!geometry)return'';const polygon=rings=>rings.map(r=>r.map((p,i)=>{const [x,y]=project(p);return(i?'L':'M')+x.toFixed(2)+' '+y.toFixed(2)}).join(' ')+' Z').join(' ');if(geometry.type==='Polygon')return polygon(geometry.coordinates||[]);if(geometry.type==='MultiPolygon')return (geometry.coordinates||[]).map(polygon).join(' ');return''}
function getRoot(){return document.getElementById('lisa-app-vg-native')||document.querySelector('.lisa-app[data-worker]')}
function openRegionReport(){const root=getRoot();if(root&&typeof root.__lisaRtrackOpen==='function')root.__lisaRtrackOpen('region')}
function getBundle(root){try{return root?.__lisaRtrackGetReportsBundle?.()||root?.__lisaRtrackReportsBundle||null}catch{return root?.__lisaRtrackReportsBundle||null}}
function getGeo(){
 const root=getRoot();if(root?.__lisaRtrackGetGeometry)return root.__lisaRtrackGetGeometry();
 if(geoPromise)return geoPromise;
 const controller=new AbortController(),timer=setTimeout(()=>controller.abort(),20000);
 geoPromise=fetch(String(root?.dataset.worker||'').replace(/\/$/,'')+'/rastreamento/geometria-brasil',{headers:{Authorization:'Bearer '+(sessionStorage.getItem('lisa_panel_token')||'')},signal:controller.signal,cache:'no-store'}).then(r=>{if(!r.ok)throw new Error('Mapa indisponível');return r.json()}).catch(e=>{geoPromise=null;throw e}).finally(()=>clearTimeout(timer));return geoPromise;
}
async function buildMap(root,card){
 const seq=++renderSeq,body=card.querySelector('[data-lisa-region-mini-body]'),foot=card.querySelector('[data-lisa-region-mini-foot]');if(!body)return;
 const bundle=getBundle(root);if(!bundle?.periods?.['60']){body.innerHTML='<div class="lisa-tracking-region-map-loading">Carregando mapa...</div>';if(foot)foot.textContent=root.__lisaRtrackReportsError||'Aguardando snapshot pré-carregado';if(root.__lisaRtrackReportsError)body.innerHTML='<div class="lisa-tracking-region-map-error">Mapa em preparação ou indisponível. Nova tentativa automática.</div>';return}
 body.innerHTML='<div class="lisa-tracking-region-map-loading">Montando mapa...</div>';
 try{
  const geo=await getGeo();if(seq!==renderSeq)return;
  const data=bundle.periods['60']||{},regions=Array.isArray(data.regions)?data.regions:[],counts=new Map(regions.map(x=>[String(x.name||''),Number(x.sent||0)])),features=(geo?.features||[]).filter(f=>featureUf(f));
  let minX=Infinity,maxX=-Infinity,minY=Infinity,maxY=-Infinity;for(const f of features)eachPoint(f.geometry,p=>{const lon=Number(p[0]),lat=Number(p[1]);if(!Number.isFinite(lon)||!Number.isFinite(lat))return;minX=Math.min(minX,lon);maxX=Math.max(maxX,lon);minY=Math.min(minY,lat);maxY=Math.max(maxY,lat)});if(!Number.isFinite(minX))throw new Error('Mapa indisponível');
  const W=300,H=160,pad=7,scale=Math.min((W-pad*2)/(maxX-minX),(H-pad*2)/(maxY-minY)),dw=(maxX-minX)*scale,dh=(maxY-minY)*scale,ox=(W-dw)/2,oy=(H-dh)/2,project=p=>[ox+(Number(p[0])-minX)*scale,oy+(maxY-Number(p[1]))*scale],regionPts={};
  const paths=features.map(f=>{const uf=featureUf(f),region=UF_REGION[uf]||'',sent=counts.get(region)||0,color=REGION_COLORS[region]||'#94a3b8',d=geometryPath(f.geometry,project),acc=regionPts[region]||(regionPts[region]={x:0,y:0,n:0});eachPoint(f.geometry,p=>{const [x,y]=project(p);if(Number.isFinite(x)&&Number.isFinite(y)){acc.x+=x;acc.y+=y;acc.n++}});return `<path d="${d}" fill="${color}" fill-opacity="${sent?'.94':'.28'}" data-region="${region}" data-has-movement="${sent?'1':'0'}"><title>${region} · ${fmt(sent)} notas nos últimos 60 dias</title></path>`}).join('');
  const labels=Object.keys(REGION_COLORS).map(region=>{const p=regionPts[region];if(!p||!p.n)return'';const sh=REGION_SHIFT[region]||[0,0],x=p.x/p.n+sh[0],y=p.y/p.n+sh[1],sent=counts.get(region)||0;return `<g class="lisa-tracking-region-label" transform="translate(${x.toFixed(1)} ${y.toFixed(1)})"><title>${region} · ${fmt(sent)} notas</title><rect x="-25" y="-9" width="50" height="18" rx="6"></rect><text x="0" y="0">${fmt(sent)}</text></g>`}).join('');
  body.innerHTML=`<svg viewBox="0 0 ${W} ${H}" role="img" aria-label="Mapa do Brasil por região">${paths}${labels}</svg>`;if(foot)foot.innerHTML=`<span class="lisa-tracking-region-map-dot"></span>${fmt(Math.max(0,Number(data.total_notes||0)-Number(data.geo_missing||0)))} notas · ${fmt(regions.length)} regiões${Number(data.geo_missing)>0?' · '+fmt(data.geo_missing)+' sem CEP/UF':''} · últimos 60 dias`;
 }catch(e){if(seq!==renderSeq)return;body.innerHTML='<div class="lisa-tracking-region-map-error">Mapa disponível no relatório Região / Estado</div>';if(foot)foot.textContent='Clique para abrir o relatório'}
}
function ensureCard(root){
 const panel=root?.querySelector('.carrier-section-side .carrier-panel'),grid=panel?.querySelector('[data-carrier-status-table],.carrier-grid-3');if(!root||!panel||!grid)return null;
 panel.querySelector('[data-lisa-tracking-state-mini-map]')?.remove();let card=panel.querySelector('[data-lisa-tracking-region-mini-map]');
 if(!card){card=document.createElement('button');card.type='button';card.className='lisa-tracking-region-map-card';card.setAttribute('data-lisa-tracking-region-mini-map','');card.setAttribute('aria-label','Abrir relatório de rastreamento por Região');card.innerHTML='<div class="lisa-tracking-region-map-head"><div class="lisa-tracking-region-map-title"><strong>Distribuição por região</strong><span>Visão dos últimos 60 dias</span></div><span class="lisa-tracking-region-map-open">Ver relatório</span></div><div class="lisa-tracking-region-map-body" data-lisa-region-mini-body></div><div class="lisa-tracking-region-map-foot" data-lisa-region-mini-foot></div>';panel.appendChild(card);card.addEventListener('click',openRegionReport)}
 return card;
}
function init(){
 const root=getRoot(),card=ensureCard(root);if(!root||!card){if(tries++<300)setTimeout(init,100);return}
 const refresh=()=>{const c=ensureCard(root);if(c)buildMap(root,c)};
 refresh();
 if(!root.__lisaMiniRegionMapPreloadedBound){root.__lisaMiniRegionMapPreloadedBound=true;root.addEventListener('lisa:rtrack-reports-loaded',refresh);root.addEventListener('lisa:rtrack-reports-error',refresh);root.addEventListener('lisa:carrier-files-updated',()=>setTimeout(refresh,1200));root.addEventListener('lisa:tracking-summary-loaded',()=>{if(getBundle(root))refresh()});root.addEventListener('lisa:monitor-updated',()=>{if(getBundle(root))refresh()})}
 let wait=0;const waitBundle=()=>{if(getBundle(root)){refresh();return}if(root.__lisaRtrackPreloadReports&&sessionStorage.getItem('lisa_panel_token')){root.__lisaRtrackPreloadReports(false).catch(()=>{});return}if(wait++<300)setTimeout(waitBundle,500)};waitBundle();
}
setTimeout(init,500);
})();

;

(()=>{
  const root=document.getElementById('lisa-app-vg-native');
  if(!root||root.dataset.popupCenterFixBound==='1')return;
  root.dataset.popupCenterFixBound='1';

  const modalSelectors=['.lisa-orders-modal','.lisa-overdue-modal','.lisa-reversa-modal'];
  const cardSelector='.lisa-orders-modal-card,.lisa-overdue-modal-card,.lisa-reversa-modal-card';

  function centerModal(modal){
    if(!modal||modal.hidden||window.innerWidth<=700)return;
    const card=modal.querySelector(cardSelector);
    if(!card)return;
    card.style.setProperty('--lisa-popup-center-shift','0px');
    requestAnimationFrame(()=>{
      if(modal.hidden)return;
      const rect=card.getBoundingClientRect();
      if(!rect.width)return;
      const targetLeft=(window.innerWidth-rect.width)/2;
      const shift=Math.round(targetLeft-rect.left);
      card.style.setProperty('--lisa-popup-center-shift',shift+'px');
    });
  }

  function centerVisible(){
    for(const sel of modalSelectors){
      const modal=root.querySelector(sel);
      if(modal&&!modal.hidden)centerModal(modal);
    }
  }

  for(const sel of modalSelectors){
    const modal=root.querySelector(sel);
    if(!modal)continue;
    const obs=new MutationObserver(()=>{
      if(!modal.hidden){
        requestAnimationFrame(()=>requestAnimationFrame(()=>centerModal(modal)));
      }
    });
    obs.observe(modal,{attributes:true,attributeFilter:['hidden','class','style']});
  }

  window.addEventListener('resize',()=>requestAnimationFrame(centerVisible),{passive:true});
  setTimeout(centerVisible,250);
})();

;

(()=>{
  const root=document.getElementById('lisa-app-vg-native');
  if(!root||root.dataset.popupVerticalCenterFixBound==='1')return;
  root.dataset.popupVerticalCenterFixBound='1';

  const modalSelectors=['.lisa-orders-modal','.lisa-overdue-modal','.lisa-reversa-modal'];
  const cardSelector='.lisa-orders-modal-card,.lisa-overdue-modal-card,.lisa-reversa-modal-card';

  function centerVertical(modal){
    if(!modal||modal.hidden||window.innerWidth<=700)return;
    const card=modal.querySelector(cardSelector);
    if(!card)return;
    card.style.setProperty('--lisa-popup-center-shift-y','0px');
    requestAnimationFrame(()=>{
      if(modal.hidden)return;
      const rect=card.getBoundingClientRect();
      if(!rect.height)return;
      const viewportHeight=window.innerHeight||document.documentElement.clientHeight||0;
      const targetTop=Math.max(24,(viewportHeight-rect.height)/2);
      const shiftY=Math.round(targetTop-rect.top);
      card.style.setProperty('--lisa-popup-center-shift-y',shiftY+'px');
    });
  }

  function centerVisible(){
    for(const sel of modalSelectors){
      const modal=root.querySelector(sel);
      if(modal&&!modal.hidden)centerVertical(modal);
    }
  }

  for(const sel of modalSelectors){
    const modal=root.querySelector(sel);
    if(!modal)continue;
    const obs=new MutationObserver(()=>{
      if(!modal.hidden){
        requestAnimationFrame(()=>requestAnimationFrame(()=>centerVertical(modal)));
      }
    });
    obs.observe(modal,{attributes:true,attributeFilter:['hidden','class','style']});
  }

  window.addEventListener('resize',()=>requestAnimationFrame(centerVisible),{passive:true});
  setTimeout(centerVisible,300);
})();

;

(() => {
const root=document.getElementById('lisa-app-vg-native'); if(!root) return;
document.body.classList.add('lisa-panel-page');
const WORKER=(root.dataset.worker||'').replace(/\/$/,'');
// Os popups nativos sao movidos para <body> para nao ficarem limitados por
// transformacoes/filters/containers do tema Shopify em resolucoes e zoom diferentes.
const popupLayerId='lisa-popup-layer-vg-native';
const stalePopupLayer=document.getElementById(popupLayerId);
if(stalePopupLayer)stalePopupLayer.remove();
const popupLayer=document.createElement('div');
popupLayer.id=popupLayerId;
popupLayer.className='lisa-popup-layer';
document.body.appendChild(popupLayer);
for(const el of root.querySelectorAll(':scope > .lisa-modal'))popupLayer.appendChild(el);
// O codigo original procura modais via $; manter compatibilidade sem IDs duplicados.
const $=s=>root.querySelector(s)||popupLayer.querySelector(s);
const $$popup=s=>[...root.querySelectorAll(s),...popupLayer.querySelectorAll(s)];
const tokenKey='lisa_panel_token';
const login=$('[data-login]'), shell=$('[data-shell]'), msg=$('[data-login-msg]');
const loginForm=$('[data-login-form]'),loginPass=$('[data-pass]'),loginSubmit=$('[data-login-submit]'),passwordToggle=$('[data-password-toggle]');
const loginUser=$('[data-user]');
const themeBtn=$('[data-theme-toggle]'), themeKey='lisa_panel_theme';

// A preferencia do painel e independente do tema do computador e do site.
function readPanelTheme(){
  try{return localStorage.getItem(themeKey)==='dark'?'dark':'light'}catch(_){return 'light'}
}
let selectedTheme=readPanelTheme();
function isDark(){return selectedTheme==='dark'}
function updateThemeButton(){if(!themeBtn)return;const dark=isDark();themeBtn.classList.toggle('is-dark',dark);themeBtn.setAttribute('aria-pressed',dark?'true':'false');themeBtn.setAttribute('aria-label',dark?'Ativar tema claro':'Ativar tema escuro');themeBtn.title=dark?'Ativar tema claro':'Ativar tema escuro'}
function applyPanelTheme(){
  const dark=isDark();
  for(const el of [document.documentElement,document.body]){
    if(el.classList.contains('dark-mode')!==dark)el.classList.toggle('dark-mode',dark);
    if(el.style.getPropertyValue('color-scheme')!==selectedTheme)el.style.setProperty('color-scheme',selectedTheme);
  }
  updateThemeButton();
}
function setTheme(dark,persist=true){
  selectedTheme=dark?'dark':'light';
  if(persist){try{localStorage.setItem(themeKey,selectedTheme)}catch(_){}}
  applyPanelTheme();
}
applyPanelTheme();
if(themeBtn)themeBtn.addEventListener('click',()=>setTheme(!isDark()));
// Se o tema do site alterar as classes depois do carregamento, restaurar a escolha.
// So alteramos classes diferentes, evitando um ciclo no MutationObserver.
const themeObserver=new MutationObserver(()=>applyPanelTheme());
themeObserver.observe(document.documentElement,{attributes:true,attributeFilter:['class']});
themeObserver.observe(document.body,{attributes:true,attributeFilter:['class']});
window.addEventListener('pageshow',()=>{selectedTheme=readPanelTheme();applyPanelTheme()});
window.addEventListener('storage',e=>{
  if(e.key===themeKey||e.key===null){selectedTheme=readPanelTheme();applyPanelTheme()}
});
const statusMeta={
 'INTEGRADO':['⇄','#2584cb'],'A SEPARAR':['⌁','#e4a72d'],'A CONFERIR':['✓','#7957d5'],
 'CONFERIDO':['✓✓','#20a05a'],'A EXPEDIR':['➤','#f28b22'],'FINALIZADO':['★','#188a4c'],'CANCELADO':['×','#7b8798'],'NÃO FINALIZADO':['!','#d93632']};
function token(){return sessionStorage.getItem(tokenKey)||''} function setToken(v){sessionStorage.setItem(tokenKey,v)}
async function api(path,opt={}){
 const h={'Content-Type':'application/json',...(opt.headers||{})};if(token())h.Authorization='Bearer '+token();
 const isGet=String(opt.method||'GET').toUpperCase()==='GET';
 const ctrl=isGet&&!opt.signal?new AbortController():null;
 const timer=ctrl?setTimeout(()=>ctrl.abort(),20000):null;
 try{
  const r=await fetch(WORKER+path,{...opt,headers:h,...(ctrl?{signal:ctrl.signal}:{})});
  const d=await r.json().catch(()=>({}));
  if(r.status===401&&path!='/login'){sessionStorage.removeItem(tokenKey);showLogin();throw new Error(d.error||'Sessão expirada.')}
  if(!r.ok){const detail=d.detail?(' — '+d.detail):'';const err=new Error((d.error||(r.status>=500?'Servidor temporariamente sobrecarregado.':'Erro na comunicação.'))+detail);err.status=r.status;throw err}
  return d;
 }catch(e){if(e?.name==='AbortError')throw new Error('A consulta demorou demais. Os dados anteriores permanecem disponíveis.');throw e}
 finally{if(timer)clearTimeout(timer)}
}
const LISA_OFFLINE_DB='lisa_panel_resiliencia_v1';
function lisaOfflineDb(){return new Promise(resolve=>{
 if(!('indexedDB' in window)){resolve(null);return}
 let req;try{req=indexedDB.open(LISA_OFFLINE_DB,1)}catch{resolve(null);return}
 req.onupgradeneeded=()=>{const db=req.result;if(!db.objectStoreNames.contains('views'))db.createObjectStore('views')};
 req.onsuccess=()=>resolve(req.result);req.onerror=()=>resolve(null);req.onblocked=()=>resolve(null);
})}
async function lisaOfflineStore(value){
 const db=await lisaOfflineDb();if(!db)return;
 try{const tx=db.transaction('views','readwrite');tx.objectStore('views').put({data:value,saved_at:new Date().toISOString()},'latest');}catch{}finally{db.close()}
}
async function lisaOfflineRead(){
 const db=await lisaOfflineDb();if(!db)return null;
 return new Promise(resolve=>{try{const tx=db.transaction('views','readonly'),req=tx.objectStore('views').get('latest');req.onsuccess=()=>{resolve(req.result||null);db.close()};req.onerror=()=>{resolve(null);db.close()}}catch{resolve(null);db.close()}});
}
async function lisaOfflineClear(){const db=await lisaOfflineDb();if(!db)return;try{const tx=db.transaction('views','readwrite');tx.objectStore('views').delete('latest')}catch{}finally{db.close()}}
// Uma única conta: Visão Geral, com senha validada no Worker.
function closeProfilePopups(){for(const area of [root,popupLayer,document.getElementById('lisacte-vg-native')]){if(!area)continue;for(const m of area.querySelectorAll('.lisa-modal,.cte-modal,.carrier-resumo-modal,.attendance-history-modal,.attendance-overview-modal'))m.hidden=true}for(const m of document.querySelectorAll('[data-owner="vg-native"]'))m.hidden=true;}
function applyAccountProfile(account){
 if(account?.profile!=='visao-geral')throw new Error('Sessão inválida. Entre em Visão Geral.');
 closeProfilePopups();
}
async function restoreAccountSession(){
 if(!token()||!WORKER){showLogin();return}
 try{const d=await api('/session');applyAccountProfile(d.account);await openLoadedPanel()}catch(e){sessionStorage.removeItem(tokenKey);showLogin();msg.textContent=e.message||'Entre novamente.'}
}

// Liberar as consultas que dependem do painel visível, mantendo o loader sobre ele.
async function openLoadedPanel(){
 setLoginLoading(true);
 shell.hidden=false;
 try{
  await load();
  await new Promise(resolve=>requestAnimationFrame(()=>requestAnimationFrame(resolve)));
  if(!token()||shell.hidden)return;
  showShell();
 }finally{setLoginLoading(false)}
}
function resetPasswordVisibility(){if(!loginPass||!passwordToggle)return;loginPass.type='password';passwordToggle.setAttribute('aria-pressed','false');passwordToggle.setAttribute('aria-label','Mostrar senha');const open=passwordToggle.querySelector('[data-eye-open]'),closed=passwordToggle.querySelector('[data-eye-closed]');if(open)open.hidden=false;if(closed)closed.hidden=true}
function showLogin(){closeProfilePopups();login.hidden=false;shell.hidden=true;if(loginPass)loginPass.value='';resetPasswordVisibility();if(msg)msg.textContent='';setTimeout(()=>loginPass?.focus(),80)}function showShell(){login.hidden=true;shell.hidden=false}
if(passwordToggle&&loginPass)passwordToggle.addEventListener('click',()=>{const visible=loginPass.type==='text';loginPass.type=visible?'password':'text';passwordToggle.setAttribute('aria-pressed',visible?'false':'true');passwordToggle.setAttribute('aria-label',visible?'Mostrar senha':'Ocultar senha');const open=passwordToggle.querySelector('[data-eye-open]'),closed=passwordToggle.querySelector('[data-eye-closed]');if(open)open.hidden=!visible;if(closed)closed.hidden=visible;loginPass.focus();try{loginPass.setSelectionRange(loginPass.value.length,loginPass.value.length)}catch{}});
async function offerBrowserCredentialSave(user,password){try{if(!user||!password||!navigator.credentials||typeof window.PasswordCredential!=='function')return;const cred=new PasswordCredential({id:user,password,name:user});await navigator.credentials.store(cred)}catch{}}
function setLoginLoading(busy){login.toggleAttribute('data-login-busy',busy);login.setAttribute('aria-busy',String(busy));let loader=login.querySelector('[data-login-loader]');if(!loader){loader=document.createElement('div');loader.setAttribute('data-login-loader','');loader.setAttribute('role','status');loader.setAttribute('aria-live','polite');loader.innerHTML='<span class="lisa-login-spinner" aria-hidden="true"></span><p>Preparando seu painel…</p>';login.querySelector('.lisa-login-frame').appendChild(loader)}loader.hidden=!busy}
if(loginForm)loginForm.addEventListener('submit',async e=>{e.preventDefault();if(login.hasAttribute('data-login-busy'))return;msg.textContent='';if(!WORKER){msg.textContent='Configure a URL do Worker nas configurações da seção.';return}const user=String(loginUser?.value||'').trim(),password=String(loginPass?.value||'');if(!user||!password){msg.textContent='Digite sua senha.';return}const labelEl=loginSubmit?.querySelector('span');const oldLabel=labelEl?.textContent||'Entrar';setLoginLoading(true);if(loginSubmit){loginSubmit.disabled=true;if(labelEl)labelEl.textContent='Entrando...'}try{const d=await api('/login',{method:'POST',body:JSON.stringify({user,profile:'visao-geral',password})});if(!d.account)throw new Error('Atualize o Worker para habilitar a conta Visão Geral.');setToken(d.token);applyAccountProfile(d.account);resetPasswordVisibility();await openLoadedPanel();if(token()&&!shell.hidden)void offerBrowserCredentialSave(user,password)}catch(err){setLoginLoading(false);msg.textContent=err.message||'Não foi possível entrar.';if(loginPass){loginPass.focus();loginPass.select()}}finally{setLoginLoading(false);if(loginSubmit){loginSubmit.disabled=false;if(labelEl)labelEl.textContent=oldLabel}}});
$('[data-refresh]').onclick=()=>load();$('[data-logout]').onclick=()=>{sessionStorage.removeItem(tokenKey);lisaOfflineClear().catch(()=>{});showLogin()};
function saldoText(s){if(s<0)return `(+${Math.abs(s)})`;if(s>0)return `(${s})`;return '(0)'}
function dayClass(finalizadas){const n=Number(finalizadas)||0;return n>=240?'day-green':n>=200?'day-yellow':'day-red'}
let ordersBusy=false;
let ordersVersion='';
let ordersVersionBusy=false;
let selectedFileKey='';
let selectedFileName='';
let selectedFileDate='';
let timelineFiles=[];
let latestVersionDate='';
let sysempPedidosLiveBusy=false;
let sysempPedidosLiveLast=null;
function live2Time(v){
 if(!v)return '--/-- --:--';const d=v instanceof Date?v:new Date(v);if(Number.isNaN(d.getTime()))return '--/-- --:--';
 return d.toLocaleString('pt-BR',{timeZone:'America/Sao_Paulo',day:'2-digit',month:'2-digit',hour:'2-digit',minute:'2-digit',hour12:false});
}
function setLive2Updated(sel,v){const n=$(sel);if(n)n.textContent='- Atua. '+live2Time(v)}
function live2Number(v){return Number.isFinite(Number(v))?Number(v):0}
function configureMlRealtimeCards(){
 for(const prefix of ['mlsp','mles']){
   const openStrong=$(`[data-${prefix}-sysemp]`),blockedStrong=$(`[data-${prefix}-blocked]`),oldInvoicedStrong=$(`[data-${prefix}-invoiced]`),todayStrong=$(`[data-${prefix}-today]`)||oldInvoicedStrong;
   const openBox=openStrong?.closest('.lisa-live2-metric'),blockedBox=blockedStrong?.closest('.lisa-live2-metric'),todayBox=todayStrong?.closest('.lisa-live2-metric'),host=openBox?.parentElement;
   if(todayStrong&&!todayStrong.hasAttribute(`data-${prefix}-today`)){todayStrong.setAttribute(`data-${prefix}-today`,'');todayStrong.removeAttribute(`data-${prefix}-invoiced`)}
   const label=todayBox?.querySelector('span');if(label)label.textContent='ENV.HOJE';
   if(host&&todayBox&&blockedBox&&todayBox.nextElementSibling!==blockedBox)host.insertBefore(todayBox,blockedBox);
 }
}
configureMlRealtimeCards();
function renderSysempPedidosLive(data){
 const channels=data?.channels||{};
 const defs=[['edi_sp','edi-sp'],['edi_es','edi-es'],['ml_sp','mlsp'],['ml_es','mles'],['shopee_sp','shopee-sp'],['shopee_es','shopee-es'],['magalu_es','magalu']];
 configureMlRealtimeCards();
 for(const [key,prefix] of defs){
   const d=channels[key]||{},isMl=['ml_sp','ml_es','shopee_sp','shopee_es','magalu_es'].includes(key);
   const values=isMl?{sysemp:live2Number(d.sysemp),today:live2Number(d.send_today),blocked:live2Number(d.blocked)}:{sysemp:live2Number(d.sysemp),blocked:live2Number(d.blocked),invoiced:live2Number(d.invoiced)};
   for(const [metric,val] of Object.entries(values)){const el=$(`[data-${prefix}-${metric}]`);if(el)el.textContent=val.toLocaleString('pt-BR')}
   setLive2Updated(`[data-${prefix}-updated]`,data?.updated_at||data?.sysemp_pedidos_updated_at||null);
 }
 const st=$('[data-mlsp-status]');
 if(st){if(data?.error){st.textContent=data.message||'Erro ao consultar SYSEMP-PEDIDOS.';st.className='lisa-live2-status lisa-mlsp-status is-error'}else{st.textContent=`SYSEMP-PEDIDOS · ${live2Number(data?.total_orders).toLocaleString('pt-BR')} pedidos analisados`;st.className='lisa-live2-status lisa-mlsp-status is-ok'}}
}
let sysempPedidosSnapshotData=null;
let sysempPedidosSnapshotPromise=null;
function hydrateSysempPedidosSnapshot(snapshot){
 const snap=snapshot&&typeof snapshot==='object'?snapshot:{};
 const summary=snap?.summary&&typeof snap.summary==='object'?snap.summary:snap;
 sysempPedidosSnapshotData=snap;
 sysempPedidosLiveLast=summary;
 sysempPedidosReportCache.clear();sysempPedidosReportPromises.clear();
 for(const [key,report] of Object.entries(snap?.reports||{}))sysempPedidosReportCache.set(key,normalizeSysempPedidosReportPayload(report));
 sysempPedidosReportCacheVersion=String(snap?.snapshot_version||[summary?.updated_at||summary?.sysemp_pedidos_updated_at||'',summary?.sysemp_updated_at||''].join('|'));
 renderSysempPedidosLive(summary);
 return snap;
}

async function getSysempPedidosSnapshot(force=false){
 if(sysempPedidosSnapshotData&&!force)return sysempPedidosSnapshotData;
 if(sysempPedidosSnapshotPromise&&!force)return sysempPedidosSnapshotPromise;
 if(force){sysempPedidosSnapshotData=null;sysempPedidosReportCache.clear();sysempPedidosReportPromises.clear()}
 let promise;promise=api('/realtime/sysemp-pedidos/snapshot').then(hydrateSysempPedidosSnapshot).finally(()=>{if(sysempPedidosSnapshotPromise===promise)sysempPedidosSnapshotPromise=null});
 sysempPedidosSnapshotPromise=promise;return promise;
}
async function loadSysempPedidosLive(force=false){
 if((sysempPedidosLiveBusy&&!force)||!token()||!WORKER)return sysempPedidosSnapshotPromise||sysempPedidosSnapshotData;
 sysempPedidosLiveBusy=true;
 try{return await getSysempPedidosSnapshot(force)}
 catch(e){renderSysempPedidosLive({...(sysempPedidosLiveLast||{}),error:true,message:String(e?.message||e)});throw e}
 finally{sysempPedidosLiveBusy=false}
}
root.__sysempPedidosRealtimeReload=()=>loadSysempPedidosLive(true);
/* Monitor de Pedido nao altera SYSEMP/SYSEMP-PEDIDOS. Nao invalida o snapshot para evitar requisicoes desnecessarias. */

let sysempPedidosReportRows=[];
let sysempPedidosReportMeta={channel:'',metric:'',title:'',available_columns:[]};

let sysempPedidosReportFilters={};
let sysempPedidosReportSort={key:'',dir:''};
const sysempPedidosReportCache=new Map();
const sysempPedidosReportPromises=new Map();
let sysempPedidosReportPreloadPromise=null;
let sysempPedidosReportOpenSeq=0;
let sysempPedidosReportCacheVersion='';
let sysempPedidosReportCacheGeneration=0;
function sysempPedidosReportCacheKey(channel,metric){return String(channel||'')+':'+String(metric||'')}
function liveReportExcelDateDisplay(v){
 const s=String(v??'').trim();if(!s)return '';
 if(/^\d{5}(?:\.\d+)?$/.test(s)){const n=Number(s);if(n>20000&&n<90000){const ms=Date.UTC(1899,11,30)+Math.round(n*86400000),d=new Date(ms),date=`${String(d.getUTCDate()).padStart(2,'0')}/${String(d.getUTCMonth()+1).padStart(2,'0')}/${d.getUTCFullYear()}`,hasTime=Math.abs(n-Math.floor(n))>0.000001;return hasTime?`${date} ${String(d.getUTCHours()).padStart(2,'0')}:${String(d.getUTCMinutes()).padStart(2,'0')}`:date}}
 return s;
}
function normalizeSysempPedidosReportPayload(input){
 const d=input&&typeof input==='object'?{...input}:{};
 const cols=Array.isArray(d.available_columns)?d.available_columns:[];
 const dateKeys=cols.filter(c=>/^DATA(?:\s|$)/.test(liveReportNorm(c?.label))).map(c=>String(c?.key||'')).filter(Boolean);
 d.rows=(Array.isArray(d.rows)?d.rows:[]).map(row=>{
   const values={...(row?.values||{})};
   for(const k of dateKeys)if(Object.prototype.hasOwnProperty.call(values,k))values[k]=liveReportExcelDateDisplay(values[k]);
   return {...row,data_pedido:liveReportExcelDateDisplay(row?.data_pedido),data_faturamento:liveReportExcelDateDisplay(row?.data_faturamento),values};
 });
 return d;
}
function getSysempPedidosReport(channel,metric){
 const key=sysempPedidosReportCacheKey(channel,metric);
 if(sysempPedidosReportCache.has(key))return Promise.resolve(sysempPedidosReportCache.get(key));
 if(sysempPedidosReportPromises.has(key))return sysempPedidosReportPromises.get(key);
 let promise;promise=getSysempPedidosSnapshot(false).then(snapshot=>{
   const raw=snapshot?.reports?.[key];if(!raw)throw new Error('Relatório ainda não disponível no snapshot pré-carregado.');
   const d=normalizeSysempPedidosReportPayload(raw);sysempPedidosReportCache.set(key,d);return d;
 }).finally(()=>{if(sysempPedidosReportPromises.get(key)===promise)sysempPedidosReportPromises.delete(key)});
 sysempPedidosReportPromises.set(key,promise);return promise;
}

function liveReportEsc(v){return String(v??'').replace(/[&<>\"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',"'":'&#39;'}[c]))}
function liveReportNorm(v){return String(v??'').normalize('NFD').replace(/[\u0300-\u036f]/g,'').toUpperCase().replace(/\s+/g,' ').trim()}
function liveReportRowValue(row,key){const v=row?.values?.[key];return v===undefined||v===null?'':v}
function liveReportDateMs(v){
 const s=String(v??'').trim();if(!s)return NaN;
 let m=s.match(/^(\d{1,2})\/(\d{1,2})\/(\d{4})(?:\s+(\d{1,2}):(\d{2}))?/);if(m)return Date.UTC(Number(m[3]),Number(m[2])-1,Number(m[1]),Number(m[4]||0),Number(m[5]||0));
 if(/^\d{5}(?:\.\d+)?$/.test(s)){const n=Number(s);if(n>20000&&n<90000)return Date.UTC(1899,11,30)+Math.round(n*86400000)}
 const d=new Date(s);return Number.isNaN(d.getTime())?NaN:d.getTime();
}
function liveReportCompare(a,b){
 const sa=String(a??'').trim(),sb=String(b??'').trim();if(!sa&&!sb)return 0;if(!sa)return 1;if(!sb)return -1;
 const num=v=>{const x=String(v||'').trim();if(!/^-?[\d.,]+$/.test(x))return NaN;return Number(x.includes(',')?x.replace(/\./g,'').replace(',','.'):x)};const na=num(sa),nb=num(sb);if(Number.isFinite(na)&&Number.isFinite(nb))return na-nb;
 const da=liveReportDateMs(sa),db=liveReportDateMs(sb);if(Number.isFinite(da)&&Number.isFinite(db))return da-db;
 return sa.localeCompare(sb,'pt-BR',{numeric:true,sensitivity:'base'});
}
function ensureSysempPedidosReportModal(){
 let modal=document.querySelector('[data-sysemp-pedidos-report-modal]');if(modal)return modal;
 const style=document.createElement('style');style.textContent=`
 .lisa-live2-metric[data-live-report]{cursor:pointer;transition:transform .15s ease,box-shadow .15s ease}.lisa-live2-metric[data-live-report]:hover{transform:translateY(-1px);box-shadow:0 5px 15px rgba(23,52,120,.10)}
 .sp-report-modal{position:fixed;z-index:1000000;inset:0;background:rgba(15,31,51,.42);backdrop-filter:blur(5px);display:grid;place-items:center;padding:22px}.sp-report-modal[hidden]{display:none!important}.sp-report-card{position:relative;width:min(1450px,96vw);height:min(780px,90vh);display:flex;flex-direction:column;background:#fff;border:1px solid #dbe6f3;border-radius:18px;box-shadow:0 28px 80px rgba(16,42,75,.28);overflow:hidden}.sp-report-head{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:18px 20px;border-bottom:1px solid #e5eaf1}.sp-report-head h3{margin:0;color:#18437b;font-size:20px}.sp-report-head small{display:block;margin-top:4px;color:#6f7b90}.sp-report-actions{display:flex;align-items:center;gap:8px}.sp-report-actions button{height:38px;padding:0 14px;border:1px solid #dbe6f3;border-radius:9px;background:#fff;color:#18437b;cursor:pointer}.sp-report-actions .sp-report-close{font-size:20px;width:40px;padding:0}.sp-report-tools{display:flex;gap:10px;padding:12px 20px;border-bottom:1px solid #e5eaf1}.sp-report-tools>input{width:min(520px,100%);height:40px;border:1px solid #cbd9ea;border-radius:9px;padding:0 12px;font:inherit}.sp-report-wrap{flex:1;min-height:0;overflow:auto;padding:0 20px 20px}.sp-report-table{width:max-content;min-width:100%;border-collapse:collapse;table-layout:fixed;font-size:11px}.sp-report-table th{position:sticky;top:0;z-index:52;background:#184b85;color:#fff;text-align:left;padding:0;white-space:nowrap;min-width:120px;vertical-align:top}.sp-report-th-resize{display:block;min-width:105px;width:100%;padding:8px 18px 7px 9px;box-sizing:border-box;position:relative}.sp-report-th-line{display:flex;align-items:center;justify-content:space-between;gap:6px;min-height:24px}.sp-report-th-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sp-report-sort{display:flex;align-items:center;gap:1px;flex:0 0 auto}.sp-report-sort button{width:20px;height:20px;border:0;border-radius:4px;background:transparent;color:#dceaff;padding:0;line-height:20px;cursor:pointer;font-size:12px}.sp-report-sort button:hover,.sp-report-sort button.is-active{background:rgba(255,255,255,.18);color:#fff}.sp-report-col-filter{display:block;width:100%;height:27px;margin-top:5px;border:1px solid rgba(255,255,255,.38);border-radius:5px;background:#fff;color:#1b2c45;padding:0 6px;font-size:10px;outline:none;box-sizing:border-box}.sp-report-col-filter:focus{border-color:#fff;box-shadow:0 0 0 1px rgba(255,255,255,.4)}.sp-report-col-handle{position:absolute;z-index:20;top:0;right:-4px;width:9px;height:100%;cursor:col-resize;touch-action:none;user-select:none}.sp-report-col-handle:after{content:"";position:absolute;top:18%;bottom:18%;left:4px;width:1px;background:currentColor;opacity:.38}.sp-report-table td{padding:9px;border-bottom:1px solid #e5eaf1;vertical-align:top;min-width:120px}.sp-report-partner{min-width:240px;max-width:none;white-space:normal;overflow-wrap:anywhere}.sp-report-table tbody tr:nth-child(even){background:#f7f9fc}.sp-report-empty{padding:28px;text-align:center;color:#6f7b90}.sp-report-block{max-width:420px;white-space:normal}.dark-mode .sp-report-card{background:#14171c;border-color:#29313b;color:#eef4fb}.dark-mode .sp-report-head{border-color:#29313b}.dark-mode .sp-report-head h3{color:#dcecff}.dark-mode .sp-report-head small{color:#9aa7b8}.dark-mode .sp-report-actions button{background:#11161d;border-color:#344154;color:#dcecff}.dark-mode .sp-report-tools{border-color:#29313b}.dark-mode .sp-report-tools>input{background:#10151c;border-color:#344154;color:#fff}.dark-mode .sp-report-table td{border-color:#29313b}.dark-mode .sp-report-table tbody tr:nth-child(even){background:#11161d}
 @media(max-width:760px){.sp-report-modal{padding:8px}.sp-report-card{width:100%;height:94vh;border-radius:12px}.sp-report-head{align-items:flex-start;padding:14px}.sp-report-actions{gap:5px;flex-wrap:wrap;justify-content:flex-end}.sp-report-actions button{padding:0 9px;font-size:10px}.sp-report-tools{padding:10px 14px}.sp-report-wrap{padding:0 14px 14px}}
 `;document.head.appendChild(style);
 modal=document.createElement('div');modal.className='sp-report-modal';modal.setAttribute('data-sysemp-pedidos-report-modal','');modal.hidden=true;modal.innerHTML=`<div class="sp-report-card"><div class="sp-report-head"><div><h3 data-sp-report-title>Relatório</h3><small data-sp-report-count>0 pedidos</small></div><div class="sp-report-actions"><button type="button" data-sp-report-daily hidden>Relatórios</button><button type="button" data-sp-report-copy>Copiar</button><button type="button" data-sp-report-download>Baixar</button><button type="button" class="sp-report-close" data-sp-report-close aria-label="Fechar">×</button></div></div><div class="sp-report-tools"><input type="search" data-sp-report-search placeholder="Buscar em todo o relatório"></div><div class="sp-report-wrap"><table class="sp-report-table"><thead data-sp-report-head></thead><tbody data-sp-report-body></tbody></table><div class="sp-report-empty" data-sp-report-empty hidden>Nenhum pedido encontrado.</div></div></div>`;
 document.body.appendChild(modal);
 modal.querySelector('[data-sp-report-close]').addEventListener('click',()=>{sysempPedidosReportOpenSeq++;modal.hidden=true});
 modal.addEventListener('click',e=>{if(e.target===modal){sysempPedidosReportOpenSeq++;modal.hidden=true;return}});
 modal.querySelector('[data-sp-report-search]').addEventListener('input',()=>renderSysempPedidosReport());
 modal.querySelector('[data-sp-report-copy]').addEventListener('click',copySysempPedidosReport);
 modal.querySelector('[data-sp-report-download]').addEventListener('click',downloadSysempPedidosReport);
 modal.querySelector('[data-sp-report-daily]').addEventListener('click',()=>openSysempPedidosDailyBillingReport());
 modal.querySelector('[data-sp-report-head]').addEventListener('click',e=>{const b=e.target.closest('button[data-sp-sort-key]');if(!b)return;e.preventDefault();e.stopPropagation();sysempPedidosReportSort={key:b.dataset.spSortKey||'',dir:b.dataset.spSortDir||'asc'};renderSysempPedidosReport()});
 modal.querySelector('[data-sp-report-head]').addEventListener('input',e=>{const inp=e.target.closest('input[data-sp-col-filter]');if(!inp)return;sysempPedidosReportFilters[inp.dataset.spColFilter]=inp.value;renderSysempPedidosReport({keepFocusKey:inp.dataset.spColFilter,keepCaret:inp.selectionStart})});
 return modal;
}
const SYSEMP_PEDIDOS_FIXED_COLUMN_LABELS=Object.freeze(['Pedido','Pedido MarketPlace','Parceiro de Negócio','Canal de Venda','Data Pedido','UF','Carrinho ML','Rota Cliente','Transportadora','Observação Pedido','ID']);
function sysempPedidosAllColumns(){return Array.isArray(sysempPedidosReportMeta.available_columns)?sysempPedidosReportMeta.available_columns:[]}
function sysempPedidosBillingDateValue(row){
 const direct=String(row?.data_faturamento||'').trim();if(direct)return liveReportExcelDateDisplay(direct);
 const values=row?.values||{};
 if(String(values.data_faturamento||'').trim())return liveReportExcelDateDisplay(values.data_faturamento);
 const col=(Array.isArray(sysempPedidosReportMeta.available_columns)?sysempPedidosReportMeta.available_columns:[]).find(c=>liveReportNorm(c?.label)==='DATA FATURAMENTO');
 return col?liveReportExcelDateDisplay(values[col.key]):'';
}
function sysempPedidosBillingDayKey(v){
 const s=liveReportExcelDateDisplay(v),m=String(s||'').match(/^(\d{1,2})\/(\d{1,2})\/(\d{4})/);
 if(!m)return '';return `${String(m[1]).padStart(2,'0')}/${String(m[2]).padStart(2,'0')}/${m[3]}`;
}
function sysempPedidosBillingDayMs(v){const m=String(v||'').match(/^(\d{2})\/(\d{2})\/(\d{4})$/);return m?Date.UTC(Number(m[3]),Number(m[2])-1,Number(m[1])):NaN}
function ensureSysempPedidosDailyBillingModal(){
 let m=document.querySelector('[data-sp-daily-billing-modal]');if(m)return m;
 const st=document.createElement('style');st.textContent=`
 .sp-daily-modal{position:fixed;z-index:1000200;inset:0;background:rgba(15,31,51,.50);backdrop-filter:blur(5px);display:grid;place-items:center;padding:24px}.sp-daily-modal[hidden]{display:none!important}.sp-daily-card{width:min(680px,94vw);max-height:82vh;display:flex;flex-direction:column;background:#fff;border:1px solid #dbe6f3;border-radius:16px;box-shadow:0 28px 80px rgba(16,42,75,.30);overflow:hidden}.sp-daily-head{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:18px 20px;border-bottom:1px solid #e5eaf1}.sp-daily-head h3{margin:0;color:#18437b;font-size:19px}.sp-daily-head small{display:block;margin-top:4px;color:#6f7b90}.sp-daily-close{width:40px;height:38px;border:1px solid #dbe6f3;border-radius:9px;background:#fff;color:#18437b;font-size:20px;cursor:pointer}.sp-daily-summary{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;padding:14px 20px}.sp-daily-kpi{border:1px solid #dbe6f3;border-radius:10px;padding:12px 14px;background:#f8fbff}.sp-daily-kpi span{display:block;font-size:10px;text-transform:uppercase;color:#73839a}.sp-daily-kpi strong{display:block;margin-top:4px;font-size:22px;color:#18437b}.sp-daily-wrap{overflow:auto;padding:0 20px 20px}.sp-daily-table{width:100%;border-collapse:collapse;font-size:12px}.sp-daily-table th{position:sticky;top:0;background:#184b85;color:#fff;text-align:left;padding:10px}.sp-daily-table th:last-child,.sp-daily-table td:last-child{text-align:right}.sp-daily-table td{padding:10px;border-bottom:1px solid #e5eaf1}.sp-daily-table tbody tr:nth-child(even){background:#f7f9fc}.sp-daily-empty{padding:24px;text-align:center;color:#6f7b90}.dark-mode .sp-daily-card{background:#14171c;border-color:#29313b;color:#eef4fb}.dark-mode .sp-daily-head{border-color:#29313b}.dark-mode .sp-daily-head h3,.dark-mode .sp-daily-kpi strong{color:#dcecff}.dark-mode .sp-daily-close{background:#11161d;border-color:#344154;color:#dcecff}.dark-mode .sp-daily-kpi{background:#11161d;border-color:#29313b}.dark-mode .sp-daily-table td{border-color:#29313b}.dark-mode .sp-daily-table tbody tr:nth-child(even){background:#11161d}@media(max-width:600px){.sp-daily-modal{padding:10px}.sp-daily-summary{grid-template-columns:1fr}.sp-daily-card{width:100%;max-height:90vh}}
 `;document.head.appendChild(st);
 m=document.createElement('div');m.className='sp-daily-modal';m.setAttribute('data-sp-daily-billing-modal','');m.hidden=true;m.innerHTML=`<div class="sp-daily-card"><div class="sp-daily-head"><div><h3 data-sp-daily-title>Faturados por dia</h3><small data-sp-daily-subtitle></small></div><button type="button" class="sp-daily-close" data-sp-daily-close aria-label="Fechar">×</button></div><div class="sp-daily-summary"><div class="sp-daily-kpi"><span>Pedidos faturados</span><strong data-sp-daily-total>0</strong></div><div class="sp-daily-kpi"><span>Dias com faturamento</span><strong data-sp-daily-days>0</strong></div></div><div class="sp-daily-wrap"><table class="sp-daily-table"><thead><tr><th>Data do faturamento</th><th>Pedidos</th></tr></thead><tbody data-sp-daily-body></tbody></table><div class="sp-daily-empty" data-sp-daily-empty hidden>Nenhuma data de faturamento encontrada.</div></div></div>`;document.body.appendChild(m);
 m.querySelector('[data-sp-daily-close]').addEventListener('click',()=>{m.hidden=true});m.addEventListener('click',e=>{if(e.target===m)m.hidden=true});return m;
}
function openSysempPedidosDailyBillingReport(){
 if(sysempPedidosReportMeta.metric!=='invoiced')return;
 const modal=ensureSysempPedidosDailyBillingModal(),counts=new Map();let semData=0;
 for(const row of sysempPedidosReportRows){const day=sysempPedidosBillingDayKey(sysempPedidosBillingDateValue(row));if(day)counts.set(day,(counts.get(day)||0)+1);else semData++}
 const rows=[...counts.entries()].sort((a,b)=>sysempPedidosBillingDayMs(b[0])-sysempPedidosBillingDayMs(a[0]));if(semData)rows.push(['Sem data',semData]);
 const channelLabel={edi_sp:'EDI SP',edi_es:'EDI ES',ml_sp:'ML SP',ml_es:'ML ES',shopee_sp:'SHOPEE SP',shopee_es:'SHOPEE ES',magalu_es:'MAGALU ES'}[sysempPedidosReportMeta.channel]||sysempPedidosReportMeta.channel||'';
 modal.querySelector('[data-sp-daily-title]').textContent=`${channelLabel} · Faturados por dia`;modal.querySelector('[data-sp-daily-subtitle]').textContent='Baseado na Data Faturamento da nota fiscal';modal.querySelector('[data-sp-daily-total]').textContent=sysempPedidosReportRows.length.toLocaleString('pt-BR');modal.querySelector('[data-sp-daily-days]').textContent=counts.size.toLocaleString('pt-BR');
 const body=modal.querySelector('[data-sp-daily-body]'),empty=modal.querySelector('[data-sp-daily-empty]');body.innerHTML=rows.map(([date,count])=>`<tr><td>${liveReportEsc(date)}</td><td><strong>${Number(count||0).toLocaleString('pt-BR')}</strong></td></tr>`).join('');empty.hidden=rows.length>0;modal.hidden=false;
}
function sysempPedidosReportColumns(){
 const available=sysempPedidosAllColumns(),byLabel=new Map();
 for(const column of available){const label=liveReportNorm(column?.label);if(!byLabel.has(label))byLabel.set(label,column)}
 return SYSEMP_PEDIDOS_FIXED_COLUMN_LABELS.map(label=>byLabel.get(liveReportNorm(label))||{key:'fixed_'+liveReportNorm(label).toLowerCase().replace(/[^a-z0-9]+/g,'_'),label,source:'sysemp-pedidos'});
}
function filteredSysempPedidosReportRows(){
 const modal=ensureSysempPedidosReportModal(),globalQ=String(modal.querySelector('[data-sp-report-search]')?.value||'').trim().toLowerCase(),cols=sysempPedidosReportColumns();
 let rows=sysempPedidosReportRows.filter(r=>{
   if(globalQ){const all=Object.values(r?.values||{}).some(v=>String(v??'').toLowerCase().includes(globalQ));if(!all)return false}
   for(const c of cols){const q=String(sysempPedidosReportFilters[c.key]||'').trim().toLowerCase();if(q&&!String(liveReportRowValue(r,c.key)).toLowerCase().includes(q))return false}
   return true;
 });
 if(sysempPedidosReportSort.key){const {key,dir}=sysempPedidosReportSort;rows=[...rows].map((r,i)=>({r,i})).sort((a,b)=>{const c=liveReportCompare(liveReportRowValue(a.r,key),liveReportRowValue(b.r,key));return c===0?a.i-b.i:c*(dir==='desc'?-1:1)}).map(x=>x.r)}
 return rows;
}
function renderSysempPedidosReport(opts={}){
 const modal=ensureSysempPedidosReportModal(),cols=sysempPedidosReportColumns(),rows=filteredSysempPedidosReportRows(),table=modal.querySelector('.sp-report-table');
 const signature=cols.map(x=>x.key).join('|');if(table&&table.dataset.lisaResizeSignature!==signature){lisaResetResizeTable(table);table.dataset.lisaResizeSignature=signature}
 modal.querySelector('[data-sp-report-title]').textContent=sysempPedidosReportMeta.title||'Relatório';const total=sysempPedidosReportRows.length;modal.querySelector('[data-sp-report-count]').textContent=rows.length===total?`${total.toLocaleString('pt-BR')} pedido${total===1?'':'s'}`:`${rows.length.toLocaleString('pt-BR')} de ${total.toLocaleString('pt-BR')} pedidos`;
 const active=sysempPedidosReportSort;
 modal.querySelector('[data-sp-report-head]').innerHTML='<tr>'+cols.map(c=>`<th><div class="sp-report-th-resize"><div class="sp-report-th-line"><span class="sp-report-th-label" title="${liveReportEsc(c.label)}">${liveReportEsc(c.label)}</span><span class="sp-report-sort"><button type="button" data-sp-sort-key="${liveReportEsc(c.key)}" data-sp-sort-dir="asc" class="${active.key===c.key&&active.dir==='asc'?'is-active':''}" title="Menor para maior">↑</button><button type="button" data-sp-sort-key="${liveReportEsc(c.key)}" data-sp-sort-dir="desc" class="${active.key===c.key&&active.dir==='desc'?'is-active':''}" title="Maior para menor">↓</button></span></div><input class="sp-report-col-filter" data-sp-col-filter="${liveReportEsc(c.key)}" value="${liveReportEsc(sysempPedidosReportFilters[c.key]||'')}" placeholder="Filtrar"><span class="sp-report-col-handle" title="Arraste para redimensionar somente esta coluna" aria-hidden="true"></span></div></th>`).join('')+'</tr>';
 const body=modal.querySelector('[data-sp-report-body]'),empty=modal.querySelector('[data-sp-report-empty]');body.innerHTML=rows.map(r=>'<tr>'+cols.map(c=>{const n=liveReportNorm(c.label),cls=n==='MOTIVO DO BLOQUEIO'?'sp-report-block':(n==='PARCEIRO DE NEGOCIO'?'sp-report-partner':'');return `<td${cls?` class="${cls}"`:''}>${liveReportEsc(liveReportRowValue(r,c.key)||'-')}</td>`}).join('')+'</tr>').join('');empty.hidden=rows.length>0;
 lisaBindIndependentColumnResize(table,'.sp-report-col-handle');
 if(opts.keepFocusKey){const inp=modal.querySelector(`[data-sp-col-filter="${CSS.escape(opts.keepFocusKey)}"]`);if(inp){inp.focus();const p=Math.min(Number(opts.keepCaret??inp.value.length),inp.value.length);try{inp.setSelectionRange(p,p)}catch{}}}
}
function applySysempPedidosReportData(channel,metric,d){
 sysempPedidosReportRows=Array.isArray(d?.rows)?d.rows:[];
 sysempPedidosReportMeta={channel,metric,title:`${d?.channel_label||channel} · ${d?.metric_label||metric}`,available_columns:Array.isArray(d?.available_columns)?d.available_columns:[],required_column_labels:Array.isArray(d?.required_column_labels)?d.required_column_labels:[]};
 const dailyBtn=ensureSysempPedidosReportModal().querySelector('[data-sp-report-daily]');if(dailyBtn)dailyBtn.hidden=metric!=='invoiced';
 renderSysempPedidosReport();
}
async function openSysempPedidosReport(channel,metric){
 const seq=++sysempPedidosReportOpenSeq,modal=ensureSysempPedidosReportModal(),cacheKey=sysempPedidosReportCacheKey(channel,metric);
 modal.hidden=false;const dailyBtn=modal.querySelector('[data-sp-report-daily]');if(dailyBtn)dailyBtn.hidden=metric!=='invoiced';modal.querySelector('[data-sp-report-search]').value='';sysempPedidosReportFilters={};sysempPedidosReportSort={key:'',dir:''};
 const cached=sysempPedidosReportCache.get(cacheKey);if(cached){applySysempPedidosReportData(channel,metric,cached);return}
 modal.querySelector('[data-sp-report-title]').textContent='Carregando relatório...';modal.querySelector('[data-sp-report-count]').textContent='Dados sendo pré-carregados';modal.querySelector('[data-sp-report-body]').innerHTML='';modal.querySelector('[data-sp-report-empty]').hidden=true;
 try{const d=await getSysempPedidosReport(channel,metric);if(seq!==sysempPedidosReportOpenSeq||modal.hidden)return;applySysempPedidosReportData(channel,metric,d)}catch(e){if(seq!==sysempPedidosReportOpenSeq||modal.hidden)return;sysempPedidosReportRows=[];sysempPedidosReportMeta={channel,metric,title:'Erro ao carregar relatório',available_columns:[]};modal.querySelector('[data-sp-report-count]').textContent=String(e?.message||e);modal.querySelector('[data-sp-report-empty]').hidden=false;modal.querySelector('[data-sp-report-empty]').textContent='Não foi possível carregar os dados.'}
}
function copySysempPedidosReport(){const cols=sysempPedidosReportColumns(),rows=filteredSysempPedidosReportRows(),txt=[cols.map(c=>c.label).join('\t'),...rows.map(r=>cols.map(c=>String(liveReportRowValue(r,c.key)??'').replace(/[\t\r\n]+/g,' ')).join('\t'))].join('\n');navigator.clipboard?.writeText(txt).then(()=>{}).catch(()=>{});}
async function downloadSysempPedidosReport(){
 try{const XLSX=await ensureXlsxLibrary(),cols=sysempPedidosReportColumns(),rows=filteredSysempPedidosReportRows(),aoa=[cols.map(c=>c.label),...rows.map(r=>cols.map(c=>String(liveReportRowValue(r,c.key)??'')))],ws=XLSX.utils.aoa_to_sheet(aoa);ws['!cols']=cols.map(c=>({wch:Math.max(16,Math.min(48,String(c.label||'').length+8))}));const wb=XLSX.utils.book_new();XLSX.utils.book_append_sheet(wb,ws,'Pedidos');const safe=(sysempPedidosReportMeta.title||'relatorio').normalize('NFD').replace(/[\u0300-\u036f]/g,'').replace(/[^A-Za-z0-9_-]+/g,'_');XLSX.writeFile(wb,safe+'.xlsx',{compression:true})}catch(e){alert(e.message||'Não foi possível gerar o Excel.')}
}
function bindSysempPedidosRealtimeReports(){
 const defs=[
  ['[data-edi-sp-sysemp]','edi_sp','sysemp'],['[data-edi-sp-blocked]','edi_sp','blocked'],['[data-edi-sp-invoiced]','edi_sp','invoiced'],
  ['[data-edi-es-sysemp]','edi_es','sysemp'],['[data-edi-es-blocked]','edi_es','blocked'],['[data-edi-es-invoiced]','edi_es','invoiced'],
  ['[data-mlsp-sysemp]','ml_sp','sysemp'],['[data-mlsp-today]','ml_sp','send_today'],['[data-mlsp-blocked]','ml_sp','blocked'],
  ['[data-mles-sysemp]','ml_es','sysemp'],['[data-mles-today]','ml_es','send_today'],['[data-mles-blocked]','ml_es','blocked'],
  ['[data-shopee-sp-sysemp]','shopee_sp','sysemp'],['[data-shopee-sp-today]','shopee_sp','send_today'],['[data-shopee-sp-blocked]','shopee_sp','blocked'],
  ['[data-shopee-es-sysemp]','shopee_es','sysemp'],['[data-shopee-es-today]','shopee_es','send_today'],['[data-shopee-es-blocked]','shopee_es','blocked'],
  ['[data-magalu-sysemp]','magalu_es','sysemp'],['[data-magalu-today]','magalu_es','send_today'],['[data-magalu-blocked]','magalu_es','blocked']
 ];
 for(const [sel,channel,metric] of defs){const strong=$(sel),box=strong?.closest('.lisa-live2-metric');if(!box||box.dataset.liveReport)return;box.dataset.liveReport=channel+':'+metric;box.setAttribute('role','button');box.tabIndex=0;const open=()=>openSysempPedidosReport(channel,metric);box.addEventListener('click',open);box.addEventListener('keydown',e=>{if(e.key==='Enter'||e.key===' '){e.preventDefault();open()}})}
}
bindSysempPedidosRealtimeReports();

/* Cabecalhos dos relatorios permanecem fixos durante a rolagem vertical. */
(function lisaEnsureStickyReportHeaders(){
 if(document.getElementById('lisa-sticky-report-head-fix'))return;
 const st=document.createElement('style');st.id='lisa-sticky-report-head-fix';
 st.textContent=`
 .lisa-orders-table thead,.lisa-overdue-table thead,.lisa-reversa-table thead,.cte-table thead,.sp-report-table thead,.carrier-resumo-view table thead{position:sticky!important;top:0!important;z-index:50!important}
 .lisa-orders-table thead th,.lisa-overdue-table thead th,.lisa-reversa-table thead th,.cte-table thead th,.sp-report-table thead th,.carrier-resumo-view table thead th{top:0!important;z-index:51!important}
 `;
 document.head.appendChild(st);
})();

/* Redimensionamento independente por COLGROUP somente com as colunas VISIVEIS.
   Os relatorios do painel possuem THs ocultos que mudam conforme o tipo de relatorio.
   O COLGROUP precisa representar apenas as colunas que realmente participam da tabela;
   caso contrario as celulas do tbody caem em colunas de largura zero e ficam empilhadas. */
function lisaResizeHeads(table){
 const row=table?.tHead?.rows?.[0]||table?.querySelector?.('thead tr');
 return row?[...row.cells]:[];
}
function lisaResizeHeadVisible(th){
 if(!th||th.hidden)return false;
 const cs=getComputedStyle(th);return cs.display!=='none'&&cs.visibility!=='collapse';
}
function lisaResizeVisibleHeads(table){return lisaResizeHeads(table).filter(lisaResizeHeadVisible)}
function lisaResizeExistingColgroup(table){
 if(!table)return null;
 for(const child of table.children||[])if(child.tagName==='COLGROUP'&&child.dataset?.lisaResizeCols==='1')return child;
 return null;
}
function lisaResetResizeTable(table){
 if(!table)return;
 const g=lisaResizeExistingColgroup(table);if(g)g.remove();
 delete table.dataset.lisaResizeFrozen;delete table.dataset.lisaResizeSignature;
 table.style.removeProperty('table-layout');
 table.style.removeProperty('width');
 table.style.removeProperty('min-width');
 table.style.removeProperty('max-width');
}
function lisaResizeSignature(table){
 const all=lisaResizeHeads(table);
 return all.map((th,i)=>lisaResizeHeadVisible(th)?('1:'+i+':'+lisaNormResizeHeader(th.innerText||th.textContent||'')):'0').join('|');
}
function lisaPrepareResizeTable(table){
 if(!table)return [];
 const visible=lisaResizeVisibleHeads(table);if(!visible.length)return [];
 const signature=lisaResizeSignature(table);
 let group=lisaResizeExistingColgroup(table);
 if(table.dataset.lisaResizeFrozen==='1'&&table.dataset.lisaResizeSignature!==signature){lisaResetResizeTable(table);group=null}
 if(table.dataset.lisaResizeFrozen==='1'&&group&&group.children.length===visible.length){
   const ready=[...group.children].map((c,i)=>Math.max(1,parseFloat(c.style.width)||visible[i]?.getBoundingClientRect().width||0));
   if(ready.every(w=>w>1))return ready;
 }
 /* Mede somente os THs visiveis ANTES de criar o colgroup. */
 const widths=visible.map(th=>Math.max(80,Math.round(th.getBoundingClientRect().width||th.offsetWidth||120)));
 if(widths.some(w=>!Number.isFinite(w)||w<=1))return [];
 const total=widths.reduce((a,b)=>a+b,0);
 if(!group){group=document.createElement('colgroup');group.dataset.lisaResizeCols='1';table.insertBefore(group,table.firstChild)}
 while(group.children.length<visible.length)group.appendChild(document.createElement('col'));
 while(group.children.length>visible.length)group.lastElementChild.remove();
 [...group.children].forEach((col,i)=>col.style.setProperty('width',widths[i]+'px','important'));
 table.style.setProperty('table-layout','fixed','important');
 table.style.setProperty('width',Math.ceil(total)+'px','important');
 table.style.setProperty('min-width',Math.ceil(total)+'px','important');
 table.style.setProperty('max-width','none','important');
 table.dataset.lisaResizeFrozen='1';table.dataset.lisaResizeSignature=signature;
 return widths;
}
function lisaVisibleColumnIndex(table,th){return lisaResizeVisibleHeads(table).indexOf(th)}
function lisaSetOnlyColumnWidth(table,index,width){
 if(!table||index<0)return;
 const visible=lisaResizeVisibleHeads(table);if(index>=visible.length)return;
 let group=lisaResizeExistingColgroup(table);
 if(table.dataset.lisaResizeFrozen!=='1'||!group||group.children.length!==visible.length){
   const prepared=lisaPrepareResizeTable(table);if(!prepared.length)return;group=lisaResizeExistingColgroup(table);
 }
 const cols=[...group.children],col=cols[index];if(!col)return;
 const oldW=Math.max(1,parseFloat(col.style.width)||visible[index].getBoundingClientRect().width||120);
 const newW=Math.max(80,Math.min(1200,Math.round(width)));
 if(Math.abs(newW-oldW)<1)return;
 const oldTableW=Math.max(1,parseFloat(table.style.width)||cols.reduce((sum,c)=>sum+(parseFloat(c.style.width)||0),0));
 /* Altera APENAS o COL da coluna ativa. As demais larguras nao sao tocadas. */
 col.style.setProperty('width',newW+'px','important');
 const newTableW=Math.max(1,Math.round(oldTableW+(newW-oldW)));
 table.style.setProperty('width',newTableW+'px','important');
 table.style.setProperty('min-width',newTableW+'px','important');
}
function lisaBindIndependentColumnResize(table,handleSelector){
 if(!table)return;
 const handles=[...table.querySelectorAll(handleSelector)];if(!handles.length)return;
 if(table.getBoundingClientRect().width>1)lisaPrepareResizeTable(table);
 handles.forEach(handle=>{
   if(handle.dataset.independentResizeBound==='1')return;
   handle.dataset.independentResizeBound='1';
   handle.addEventListener('pointerdown',e=>{
     if(e.button!==undefined&&e.button!==0)return;
     e.preventDefault();e.stopPropagation();
     const th=handle.closest('th');if(!th||!lisaResizeHeadVisible(th))return;
     const widths=lisaPrepareResizeTable(table);if(!widths.length)return;
     const index=lisaVisibleColumnIndex(table,th);if(index<0)return;
     const startX=e.clientX,startW=widths[index]||th.getBoundingClientRect().width;
     handle.setPointerCapture?.(e.pointerId);document.body.classList.add('lisa-column-resizing');
     const move=ev=>{ev.preventDefault();lisaSetOnlyColumnWidth(table,index,startW+(ev.clientX-startX))};
     const up=ev=>{handle.removeEventListener('pointermove',move);handle.removeEventListener('pointerup',up);handle.removeEventListener('pointercancel',up);document.body.classList.remove('lisa-column-resizing');try{handle.releasePointerCapture?.(ev.pointerId)}catch{}};
     handle.addEventListener('pointermove',move);handle.addEventListener('pointerup',up);handle.addEventListener('pointercancel',up);
   });
 });
}

/* Redimensionamento horizontal da coluna Destinatario em todos os relatorios do painel. */
function lisaNormResizeHeader(v){return String(v??'').normalize('NFD').replace(/[\u0300-\u036f]/g,'').toUpperCase().replace(/\s+/g,' ').trim()}
function lisaBindRecipientResize(table){
 if(!table)return;
 const heads=lisaResizeHeads(table);
 let index=-1;
 for(let i=0;i<heads.length;i++){
   const label=lisaNormResizeHeader(heads[i].innerText||heads[i].textContent||'');
   if(label.includes('DESTINATARIO')){index=i;break}
 }
 if(index<0)return;
 const th=heads[index];
 th.style.position='sticky';th.style.top='0';th.style.zIndex='55';
 let handle=th.querySelector(':scope > .lisa-recipient-resize-handle');
 if(!handle){
   handle=document.createElement('span');handle.className='lisa-recipient-resize-handle';handle.title='Arraste para redimensionar Destinatario';handle.setAttribute('aria-hidden','true');th.appendChild(handle);
 }
 if(handle.dataset.independentResizeBound==='1')return;
 handle.dataset.independentResizeBound='1';
 /* Prepara sem esperar o clique sempre que o relatorio ja estiver aberto. */
 if(table.getBoundingClientRect().width>1)lisaPrepareResizeTable(table);
 handle.addEventListener('pointerdown',e=>{
   if(e.button!==undefined&&e.button!==0)return;
   e.preventDefault();e.stopPropagation();
   const widths=lisaPrepareResizeTable(table);if(!widths.length)return;
   const visibleIndex=lisaVisibleColumnIndex(table,th);if(visibleIndex<0)return;
   const startX=e.clientX,startW=widths[visibleIndex]||th.getBoundingClientRect().width;
   handle.setPointerCapture?.(e.pointerId);document.body.classList.add('lisa-column-resizing');
   const move=ev=>{
     ev.preventDefault();
     lisaSetOnlyColumnWidth(table,visibleIndex,Math.max(120,Math.min(900,startW+(ev.clientX-startX))));
     for(const tbody of table.tBodies||[])for(const tr of tbody.rows||[]){const td=tr.cells?.[visibleIndex];if(td){td.style.whiteSpace='normal';td.style.overflowWrap='anywhere'}}
   };
   const up=ev=>{handle.removeEventListener('pointermove',move);handle.removeEventListener('pointerup',up);handle.removeEventListener('pointercancel',up);document.body.classList.remove('lisa-column-resizing');try{handle.releasePointerCapture?.(ev.pointerId)}catch{}};
   handle.addEventListener('pointermove',move);handle.addEventListener('pointerup',up);handle.addEventListener('pointercancel',up);
 });
}
function lisaScanRecipientResize(rootNode=document){
 const scope=rootNode?.querySelectorAll?rootNode:document;
 if(scope.matches?.('table'))lisaBindRecipientResize(scope);
 for(const table of scope.querySelectorAll?.('table')||[])lisaBindRecipientResize(table);
}
if(!window.__lisaRecipientResizeInstalled){
 window.__lisaRecipientResizeInstalled=true;
 const st=document.createElement('style');st.textContent=`
 .lisa-recipient-resize-handle{position:absolute;z-index:80;top:0;right:-4px;width:9px;height:100%;cursor:col-resize;touch-action:none;user-select:none}
 .lisa-recipient-resize-handle:after{content:"";position:absolute;top:20%;bottom:20%;left:4px;width:1px;background:currentColor;opacity:.35}
 body.lisa-column-resizing,body.lisa-column-resizing *{cursor:col-resize!important;user-select:none!important}
 table[data-lisa-resize-frozen="1"] th,table[data-lisa-resize-frozen="1"] td{min-width:0!important;max-width:none!important;box-sizing:border-box}
 table[data-lisa-resize-frozen="1"] th input,table[data-lisa-resize-frozen="1"] th select,table[data-lisa-resize-frozen="1"] th button{min-width:0!important;max-width:100%!important;box-sizing:border-box!important}
 `;document.head.appendChild(st);
 lisaScanRecipientResize(document);
 const ob=new MutationObserver(muts=>{for(const m of muts)for(const n of m.addedNodes||[])if(n.nodeType===1)lisaScanRecipientResize(n)});ob.observe(document.body,{childList:true,subtree:true});
}
async function load(){const tasks=[loadLatestVersionInfo(),loadReversaMeta()];tasks.push(loadOrders(),loadSysempPedidosLive(),loadAttendance());await Promise.allSettled(tasks)}
let carrierSummaryItems=[];
let carrierOccurrenceCache={at:0,rows:[]};
function carrierNormHeader(v){return String(v??'').trim().normalize('NFD').replace(/[\u0300-\u036f]/g,'').toUpperCase().replace(/[^A-Z0-9]+/g,' ').trim()}
function carrierNormKey(v){return carrierNormHeader(v).replace(/\s+/g,' ').trim()}
function carrierNoteKey(v){return String(v??'').trim().replace(/\.0+$/,'').replace(/\D+/g,'').replace(/^0+(?=\d)/,'')}

function carrierParseDate(value){
 if(value instanceof Date&&!Number.isNaN(value.getTime()))return new Date(value.getFullYear(),value.getMonth(),value.getDate());
 if(typeof value==='number'&&Number.isFinite(value)&&value>20000&&value<100000){const ms=Date.UTC(1899,11,30)+Math.round(value)*86400000,d=new Date(ms);return new Date(d.getUTCFullYear(),d.getUTCMonth(),d.getUTCDate())}
 const s=String(value??'').trim();if(!s)return null;
 if(/^\d{5}(?:\.0+)?$/.test(s)){const n=Number(s);if(Number.isFinite(n)&&n>20000&&n<100000){const ms=Date.UTC(1899,11,30)+Math.round(n)*86400000,d=new Date(ms);return new Date(d.getUTCFullYear(),d.getUTCMonth(),d.getUTCDate())}}
 let m=s.match(/\b(\d{1,2})[\/-](\d{1,2})[\/-](\d{4})\b/);if(m)return new Date(+m[3],+m[2]-1,+m[1]);m=s.match(/\b(\d{4})-(\d{1,2})-(\d{1,2})\b/);if(m)return new Date(+m[1],+m[2]-1,+m[3]);return null;
}
function carrierFormatDate(value){const d=carrierParseDate(value);if(!d)return String(value??'').trim()||'-';return String(d.getDate()).padStart(2,'0')+'/'+String(d.getMonth()+1).padStart(2,'0')+'/'+d.getFullYear()}
function carrierStartOfDay(d=new Date()){return new Date(d.getFullYear(),d.getMonth(),d.getDate())}
function carrierIsOnTimeDelivered(value){const s=carrierNormHeader(value);return /ENTREGUE DENTRO DO PRAZO/.test(s)}
function carrierIsDeliveredStatus(value){
 const s=carrierNormHeader(value);
 return /(^| )ENTREGUE($| )|CARGA ENTREGUE|ENTREGA REALIZADA|MERCADORIA ENTREGUE|OBJETO ENTREGUE/.test(s);
}
function carrierIsOverdueStatus(value){
 const s=carrierNormHeader(value);
 return /ATRASAD/.test(s);
}
function carrierIsReturnedStatus(value){
 const s=carrierNormHeader(value);
 if(!s)return false;
 return /(^| )DEVOLVIDO($| )/.test(s)||/EM DEVOLUCAO/.test(s);
}
function carrierIsOccurrenceExcludedStatus(value){
 const s=carrierNormHeader(value);
 if(!s)return false;
 return /ATRASAD/.test(s)||/DENTRO DO PRAZO/.test(s)||/FORA DO PRAZO/.test(s)||/(^| )DEVOLVIDO($| )/.test(s)||carrierIsDeliveredStatus(s);
}
function carrierDelayDays(item){
 const forecast=carrierParseDate(item?.previsao_entrega);if(!forecast)return 0;
 const status=item?.status_entrega||item?.status||item?.delivery_status||'';const occurrence=item?.ocorrencia||'';
 if(carrierIsOnTimeDelivered(status)||carrierIsOnTimeDelivered(occurrence)||/DENTRO DO PRAZO/.test(carrierNormHeader(status))||/DENTRO DO PRAZO/.test(carrierNormHeader(occurrence)))return 0;
 const delivered=carrierIsDeliveredStatus(status)||carrierIsDeliveredStatus(occurrence)||/FORA DO PRAZO/.test(carrierNormHeader(status))||/FORA DO PRAZO/.test(carrierNormHeader(occurrence));
 const endDate=delivered?(carrierParseDate(item?.data_tracking)||carrierStartOfDay()):carrierStartOfDay();
 return Math.max(0,Math.floor((carrierStartOfDay(endDate)-carrierStartOfDay(forecast))/86400000));
}
function carrierPrepareReportItem(item,carrierName=''){const out={...item,transportadora:String(item?.transportadora||carrierName||'').trim()};out.dias_atraso=carrierDelayDays(out);out.previsao_entrega=carrierFormatDate(out.previsao_entrega);return out}
function carrierTodaySaoPaulo(){
 const parts=new Intl.DateTimeFormat('en-CA',{timeZone:'America/Sao_Paulo',year:'numeric',month:'2-digit',day:'2-digit'}).formatToParts(new Date()),get=t=>Number(parts.find(x=>x.type===t)?.value||0);
 return new Date(get('year'),get('month')-1,get('day'));
}
function carrierAwaitingPickupInfo(item){
 const started=carrierParseDate(item?.status_started_at||item?.data_tracking||'');
 const note='Prazo considerado: 7 dias corridos. Os Correios não abrem aos sábados e domingos.';
 if(!started)return {startedLabel:'-',daysLabel:'SEM DATA',className:'is-unknown',title:note};
 const today=carrierTodaySaoPaulo(),elapsed=Math.floor((carrierStartOfDay(today)-carrierStartOfDay(started))/86400000),deadline=new Date(started);deadline.setDate(deadline.getDate()+7);
 let daysLabel='',className='';
 if(elapsed<0){daysLabel='7 dias';className='is-ok'}
 else if(elapsed<7){const left=7-elapsed;daysLabel=left===1?'1 dia':left+' dias';className=left<=2?'is-warning':'is-ok'}
 else if(elapsed===7){daysLabel='VENCE HOJE';className='is-warning'}
 else{daysLabel='PRAZO ENCERRADO';className='is-expired'}
 const deadlineLabel=carrierFormatDate(deadline);
 return {startedLabel:carrierFormatDate(started),daysLabel,className,title:`${note} Data limite estimada: ${deadlineLabel}.`};
}

function carrierIsAwaitingPickupItem(item){
 const raw=[item?.status_entrega,item?.ocorrencia,item?.status].map(v=>String(v||'')).join(' ').normalize('NFD').replace(/[\u0300-\u036f]/g,'').toUpperCase();
 return /AGUARDANDO RETIRADA|OBJETO AGUARDANDO RETIRADA NO ENDERECO INDICADO/.test(raw);
}
function carrierMetricDetails(carrier,key){
 const details=carrier?.details||{};
 if(key==='awaiting_pickup')return (Array.isArray(details.problem)?details.problem:[]).filter(carrierIsAwaitingPickupItem);
 if(key==='problem')return (Array.isArray(details.problem)?details.problem:[]).filter(x=>!carrierIsAwaitingPickupItem(x));
 if(key==='ticket_open')return Array.isArray(details.ticket_open)?details.ticket_open:[];
 return Array.isArray(details[key])?details[key]:[];
}
function carrierMetricCount(carrier,key){
 if(key==='awaiting_pickup'||key==='problem')return carrierMetricDetails(carrier,key).length;
 return Array.isArray(carrier?.details?.[key])?carrier.details[key].length:(Number(carrier?.[key])||0);
}


function carrierTableHtml(rows){
 const defs=[['in_transit','TRAJETO'],['problem','PROBLEMA'],['awaiting_pickup','AG. RETIRADA'],['ocorrencias','OCORRÊNCIA'],['delivered','ENTREGUE']];
 return `<div class="carrier-list-col carrier-side-card carrier-resumo-table-wrap"><table class="carrier-compact-table"><thead><tr><th>NOME</th>${defs.map(([,label])=>`<th>${label}</th>`).join('')}</tr></thead><tbody>${rows.map(({x,idx})=>{const n=String(x.name||'-'),nk=carrierNormKey(n),isC=/CORREIO/.test(nk),isP=/PATRUS/.test(nk),name=isC?`<button type="button" class="carrier-name-vipp" data-carrier-vipp="${idx}">${esc(n)}</button>`:(isP?`<button type="button" class="carrier-name-vipp" data-carrier-patrus="${idx}">${esc(n)}</button>`:esc(n));return `<tr><td title="${esc(n)}">${name}</td>${defs.map(([key,label])=>{const q=carrierMetricCount(x,key);return `<td><button type="button" class="carrier-row-btn${q?'':' is-zero'}" data-carrier-index="${idx}" data-carrier-metric="${key}" title="Ver ${esc(label.toLowerCase())} de ${esc(n)}">${q}</button></td>`}).join('')}</tr>`}).join('')}</tbody></table></div>`;
}
function carrierTotals(){
 return {
  transit:carrierSummaryItems.reduce((sum,x)=>sum+carrierMetricCount(x,'in_transit'),0),
  occurrences:carrierSummaryItems.reduce((sum,x)=>sum+carrierMetricCount(x,'ocorrencias'),0),
  returned:carrierSummaryItems.reduce((sum,x)=>sum+carrierMetricCount(x,'devolvidos'),0),
  overdue:carrierSummaryItems.reduce((sum,x)=>sum+carrierMetricCount(x,'atrasados'),0),
  awaitingPickup:carrierSummaryItems.reduce((sum,x)=>sum+carrierMetricCount(x,'awaiting_pickup'),0),
  ticketOpen:carrierSummaryItems.reduce((sum,x)=>sum+carrierMetricCount(x,'ticket_open'),0)
 };
}
function carrierSummaryHtml(){
 const t=carrierTotals();
 return `<div class="carrier-summary-cards" data-carrier-summary-cards><button type="button" class="carrier-summary-card is-transit" data-carrier-summary-open="in_transit"><span>EM TRANSPORTE</span><strong>${t.transit.toLocaleString('pt-BR')}</strong></button><button type="button" class="carrier-summary-card is-occurrence" data-carrier-summary-open="ocorrencias"><span>OCORRÊNCIA</span><strong>${t.occurrences.toLocaleString('pt-BR')}</strong></button><button type="button" class="carrier-summary-card is-returned" data-carrier-summary-open="devolvidos"><span>DEVOLVIDO</span><strong>${t.returned.toLocaleString('pt-BR')}</strong></button><button type="button" class="carrier-summary-card is-overdue" data-carrier-summary-open="atrasados"><span>ATRASADO</span><strong>${t.overdue.toLocaleString('pt-BR')}</strong></button><button type="button" class="carrier-summary-card is-awaiting-pickup" data-carrier-summary-open="awaiting_pickup"><span>AGUARDANDO RETIRADA</span><strong>${t.awaitingPickup.toLocaleString('pt-BR')}</strong></button><button type="button" class="carrier-summary-card is-ticket-open" data-carrier-summary-open="ticket_open"><span>TICKET ABERTO</span><strong>${t.ticketOpen.toLocaleString('pt-BR')}</strong></button></div>`;
}
function carrierMetricLabel(metric){
 return ({in_transit:'EM TRANSPORTE',problem:'PROBLEMA',awaiting_pickup:'AGUARDANDO RETIRADA',ocorrencias:'OCORRÊNCIA',delivered:'ENTREGUE',devolvidos:'DEVOLVIDO',atrasados:'ATRASADO',ticket_open:'TICKET ABERTO'})[metric]||String(metric||'').toUpperCase();
}
function carrierReportTableHtml(items,metric=''){
 const rows=Array.isArray(items)?items:[];
 if(!rows.length)return '<div class="carrier-resumo-empty">Nenhum pedido encontrado neste relatório.</div>';
 if(metric==='ticket_open'&&root.__carrierTicketReportHtml)return root.__carrierTicketReportHtml(rows,esc);
 const pickup=metric==='awaiting_pickup';
 const pickupHead=pickup?'<th>CTE / RASTREIO</th><th>ENTROU EM</th><th>DIAS P/ RETIRAR</th>':'';
 return `<div class="carrier-resumo-report-wrap"><table class="carrier-resumo-report-table${pickup?' is-awaiting-pickup':''}"><thead><tr><th>PEDIDO</th><th>NOTA</th><th>DESTINATÁRIO</th><th>CAMPANHA</th><th>TRANSPORTADORA</th><th>STATUS TR</th>${pickupHead}<th>OCORRÊNCIA</th><th>TRATATIVAS</th><th>DATA COLETA</th></tr></thead><tbody>${rows.map(x=>{const q=pickup?carrierAwaitingPickupInfo(x):null,pickupCells=pickup?`<td>${esc(x.cte||x.rastreio||'-')}</td><td>${esc(q.startedLabel)}</td><td><span class="carrier-pickup-days ${q.className}" title="${esc(q.title)}">${esc(q.daysLabel)}</span></td>`:'';return `<tr><td>${esc(x.pedido_cliente||x.pedido||'-')}</td><td>${esc(x.nota_cliente||x.nota_lisa||x.nota||'-')}</td><td>${esc(x.destinatario||'-')}</td><td>${esc(x.campanha||'-')}</td><td>${esc(x.transportadora||'-')}</td><td>${esc(x.status_entrega||x.status||'-')}</td>${pickupCells}<td>${esc(x.ocorrencia||'-')}</td><td>${esc(x.tratativas||'-')}</td><td>${esc(x.data_coleta||'-')}</td></tr>`}).join('')}</tbody></table></div>`;
}
let carrierVippCache=null;
async function carrierVippData(force=false){if(carrierVippCache&&!force)return carrierVippCache;carrierVippCache=await api('/correios/report');return carrierVippCache}
function carrierVippEnrich(items){const rows=carrierVippCache?.items||[],m=new Map(rows.map(x=>[String(x.codigo||'').toUpperCase(),x]));return (items||[]).map(x=>{const v=m.get(String(x.cte||x.rastreio||'').toUpperCase());return v?{...x,status_started_at:v.aguardando_retirada_em||x.status_started_at||'',vipp_status:v.status||'',vipp_data_evento:v.data_evento||''}:x})}
function carrierVippTable(d){const rows=d?.items||[];if(!rows.length)return '<div class="carrier-resumo-empty">Ainda não há relatório Excel do VIPP importado.</div>';return `<div class="carrier-resumo-report-wrap"><table class="carrier-resumo-report-table carrier-vipp-table"><thead><tr><th>RASTREIO</th><th>NOTA</th><th>DESTINATÁRIO</th><th>PEDIDO</th><th>POSTADO EM</th><th>PREVISÃO DE ENTREGA</th><th>STATUS CORREIOS</th><th>ÚLTIMO EVENTO</th><th>DATA DO ÚLTIMO EVENTO</th></tr></thead><tbody>${rows.map(x=>`<tr data-vipp-code="${esc(x.codigo||'')}"><td>${esc(x.codigo||'-')}</td><td>${esc(x.nota||'-')}</td><td>${esc(x.destinatario||'-')}</td><td>${esc(x.pedido||'-')}</td><td>${esc(carrierFormatDate(x.data_postagem)||'-')}</td><td data-vipp-prev>${esc(carrierFormatDate(x.previsao)||'-')}</td><td data-vipp-status>${esc(x.grupo_status||x.status||'-')}</td><td data-vipp-event>${esc(x.ultimo_evento||x.status||'-')}</td><td data-vipp-date>${esc(x.data_evento?new Date(x.data_evento).toLocaleString('pt-BR'):'-')}</td></tr>`).join('')}</tbody></table></div>`}
function carrierVippAlertRow(tr,prev,status){tr.style.backgroundColor='';const m=String(prev||'').match(/(\d{2})\/(\d{2})\/(\d{4})/);if(!m)return;const p=new Date(+m[3],+m[2]-1,+m[1]),n=new Date();n.setHours(0,0,0,0);const diff=Math.round((p-n)/86400000),del=/ENTREGUE|ENTREGA REALIZADA/i.test(String(status||''));if(!del&&diff<0)tr.style.backgroundColor='#ffe7e7';else if(!del&&diff===1)tr.style.backgroundColor='#fff1d6'}
async function carrierVippFillTracking(modal,rows){
 // Abrir o relatorio le somente o snapshot, sem novas consultas por objeto.
 for(const x of rows||[]){const tr=modal.querySelector(`[data-vipp-code="${CSS.escape(String(x.codigo||''))}"]`);if(tr)carrierVippAlertRow(tr,carrierFormatDate(x.previsao),x.status)}
}
async function renderCarrierVipp(modal){const v=modal.querySelector('[data-carrier-resumo-view]'),b=modal.querySelector('[data-carrier-resumo-back]'),t=modal.querySelector('[data-carrier-resumo-view-title]');if(!v)return;if(b)b.hidden=false;if(t)t.textContent='CORREIOS / VIPP · Excel importado';v.innerHTML='<div class="carrier-resumo-empty">Carregando último Excel importado do VIPP...</div>';try{const d=await carrierVippData(true);v.innerHTML=carrierVippTable(d);if(t)t.textContent=`CORREIOS / VIPP · Excel · ${Number(d.total||d.items?.length||0).toLocaleString('pt-BR')} postagens · atualizado ${d.updated_at?new Date(d.updated_at).toLocaleString('pt-BR'):'--'}`;carrierVippFillTracking(modal,d.items||[])}catch(e){v.innerHTML=`<div class="carrier-resumo-empty">${esc(e.message||'Falha ao carregar os Correios.')}</div>`}}
function carrierPatrusTable(d){const a=d?.items||[];if(!d?.available||!a.length)return `<div class="carrier-resumo-empty">${esc(d?.message||'Aguardando dados Patrus.')}</div>`;const f=(x,...k)=>{for(const q of k)if(x?.[q]!=null&&String(x[q]).trim())return x[q];return '-'};return `<div class="carrier-resumo-report-wrap"><table class="carrier-resumo-report-table"><thead><tr><th>CT-E</th><th>NOTA</th><th>PEDIDO</th><th>SITUAÇÃO</th><th>PREVISÃO</th><th>ENTREGA</th></tr></thead><tbody>${a.map(x=>`<tr><td>${esc(f(x,'documento','cte'))}</td><td>${esc(f(x,'nota','nota_fiscal'))}</td><td>${esc(f(x,'pedido'))}</td><td>${esc(f(x,'situacao','status'))}</td><td>${esc(f(x,'previsao_entrega','previsao'))}</td><td>${esc(f(x,'data_entrega','entrega'))}</td></tr>`).join('')}</tbody></table></div>`}
async function renderCarrierPatrus(m){const v=m.querySelector('[data-carrier-resumo-view]'),b=m.querySelector('[data-carrier-resumo-back]'),t=m.querySelector('[data-carrier-resumo-view-title]');if(b)b.hidden=false;if(t)t.textContent='PATRUS · 60 dias';v.innerHTML='<div class="carrier-resumo-empty">Carregando dados da Patrus...</div>';try{const d=await api('/patrus/report');v.innerHTML=carrierPatrusTable(d);if(t)t.textContent=d.available?`PATRUS · ${Number(d.total||0).toLocaleString('pt-BR')} registros`:'PATRUS · aguardando'}catch(e){v.innerHTML=`<div class="carrier-resumo-empty">${esc(e.message||'Falha ao carregar a Patrus.')}</div>`}}
function ensureVippDiag(){let m=$('[data-vipp-diag]');if(m)return m;m=document.createElement('div');m.className='carrier-vipp-diag';m.dataset.vippDiag='';m.hidden=true;m.innerHTML='<div class="carrier-vipp-diag-card"><button type="button" class="carrier-resumo-x" data-vipp-close>×</button><h3>CORREIOS / VIPP · Excel</h3><div data-vipp-diag-body>Carregando...</div></div>';root.appendChild(m);m.addEventListener('click',e=>{if(e.target===m||e.target.closest('[data-vipp-close]'))m.hidden=true});return m}
async function openVippDiag(){const m=ensureVippDiag(),b=m.querySelector('[data-vipp-diag-body]');m.hidden=false;b.textContent='Lendo última importação...';try{const d=await api('/correios/status');b.innerHTML=`<div class="carrier-vipp-diag-grid"><span>API Correios</span><b>DESATIVADA</b><span>Origem</span><b>Excel VIPP</b><span>Atualização</span><b>A cada 3 horas pelo programa</b><span>Última importação</span><b>${d.cache?.updated_at?new Date(d.cache.updated_at).toLocaleString('pt-BR'):'Nunca'}</b><span>Registros</span><b>${Number(d.cache?.total||0).toLocaleString('pt-BR')}</b></div>`}catch(e){b.textContent=e.message||'Falha ao consultar importação.'}}
function ensurePatrusDiag(){let m=$('[data-patrus-diag]');if(m)return m;m=document.createElement('div');m.className='carrier-vipp-diag';m.dataset.patrusDiag='';m.hidden=true;m.innerHTML='<div class="carrier-vipp-diag-card"><button type="button" class="carrier-resumo-x" data-patrus-close>×</button><h3>Integração Patrus</h3><div data-patrus-diag-body>Carregando...</div></div>';root.appendChild(m);m.addEventListener('click',e=>{if(e.target===m||e.target.closest('[data-patrus-close]'))m.hidden=true});return m}
async function openPatrusDiag(){const m=ensurePatrusDiag(),b=m.querySelector('[data-patrus-diag-body]');m.hidden=false;b.textContent='Lendo Patrus...';try{const d=await api('/patrus/status?live=1'),c=d.configured||{},l=d.live_test||{},s=d.sync||{},a=d.cache||{},r=s.result||{},msg=l.error||s.last_error||r.error||r.message||'';b.innerHTML=`<div class="carrier-vipp-diag-grid"><span>Usuário</span><b>${c.usuario?'OK':'NÃO'}</b><span>Subscription</span><b>${c.subscription?'OK':'NÃO'}</b><span>Token</span><b>${l.ok?'OK':'ERRO'}</b><span>Base</span><b>${a.available?Number(a.total||0).toLocaleString('pt-BR')+' registros':'AGUARDANDO DADOS'}</b><span>Janela</span><b>${esc(s.slot||'-')}</b><span>Estado</span><b>${esc(String(s.phase||'waiting').toUpperCase())}</b></div>${msg?`<pre>${esc(msg)}${l.response_preview?`\n\n${esc(l.response_preview)}`:''}</pre>`:''}`}catch(e){b.textContent=e.message||'Falha na Patrus.'}}
function ensureCarrierResumoModal(){
 let modal=$('[data-carrier-resumo-modal]');if(modal)return modal;
 modal=document.createElement('div');modal.className='carrier-resumo-modal';modal.setAttribute('data-carrier-resumo-modal','');modal.hidden=true;
 modal.innerHTML=`<div class="carrier-resumo-shell carrier-resumo-table-shell"><button type="button" class="carrier-resumo-x" data-carrier-resumo-close>×</button><div class="carrier-resumo-head"><div><h3>Transportadoras</h3><span>Resumo geral e detalhamento por transportadora</span></div><div class="carrier-resumo-head-actions"><button type="button" class="carrier-ticket-add-btn" data-carrier-ticket-add hidden>ADICIONAR TICKET</button></div></div><div class="carrier-resumo-global-kpis" data-carrier-resumo-kpis></div><div class="carrier-resumo-view-head"><button type="button" class="carrier-resumo-back" data-carrier-resumo-back hidden>← VOLTAR</button><span data-carrier-resumo-view-title>Tabela geral</span><div class="carrier-report-actions" data-carrier-report-actions hidden><button type="button" data-carrier-report-copy>COPIAR</button><button type="button" data-carrier-report-download>BAIXAR</button></div></div><div class="carrier-resumo-view" data-carrier-resumo-view></div></div>`;
 root.appendChild(modal);
 const syncReportActions=()=>{const a=modal.querySelector('[data-carrier-report-actions]'),v=modal.querySelector('[data-carrier-resumo-view]');if(a)a.hidden=!v?.querySelector('table')};
 const viewObs=new MutationObserver(syncReportActions);viewObs.observe(modal.querySelector('[data-carrier-resumo-view]'),{childList:true,subtree:true});
 const tableMatrix=()=>{const t=modal.querySelector('[data-carrier-resumo-view] table');if(!t)return [];return [...t.querySelectorAll('tr')].map(r=>[...r.querySelectorAll('th,td')].map(c=>String(c.innerText||c.textContent||'').trim().replace(/\s*\n\s*/g,' ')))};
 const safeName=()=>String(modal.querySelector('[data-carrier-resumo-view-title]')?.textContent||'transportadoras').normalize('NFD').replace(/[\u0300-\u036f]/g,'').replace(/[^a-zA-Z0-9_-]+/g,'_').replace(/^_+|_+$/g,'').toLowerCase()||'transportadoras';
 modal.addEventListener('click',async e=>{
  if(e.target.closest('[data-carrier-report-copy]')){const m=tableMatrix();if(!m.length)return;const txt=m.map(r=>r.join('\t')).join('\n');try{await navigator.clipboard.writeText(txt)}catch{const ta=document.createElement('textarea');ta.value=txt;document.body.appendChild(ta);ta.select();document.execCommand('copy');ta.remove()}const b=e.target.closest('button'),old=b.textContent;b.textContent='COPIADO';setTimeout(()=>b.textContent=old,1200);return}
  if(e.target.closest('[data-carrier-report-download]')){const m=tableMatrix();if(!m.length)return;const csv='\uFEFF'+m.map(r=>r.map(v=>'"'+String(v).replace(/"/g,'""')+'"').join(';')).join('\r\n'),blob=new Blob([csv],{type:'text/csv;charset=utf-8'}),u=URL.createObjectURL(blob),a=document.createElement('a');a.href=u;a.download=safeName()+'.csv';document.body.appendChild(a);a.click();a.remove();setTimeout(()=>URL.revokeObjectURL(u),1000);return}
 });
 modal.addEventListener('click',e=>{
  if(e.target===modal||e.target.closest('[data-carrier-resumo-close]')){modal.hidden=true;return}
  if(e.target.closest('[data-carrier-resumo-back]')){renderCarrierResumoGeneral(modal);return}
  const cv=e.target.closest('[data-carrier-vipp]');if(cv){if(e.getModifierState?.('AltGraph')||(e.altKey&&e.ctrlKey))openVippDiag();else renderCarrierVipp(modal);return}
  const cp=e.target.closest('[data-carrier-patrus]');if(cp){if(e.getModifierState?.('AltGraph')||(e.altKey&&e.ctrlKey)){e.preventDefault();e.stopPropagation();openPatrusDiag()}else renderCarrierPatrus(modal);return}
  const summary=e.target.closest('[data-carrier-summary-open]');if(summary){renderCarrierResumoMetric(modal,summary.dataset.carrierSummaryOpen||'');return}
  const metric=e.target.closest('[data-carrier-metric]');if(metric){renderCarrierResumoCarrierMetric(modal,Number(metric.dataset.carrierIndex),metric.dataset.carrierMetric||'');return}
 });
 return modal;
}
function renderCarrierResumoBase(modal){
 const kpis=modal.querySelector('[data-carrier-resumo-kpis]');if(kpis)kpis.innerHTML=carrierSummaryHtml();
}
function renderCarrierResumoGeneral(modal){
 renderCarrierResumoBase(modal);
 const view=modal.querySelector('[data-carrier-resumo-view]'),back=modal.querySelector('[data-carrier-resumo-back]'),title=modal.querySelector('[data-carrier-resumo-view-title]');if(!view)return;
 const indexed=carrierSummaryItems.map((x,idx)=>({x,idx}));
 if(back)back.hidden=true;if(title)title.textContent='Tabela geral';
 view.innerHTML=carrierTableHtml(indexed);
 const addBtn=modal.querySelector('[data-carrier-ticket-add]');if(addBtn)addBtn.hidden=!indexed.length;
}
async function renderCarrierResumoMetric(modal,metric){
 renderCarrierResumoBase(modal);
 let items=[];
 for(const carrier of carrierSummaryItems)for(const x of carrierMetricDetails(carrier,metric))items.push(carrierPrepareReportItem(x,carrier?.name||''));
 if(metric==='awaiting_pickup'){try{await carrierVippData();items=carrierVippEnrich(items)}catch(e){console.debug('ViPP:',e)}}
 const view=modal.querySelector('[data-carrier-resumo-view]'),back=modal.querySelector('[data-carrier-resumo-back]'),title=modal.querySelector('[data-carrier-resumo-view-title]');if(!view)return;
 if(back)back.hidden=false;if(title)title.textContent=`${carrierMetricLabel(metric)} · ${items.length.toLocaleString('pt-BR')}`;
 view.innerHTML=carrierReportTableHtml(items,metric);
}
async function renderCarrierResumoCarrierMetric(modal,index,metric){
 renderCarrierResumoBase(modal);
 const carrier=carrierSummaryItems[Number(index)];if(!carrier)return;
 let items=carrierMetricDetails(carrier,metric).map(x=>carrierPrepareReportItem(x,carrier?.name||''));
 if(metric==='awaiting_pickup'){try{await carrierVippData();items=carrierVippEnrich(items)}catch(e){console.debug('ViPP:',e)}}
 const view=modal.querySelector('[data-carrier-resumo-view]'),back=modal.querySelector('[data-carrier-resumo-back]'),title=modal.querySelector('[data-carrier-resumo-view-title]');if(!view)return;
 if(back)back.hidden=false;if(title)title.textContent=`${carrier.name||'-'} · ${carrierMetricLabel(metric)} · ${items.length.toLocaleString('pt-BR')}`;
 view.innerHTML=carrierReportTableHtml(items,metric);
}
function openCarrierResumo(metric=''){
 const modal=ensureCarrierResumoModal();
 if(metric)renderCarrierResumoMetric(modal,metric);else renderCarrierResumoGeneral(modal);
 modal.hidden=false;
}
const TRACKING_SIDE_CACHE_KEY='lisa_tracking_summary_visible_v4';
function trackingSideCounts(counts,total){
 const values={...(counts||{})};
 const all=total??values.all;
 if(all!==null&&all!==undefined&&Number.isFinite(Number(all))&&Number(all)>=0)values.all=Number(all);
 for(const [k,v] of Object.entries(values)){
  const el=root.querySelector(`[data-tracking-count="${k}"]`);
  if(el)el.textContent=Number(v||0).toLocaleString('pt-BR');
 }
}
function trackingSideReadLocal(){
 try{
  const d=JSON.parse(localStorage.getItem(TRACKING_SIDE_CACHE_KEY)||'null');
  if(d?.counts)trackingSideCounts(d.counts,d.total);
  return d;
 }catch{return null}
}
async function loadTrackingSummaryCards(){
 if(!token()||!WORKER||shell.hidden)return;
 trackingSideReadLocal();
 try{
  const d=await api('/rastreamento/summary');
  if(d?.counts){
   const counts={...d.counts};
   if(d.total!==null&&d.total!==undefined&&Number.isFinite(Number(d.total))&&Number(d.total)>=0)counts.all=Number(d.total);
   trackingSideCounts(counts);
   root.dataset.trackingSnapshotAt=String(d.updated_at||'');
   try{localStorage.setItem(TRACKING_SIDE_CACHE_KEY,JSON.stringify({counts,total:d.total??counts.all,updated_at:d.updated_at||null}))}catch{}
   root.dispatchEvent(new CustomEvent('lisa:tracking-summary-loaded',{detail:{counts,total:d.total??counts.all,updated_at:d.updated_at||null}}));
  }
 }catch(e){
  console.debug('Resumo de rastreamento indisponível; mantendo último cache válido.',e);
 }
}
root.__loadTrackingSummaryCards=loadTrackingSummaryCards;
function renderCarriers(){
 const host=$('[data-carrier-status-table]');
 if(!host)return;
 host.innerHTML=`<div class="tracking-side-grid" data-tracking-side-grid>
  <button type="button" class="tracking-side-card" data-tracking-view="all"><span>TODOS</span><strong data-tracking-count="all">—</strong></button>
  <button type="button" class="tracking-side-card" data-tracking-view="without"><span>SEM CTE</span><strong data-tracking-count="without">—</strong></button>
  <button type="button" class="tracking-side-card" data-tracking-view="transit"><span>EM TRAJETO</span><strong data-tracking-count="transit">—</strong></button>
  <button type="button" class="tracking-side-card" data-tracking-view="nostatus"><span>SEM STATUS</span><strong data-tracking-count="nostatus">—</strong></button>
  <button type="button" class="tracking-side-card" data-tracking-view="awaiting"><span>AGUARD. RETIRADA</span><strong data-tracking-count="awaiting">—</strong></button>
  <button type="button" class="tracking-side-card" data-tracking-view="returning"><span>DEVOLUÇÃO</span><strong data-tracking-count="returning">—</strong></button>
  <button type="button" class="tracking-side-card" data-tracking-view="overdue"><span>ATRASADO</span><strong data-tracking-count="overdue">—</strong></button>
  <button type="button" class="tracking-side-card" data-tracking-view="delivered"><span>ENTREGUE</span><strong data-tracking-count="delivered">—</strong></button>
  <button type="button" class="tracking-side-card" data-tracking-view="ticket"><span>TICKET</span><strong data-tracking-count="ticket">—</strong></button>
 </div>`;
 trackingSideReadLocal();
 return loadTrackingSummaryCards();
}



async function loadCarriers(){
 const host=$('[data-carrier-status-table]');
 if(!host||!token()||!WORKER||shell.hidden)return;
 if(!host.querySelector('[data-tracking-side-grid]'))await renderCarriers();
 else await loadTrackingSummaryCards();
}

// Diagnóstico de integração Shopify (AltGr + clique em VISÃO GERAL).
function carrierIntegrationDiagStyles(){
 if(document.getElementById('carrier-integration-diag-style'))return;
 const s=document.createElement('style');s.id='carrier-integration-diag-style';s.textContent=`
 .carrier-integration-diag{position:fixed;z-index:1000002;inset:0;background:rgba(15,25,45,.44);backdrop-filter:blur(6px);display:grid;place-items:center;padding:20px}
 .carrier-integration-diag[hidden]{display:none!important}.carrier-integration-shell{width:min(640px,96vw);max-height:90vh;overflow:auto;background:var(--card,#fff);border:1px solid var(--line,#e5eaf1);border-radius:18px;padding:20px;box-shadow:0 24px 70px rgba(0,0,0,.22);position:relative}
 .carrier-integration-head{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:16px}.carrier-integration-head h3{margin:0;color:var(--blue,#18437b);font-size:22px;font-weight:400}.carrier-integration-head p{margin:4px 0 0;color:var(--muted,#6f7b90);font-size:11px}.carrier-integration-actions{display:flex;gap:8px;align-items:center}.carrier-integration-last-test{font-size:10px;color:var(--muted,#6f7b90);white-space:nowrap;margin-right:4px}.carrier-integration-test,.carrier-integration-close{border:1px solid var(--line,#e5eaf1);background:var(--card,#fff);color:var(--blue,#18437b);border-radius:9px;padding:9px 12px;cursor:pointer;font:inherit}.carrier-integration-close{font-size:20px;padding:4px 10px}
 .carrier-integration-section{margin-top:16px}.carrier-integration-section:first-child{margin-top:0}.carrier-integration-section-title{font-size:11px;letter-spacing:.08em;text-transform:uppercase;color:var(--muted,#6f7b90);margin:0 0 8px;padding-left:2px}.carrier-integration-grid{display:grid;grid-template-columns:minmax(0,1fr);gap:10px}.carrier-integration-card{border:1px solid var(--line,#e5eaf1);border-radius:12px;padding:12px;background:#f8fafc;display:grid;grid-template-columns:minmax(0,1fr) auto;gap:8px 12px;align-items:start}.carrier-integration-card h4{margin:0;font-size:13px;color:var(--text,#17233a);font-weight:400}.carrier-integration-status{border-radius:999px;padding:5px 8px;font-size:9px;white-space:nowrap;border:1px solid}.carrier-integration-status.is-ok{color:#137a42;background:#eaf8f0;border-color:#a7dfbd}.carrier-integration-status.is-error{color:#b42318;background:#fff1f0;border-color:#ffc4bf}.carrier-integration-status.is-warn{color:#9a6500;background:#fff8e6;border-color:#f1d28c}.carrier-integration-status.is-sheet{color:#135a9c;background:#edf6ff;border-color:#b8d8f6}.carrier-integration-status.is-off{color:#697586;background:#eef2f6;border-color:#d6dee8}.carrier-integration-detail{grid-column:1/-1;color:var(--muted,#6f7b90);font-size:10px;line-height:1.35;word-break:break-word}.carrier-integration-meta{grid-column:1/-1;font-size:9px;color:var(--muted,#6f7b90)}.carrier-integration-loading{padding:28px;text-align:center;color:var(--muted,#6f7b90)}
 .dark-mode .carrier-integration-shell{background:#14171c;border-color:#29313b}.dark-mode .carrier-integration-card{background:#101722;border-color:#2d3b4d}.dark-mode .carrier-integration-card h4{color:#eef5ff}.dark-mode .carrier-integration-test,.dark-mode .carrier-integration-close{background:#14171c;border-color:#34404f;color:#dcecff}.dark-mode .carrier-integration-status.is-sheet{color:#b9dcff;background:#11263b;border-color:#28557e}
 @media(max-width:560px){.carrier-integration-head{align-items:flex-start;flex-wrap:wrap}.carrier-integration-actions{width:100%;justify-content:space-between;flex-wrap:wrap}}
 `;document.head.appendChild(s)
}
function ensureCarrierIntegrationDiag(){
 carrierIntegrationDiagStyles();let m=$('[data-carrier-integration-diag]');if(m)return m;
 m=document.createElement('div');m.className='carrier-integration-diag';m.dataset.carrierIntegrationDiag='';m.hidden=true;
 m.innerHTML=`<div class="carrier-integration-shell"><div class="carrier-integration-head"><div><h3>Integração Shopify</h3><p>Status da conexão do painel com a API Shopify.</p></div><div class="carrier-integration-actions"><span class="carrier-integration-last-test" data-carrier-integration-last-test>Último teste: --</span><button type="button" class="carrier-integration-test" data-carrier-integration-test>TESTAR NOVAMENTE</button><button type="button" class="carrier-integration-close" data-carrier-integration-close>×</button></div></div><div data-carrier-integration-body class="carrier-integration-loading">Carregando Shopify...</div></div>`;
 root.appendChild(m);m.addEventListener('click',async e=>{
  if(e.target===m||e.target.closest('[data-carrier-integration-close]')){m.hidden=true;return}
  if(e.target.closest('[data-carrier-integration-test]'))loadCarrierIntegrationDiag(m,true)
 });return m
}
function carrierIntegrationStatusClass(status){return status==='ok'?'is-ok':status==='error'?'is-error':status==='sheet'?'is-sheet':status==='not_configured'||status==='not_authorized'||status==='configured'?'is-warn':'is-off'}
// Apenas Shopify é elegível para o diagnóstico. Nunca mostrar as outras plataformas ou transportadoras.
function integrationShopify(d){
 if(d?.shopify?.id==='shopify')return d.shopify;
 return Array.isArray(d?.platforms)?d.platforms.find(x=>String(x?.id||'').toLowerCase()==='shopify')||null:null;
}
function integrationShopifyHtml(shop){return `<article class="carrier-integration-card"><h4>SHOPIFY</h4><span class="carrier-integration-status ${carrierIntegrationStatusClass(shop.status)}">${esc(shop.label||'-')}</span><div class="carrier-integration-detail">${esc(shop.detail||'')}</div>${shop.live_test?.http_status?`<div class="carrier-integration-meta">HTTP ${esc(shop.live_test.http_status)}</div>`:''}</article>`}
const LISA_INTEGRACOES_LOCAL_KEY='lisa_integracao_shopify_v1';
function integrationTestDate(v){if(!v)return 'ainda não realizado';const d=new Date(v);if(Number.isNaN(d.getTime()))return 'ainda não realizado';return d.toLocaleString('pt-BR',{timeZone:'America/Sao_Paulo',day:'2-digit',month:'2-digit',year:'numeric',hour:'2-digit',minute:'2-digit',second:'2-digit',hour12:false}).replace(',','')}
function renderCarrierIntegrationDiag(m,d){
 const body=m.querySelector('[data-carrier-integration-body]'),stamp=m.querySelector('[data-carrier-integration-last-test]');
 if(!body)return;
 const shop=integrationShopify(d);
 body.className='carrier-integration-grid';
 body.innerHTML=shop?integrationShopifyHtml(shop):'<div class="carrier-integration-loading">O Worker não retornou o status da integração Shopify.</div>';
 const lastTest=d?.tested_at||readLocalCarrierIntegrationDiag()?.tested_at||null;
 if(stamp)stamp.textContent='Último teste: '+integrationTestDate(lastTest);
 // O cache local contém SOMENTE o status do Shopify, nunca dados das integrações removidas.
 if(shop)try{localStorage.setItem(LISA_INTEGRACOES_LOCAL_KEY,JSON.stringify({shopify:shop,tested_at:lastTest}))}catch{}
}
function readLocalCarrierIntegrationDiag(){try{const d=JSON.parse(localStorage.getItem(LISA_INTEGRACOES_LOCAL_KEY)||'null');return d&&integrationShopify(d)?d:null}catch{return null}}
async function loadCarrierIntegrationDiag(m,live=false){
 const body=m.querySelector('[data-carrier-integration-body]'),btn=m.querySelector('[data-carrier-integration-test]'),stamp=m.querySelector('[data-carrier-integration-last-test]');
 const hasCards=!!body?.querySelector('.carrier-integration-card');
 if(btn&&live){btn.disabled=true;btn.textContent='TESTANDO...'}
 if(!hasCards&&!live){body.className='carrier-integration-loading';body.textContent='Carregando último diagnóstico...'}
 try{
  const d=await api('/integracoes/status'+(live?'?live=1':''));renderCarrierIntegrationDiag(m,d);
 }catch(e){
  if(!hasCards){body.className='carrier-integration-loading';body.innerHTML=`<span style="color:#b42318">${esc(e.message||'Falha ao consultar as integrações.')}</span>`}
  else if(stamp)stamp.textContent='Último teste: falha ao atualizar';
 }finally{if(btn&&live){btn.disabled=false;btn.textContent='TESTAR NOVAMENTE'}}
}
async function openCarrierIntegrationDiag(){
 const m=ensureCarrierIntegrationDiag(),local=readLocalCarrierIntegrationDiag();
 if(local)renderCarrierIntegrationDiag(m,local);
 m.hidden=false;
 await loadCarrierIntegrationDiag(m,false);
}

// AltGr + clique em VISÃO GERAL abre o painel técnico de integrações.
let lisaIntegrationAltGrHeld=false;
const integrationAltGr=e=>e.key==='AltGraph'||(e.altKey&&e.ctrlKey)||(e.code==='AltRight'&&e.altKey);
document.addEventListener('keydown',e=>{if(integrationAltGr(e))lisaIntegrationAltGrHeld=true},true);
document.addEventListener('keyup',e=>{if(e.key==='AltGraph'||e.key==='Alt'||e.key==='Control'||(!e.altKey&&!e.ctrlKey))lisaIntegrationAltGrHeld=false},true);
window.addEventListener('blur',()=>{lisaIntegrationAltGrHeld=false});
const visionTitle=root.querySelector('.lisa-hero-title h1');
if(visionTitle&&!visionTitle.dataset.integrationDiagBound){visionTitle.dataset.integrationDiagBound='1';visionTitle.title='AltGr + clique: integração Shopify';visionTitle.addEventListener('click',e=>{const alt= lisaIntegrationAltGrHeld || (typeof e.getModifierState==='function'&&e.getModifierState('AltGraph')) || (e.altKey&&e.ctrlKey);if(!alt)return;e.preventDefault();e.stopPropagation();openCarrierIntegrationDiag()},true)}

const carrierSection=$('[data-carrier-section]');if(carrierSection&&!carrierSection.dataset.bound){carrierSection.dataset.bound='1';carrierSection.addEventListener('click',e=>{const summary=e.target.closest('[data-carrier-summary-open]');if(summary){openCarrierResumo(summary.dataset.carrierSummaryOpen||'');return}const resumo=e.target.closest('[data-carrier-resumo-open]');if(resumo){openCarrierResumo();return}})}
root.addEventListener('lisa:tickets-updated',async()=>{await loadCarriers();const m=$('[data-carrier-resumo-modal]');if(m&&!m.hidden){const title=m.querySelector('[data-carrier-resumo-view-title]');if(String(title?.textContent||'').startsWith('TICKET ABERTO'))renderCarrierResumoMetric(m,'ticket_open')}});
async function loadOrders(silent=false){
 if(ordersBusy)return;
 ordersBusy=true;
 const errBox=$('[data-data-error]');
 if(!silent&&errBox){errBox.hidden=true;errBox.textContent=''}
 try{
  const path=selectedFileKey?('/dashboard?key='+encodeURIComponent(selectedFileKey)):'/dashboard';
  const d=await api(path);
  if(!selectedFileKey){ordersPreloadedSnapshot=null;lisaOfflineStore(d).catch(()=>{});}
  else ordersPreloadedSnapshot=null;
  render(d);
  await loadCarriers();
  ordersVersion=String(d.version||d.updated_at||'');
  if(errBox){errBox.hidden=true;errBox.textContent=''}
 }catch(e){
  console.error(e);
  if(!selectedFileKey&&!ordersVersion){
   const last=await lisaOfflineRead().catch(()=>null);
   if(last?.data?.ok&&last?.data?.version&&Date.now()-Date.parse(last.saved_at)<24*60*60*1000){
    render(last.data);
    ordersPreloadedSnapshot=null;
    ordersVersion=String(last.data.version||last.data.updated_at||'');
    if(errBox){const updated=new Date(last.saved_at);const fmt=Number.isNaN(updated.getTime())?'anterior':updated.toLocaleString('pt-BR');errBox.textContent=`Cloudflare temporariamente indisponível. Exibindo dados salvos no navegador em ${fmt}. Os valores podem estar desatualizados.`;errBox.hidden=false}
    return;
   }
  }
  if(errBox){errBox.textContent=`Falha ao atualizar. ${e.message||'Última informação permanece na tela.'}`;errBox.hidden=false}
 }finally{ordersBusy=false}
}

function setLatestVersionButton(value){
 if(value)latestVersionDate=value;
 const el=$('[data-last-update]');
 const prefix=$('[data-latest-prefix]');
 if(prefix)prefix.textContent='Atualizado ';
 if(el)el.textContent=formatTimelineDate(latestVersionDate)||'--/-- --:--';
}
async function loadLatestVersionInfo(){
 if(!token()||!WORKER||shell.hidden)return;
 try{const d=await api('/version');setLatestVersionButton(d.updated_at||d.version)}
 catch(e){console.debug('Não foi possível atualizar a data da última versão.',e)}
}
async function checkOrdersVersion(){
 if(ordersVersionBusy||ordersBusy||!token()||!WORKER||shell.hidden)return;
 ordersVersionBusy=true;
 try{
   const d=await api('/version');
   setLatestVersionButton(d.updated_at||d.version);
   const remoteVersion=String(d.version||d.updated_at||'');
   const changed=!!remoteVersion&&remoteVersion!==ordersVersion;
   const offlineWarning=$('[data-data-error]');
   if(!selectedFileKey&&remoteVersion&&(changed||(offlineWarning&&!offlineWarning.hidden))){
     await loadOrders(true);
     if(changed)root.dispatchEvent(new CustomEvent('lisa:monitor-updated',{detail:{version:remoteVersion}}));
   }
 }catch(e){
   console.debug('Verificação de versão do painel indisponível.',e);
 }finally{ordersVersionBusy=false}
}

function render(d){
 const previousVersion=ordersDisplayedVersion;
 ordersDisplayedVersion=String(d.version||d.updated_at||'');
 selectedFileName=String(d.file_name||'');
 selectedFileDate=formatTimelineDate(d.updated_at);
 setLatestVersionButton(latestVersionDate);
 const downloadBtn=$('[data-download]');
 const downloadLabel=$('[data-download-label]');
 if(downloadBtn) downloadBtn.hidden=!selectedFileName;
 if(downloadLabel) downloadLabel.textContent=selectedFileDate?('Baixar '+selectedFileDate):'Baixar';
 renderTimelineMenu();
 const statuses=d.statuses||{};
 const topOrder=['INTEGRADO','A SEPARAR','A CONFERIR','CONFERIDO','A EXPEDIR'];
 const nonFinalized=topOrder.reduce((s,n)=>s+(Number(statuses[n])||0),0);
 const allStatusEntries=[...topOrder.map(n=>[n,Number(statuses[n])||0]),['NÃO FINALIZADO',nonFinalized],['FINALIZADO',Number(statuses['FINALIZADO'])||0],['CANCELADO',Number(statuses['CANCELADO'])||0]];
 $('[data-status-grid]').innerHTML=allStatusEntries.map(([n,v])=>{const m=statusMeta[n]||['•','#18437b'];return `<article class="status-card is-clickable" data-filter-status="${esc(n)}" role="button" tabindex="0"><div class="status-icon" style="background:${m[1]}">${m[0]}</div><div><small>${esc(n)}</small><strong>${v}</strong></div></article>`}).join('');
 const campaignObj=d.campaigns||{};
 const campaignGrandTotal=Object.values(campaignObj).reduce((sum,st)=>sum+['INTEGRADO','A SEPARAR','A CONFERIR','CONFERIDO','A EXPEDIR','FINALIZADO','CANCELADO'].reduce((a,n)=>a+(Number(st?.[n])||0),0),0);
 const grandEl=$('[data-campaign-grand-total]');if(grandEl)grandEl.textContent=campaignGrandTotal.toLocaleString('pt-BR');
 const campaignIcon=camp=>{const n=normUi(camp);if(n.includes('MAGALU'))return 'M';if(n.includes('MERCADO LIVRE'))return 'ML';if(n.includes('SHOPEE'))return 'S';if(n.includes('SHOPIFY'))return 'S';if(n.includes('EDI'))return '↔';if(n.includes('VENDAS INTERNAS'))return '▦';return '•'};
 $('[data-campaign-grid]').innerHTML=Object.entries(campaignObj).map(([camp,st])=>{const campaignBaseOrder=['INTEGRADO','A SEPARAR','A CONFERIR','CONFERIDO','A EXPEDIR'];const campaignNonFinalized=campaignBaseOrder.reduce((sum,n)=>sum+(Number(st?.[n])||0),0);const campaignEntries=[...campaignBaseOrder.map(n=>[n,Number(st?.[n])||0]),['NÃO FINALIZADO',campaignNonFinalized],['FINALIZADO',Number(st?.['FINALIZADO'])||0],['CANCELADO',Number(st?.['CANCELADO'])||0]];const total=campaignBaseOrder.reduce((sum,n)=>sum+(Number(st?.[n])||0),0)+(Number(st?.['FINALIZADO'])||0)+(Number(st?.['CANCELADO'])||0);const expedir=Number(st?.['A EXPEDIR'])||0;const progress=campaignNonFinalized>0?Math.max(0,Math.min(100,(expedir/campaignNonFinalized)*100)):0;return `<article class="campaign-card" data-campaign-card="${esc(camp)}"><div class="campaign-card-summary"><span class="campaign-badge">${esc(campaignIcon(camp))}</span><button type="button" class="campaign-total-word campaign-summary-name" data-filter-campaign="${esc(camp)}">${esc(camp)}</button><strong>${total.toLocaleString('pt-BR')}</strong></div><div class="campaign-stats">${campaignEntries.map(([n,v])=>`<button type="button" class="mini-stat mini-stat-btn" data-filter-campaign="${esc(camp)}" data-filter-status="${esc(n)}"><b>${v}</b><span>${esc(n)}</span></button>`).join('')}</div><div class="campaign-progress" title="${progress.toFixed(0)}% dos pedidos não finalizados estão em A EXPEDIR"><span style="width:${progress}%"></span></div></article>`}).join('');
 renderOverdue(d.overdue||{count:0,items:[]});
 renderMovement(d.last_business_days||[],campaignObj);
 if(previousVersion&&previousVersion!==ordersDisplayedVersion&&!$('[data-orders-modal]')?.hidden&&currentOrdersList.dashboardFilter){openOrdersList(currentOrdersList.dashboardFilter)}
 renderEvolution(d.last_business_days||[]);
 }
function movementWeekday(dateBR){
 const m=String(dateBR||'').match(/^(\d{2})\/(\d{2})\/(\d{4})$/);if(!m)return '';
 const names=['DOM','SEG','TER','QUA','QUI','SEX','SAB'];
 return names[new Date(Date.UTC(Number(m[3]),Number(m[2])-1,Number(m[1]))).getUTCDay()]||'';
}
function renderMovement(days,currentCampaigns){
 const host=$('[data-days-grid]');if(!host)return;
 if(!Array.isArray(days)||!days.length){host.innerHTML='<div class="movement-empty">Nenhuma movimentação disponível.</div>';return}
 const campaigns=Object.keys(days[0]?.campaigns||{});
 const pendingStatuses=['INTEGRADO','A SEPARAR','A CONFERIR','CONFERIDO','A EXPEDIR'];
 const fazerPorCampanha=camp=>pendingStatuses.reduce((sum,status)=>sum+(Number(currentCampaigns?.[camp]?.[status])||0),0);
 const fazerTotal=campaigns.reduce((sum,camp)=>sum+fazerPorCampanha(camp),0);
 const cols=`170px 54px repeat(${days.length},minmax(105px,1fr))`;
 const html=[];
 html.push(`<div class="movement-table" style="grid-template-columns:${cols}">`);
 html.push('<div class="movement-cell movement-name movement-head-label">CAMPANHA/6DIAS</div>');
 html.push('<div class="movement-cell movement-fazer movement-head-label movement-fazer-head">Falta</div>');
 for(const day of days){
   const cls=dayClass(day.finalizadas);
   const weekday=movementWeekday(day.date);
   html.push(`<div class="movement-cell movement-date ${cls}"><b>${esc(day.date.slice(0,5))}${weekday?` - ${weekday}`:''}</b><span>Final. / Imp. (Saldo)</span></div>`);
 }
 html.push('<div class="movement-cell movement-name movement-total-name">TOTAL</div>');
 html.push(`<div class="movement-cell movement-fazer movement-total-fazer"><b>${fazerTotal}</b></div>`);
 for(const day of days){
   const cls=dayClass(day.finalizadas);
   html.push(`<div class="movement-cell movement-value movement-total ${cls}"><b>${day.finalizadas}/${day.importadas}</b><span>${saldoText(day.saldo)}</span></div>`);
 }
 for(const campaign of campaigns){
   html.push(`<div class="movement-cell movement-name">${esc(campaign)}</div>`);
   html.push(`<div class="movement-cell movement-fazer"><b>${fazerPorCampanha(campaign)}</b></div>`);
   for(const day of days){
     const x=day.campaigns?.[campaign]||{finalizadas:0,importadas:0,saldo:0};
     const cls=dayClass(day.finalizadas);
     html.push(`<div class="movement-cell movement-value ${cls}"><b>${Number(x.finalizadas)||0}/${Number(x.importadas)||0}</b><span>${saldoText(Number(x.saldo)||0)}</span></div>`);
   }
 }
 html.push('</div>');
 host.innerHTML=html.join('');
}
function renderEvolution(days){
 const host=$('[data-evolution-chart]'),legend=$('[data-evolution-legend]');if(!host||!legend)return;
 if(!Array.isArray(days)||!days.length){host.innerHTML='<div class="evolution-empty">Sem dados.</div>';legend.innerHTML='';return}
 const campaigns=Object.keys(days[0]?.campaigns||{}).slice(0,6);
 const palette=['#1687f8','#4aa8ff','#ffb000','#ff5b43','#13b97a','#7a48e8'];
 const width=560,height=220,padL=38,padR=12,padT=14,padB=30,innerW=width-padL-padR,innerH=height-padT-padB;
 const series=campaigns.map((camp,idx)=>({camp,color:palette[idx%palette.length],vals:days.map(day=>{const x=day?.campaigns?.[camp]||{};return Number(x.importadas)||0})}));
 const max=Math.max(1,...series.flatMap(s=>s.vals));
 const yMax=Math.ceil(max/50)*50||50;
 const x=(i)=>padL+(days.length<=1?0:i*innerW/(days.length-1));
 const y=(v)=>padT+innerH-(v/yMax)*innerH;
 const grid=[];for(let i=0;i<=4;i++){const val=Math.round(yMax*(4-i)/4);const yy=padT+i*innerH/4;grid.push(`<line x1="${padL}" y1="${yy}" x2="${width-padR}" y2="${yy}" stroke="currentColor" opacity=".10"/><text x="2" y="${yy+3}" font-size="9" fill="currentColor" opacity=".55">${val}</text>`)}
 const lines=series.map(s=>{const pts=s.vals.map((v,i)=>`${x(i)},${y(v)}`).join(' ');const dots=s.vals.map((v,i)=>`<circle cx="${x(i)}" cy="${y(v)}" r="3.4" fill="${s.color}"/>`).join('');return `<polyline fill="none" stroke="${s.color}" stroke-width="2.3" points="${pts}"/>${dots}`}).join('');
 const labels=days.map((d,i)=>`<text x="${x(i)}" y="${height-8}" text-anchor="middle" font-size="9" fill="currentColor" opacity=".72">${esc(String(d.date||'').slice(0,5))}</text>`).join('');
 host.innerHTML=`<svg viewBox="0 0 ${width} ${height}" role="img" aria-label="Evolução de pedidos">${grid.join('')}${lines}${labels}</svg>`;
 legend.innerHTML=series.map(s=>`<span><i style="background:${s.color}"></i>${esc(s.camp.replace('EDI - ABASTECE ES','EDI').replace('MERCADO LIVRE - ES','Mercado Livre').replace('VENDAS INTERNAS - ES','Vendas Internas'))}</span>`).join('');
}
function normUi(v){return String(v??'').trim().replace(/\s+/g,' ').toUpperCase()}
function reportDateKey(v){
  const s=String(v??'').trim();
  let m=s.match(/\b(\d{2})\/(\d{2})\/(\d{4})\b/);
  if(m)return `${m[3]}-${m[2]}-${m[1]}`;
  m=s.match(/\b(\d{4})-(\d{2})-(\d{2})\b/);
  return m?`${m[1]}-${m[2]}-${m[3]}`:'';
}
function reportTextIncludes(value,query){const q=normUi(query);return !q||normUi(value).includes(q)}
let currentOrdersList={title:'Pedidos',items:[],allItems:[],finalizedOnly:false,expedirOnly:false,problemOnly:false,ocorrenciaOnly:false,carrierReport:false};
let ordersPreloadedSnapshot=null;
let ordersDisplayedVersion='',ordersReportRequest=0;
let ordersSort={key:'',type:'text',dir:''};
let overdueSort={key:'',type:'text',dir:''};
let reversaSort={index:-1,type:'text',dir:''};
function reportDateSortValue(v){const s=String(v??'').trim();let m=s.match(/\b(\d{2})\/(\d{2})\/(\d{4})(?:\s+(\d{1,2}):(\d{2})(?::(\d{2}))?)?/);if(m)return Date.UTC(+m[3],+m[2]-1,+m[1],+(m[4]||0),+(m[5]||0),+(m[6]||0));m=s.match(/\b(\d{4})-(\d{2})-(\d{2})/);return m?Date.UTC(+m[1],+m[2]-1,+m[3]):Number.NEGATIVE_INFINITY;}
function reportNumberSortValue(v){const n=Number(String(v??'').replace(/[^\d,.-]/g,'').replace(/\./g,'').replace(',','.'));return Number.isFinite(n)?n:Number.NEGATIVE_INFINITY;}
function reportCompareValues(a,b,type){if(type==='number')return reportNumberSortValue(a)-reportNumberSortValue(b);if(type==='date')return reportDateSortValue(a)-reportDateSortValue(b);return String(a??'').localeCompare(String(b??''),'pt-BR',{numeric:true,sensitivity:'base'});}
function applyReportSort(items,sort){const list=[...(items||[])];if(!sort?.key||!sort?.dir)return list;const mult=sort.dir==='asc'?1:-1;return list.map((item,index)=>({item,index})).sort((a,b)=>{const c=reportCompareValues(a.item?.[sort.key],b.item?.[sort.key],sort.type);return c===0?a.index-b.index:c*mult}).map(x=>x.item);}
function updateSortIndicators(scope,sort,attr){for(const th of $$popup(`${scope} [${attr}]`)){th.classList.remove('is-sort-active');th.removeAttribute('data-sort-dir');if(sort?.key&&th.getAttribute(attr)===sort.key){th.classList.add('is-sort-active');th.dataset.sortDir=sort.dir}}}
function nextSort(current,key,type){if(current.key!==key)return {key,type,dir:type==='number'?'desc':'asc'};return {key,type,dir:current.dir==='asc'?'desc':'asc'};}
function resetOrdersSort(){ordersSort={key:'',type:'text',dir:''};updateSortIndicators('.lisa-orders-table',ordersSort,'data-orders-sort');}
function resetOverdueSort(){overdueSort={key:'',type:'text',dir:''};updateSortIndicators('.lisa-overdue-table',overdueSort,'data-overdue-sort');}
function resetReversaSort(){reversaSort={index:-1,type:'text',dir:''};}
let currentOverdueView=[];
function reportUnique(items,key){return [...new Set((items||[]).map(x=>String(x?.[key]||'').trim()).filter(Boolean))].sort((a,b)=>a.localeCompare(b,'pt-BR',{sensitivity:'base'}))}
function fillReportSelect(el,values,selected=''){
  if(!el)return;
  const keep=String(selected||el.value||'');
  el.innerHTML='<option value="">Todos</option>'+values.map(v=>`<option value="${esc(v)}">${esc(v)}</option>`).join('');
  if(values.includes(keep))el.value=keep;else el.value='';
}
function itemMatchesReportFilters(x,f={}){
  return (!f.dataApi||reportDateKey(x.data_api)===f.dataApi)
    &&reportTextIncludes(x.nota,f.nota)
    &&reportTextIncludes(x.destinatario,f.destinatario)
    &&(!f.campaign||normUi(x.campanha)===normUi(f.campaign))
    &&(!f.status||normUi(currentOrdersList.ocorrenciaOnly?x.ocorrencia:(currentOrdersList.problemOnly?x.status_entrega:x.status))===normUi(f.status))
    &&reportTextIncludes(x.status_entrega,f.trackingStatus)
    &&reportTextIncludes(x.ocorrencia,f.ocorrencia)
    &&reportTextIncludes(x.tratativas,f.tratativas)
    &&(!f.previsao||reportDateKey(x.previsao_entrega)===f.previsao)
    &&(!f.transportadora||normUi(x.transportadora)===normUi(f.transportadora))
    &&(!f.diasAtraso||String(Number(x.dias_atraso)||0)===String(f.diasAtraso))
    &&(!f.dataFinalizado||reportDateKey(x.data_finalizado)===f.dataFinalizado)
    &&(!f.dataRomaneio||reportDateKey(x.data_romaneio)===f.dataRomaneio);
}
function ordersFilters(){return {dataApi:$('[data-orders-filter-data-api]')?.value||'',nota:$('[data-orders-filter-nota]')?.value||'',destinatario:$('[data-orders-filter-destinatario]')?.value||'',campaign:$('[data-orders-filter-campaign]')?.value||'',status:currentOrdersList.carrierReport?'':($('[data-orders-filter-status]')?.value||''),trackingStatus:currentOrdersList.carrierReport?($('[data-orders-filter-tracking-status]')?.value||''):'',ocorrencia:currentOrdersList.carrierReport?($('[data-orders-filter-ocorrencia]')?.value||''):'',tratativas:currentOrdersList.carrierReport?($('[data-orders-filter-tratativas]')?.value||''):'',previsao:currentOrdersList.carrierReport?($('[data-orders-filter-previsao]')?.value||''):'',diasAtraso:currentOrdersList.carrierReport?($('[data-orders-filter-dias-atraso]')?.value||''):'',transportadora:$('[data-orders-filter-transportadora]')?.value||'',dataFinalizado:currentOrdersList.finalizedOnly?($('[data-orders-filter-data-finalizado]')?.value||''):'',dataRomaneio:(currentOrdersList.finalizedOnly||currentOrdersList.expedirOnly)?($('[data-orders-filter-data-romaneio]')?.value||''):''};}
function overdueFilters(){return {dataApi:$('[data-overdue-filter-data-api]')?.value||'',nota:$('[data-overdue-filter-nota]')?.value||'',destinatario:$('[data-overdue-filter-destinatario]')?.value||'',campaign:$('[data-overdue-filter-campaign]')?.value||'',status:$('[data-overdue-filter-status]')?.value||'',transportadora:$('[data-overdue-filter-transportadora]')?.value||'',diasAtraso:$('[data-overdue-filter-dias]')?.value||''};}
function setOrdersExtraColumns(finalizedOnly,expedirOnly){const f=$('[data-orders-filter-data-finalizado]')?.closest('[data-orders-finalized-col]'),r=$('[data-orders-filter-data-romaneio]')?.closest('[data-orders-finalized-col]');if(f)f.hidden=!finalizedOnly;if(r)r.hidden=!(finalizedOnly||expedirOnly);const a=$('[data-orders-filter-data-finalizado]'),b=$('[data-orders-filter-data-romaneio]');if(!finalizedOnly&&a)a.value='';if(!(finalizedOnly||expedirOnly)&&b)b.value='';}
function setOrdersProblemColumns(trackingOnly,ocorrenciaOnly=false,showOccurrenceToo=false){const statusCol=$('[data-orders-status-col]'),trackingCol=$('[data-orders-tracking-status-col]'),ocorrenciaCol=$('[data-orders-ocorrencia-col]');if(statusCol)statusCol.hidden=!!trackingOnly;if(trackingCol)trackingCol.hidden=!(trackingOnly&&(!ocorrenciaOnly||showOccurrenceToo));if(ocorrenciaCol)ocorrenciaCol.hidden=!(ocorrenciaOnly||showOccurrenceToo);}
function setOrdersCarrierColumns(show){for(const el of $$popup('[data-orders-carrier-col],[data-orders-tratativas-col]'))el.hidden=!show;const card=$('.lisa-orders-modal-card');if(card)card.classList.toggle('is-carrier-report',!!show)}
// A largura e o scroll dos relatorios sao controlados pelo CSS responsivo.
function renderOrdersReport(){
  const all=Array.isArray(currentOrdersList.allItems)?currentOrdersList.allItems:[];
  const filtered=all.filter(x=>itemMatchesReportFilters(x,ordersFilters()));const items=applyReportSort(filtered,ordersSort);currentOrdersList.items=items;updateSortIndicators('.lisa-orders-table',ordersSort,'data-orders-sort');
  const body=$('[data-orders-body]'),empty=$('[data-orders-empty]'),count=$('[data-orders-count]');
  if(body)body.innerHTML=items.map(x=>{const statusCell=currentOrdersList.carrierReport?`<td>${esc(x.status_entrega||'-')}</td><td>${esc(x.ocorrencia||'-')}</td><td>${esc(x.tratativas||'-')}</td>`:(currentOrdersList.ocorrenciaOnly?`<td>${esc(x.ocorrencia||'-')}</td>`:(currentOrdersList.problemOnly?`<td>${esc(x.status_entrega||'-')}</td>`:`<td>${esc(x.status||'-')}</td>`));const carrierCells=currentOrdersList.carrierReport?`<td>${esc(x.previsao_entrega||'-')}</td><td>${Number(x.dias_atraso)||0}</td>`:'';const base=`<td>${esc(x.data_api||'-')}</td><td>${esc(x.nota||'-')}</td><td>${esc(x.destinatario||'-')}</td><td>${esc(x.campanha||'-')}</td>${statusCell}${carrierCells}<td>${esc(x.transportadora||'-')}</td>`;const extra=currentOrdersList.finalizedOnly?`<td>${esc(x.data_romaneio||'-')}</td><td>${esc(x.data_finalizado||'-')}</td>`:(currentOrdersList.expedirOnly?`<td>${esc(x.data_romaneio||'-')}</td>`:'');return `<tr>${base}${extra}</tr>`}).join('');
  if(count)count.textContent=`${items.length} ${items.length===1?'pedido':'pedidos'}`;if(empty)empty.hidden=items.length>0;
}
function setupOrdersFilters(items){fillReportSelect($('[data-orders-filter-campaign]'),reportUnique(items,'campanha'));fillReportSelect($('[data-orders-filter-status]'),reportUnique(items,'status'));fillReportSelect($('[data-orders-filter-transportadora]'),reportUnique(items,'transportadora'));for(const sel of ['[data-orders-filter-data-api]','[data-orders-filter-nota]','[data-orders-filter-destinatario]','[data-orders-filter-tracking-status]','[data-orders-filter-ocorrencia]','[data-orders-filter-tratativas]','[data-orders-filter-previsao]','[data-orders-filter-dias-atraso]','[data-orders-filter-data-finalizado]','[data-orders-filter-data-romaneio]']){const el=$(sel);if(el)el.value='';}}
function setupOverdueFilters(items){fillReportSelect($('[data-overdue-filter-campaign]'),reportUnique(items,'campanha'));fillReportSelect($('[data-overdue-filter-status]'),reportUnique(items,'status'));fillReportSelect($('[data-overdue-filter-transportadora]'),reportUnique(items,'transportadora'));fillReportSelect($('[data-overdue-filter-dias]'),[...new Set((items||[]).map(x=>String(Number(x?.dias_atraso)||0)))].sort((a,b)=>Number(a)-Number(b)));for(const sel of ['[data-overdue-filter-data-api]','[data-overdue-filter-nota]','[data-overdue-filter-destinatario]']){const el=$(sel);if(el)el.value='';}}
function renderOverdueReport(){
  const all=Array.isArray(currentOverdue.items)?currentOverdue.items:[];const filtered=all.filter(x=>itemMatchesReportFilters(x,overdueFilters()));const items=applyReportSort(filtered,overdueSort);currentOverdueView=items;updateSortIndicators('.lisa-overdue-table',overdueSort,'data-overdue-sort');
  const body=$('[data-overdue-body]'),empty=$('[data-overdue-empty]'),count=$('[data-overdue-modal-count]');
  if(body)body.innerHTML=items.map(x=>`<tr><td>${esc(x.data_api||'-')}</td><td>${esc(x.nota||'-')}</td><td>${esc(x.destinatario||'-')}</td><td>${esc(x.campanha||'-')}</td><td>${esc(x.status||'-')}</td><td>${esc(x.transportadora||'-')}</td><td>${Number(x.dias_atraso)||0}</td></tr>`).join('');
  if(count)count.textContent=`${items.length} ${items.length===1?'pedido':'pedidos'}`;if(empty)empty.hidden=items.length>0;
}
function listItemXmlRows(items){return items.map(x=>`<Row><Cell><Data ss:Type="String">${xmlEsc(x.nota||'')}</Data></Cell><Cell><Data ss:Type="String">${xmlEsc(x.destinatario||'')}</Data></Cell><Cell><Data ss:Type="String">${xmlEsc(x.campanha||'')}</Data></Cell><Cell><Data ss:Type="String">${xmlEsc(x.status||'')}</Data></Cell><Cell><Data ss:Type="String">${xmlEsc(x.transportadora||'')}</Data></Cell><Cell><Data ss:Type="String">${xmlEsc(x.data_api||'')}</Data></Cell></Row>`).join('')}

async function openOrdersList({campaign='',status='',nonFinalized=false,title='Pedidos'}={}){resetOrdersSort();
  const modal=$('[data-orders-modal]'),body=$('[data-orders-body]'),empty=$('[data-orders-empty]'),count=$('[data-orders-count]'),ttl=$('[data-orders-title]'),loading=$('[data-orders-loading]'),wrap=$('[data-orders-table-wrap]');
  if(!modal||!body)return;modal.hidden=false;if(ttl)ttl.textContent=title;if(count)count.textContent='';
  currentOrdersList={...currentOrdersList,dashboardFilter:{campaign,status,nonFinalized,title}};
  const requestedVersion=ordersDisplayedVersion,requestId=++ordersReportRequest;
  const usePreloaded=!selectedFileKey&&ordersPreloadedSnapshot?.monitor_updated_at===requestedVersion&&Array.isArray(ordersPreloadedSnapshot?.items);
  const finish=items=>{if(requestId!==ordersReportRequest||requestedVersion!==ordersDisplayedVersion)return;const finalizedOnly=normUi(status)==='FINALIZADO',expedirOnly=normUi(status)==='A EXPEDIR';currentOrdersList={title,items:[...items],allItems:[...items],finalizedOnly,expedirOnly,problemOnly:false,ocorrenciaOnly:false,carrierReport:false,dashboardFilter:{campaign,status,nonFinalized,title}};setOrdersExtraColumns(finalizedOnly,expedirOnly);setOrdersProblemColumns(false);setOrdersCarrierColumns(false);setupOrdersFilters(items);renderOrdersReport();if(loading)loading.hidden=true;if(wrap)wrap.hidden=false;requestAnimationFrame(()=>{const t=$('.lisa-orders-table');if(t){lisaBindRecipientResize(t);lisaPrepareResizeTable(t)}})};
  if(usePreloaded){
    if(loading)loading.hidden=true;if(wrap)wrap.hidden=false;
    const c=normUi(campaign),s=normUi(status),items=(ordersPreloadedSnapshot.items||[]).filter(x=>(!c||normUi(x?.campanha)===c)&&(!s||normUi(x?.status)===s)&&(!nonFinalized||['INTEGRADO','A SEPARAR','A CONFERIR','CONFERIDO','A EXPEDIR'].includes(normUi(x?.status))));finish(items);return;
  }
  if(loading){loading.hidden=false;loading.textContent='Carregando pedidos...'}if(wrap)wrap.hidden=true;
  const qs=new URLSearchParams();if(requestedVersion)qs.set('monitor_version',requestedVersion);if(selectedFileName)qs.set('monitor_file',selectedFileName);if(campaign)qs.set('campaign',campaign);if(status)qs.set('status',status);if(nonFinalized)qs.set('non_finalized','1');if(selectedFileKey)qs.set('key',selectedFileKey);
  try{const d=await api('/pedidos?'+qs.toString());const returnedVersion=String(d.monitor_version||d.updated_at||'');if(requestedVersion&&returnedVersion!==requestedVersion)throw new Error('Relatório de outra atualização. Atualize o painel e tente novamente.');finish(Array.isArray(d.items)?d.items:[])}catch(e){if(requestId!==ordersReportRequest||requestedVersion!==ordersDisplayedVersion)return;currentOrdersList={title,items:[],allItems:[],finalizedOnly:false,expedirOnly:false,problemOnly:false,ocorrenciaOnly:false,carrierReport:false};setOrdersExtraColumns(false,false);setOrdersProblemColumns(false);setOrdersCarrierColumns(false);body.innerHTML='';if(empty){empty.hidden=false;empty.textContent=e.message||'Erro ao carregar pedidos.'}if(loading)loading.hidden=true;if(wrap)wrap.hidden=false}
}
function bindDashboardDrilldowns(){
 const sg=$('[data-status-grid]'),cg=$('[data-campaign-grid]');
 if(sg&&!sg.dataset.bound){sg.dataset.bound='1';sg.addEventListener('click',e=>{const card=e.target.closest('[data-filter-status]');if(!card)return;const status=card.dataset.filterStatus||'';openOrdersList({status:status==='NÃO FINALIZADO'?'':status,nonFinalized:status==='NÃO FINALIZADO',title:status});});sg.addEventListener('keydown',e=>{if((e.key==='Enter'||e.key===' ')&&e.target.closest('[data-filter-status]')){e.preventDefault();e.target.closest('[data-filter-status]').click()}})}
 if(cg&&!cg.dataset.bound){cg.dataset.bound='1';cg.addEventListener('click',e=>{const target=e.target.closest('[data-filter-campaign]');if(!target)return;const campaign=target.dataset.filterCampaign||'';const rawStatus=target.dataset.filterStatus||'';const isNonFinalized=rawStatus==='NÃO FINALIZADO';const status=rawStatus==='NÃO FINALIZADO'?'':rawStatus;openOrdersList({campaign,status,nonFinalized:isNonFinalized,title:isNonFinalized?`${campaign} - NÃO FINALIZADO`:status?`${campaign} - ${status}`:campaign});});}
}
let currentOverdue={count:0,items:[]};
function renderOverdue(data){
  currentOverdue=data&&typeof data==='object'?data:{count:0,items:[]};
  if(!Array.isArray(currentOverdue.items))currentOverdue.items=[];
  const count=Number(currentOverdue.count ?? currentOverdue.items.length)||0;
  const el=$('[data-overdue-count]');if(el)el.textContent=String(count);

  const summary=$('[data-overdue-days-summary]');
  if(summary){
    const grouped={};
    for(const item of currentOverdue.items){
      const d=Math.max(1,Number(item?.dias_atraso)||1);
      grouped[d]=(grouped[d]||0)+1;
    }
    const days=Object.keys(grouped).map(Number).sort((a,b)=>a-b);
    if(!days.length){
      summary.innerHTML='<span>0 - 1 dia</span>';
      summary.classList.remove('is-two-columns');
    }else{
      summary.innerHTML=days.map(d=>`<span><b>${grouped[d]}</b> - ${d} ${d===1?'dia':'dias'}</span>`).join('');
      summary.classList.toggle('is-two-columns',days.length>=5);
      if(days.length>=5){summary.style.gridTemplateRows=`repeat(${Math.ceil(days.length/2)},auto)`;}else{summary.style.gridTemplateRows='';}
    }
  }


}
function openOverdueModal(){resetOverdueSort();
  const modal=$('[data-overdue-modal]');if(!modal)return;
  const items=Array.isArray(currentOverdue.items)?currentOverdue.items:[];
  setupOverdueFilters(items);
  renderOverdueReport();
  modal.hidden=false;
}
function xmlEsc(v){return String(v??'').replace(/[&<>\"]/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;'}[c]||c))}


async function downloadOverdueXlsx(){
  const items=Array.isArray(currentOverdueView)?currentOverdueView:[];if(!items.length)return;
  try{
    const XLSX=await ensureXlsxLibrary();
    const rows=items.map(x=>({
      'Data API':String(x.data_api||''),
      'Nota':String(x.nota||''),
      'Nome destinatário':String(x.destinatario||''),
      'Campanha':String(x.campanha||''),
      [currentOrdersList.ocorrenciaOnly?'Ocorrência':(currentOrdersList.problemOnly?'Status Rastreamento':'Status')]:String(currentOrdersList.ocorrenciaOnly?(x.ocorrencia||''):(currentOrdersList.problemOnly?(x.status_entrega||''):(x.status||''))),
      'Nome transportadora':String(x.transportadora||''),
      'Dias de atraso':Number(x.dias_atraso)||0
    }));
    const ws=XLSX.utils.json_to_sheet(rows);
    ws['!cols']=[{wch:22},{wch:16},{wch:38},{wch:28},{wch:20},{wch:30},{wch:15}];
    const wb=XLSX.utils.book_new();
    XLSX.utils.book_append_sheet(wb,ws,'Em Atraso');
    XLSX.writeFile(wb,'pedidos_em_atraso_'+new Date().toLocaleDateString('pt-BR').replace(/\//g,'-')+'.xlsx',{compression:true});
  }catch(e){alert(e.message||'Não foi possível gerar o Excel.');}
}
async function downloadOrdersXlsx(){
  const items=Array.isArray(currentOrdersList.items)?currentOrdersList.items:[];if(!items.length)return;
  try{
    const XLSX=await ensureXlsxLibrary();
    const rows=items.map(x=>{const row={'Data API':String(x.data_api||''),'Nota':String(x.nota||''),'Nome destinatário':String(x.destinatario||''),'Campanha':String(x.campanha||'')};if(currentOrdersList.carrierReport){row['Status Rastreamento']=String(x.status_entrega||'');row['Ocorrência']=String(x.ocorrencia||'');row['Tratativas']=String(x.tratativas||'');row['Previsão de Entrega']=String(x.previsao_entrega||'');row['Dias de atraso']=Number(x.dias_atraso)||0;}else if(currentOrdersList.ocorrenciaOnly)row['Ocorrência']=String(x.ocorrencia||'');else if(currentOrdersList.problemOnly)row['Status Rastreamento']=String(x.status_entrega||'');else row['Status']=String(x.status||'');row['Nome transportadora']=String(x.transportadora||'');if(currentOrdersList.finalizedOnly){row['DataRomaneio']=String(x.data_romaneio||'');row['Data Finalizado']=String(x.data_finalizado||'');}else if(currentOrdersList.expedirOnly){row['DataRomaneio']=String(x.data_romaneio||'');}return row;});
    const ws=XLSX.utils.json_to_sheet(rows);
    ws['!cols']=currentOrdersList.finalizedOnly?[{wch:22},{wch:16},{wch:38},{wch:28},{wch:20},{wch:30},{wch:22},{wch:22}]:(currentOrdersList.expedirOnly?[{wch:22},{wch:16},{wch:38},{wch:28},{wch:20},{wch:30},{wch:22}]:(currentOrdersList.carrierReport?[{wch:22},{wch:16},{wch:38},{wch:28},{wch:24},{wch:34},{wch:34},{wch:18},{wch:15},{wch:30}]:[{wch:22},{wch:16},{wch:38},{wch:28},{wch:20},{wch:30}]));
    const wb=XLSX.utils.book_new();XLSX.utils.book_append_sheet(wb,ws,'Pedidos');
    const base=String(currentOrdersList.title||'pedidos').normalize('NFD').replace(/[\u0300-\u036f]/g,'').replace(/[^a-zA-Z0-9_-]+/g,'_').replace(/^_+|_+$/g,'').toLowerCase()||'pedidos';
    XLSX.writeFile(wb,base+'.xlsx',{compression:true});
  }catch(e){alert(e.message||'Não foi possível gerar o Excel.');}
}


function reportCopyValue(v){
  const s=String(v??'').replace(/\r?\n+/g,' ').replace(/\s{2,}/g,' ').trim();
  return s||'-';
}
function formatOrdersForCopy(items){
  return (items||[]).map(x=>{
    const lines=[
      `Nota: ${reportCopyValue(x.nota)}`,
      `Destinatário: ${reportCopyValue(x.destinatario)}`,
      `Campanha: ${reportCopyValue(x.campanha)}`
    ];
    if(currentOrdersList.carrierReport){lines.push(`Status Rastreamento: ${reportCopyValue(x.status_entrega)}`);lines.push(`Ocorrência: ${reportCopyValue(x.ocorrencia)}`);lines.push(`Tratativas: ${reportCopyValue(x.tratativas)}`);lines.push(`Previsão de Entrega: ${reportCopyValue(x.previsao_entrega)}`);lines.push(`Dias de atraso: ${Number(x.dias_atraso)||0}`)}
    else lines.push(`${currentOrdersList.ocorrenciaOnly?'Ocorrência':(currentOrdersList.problemOnly?'Status Rastreamento':'Status')}: ${reportCopyValue(currentOrdersList.ocorrenciaOnly?x.ocorrencia:(currentOrdersList.problemOnly?x.status_entrega:x.status))}`);
    lines.push(`Transportadora: ${reportCopyValue(x.transportadora)}`);lines.push(`Data API: ${reportCopyValue(x.data_api)}`);
    return lines.join('\n');
  }).join('\n\n');
}
async function copyReportItems(items,button){
  const list=Array.isArray(items)?items:[];
  if(!list.length){alert('Não há dados para copiar.');return}
  const value=formatOrdersForCopy(list);
  try{
    await navigator.clipboard.writeText(value);
  }catch(e){
    const ta=document.createElement('textarea');
    ta.value=value;
    ta.style.position='fixed';ta.style.left='-9999px';ta.style.top='0';
    document.body.appendChild(ta);ta.focus();ta.select();
    const ok=document.execCommand('copy');ta.remove();
    if(!ok){alert('Não foi possível copiar os dados.');return}
  }
  if(button){
    const old=button.textContent;
    button.textContent='Copiado!';
    button.disabled=true;
    setTimeout(()=>{button.textContent=old;button.disabled=false},1200);
  }
}

const ordersTable=$('.lisa-orders-table');if(ordersTable)ordersTable.addEventListener('click',e=>{if(e.target.closest('.report-col-filter'))return;const th=e.target.closest('[data-orders-sort]');if(!th)return;ordersSort=nextSort(ordersSort,th.dataset.ordersSort||'',th.dataset.sortType||'text');renderOrdersReport();});
const overdueTable=$('.lisa-overdue-table');if(overdueTable)overdueTable.addEventListener('click',e=>{if(e.target.closest('.report-col-filter'))return;const th=e.target.closest('[data-overdue-sort]');if(!th)return;overdueSort=nextSort(overdueSort,th.dataset.overdueSort||'',th.dataset.sortType||'text');renderOverdueReport();});
const overdueCard=$('[data-overdue-card]');if(overdueCard){overdueCard.addEventListener('click',openOverdueModal);overdueCard.addEventListener('keydown',e=>{if(e.key==='Enter'||e.key===' '){e.preventDefault();openOverdueModal()}})}
const overdueCopy=$('[data-overdue-copy]');if(overdueCopy)overdueCopy.addEventListener('click',()=>copyReportItems(currentOverdueView,overdueCopy));
const overdueDownload=$('[data-overdue-download]');if(overdueDownload)overdueDownload.addEventListener('click',downloadOverdueXlsx);
const overdueClose=$('[data-overdue-close]');if(overdueClose)overdueClose.addEventListener('click',()=>{const m=$('[data-overdue-modal]');if(m)m.hidden=true;resetOverdueSort()});
const overdueModal=$('[data-overdue-modal]');if(overdueModal)overdueModal.addEventListener('click',e=>{if(e.target===overdueModal){overdueModal.hidden=true;resetOverdueSort()}});
const ordersClose=$('[data-orders-close]');if(ordersClose)ordersClose.addEventListener('click',()=>{const m=$('[data-orders-modal]');if(m)m.hidden=true;resetOrdersSort()});
const ordersModal=$('[data-orders-modal]');if(ordersModal)ordersModal.addEventListener('click',e=>{if(e.target===ordersModal){ordersModal.hidden=true;resetOrdersSort()}});
const ordersCopy=$('[data-orders-copy]');if(ordersCopy)ordersCopy.addEventListener('click',()=>copyReportItems(currentOrdersList.items,ordersCopy));
const ordersDownload=$('[data-orders-download]');if(ordersDownload)ordersDownload.addEventListener('click',downloadOrdersXlsx);
for(const el of $$popup('[data-orders-filter-campaign],[data-orders-filter-status],[data-orders-filter-transportadora],[data-orders-filter-data-api],[data-orders-filter-previsao],[data-orders-filter-data-finalizado],[data-orders-filter-data-romaneio]'))el.addEventListener('change',renderOrdersReport);
for(const el of $$popup('[data-orders-filter-nota],[data-orders-filter-destinatario],[data-orders-filter-tracking-status],[data-orders-filter-ocorrencia],[data-orders-filter-tratativas],[data-orders-filter-dias-atraso]'))el.addEventListener('input',renderOrdersReport);
for(const el of $$popup('[data-overdue-filter-campaign],[data-overdue-filter-status],[data-overdue-filter-transportadora],[data-overdue-filter-data-api],[data-overdue-filter-dias]'))el.addEventListener('change',renderOverdueReport);
for(const el of $$popup('[data-overdue-filter-nota],[data-overdue-filter-destinatario]'))el.addEventListener('input',renderOverdueReport);
bindDashboardDrilldowns();

const attendanceOrder=['Alef','Arthur','Daniel','Eduardo','Erick','Geovane','Jonathas','Leonardo','Vinicius'];
const attendanceGroups=['Contratos','Sac','Vendas','Vendas Ativas'];
const attendanceSpecial=['Spam'];
const attendanceCardLabel=key=>({'__total':'Total','__unassigned':'Não atribuído','__openarrival':'Aberto','Spam':'Spam'}[key]||key);
function ensureAttendanceRows(){
 const list=$('[data-attendance-list]');if(!list||list.children.length)return;
 const rows=[];
 rows.push('<div class="attendance-row summary total" data-att-key="__total"><span class="attendance-card-title">Total</span><b>0</b></div>');
 rows.push('<div class="attendance-row summary unassigned" data-att-key="__unassigned"><span class="attendance-card-title">Não atribuído</span><b>0</b></div>');
 rows.push('<div class="attendance-row summary open-arrival" data-att-key="__openarrival"><span class="attendance-card-title">Aberto</span><b>0</b></div>');
 rows.push('<div class="attendance-divider"></div>');
 const attendanceRowCounts=[];
 const totalPeople=attendanceOrder.length;
 const basePeople=Math.floor(totalPeople/3),extraPeople=totalPeople%3;
 for(let r=0;r<3;r++)attendanceRowCounts.push(basePeople+(r<extraPeople?1:0));
 let personIndex=0;
 for(const rowCount of attendanceRowCounts){
  if(rowCount<=0)continue;
  const span=Math.max(1,Math.floor(12/rowCount));
  for(let j=0;j<rowCount&&personIndex<attendanceOrder.length;j++,personIndex++){
   const n=attendanceOrder[personIndex];
   rows.push(`<div class="attendance-row attendance-person" style="--att-span:${span}" data-att-key="${esc(n)}"><span>${esc(n)}</span><b>0</b></div>`);
  }
 }
 rows.push('<div class="attendance-divider"></div>');
 for(const n of attendanceGroups)rows.push(`<div class="attendance-row attendance-group" data-att-key="${esc(n)}"><span>${esc(n)}</span><b>0</b></div>`);
 for(const n of attendanceSpecial)rows.push(`<div class="attendance-row attendance-team-spam" data-att-key="${esc(n)}"><span>${esc(attendanceCardLabel(n))}</span><b>0</b></div>`);
 list.innerHTML=rows.join('');list.hidden=false;
}
function setAttendanceValue(key,value,openCount=null){
 const row=[...root.querySelectorAll('[data-att-key]')].find(el=>el.dataset.attKey===key);
 if(!row)return;
 const b=row.querySelector('b');if(!b)return;
 const total=Number(value)||0;
 if(openCount===null||openCount===undefined){b.textContent=String(total);return}
 const opened=Math.max(0,Number(openCount)||0);
 b.innerHTML=`<span class="attendance-open-count">${opened}</span><span class="attendance-slash">/</span><span class="attendance-total-count">${total}</span>`;
}
let attendanceBusy=false;
function renderAttendanceData(d){
 ensureAttendanceRows();
 setAttendanceValue('__total',d.total,d.open_total);
 setAttendanceValue('__unassigned',d.unassigned);
 setAttendanceValue('__openarrival',d.open_without_agent);
 for(const n of attendanceOrder)setAttendanceValue(n,d.groups?.[n],d.open_groups?.[n]);
 for(const n of attendanceGroups)setAttendanceValue(n,d.groups?.[n],d.open_groups?.[n]);
 for(const n of attendanceSpecial)setAttendanceValue(n,d.groups?.[n],d.open_groups?.[n]);
 const missing=(Array.isArray(d.missing_freshdesk_groups)?d.missing_freshdesk_groups:[]).filter(n=>attendanceGroups.includes(n));
 for(const n of missing){const row=[...root.querySelectorAll('[data-att-key]')].find(el=>el.dataset.attKey===n);if(row){const val=row.querySelector('b');if(val)val.textContent='—';row.title='Grupo não localizado na API de grupos do Freshdesk';}}
 const alert=$('[data-attendance-error]');if(alert&&missing.length){alert.textContent=(d.freshdesk_group_error?'Não foi possível consultar os grupos do Freshdesk: '+d.freshdesk_group_error:'Grupo(s) não encontrado(s) no Freshdesk: '+missing.join(', ')+'. Confira a configuração dos grupos.');alert.hidden=false;}
 const list=$('[data-attendance-list]');if(list)list.hidden=false;
}
async function loadAttendance(){
 if(attendanceBusy)return;
 attendanceBusy=true;
 const list=$('[data-attendance-list]'),loading=$('[data-attendance-loading]'),err=$('[data-attendance-error]'),btn=$('[data-attendance-refresh]');
 if(!list||!loading||!err){attendanceBusy=false;return}
 const firstLoad=!list.children.length;
 err.hidden=true;err.textContent='';
 if(firstLoad){loading.hidden=false;}else{loading.hidden=true;ensureAttendanceRows();}
 try{
   const d=await api('/atendimento');
   renderAttendanceData(d);
   // Pre-carrega o snapshot historico sem bloquear o painel. A primeira vez tambem
   // dispara uma carga via API caso o banco ainda esteja vazio ou em backfill.
   preloadAttendanceHistory(false);
   const u=d.updated_at?new Date(d.updated_at):new Date();
   $('[data-attendance-update]').textContent=!Number.isNaN(u.getTime())?u.toLocaleString('pt-BR',{timeZone:'America/Sao_Paulo',day:'2-digit',month:'2-digit',hour:'2-digit',minute:'2-digit'}):'--/-- --:--';
 }catch(e){console.error(e);err.textContent='Atendimento indisponível: '+String(e?.message||'verifique o snapshot no Cloudflare');err.hidden=false}
 finally{loading.hidden=true;loading.textContent='Carregando atendimento...';attendanceBusy=false}
}

function attendanceHistoryFormatMinutes(value){
 const mins=Number(value);
 if(!Number.isFinite(mins)||mins<0)return '-';
 if(mins<60)return `${Math.round(mins)} min`;
 const h=Math.floor(mins/60),m=Math.round(mins%60);
 return m?`${h}h ${m}min`:`${h}h`;
}
function attendanceHistoryCurrentMonthKey(){
 const d=new Date();return `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}`;
}
function attendanceHistoryMonthLabel(key){
 const m=String(key||'').match(/^(\d{4})-(\d{2})$/);if(!m)return String(key||'');
 const nomes=['Janeiro','Fevereiro','Março','Abril','Maio','Junho','Julho','Agosto','Setembro','Outubro','Novembro','Dezembro'];
 return `${nomes[Number(m[2])-1]||m[2]}/${m[1]}`;
}
function attendanceHistoryPlaceholderMonth(){
 const now=new Date(),month=attendanceHistoryCurrentMonthKey(),daily=[];
 for(let day=1;day<=now.getDate();day++)daily.push({date:`${month}-${String(day).padStart(2,'0')}`,label:`${String(day).padStart(2,'0')}/${String(now.getMonth()+1).padStart(2,'0')}`,finalized:0,avg_first_response_minutes:null});
 return {month,label:attendanceHistoryMonthLabel(month),total_finalized:0,avg_first_response_minutes:null,daily:daily.reverse()};
}
function attendanceHistoryNormalizeMonths(value){
 const months=Array.isArray(value)?value.filter(x=>x&&typeof x==='object'):[];
 return months.length?months:[attendanceHistoryPlaceholderMonth()];
}
const ATTENDANCE_HISTORY_CLIENT_CACHE_KEY='lisa_attendance_history_cache_v6';
const ATTENDANCE_HISTORY_CLIENT_KICK_KEY='lisa_attendance_history_kick_v6';
let attendanceHistoryClientData=null,attendanceHistoryPreloadPromise=null;
function attendanceHistoryReadClientCache(){
 if(attendanceHistoryClientData?.teams)return attendanceHistoryClientData;
 try{const raw=localStorage.getItem(ATTENDANCE_HISTORY_CLIENT_CACHE_KEY);if(!raw)return null;const d=JSON.parse(raw);if(d&&d.teams&&typeof d.teams==='object'){attendanceHistoryClientData=d;return d}}catch{}
 return null;
}
function attendanceHistoryWriteClientCache(data){
 if(!data||!data.teams||typeof data.teams!=='object')return;
 attendanceHistoryClientData=data;
 try{localStorage.setItem(ATTENDANCE_HISTORY_CLIENT_CACHE_KEY,JSON.stringify(data))}catch{}
}
function attendanceHistoryUpdateCachedTeam(name,months,meta={}){
 const key=String(name||'').trim();if(!key)return;
 const base=attendanceHistoryReadClientCache()||attendanceOverviewPlaceholderData();
 if(!base.teams||typeof base.teams!=='object')base.teams={};
 base.teams[key]=attendanceHistoryNormalizeMonths(months);
 for(const field of ['updated_at','sync_cursor','backfill_in_progress','backfill_completed_at','db_status','last_backfill_count','last_recent_count'])if(meta[field]!==undefined)base[field]=meta[field];
 attendanceHistoryWriteClientCache(base);
}
function attendanceHistoryCachedMonths(name){
 const d=attendanceHistoryReadClientCache(),list=d?.teams?.[String(name||'').trim()];
 return Array.isArray(list)&&list.length?list:null;
}


async function preloadAttendanceHistory(forceSync=false){
 if(attendanceHistoryPreloadPromise&&!forceSync)return attendanceHistoryPreloadPromise;
 let promise;promise=(async()=>{
   try{
     const d=await api('/atendimento/historico?team=__all');
     if(d?.teams&&d.updated_at)attendanceHistoryWriteClientCache(d);
     const waiting=!d?.updated_at||d?.db_status==='waiting'||d?.backfill_in_progress===true;
     // Historic data is prepared by Cloudflare Cron, not by a browser.
     return attendanceHistoryReadClientCache()||d||null;
   }catch(e){console.error('Falha ao pré-carregar histórico de atendimento:',e);return attendanceHistoryReadClientCache()}
 })().finally(()=>{if(attendanceHistoryPreloadPromise===promise)attendanceHistoryPreloadPromise=null});
 attendanceHistoryPreloadPromise=promise;return promise;
}
function renderAttendanceHistoryMonths(host,months){
 const list=attendanceHistoryNormalizeMonths(months);
 if(!host)return;
 host.innerHTML=list.map((x,i)=>`<button type="button" class="attendance-history-month-card" data-attendance-history-month="${i}"><div class="attendance-history-month-title">${esc(x.label||attendanceHistoryMonthLabel(x.month)||'-')}</div><div class="attendance-history-month-kpis"><div><span>FINALIZADOS</span><strong>${hasMonth?Number(x.total_finalized||0).toLocaleString('pt-BR'):'—'}</strong></div><div><span>MÉDIA 1ª RESPOSTA</span><strong>${hasMonth?attendanceHistoryFormatMinutes(x.avg_first_response_minutes):'—'}</strong></div></div><small>Clique para ver o desempenho por dia</small></button>`).join('');
}
function renderAttendanceHistoryDays(body,rows){
 const list=Array.isArray(rows)&&rows.length?rows:[];
 if(body)body.innerHTML=list.map(x=>`<tr><td>${esc(x.label||x.date||'-')}</td><td>${Number(x.finalized||0).toLocaleString('pt-BR')}</td><td>${attendanceHistoryFormatMinutes(x.avg_first_response_minutes)}</td></tr>`).join('');
}
function ensureAttendanceHistoryDayModal(){
 let modal=$('[data-attendance-history-day-modal]');if(modal)return modal;
 modal=document.createElement('div');modal.className='attendance-history-day-modal';modal.setAttribute('data-attendance-history-day-modal','');modal.hidden=true;
 modal.innerHTML=`<div class="attendance-history-day-shell"><button type="button" class="attendance-history-x" data-attendance-history-day-close aria-label="Fechar">×</button><div class="attendance-history-head"><div><h3 data-attendance-history-day-title>Desempenho diário</h3><span>Tickets finalizados e tempo médio de 1ª resposta por dia</span></div></div><div class="attendance-history-summary"><div><span>FINALIZADOS NO MÊS</span><strong data-attendance-history-day-total>0</strong></div><div><span>MÉDIA 1ª RESPOSTA</span><strong data-attendance-history-day-response>-</strong></div></div><div class="attendance-history-table-wrap"><table class="attendance-history-table"><thead><tr><th>Dia</th><th>Finalizados</th><th>Média 1ª resposta</th></tr></thead><tbody data-attendance-history-day-body></tbody></table></div></div>`;
 root.appendChild(modal);
 const close=()=>{modal.hidden=true};modal.querySelector('[data-attendance-history-day-close]')?.addEventListener('click',close);modal.addEventListener('click',e=>{if(e.target===modal)close()});return modal;
}
function openAttendanceHistoryMonth(person,month){
 const modal=ensureAttendanceHistoryDayModal(),x=month||attendanceHistoryPlaceholderMonth();
 modal.querySelector('[data-attendance-history-day-title]').textContent=`${attendanceCardLabel(person)} · ${x.label||attendanceHistoryMonthLabel(x.month)}`;
 modal.querySelector('[data-attendance-history-day-total]').textContent=Number(x.total_finalized||0).toLocaleString('pt-BR');
 modal.querySelector('[data-attendance-history-day-response]').textContent=attendanceHistoryFormatMinutes(x.avg_first_response_minutes);
 renderAttendanceHistoryDays(modal.querySelector('[data-attendance-history-day-body]'),x.daily||[]);modal.hidden=false;
}
function attendanceOverviewPlaceholderData(){
 const month=attendanceHistoryPlaceholderMonth();
 const teams={};
 for(const name of [...attendanceOrder,...attendanceGroups,...attendanceSpecial,'__total','__unassigned','__openarrival'])teams[name]=[{month:month.month,label:month.label,total_finalized:0,avg_first_response_minutes:null}];
 return {ok:true,scope:'all',months:[month.month],teams,updated_at:null,sync_cursor:'2026-01-01T00:00:00.000Z',backfill_in_progress:true,backfill_completed_at:null,db_status:'waiting',channels:{db_status:'waiting',backfill_in_progress:true,updated_at:null,months:[{month:month.month,label:month.label,total:0,email:0,whatsapp:0,other:0}]}};
}
function attendanceOverviewMonthKeys(data){
 const keys=new Set();
 for(const m of (Array.isArray(data?.months)?data.months:[]))if(/^\d{4}-\d{2}$/.test(String(m||'')))keys.add(String(m));
 for(const name of attendanceOrder)for(const x of (Array.isArray(data?.teams?.[name])?data.teams[name]:[]))if(/^\d{4}-\d{2}$/.test(String(x?.month||'')))keys.add(String(x.month));
 if(!keys.size)keys.add(attendanceHistoryCurrentMonthKey());
 return [...keys].sort().reverse();
}
function attendanceOverviewMonthFor(data,name,monthKey){
 const list=Array.isArray(data?.teams?.[name])?data.teams[name]:[];
 const found=list.find(x=>String(x?.month||'')===String(monthKey||''));
 if(found)return found;
 return {month:monthKey,label:attendanceHistoryMonthLabel(monthKey),total_finalized:0,avg_first_response_minutes:null};
}
function attendanceOverviewGeneralAverage(data,monthKey){
 let weighted=0,totalWeight=0;
 for(const name of attendanceOrder){
   const x=attendanceOverviewMonthFor(data,name,monthKey);
   const avg=Number(x?.avg_first_response_minutes),weight=Number(x?.total_finalized||0);
   if(Number.isFinite(avg)&&avg>=0&&Number.isFinite(weight)&&weight>0){weighted+=avg*weight;totalWeight+=weight}
 }
 return totalWeight>0?weighted/totalWeight:null;
}
function attendanceOverviewFormatDateTime(value){
 const d=value?new Date(value):null;if(!d||Number.isNaN(d.getTime()))return '';
 return d.toLocaleString('pt-BR',{timeZone:'America/Sao_Paulo',day:'2-digit',month:'2-digit',year:'numeric',hour:'2-digit',minute:'2-digit'});
}
function attendanceOverviewChannelMonth(data,monthKey){
 const list=Array.isArray(data?.channels?.months)?data.channels.months:[];
 const found=list.find(x=>String(x?.month||'')===String(monthKey||''));
 return found||{month:monthKey,label:attendanceHistoryMonthLabel(monthKey),total:0,email:0,whatsapp:0,other:0};
}
function attendanceOverviewChannelMonths(data){
 const list=Array.isArray(data?.channels?.months)?data.channels.months:[];
 return list.length?list:attendanceOverviewMonthKeys(data).map(month=>({month,label:attendanceHistoryMonthLabel(month),total:0,email:0,whatsapp:0,other:0}));
}
function ensureAttendanceChannelsModal(){
 let modal=$('[data-attendance-channels-modal]');if(modal)return modal;
 modal=document.createElement('div');modal.className='attendance-history-day-modal attendance-channels-modal';modal.setAttribute('data-attendance-channels-modal','');modal.hidden=true;
 modal.innerHTML=`<div class="attendance-history-day-shell attendance-channels-shell"><button type="button" class="attendance-history-x" data-attendance-channels-close aria-label="Fechar">×</button><div class="attendance-history-head"><div><h3>Atendimentos recebidos por canal</h3><span>Quantidade de tickets criados em cada mês</span></div></div><div class="attendance-history-table-wrap"><table class="attendance-history-table"><thead><tr><th>Mês</th><th>Total</th><th>E-mail</th><th>WhatsApp</th></tr></thead><tbody data-attendance-channels-body></tbody></table></div></div>`;
 root.appendChild(modal);
 const close=()=>{modal.hidden=true};modal.querySelector('[data-attendance-channels-close]')?.addEventListener('click',close);modal.addEventListener('click',e=>{if(e.target===modal)close()});return modal;
}
function openAttendanceChannelsHistory(data){
 const modal=ensureAttendanceChannelsModal(),body=modal.querySelector('[data-attendance-channels-body]');
 const rows=attendanceOverviewChannelMonths(data);
 if(body)body.innerHTML=rows.map(x=>`<tr><td>${esc(x.label||attendanceHistoryMonthLabel(x.month)||'-')}</td><td>${Number(x.total||0).toLocaleString('pt-BR')}</td><td>${Number(x.email||0).toLocaleString('pt-BR')}</td><td>${Number(x.whatsapp||0).toLocaleString('pt-BR')}</td></tr>`).join('');
 modal.hidden=false;
}
function renderAttendanceOverviewSync(modal,data){
 if(!modal)return;
 const box=modal.querySelector('[data-attendance-overview-sync]'),badge=modal.querySelector('[data-attendance-overview-sync-badge]'),detail=modal.querySelector('[data-attendance-overview-sync-detail]'),meta=modal.querySelector('[data-attendance-overview-sync-meta]');
 if(!box||!badge||!detail||!meta)return;
 const ready=data?.db_status==='ready'||data?.backfill_in_progress===false;
 const waiting=data?.db_status==='waiting'||(!data?.updated_at&&data?.backfill_in_progress!==false);
 box.classList.toggle('is-ready',ready);box.classList.toggle('is-syncing',!ready);
 if(ready){
   badge.textContent='HISTÓRICO 2026: COMPLETO ✓';
   const completed=attendanceOverviewFormatDateTime(data?.backfill_completed_at);
   detail.textContent='Todos os dados históricos desde 01/01/2026 foram importados para o banco do Cloudflare.';
   meta.textContent=completed?`Concluído em ${completed}`:(data?.updated_at?`Última atualização: ${attendanceOverviewFormatDateTime(data.updated_at)}`:'Histórico concluído.');
 }else if(waiting){
   badge.textContent='DADOS SALVOS INDISPONÍVEIS';
   detail.textContent='O resultado salvo ainda não está disponível. A preparação depende da rotina agendada.';
   meta.textContent='A carga continua em segundo plano sem bloquear a abertura do painel.';
 }else{
   badge.textContent='SINCRONIZAÇÃO AUTOMÁTICA ATIVA';
   const cursor=attendanceOverviewFormatDateTime(data?.sync_cursor),updated=attendanceOverviewFormatDateTime(data?.updated_at);
   detail.textContent=cursor?`Último ponto processado: ${cursor}`:'Importação histórica em andamento.';
   const batch=Number(data?.last_backfill_count||0),recent=Number(data?.last_recent_count||0),parts=[];
   if(updated)parts.push(`Última sincronização: ${updated}`);
   if(batch>0)parts.push(`${batch.toLocaleString('pt-BR')} históricos no último lote`);
   if(recent>0)parts.push(`${recent.toLocaleString('pt-BR')} recentes revisados`);
   meta.textContent=parts.join(' · ')||'O cron continuará do ponto salvo na próxima execução.';
 }
}
function renderAttendanceOverview(modal){
 if(!modal)return;
 const data=modal.__attendanceOverviewData||attendanceOverviewPlaceholderData();
 const keys=attendanceOverviewMonthKeys(data),select=modal.querySelector('[data-attendance-overview-month]'),host=modal.querySelector('[data-attendance-overview-cards]');
 let selected=String(modal.__attendanceOverviewMonth||'');if(!keys.includes(selected)||(!modal.__attendanceMonthExplicit&&!Object.values(data.teams||{}).some(months=>months.some(m=>m.month===selected&&Number(m.total_finalized)>0))))selected=keys.find(k=>Object.values(data.teams||{}).some(months=>months.some(m=>m.month===k&&Number(m.total_finalized)>0)))||keys[0]||attendanceHistoryCurrentMonthKey();modal.__attendanceOverviewMonth=selected;
 if(select){select.innerHTML=keys.map(k=>`<option value="${esc(k)}"${k===selected?' selected':''}>${esc(attendanceHistoryMonthLabel(k))}</option>`).join('');select.value=selected}
 const general=modal.querySelector('[data-attendance-overview-general-response]');
 const hasMonth=Object.values(data.teams||{}).some(months=>months.some(m=>m.month===selected&&Number(m.total_finalized)>0));
 if(general)general.textContent=hasMonth?attendanceHistoryFormatMinutes(attendanceOverviewGeneralAverage(data,selected)):'—';
 const channel=attendanceOverviewChannelMonth(data,selected);
 const channelTotal=modal.querySelector('[data-attendance-channel-total]'),channelEmail=modal.querySelector('[data-attendance-channel-email]'),channelWhatsapp=modal.querySelector('[data-attendance-channel-whatsapp]'),channelStatus=modal.querySelector('[data-attendance-channel-status]');
 if(channelTotal)channelTotal.textContent=data.channels?.months?.some(m=>m.month===selected)?Number(channel.total||0).toLocaleString('pt-BR'):'—';
 if(channelEmail)channelEmail.textContent=data.channels?.months?.some(m=>m.month===selected)?Number(channel.email||0).toLocaleString('pt-BR'):'—';
 if(channelWhatsapp)channelWhatsapp.textContent=data.channels?.months?.some(m=>m.month===selected)?Number(channel.whatsapp||0).toLocaleString('pt-BR'):'—';
 if(channelStatus){const ready=data?.channels?.db_status==='ready'||data?.channels?.backfill_in_progress===false;channelStatus.textContent=ready?'Histórico de canais completo':'Histórico de canais sincronizando';channelStatus.classList.toggle('is-ready',ready)}
 renderAttendanceOverviewSync(modal,data);
 if(host)host.innerHTML=attendanceOrder.map(name=>{const x=attendanceOverviewMonthFor(data,name,selected);return `<button type="button" class="attendance-overview-person-card" data-attendance-overview-person="${esc(name)}"><div class="attendance-overview-person-name">${esc(name)}</div><div class="attendance-overview-person-kpis"><div><span>FINALIZADOS</span><strong>${hasMonth?Number(x.total_finalized||0).toLocaleString('pt-BR'):'—'}</strong></div><div><span>MÉDIA 1ª RESPOSTA</span><strong>${hasMonth?attendanceHistoryFormatMinutes(x.avg_first_response_minutes):'—'}</strong></div></div><small>Clique para abrir o histórico do atendente</small></button>`}).join('');
}
function ensureAttendanceOverviewModal(){
 let modal=$('[data-attendance-overview-modal]');if(modal)return modal;
 modal=document.createElement('div');modal.className='attendance-overview-modal';modal.setAttribute('data-attendance-overview-modal','');modal.hidden=true;
 modal.innerHTML=`<div class="attendance-overview-shell"><button type="button" class="attendance-history-x" data-attendance-overview-close aria-label="Fechar">×</button><div class="attendance-history-head"><div><h3>Desempenho da equipe</h3><span>Histórico mensal de cada atendente</span></div></div><div class="attendance-overview-general"><span>MÉDIA GERAL DE 1ª RESPOSTA</span><strong data-attendance-overview-general-response>—</strong><small>Média ponderada da equipe no mês selecionado</small></div><div class="attendance-overview-channel-grid"><button type="button" class="attendance-overview-channel-card" data-attendance-channel-open><span>ATENDIMENTOS RECEBIDOS</span><strong data-attendance-channel-total>0</strong><small>Total no mês selecionado</small></button><button type="button" class="attendance-overview-channel-card" data-attendance-channel-open><span>E-MAIL</span><strong data-attendance-channel-email>0</strong><small>Recebidos por e-mail</small></button><button type="button" class="attendance-overview-channel-card" data-attendance-channel-open><span>WHATSAPP</span><strong data-attendance-channel-whatsapp>0</strong><small>Recebidos pelo WhatsApp</small></button></div><div class="attendance-overview-channel-status" data-attendance-channel-status>Histórico de canais carregando do banco</div><div class="attendance-overview-sync is-syncing" data-attendance-overview-sync><div><strong data-attendance-overview-sync-badge>DADOS SALVOS INDISPONÍVEIS</strong><span data-attendance-overview-sync-detail>Consultando o último resultado salvo.</span></div><small data-attendance-overview-sync-meta>A atualização ocorre em segundo plano.</small></div><div class="attendance-overview-toolbar"><label>MÊS<select data-attendance-overview-month></select></label><div class="attendance-history-state" data-attendance-overview-state>Carregando o último resultado salvo.</div></div><div class="attendance-overview-cards" data-attendance-overview-cards></div></div>`;
 root.appendChild(modal);modal.__attendanceOverviewData=attendanceOverviewPlaceholderData();modal.__attendanceOverviewMonth=attendanceHistoryCurrentMonthKey();renderAttendanceOverview(modal);
 const close=()=>{modal.hidden=true};modal.querySelector('[data-attendance-overview-close]')?.addEventListener('click',close);modal.addEventListener('click',e=>{if(e.target===modal)close()});
 modal.querySelector('[data-attendance-overview-month]')?.addEventListener('change',e=>{modal.__attendanceMonthExplicit=true;modal.__attendanceOverviewMonth=String(e.target.value||'');renderAttendanceOverview(modal)});
 modal.querySelector('[data-attendance-overview-cards]')?.addEventListener('click',e=>{const card=e.target.closest('[data-attendance-overview-person]');if(!card)return;openAttendanceHistory(card.dataset.attendanceOverviewPerson||'')});
 modal.querySelectorAll('[data-attendance-channel-open]').forEach(btn=>btn.addEventListener('click',()=>openAttendanceChannelsHistory(modal.__attendanceOverviewData||attendanceOverviewPlaceholderData())));
 return modal;
}
async function openAttendanceOverview(){
 const modal=ensureAttendanceOverviewModal(),state=modal.querySelector('[data-attendance-overview-state]');
 const saved=attendanceHistoryReadClientCache();
 modal.__attendanceOverviewData=saved?.teams?saved:attendanceOverviewPlaceholderData();modal.__attendanceMonthExplicit=false;modal.__attendanceOverviewMonth=attendanceHistoryCurrentMonthKey();modal.hidden=false;renderAttendanceOverview(modal);
 if(state){state.textContent=saved?.updated_at?'Resultado salvo exibido imediatamente · conferindo atualização em segundo plano.':'Preparando a primeira carga via API.';state.classList.remove('is-error')}
 try{
   const d=await api('/atendimento/historico?team=__all');
   if(d?.teams&&d.updated_at)attendanceHistoryWriteClientCache(d);
   modal.__attendanceOverviewData=d?.teams&&d.updated_at?d:(saved?.updated_at?saved:d||attendanceOverviewPlaceholderData());renderAttendanceOverview(modal);
   if(state){const updated=d?.updated_at?attendanceOverviewFormatDateTime(d.updated_at):'';state.textContent=d?.backfill_in_progress?`Resultados salvos disponíveis${updated?' · atualizado '+updated:''} · histórico completo continua em segundo plano.`:`Banco histórico pronto${updated?' · atualizado '+updated:''}.`;state.classList.remove('is-error')}
   /* Historico atualizado via Cron; nao disparar sync ao visualizar. */
 }catch(e){console.error(e);if(state){state.textContent=saved?.teams?'Exibindo o último resultado salvo. A atualização será tentada novamente automaticamente.':'Carga inicial ainda não disponível; a sincronização automática continuará em segundo plano.';state.classList.toggle('is-error',!saved?.teams)}}
}
function ensureAttendanceHistoryModal(){
 let modal=$('[data-attendance-history-modal]');if(modal)return modal;
 modal=document.createElement('div');modal.className='attendance-history-modal';modal.setAttribute('data-attendance-history-modal','');modal.hidden=true;
 modal.innerHTML=`<div class="attendance-history-shell"><button type="button" class="attendance-history-x" data-attendance-history-close aria-label="Fechar">×</button><div class="attendance-history-head"><div><h3 data-attendance-history-title>Desempenho do atendimento</h3><span>Histórico armazenado por mês · clique em um mês para abrir os dias</span></div></div><div class="attendance-history-state" data-attendance-history-state>Banco aguardando a primeira sincronização.</div><div class="attendance-history-months" data-attendance-history-months></div></div>`;
 root.appendChild(modal);modal.__attendanceHistoryMonths=[attendanceHistoryPlaceholderMonth()];modal.__attendanceHistoryPerson='';renderAttendanceHistoryMonths(modal.querySelector('[data-attendance-history-months]'),modal.__attendanceHistoryMonths);
 const close=()=>{modal.hidden=true};modal.querySelector('[data-attendance-history-close]')?.addEventListener('click',close);modal.addEventListener('click',e=>{if(e.target===modal)close()});modal.querySelector('[data-attendance-history-months]')?.addEventListener('click',e=>{const card=e.target.closest('[data-attendance-history-month]');if(!card)return;const i=Number(card.dataset.attendanceHistoryMonth||0),month=modal.__attendanceHistoryMonths?.[i];openAttendanceHistoryMonth(modal.__attendanceHistoryPerson||'',month)});return modal;
}
async function openAttendanceHistory(person){
 const name=String(person||'').trim();if(!name)return;
 const modal=ensureAttendanceHistoryModal(),title=modal.querySelector('[data-attendance-history-title]'),state=modal.querySelector('[data-attendance-history-state]'),monthsHost=modal.querySelector('[data-attendance-history-months]');
 const savedMonths=attendanceHistoryCachedMonths(name);
 modal.__attendanceHistoryPerson=name;modal.__attendanceHistoryMonths=savedMonths||[attendanceHistoryPlaceholderMonth()];modal.hidden=false;if(title)title.textContent=`${attendanceCardLabel(name)} · desempenho`;if(state){state.textContent=savedMonths?'Resultado salvo exibido imediatamente · atualizando em segundo plano.':'Preparando a primeira carga via API.';state.classList.remove('is-error')}renderAttendanceHistoryMonths(monthsHost,modal.__attendanceHistoryMonths);
 try{
   const d=await api('/atendimento/historico?team='+encodeURIComponent(name));
   modal.__attendanceHistoryMonths=attendanceHistoryNormalizeMonths(d.months);renderAttendanceHistoryMonths(monthsHost,modal.__attendanceHistoryMonths);
   attendanceHistoryUpdateCachedTeam(name,modal.__attendanceHistoryMonths,d||{});
   if(state){const updated=d.updated_at?new Date(d.updated_at):null;const when=updated&&!Number.isNaN(updated.getTime())?updated.toLocaleString('pt-BR',{timeZone:'America/Sao_Paulo',day:'2-digit',month:'2-digit',hour:'2-digit',minute:'2-digit'}):'';state.textContent=d.backfill_in_progress?`Resultado salvo${when?' · atualizado '+when:''} · histórico completo continua em segundo plano.`:`Banco atualizado${when?' em '+when:''}`;state.classList.remove('is-error')}
   /* Historico atualizado via Cron; nao disparar sync ao visualizar. */
 }catch(e){console.error(e);if(state){state.textContent=savedMonths?'Exibindo o último resultado salvo. A API será consultada novamente automaticamente.':'Carga inicial ainda em andamento.';state.classList.toggle('is-error',!savedMonths)}}
}
const attendanceListForHistory=$('[data-attendance-list]');
if(attendanceListForHistory&&!attendanceListForHistory.dataset.historyBound){
 attendanceListForHistory.dataset.historyBound='1';
 // AltGr nem sempre aparece como modificador do evento click no Chrome/Windows.
 let attendanceAltGrHeld=false;
 const isAttendanceAltGr=e=>e.key==='AltGraph'||(e.altKey&&e.ctrlKey)||(e.code==='AltRight'&&e.altKey);
 document.addEventListener('keydown',e=>{if(isAttendanceAltGr(e))attendanceAltGrHeld=true},true);
 document.addEventListener('keyup',e=>{if(e.key==='AltGraph'||e.key==='Alt'||e.key==='Control'||(!e.altKey&&!e.ctrlKey))attendanceAltGrHeld=false},true);
 window.addEventListener('blur',()=>{attendanceAltGrHeld=false});
 attendanceListForHistory.addEventListener('click',e=>{
  const row=e.target.closest('.attendance-row[data-att-key]');if(!row||!attendanceListForHistory.contains(row))return;
  const altGraph=typeof e.getModifierState==='function'&&e.getModifierState('AltGraph');
  if(!(altGraph||(e.altKey&&e.ctrlKey)||attendanceAltGrHeld))return;
  const key=String(row.dataset.attKey||'');if(!key)return;
  e.preventDefault();e.stopPropagation();
  if(key==='__total')openAttendanceOverview();else openAttendanceHistory(key);
 });
}
setInterval(()=>{
  if(!document.hidden&&token()&&WORKER&&!shell.hidden)loadAttendance();
},15*60*1000);

setInterval(()=>{
  if(!document.hidden)checkOrdersVersion();
},10*60*1000);

function esc(s){return String(s??'').replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]))}
function formatTimelineDate(value){
  const d=value?new Date(value):null;
  if(!d||Number.isNaN(d.getTime()))return '';
  return d.toLocaleString('pt-BR',{timeZone:'America/Sao_Paulo',day:'2-digit',month:'2-digit',hour:'2-digit',minute:'2-digit'}).replace(',','');
}
function renderTimelineMenu(){
  const menu=$('[data-timeline-menu]');if(!menu)return;
  if(!timelineFiles.length){menu.innerHTML='<span class="lisa-timeline-item">Nenhum arquivo disponível.</span>';return}
  menu.innerHTML=timelineFiles.map(f=>{
    const label=formatTimelineDate(f.uploaded_at)||'Data indisponível';
    const active=selectedFileKey?f.key===selectedFileKey:!!f.is_latest;
    return `<button type="button" class="lisa-timeline-item${active?' is-active':''}" data-timeline-key="${esc(f.key)}">${esc(label)}</button>`;
  }).join('');
}
async function loadTimeline(){
  const menu=$('[data-timeline-menu]');if(!menu)return;
  menu.hidden=false;menu.innerHTML='<span class="lisa-timeline-item">Carregando...</span>';
  try{const d=await api('/timeline');timelineFiles=Array.isArray(d.files)?d.files:[];renderTimelineMenu()}
  catch(e){menu.innerHTML=`<span class="lisa-timeline-item">${esc(e.message||'Erro ao carregar.')}</span>`}
}
const latestBtn=$('[data-go-latest]');
if(latestBtn)latestBtn.addEventListener('click',async()=>{
  selectedFileKey='';
  if(timelineMenu)timelineMenu.hidden=true;
  await loadOrders();
  await loadLatestVersionInfo();
});
const timelineBtn=$('[data-timeline]'),timelineMenu=$('[data-timeline-menu]');
if(timelineBtn)timelineBtn.addEventListener('click',async e=>{e.stopPropagation();if(!timelineMenu)return;if(!timelineMenu.hidden){timelineMenu.hidden=true;return}await loadTimeline()});
if(timelineMenu)timelineMenu.addEventListener('click',async e=>{
  const btn=e.target.closest('[data-timeline-key]');if(!btn)return;
  const key=btn.getAttribute('data-timeline-key')||'';if(!key)return;
  const item=timelineFiles.find(f=>f.key===key);
  selectedFileKey=item?.is_latest?'':key;timelineMenu.hidden=true;
  await loadOrders();
});
document.addEventListener('click',e=>{if(timelineMenu&&!timelineMenu.hidden&&!e.target.closest('.lisa-timeline-wrap'))timelineMenu.hidden=true});

const downloadBtn=$('[data-download]');
if(downloadBtn) downloadBtn.addEventListener('click',async()=>{
  if(!token()) return showLogin();
  const oldText=downloadBtn.innerHTML;
  downloadBtn.disabled=true; downloadBtn.innerHTML='⇩ <span>Baixando...</span>';
  try{
     const path='/download'+(selectedFileKey?('?key='+encodeURIComponent(selectedFileKey)):'');
     const r=await fetch(WORKER+path,{cache:'no-store',headers:{Authorization:'Bearer '+token()}});
     if(r.status===401){sessionStorage.removeItem(tokenKey);showLogin();throw new Error('Sessão expirada.');}
     if(!r.ok){let d={};try{d=await r.json()}catch{}throw new Error(d.error||'Não foi possível baixar o arquivo.');}
     const blob=await r.blob();
     const a=document.createElement('a');
     a.href=URL.createObjectURL(blob);
     a.download=selectedFileName||'LISA.txt';
     document.body.appendChild(a);a.click();a.remove();setTimeout(()=>URL.revokeObjectURL(a.href),1500);
  }catch(e){alert(e.message||'Erro ao baixar o arquivo.');}
  finally{downloadBtn.disabled=false;downloadBtn.innerHTML=oldText;const dl=$('[data-download-label]');if(dl)dl.textContent=selectedFileDate?('Baixar '+selectedFileDate):'Baixar';}
});

let reversaMetaData=null;
function setReversaButtons(meta){
  reversaMetaData=meta&&meta.ok?meta:null;
  const view=$('[data-reversa-view]');
  let received='';
  if(reversaMetaData?.uploaded_at){
    const dt=new Date(reversaMetaData.uploaded_at);
    if(!Number.isNaN(dt.getTime())){
      const parts=new Intl.DateTimeFormat('pt-BR',{timeZone:'America/Sao_Paulo',day:'2-digit',month:'2-digit',hour:'2-digit',minute:'2-digit',hour12:false}).formatToParts(dt);
      const get=t=>parts.find(x=>x.type===t)?.value||'';
      received=`${get('day')}/${get('month')} ${get('hour')}:${get('minute')}`;
    }
  }
  if(view){view.disabled=!reversaMetaData;view.textContent='REVERSA';}
}
async function loadReversaMeta(){
  if(!token()||!WORKER||shell.hidden)return;
  try{const d=await api('/reversa/meta');setReversaButtons(d)}
  catch(e){setReversaButtons(null);console.debug('Reversa ainda não disponível.',e)}
}
let currentReversaData=null;
let reversaSearch='';
function ensureReversaSearch(){
  const card=$('.lisa-reversa-modal-card');if(!card)return null;
  let row=card.querySelector('[data-reversa-search-row]');
  if(!row){
    row=document.createElement('div');
    row.className='lisa-reversa-search-row';
    row.setAttribute('data-reversa-search-row','');
    row.innerHTML='<input type="search" class="lisa-reversa-search-input" data-reversa-search autocomplete="off" placeholder="Buscar em todo o documento" aria-label="Buscar em todo o documento de reversa">';
    const loading=card.querySelector('[data-reversa-loading]');
    const tableWrap=card.querySelector('[data-reversa-table-wrap]');
    card.insertBefore(row,loading||tableWrap||null);
    const input=row.querySelector('[data-reversa-search]');
    if(input)input.addEventListener('input',()=>{reversaSearch=input.value||'';if(currentReversaData)renderReversaTable(currentReversaData)});
  }
  return row.querySelector('[data-reversa-search]');
}
function renderReversaTable(d){
  currentReversaData=d&&typeof d==='object'?d:null;
  const head=$('[data-reversa-head]'),body=$('[data-reversa-body]'),wrap=$('[data-reversa-table-wrap]');
  const title=$('[data-reversa-title]'),count=$('[data-reversa-count]'),loading=$('[data-reversa-loading]');
  const cols=Array.isArray(d.columns)?d.columns:[],rows=Array.isArray(d.rows)?d.rows:[];
  const searchInput=ensureReversaSearch();
  const query=normUi(reversaSearch||searchInput?.value||'');
  const filteredRows=query?rows.filter(r=>(Array.isArray(r)?r:[]).some(v=>normUi(v).includes(query))):rows;
  if(title)title.textContent='Reversa'+(d.range_label?' '+d.range_label:'');
  if(count)count.textContent=`${query?filteredRows.length+' de '+rows.length:rows.length} registros • ${d.file_name||''}`;
  if(head)head.innerHTML='<tr>'+cols.map((c,i)=>{const n=normUi(c),type=/DATA|DATE/.test(n)?'date':(/QTD|QUANTIDADE|DIAS|NUMERO|NÚMERO|NOTA|ROMANEIO/.test(n)?'number':'text');return `<th class="report-sort-th${reversaSort.index===i?' is-sort-active':''}" data-reversa-sort-index="${i}" data-sort-type="${type}"${reversaSort.index===i?` data-sort-dir="${reversaSort.dir}"`:''}><button type="button" class="report-sort-btn">${esc(c)} <span class="report-sort-arrows">↑↓</span></button></th>`}).join('')+'</tr>';
  const shownRows=reversaSort.index>=0?[...filteredRows].map((r,index)=>({r,index})).sort((a,b)=>{const c=reportCompareValues(a.r?.[reversaSort.index],b.r?.[reversaSort.index],reversaSort.type);return c===0?a.index-b.index:c*(reversaSort.dir==='asc'?1:-1)}).map(x=>x.r):filteredRows;
  if(body)body.innerHTML=shownRows.length?shownRows.map(r=>'<tr>'+cols.map((_,i)=>`<td>${esc(r?.[i]??'')}</td>`).join('')+'</tr>').join(''):`<tr><td class="lisa-reversa-no-results" colspan="${Math.max(1,cols.length)}">Nenhum registro encontrado para esta busca.</td></tr>`;
  const popupDownload=$('[data-reversa-popup-download]');if(popupDownload)popupDownload.disabled=!rows.length;
  if(loading)loading.hidden=true;if(wrap)wrap.hidden=false;
}
const reversaTable=$('.lisa-reversa-table');if(reversaTable)reversaTable.addEventListener('click',e=>{const th=e.target.closest('[data-reversa-sort-index]');if(!th)return;const index=Number(th.dataset.reversaSortIndex),type=th.dataset.sortType||'text';if(reversaSort.index!==index)reversaSort={index,type,dir:type==='number'?'desc':'asc'};else reversaSort={index,type,dir:reversaSort.dir==='asc'?'desc':'asc'};if(currentReversaData)renderReversaTable(currentReversaData);});
const reversaModal=$('[data-reversa-modal]');
const reversaView=$('[data-reversa-view]');
if(reversaView)reversaView.addEventListener('click',async()=>{resetReversaSort();
  if(!reversaMetaData)return;
  reversaSearch='';
  const searchInput=ensureReversaSearch();if(searchInput)searchInput.value='';
  if(reversaModal)reversaModal.hidden=false;
  const loading=$('[data-reversa-loading]'),wrap=$('[data-reversa-table-wrap]'),body=$('[data-reversa-body]');
  if(loading){loading.hidden=false;loading.textContent='Carregando reversa...'}if(wrap)wrap.hidden=true;if(body)body.innerHTML='';
  try{const d=await api('/reversa/data');setReversaButtons(d);renderReversaTable(d)}
  catch(e){if(loading){loading.hidden=false;loading.textContent=e.message||'Não foi possível abrir a reversa.'}}
});
const reversaClose=$('[data-reversa-close]');if(reversaClose)reversaClose.addEventListener('click',()=>{if(reversaModal)reversaModal.hidden=true;resetReversaSort()});
if(reversaModal)reversaModal.addEventListener('click',e=>{if(e.target===reversaModal){reversaModal.hidden=true;resetReversaSort()}});
const reversaPopupDownload=$('[data-reversa-popup-download]');
if(reversaPopupDownload)reversaPopupDownload.addEventListener('click',async()=>{
  const d=currentReversaData;
  const cols=Array.isArray(d?.columns)?d.columns:[];
  const rows=Array.isArray(d?.rows)?d.rows:[];
  if(!cols.length||!rows.length)return;
  const oldText=reversaPopupDownload.textContent;
  reversaPopupDownload.disabled=true;reversaPopupDownload.textContent='Baixando...';
  try{
    const XLSX=await ensureXlsxLibrary();
    const aoa=[cols,...rows.map(r=>cols.map((_,i)=>r?.[i]??''))];
    const ws=XLSX.utils.aoa_to_sheet(aoa);
    ws['!cols']=cols.map((c,i)=>{let max=String(c??'').length;for(let j=0;j<Math.min(rows.length,500);j++)max=Math.max(max,String(rows[j]?.[i]??'').length);return {wch:Math.min(Math.max(max+2,10),42)}});
    const wb=XLSX.utils.book_new();XLSX.utils.book_append_sheet(wb,ws,'Reversa');
    const range=String(d?.range_label||'').replace(/[^0-9A-Za-z_-]+/g,'_').replace(/^_+|_+$/g,'');
    XLSX.writeFile(wb,'reversa'+(range?'_'+range:'')+'.xlsx',{compression:true});
  }catch(e){alert(e.message||'Não foi possível gerar o Excel da reversa.');}
  finally{reversaPopupDownload.disabled=false;reversaPopupDownload.textContent=oldText;}
});

const modal=$('[data-modal]');$('[data-open-search]').onclick=()=>{modal.hidden=false;void consultLoadBase(selectedFileKey||'').catch(()=>{});void consultRefreshTrackingBackground();setTimeout(()=>searchInput?.focus(),0)};function closeConsult(){modal.hidden=true;consultSearchSeq++;if(consultTrackingController){consultTrackingController.abort();consultTrackingController=null;}}$('[data-close-modal]').onclick=closeConsult;if(modal)modal.addEventListener('click',e=>{if(e.target===modal)closeConsult()});
const searchInput=$('[data-search]'),searchChipsEl=$('[data-search-chips]'),searchChipBox=$('[data-search-chip-box]');
let searchTokens=[];
let consultSearchSeq=0,consultSearchBusy=false,consultTrackingController=null;
let consultLastEntries=[];
const consultSearchButton=$('[data-search-btn]');
function renderSearchTokens(){
  if(!searchChipsEl)return;
  searchChipsEl.innerHTML=searchTokens.map((v,i)=>`<span class="search-chip"><span>${esc(v)}</span><button type="button" class="search-chip-remove" data-search-chip-remove="${i}" aria-label="Remover ${esc(v)}">×</button></span>`).join('');
}
function addSearchToken(value){
  const v=String(value??'').trim();
  if(!v)return false;
  if(!searchTokens.includes(v))searchTokens.push(v);
  return true;
}
function commitSearchInput(){
  if(!searchInput)return;
  const raw=searchInput.value;
  if(!raw.trim())return;
  for(const part of raw.split(/[,;\s]+/))addSearchToken(part);
  searchInput.value='';
  renderSearchTokens();
}
function collectSearchQueries(){commitSearchInput();return [...searchTokens]}
function clearSearchTokens(){
  consultSearchSeq++;
  if(consultTrackingController){consultTrackingController.abort();consultTrackingController=null;}
  consultLastEntries=[];
  searchTokens=[];
  if(searchInput)searchInput.value='';
  renderSearchTokens();
  const box=$('[data-search-result]');if(box)box.innerHTML='';
  searchInput?.focus();
}
if(consultSearchButton)consultSearchButton.onclick=search;
const searchClearBtn=$('[data-search-clear]');if(searchClearBtn)searchClearBtn.addEventListener('click',clearSearchTokens);
if(searchChipBox)searchChipBox.addEventListener('click',e=>{if(!e.target.closest('[data-search-chip-remove]'))searchInput?.focus()});
if(searchChipsEl)searchChipsEl.addEventListener('click',e=>{const b=e.target.closest('[data-search-chip-remove]');if(!b)return;const i=Number(b.dataset.searchChipRemove);if(Number.isInteger(i)&&i>=0&&i<searchTokens.length){searchTokens.splice(i,1);renderSearchTokens();searchInput?.focus()}});
if(searchInput){
  searchInput.addEventListener('keydown',e=>{
    if(e.key===','||e.key===';'||e.key===' '||e.key==='Enter'){
      e.preventDefault();commitSearchInput();
      if(e.key==='Enter')search();
      return;
    }
    if(e.key==='Backspace'&&!searchInput.value&&searchTokens.length){searchTokens.pop();renderSearchTokens()}
  });
  searchInput.addEventListener('input',()=>{if(/[,;\s]/.test(searchInput.value))commitSearchInput()});
}
function trackingHtml(items){
  if(!Array.isArray(items)||!items.length)return '<div class="tracking-empty">Sem rastreamento localizado nos arquivos das transportadoras.</div>';
  return '<div class="tracking-list lisa-consult-tracking"><h4>Rastreamento</h4>'+items.map(t=>{
    const link=t.tracking_link||'';
    const linkPart=link
      ? `<div class="tracking-link-row"><a href="${esc(link)}" target="_blank" rel="noopener noreferrer">Abrir rastreamento ↗</a><button type="button" class="ghost tracking-copy-btn" data-copy-tracking-link="${esc(link)}">Copiar link</button></div>`
      : `<div class="tracking-internal">${esc(t.tracking_label||'Rastreio interno')}</div>`;
    return `<div class="tracking-card">
      <div><span>CTE</span><b>${esc(t.cte_display||t.cte||'-')}</b></div>
      <div><span>Transportadora</span><b>${esc(t.carrier||'-')}</b></div>
      <div><span>Status de transporte</span><b>${esc(t.transport_status||'-')}</b></div>
      <div class="tracking-link-block"><span>Rastreamento</span>${linkPart}</div>
    </div>`;
  }).join('')+'</div>';
}

// As bases sao servidas prontas do R2 somente uma vez; todas as buscas seguintes
// ocorrem no navegador e NAO chamam /nota nem /rastreamento/consulta.
const consultBases=new Map();let consultTrackingBase=null;
const consultBasesLoading=new Map();let consultTrackingLoading=null;let consultCacheGeneration=0;
const consultDigits=v=>String(v??'').trim().replace(/\.0+$/,'').replace(/\D+/g,'').replace(/^0+(?=\d)/,'');
const consultNormalize=v=>String(v??'').trim().normalize('NFD').replace(/[\u0300-\u036f]/g,'').toUpperCase();
function consultResetCaches(){
  consultCacheGeneration++;consultSearchSeq++;
  consultBases.clear();consultTrackingBase=null;consultBasesLoading.clear();consultTrackingLoading=null;
  if(consultTrackingController){consultTrackingController.abort();consultTrackingController=null;}
}
function consultCarrierInfo(carrier, cte) {
  const n = consultNormalize(carrier).replace(/[^A-Z0-9]+/g,' ').trim();
  const rawCte = String(cte || '').trim();
  let displayCte = rawCte;
  let link = '';
  let label = 'Link não configurado';
  let internal = false;

  if (n.includes('ALFA TRANSP')) {
    link = 'https://alfatransportes.com.br';
  } else if (n.includes('ASAP')) {
    link = 'https://rastreio.asaplog.com.br';
  } else if (n.includes('BRASPRESS')) {
    link = 'https://www.braspress.com/rastreie-sua-encomenda/';
  } else if (n.includes('CONTINENTAL')) {
    internal = true; label = 'Rastreio interno';
  } else if (n.includes('CORREIO PPN ES')) {
    link = 'https://rastreamento.correios.com.br/app/index.ph';
  } else if (n.includes('CORREIO')) {
    link = 'https://rastreamento.correios.com.br/app/index.php';
  } else if (n.includes('EBAZAR') || n.includes('MERCADO ENVIOS') || n.includes('MERCADO LIVRE')) {
    internal = true; label = 'Rastreio interno';
  } else if (n.includes('FAVORITA')) {
    link = 'https://cliente.favorita.com.br/rastreamento';
  } else if (n === 'J T' || n === 'JT' || n.includes('J T EXPRESS') || n.includes('JT EXPRESS')) {
    link = 'https://www.jtexpress.com.br';
  } else if (n.includes('JAMEF')) {
    link = 'https://www.jamef.com.br';
  } else if (n.includes('LATAM') || n.includes('TAM CARGO') || n.includes('TAM LINHAS') || n.includes('TAM VIX')) {
    // LATAM Cargo usa AWB/CTE no formato PPPNNNNNNNN: os 3 primeiros digitos
    // sao o prefixo da companhia e o restante e o numero do documento.
    // Ex.: 95739839866 -> docPrefix=957 e docNumber=39839866.
    const latamDigits = rawCte.replace(/\D+/g,'');
    if (latamDigits.length >= 4) {
      const latamPrefix = latamDigits.slice(0,3);
      const latamNumber = latamDigits.slice(3);
      displayCte = latamDigits;
      link = 'https://www.latamcargo.com/en/trackshipment?docNumber=' + encodeURIComponent(latamNumber) + '&docPrefix=' + encodeURIComponent(latamPrefix) + '&soType=SO';
      label = 'Rastrear na LATAM Cargo';
    }
  } else if (n.includes('MAGALU ENTREGAS') || n.includes('MAGAZINE LUIZA')) {
    internal = true; label = 'Rastreio interno';
  } else if (n.includes('PATRUS')) {
    link = 'https://patrus.com.br/rastreie-sua-carga/';
  } else if (n.includes('SHOPEE') || n.includes('SHPS TECNOLOGIA')) {
    internal = true; label = 'Rastreio interno';
  } else if (n.includes('TOTAL EXPRESS')) {
    link = 'https://totalconecta.totalexpress.com.br/rastreamento/';
  } else if (n.includes('TNT')) {
    link = 'https://radar.tntbrasil.com.br/radar/public/localizacaoSimplificada.do';
  }

  return { cte_display: displayCte, tracking_link: link, tracking_label: label, tracking_internal: internal };
}

function consultTrackingFromSummary(summary){
 if(summary?.kind==='consulta_tracking'&&Array.isArray(summary.items))return summary;
 if(!summary||!Array.isArray(summary.items)||!summary.snapshot_version)throw new Error('Base de rastreamento invalida.');
 const items=[];
 for(const row of summary.items){
  const aliases=[...new Set([row.nota_cliente,row.nota_lisa,row.nota].filter(v=>/^\d+(?:\.0+)?$/.test(String(v??'').trim())).map(consultDigits).filter(Boolean))];
  const cte=String(row.cte||'').trim(),carrier=String(row.transportadora||'').trim(),status=String(row.status_tr||'').trim();
  if(!aliases.length||(!cte&&!status))continue;
  items.push({aliases,cte,carrier,transport_status:status,ocorrencia:String(row.ocorrencia_tr||''),previsao_entrega:String(row.previsao_entrega_tr||''),data_entrega:String(row.data_entrega_tr||''),...consultCarrierInfo(carrier,cte)});
 }
 return {kind:'consulta_tracking',source:'tracking-summary',source_updated_at:summary.updated_at,items};
}
function consultIndexTracking(snapshot){
  const byNote=new Map();
  for(const rec of (snapshot?.items||[]))for(const alias of rec?.aliases||[]){
    const key=consultDigits(alias);if(!key)continue;
    if(!byNote.has(key))byNote.set(key,[]);
    byNote.get(key).push(rec);
  }
  return {data:snapshot,byNote};
}
async function consultLoadTracking(){
  if(consultTrackingBase)return consultTrackingBase;
  if(consultTrackingLoading)return consultTrackingLoading;
  const generation=consultCacheGeneration;
  const promise=api('/consulta/tracking').then(snapshot=>{
    if(generation!==consultCacheGeneration)throw new Error('Base alterada durante o carregamento.');
    return (consultTrackingBase=consultIndexTracking(consultTrackingFromSummary(snapshot)));
  }).finally(()=>{if(consultTrackingLoading===promise)consultTrackingLoading=null});
  consultTrackingLoading=promise;
  return consultTrackingLoading;
}
async function consultLoadBase(key){
  const name=String(key||'');
  if(consultBases.has(name))return consultBases.get(name);
  if(consultBasesLoading.has(name))return consultBasesLoading.get(name);
  const generation=consultCacheGeneration;
  const promise=api('/consulta/snapshot'+(name?'?key='+encodeURIComponent(name):'')).then(data=>{
    if(generation!==consultCacheGeneration)throw new Error('Base alterada durante o carregamento.');
    if(data?.kind!=='consulta_snapshot'||!Array.isArray(data?.items))throw new Error('Base de consulta invalida.');
    consultBases.set(name,data);return data;
  }).finally(()=>{if(consultBasesLoading.get(name)===promise)consultBasesLoading.delete(name)});
  consultBasesLoading.set(name,promise);return promise;
}
function consultRowsForQuery(q,snapshot){
  const digit=consultDigits(q),target=consultNormalize(q);
  function matches(entry){
    const r=entry?.row||{},direct=[r['Nota Cliente'],r['Nota Lisa'],r['Pedido Cliente']].some(value=>consultNormalize(value).includes(target));
    return direct||!!digit&&(entry?.aliases||[]).some(value=>consultDigits(value)===digit);
  }
  const found=[];
  for(const entry of snapshot.items){if(matches(entry)){found.push(entry.row);if(found.length===25)break}}
  if(!found.length)for(const entry of snapshot.extra||[]){if(matches(entry)){found.push(entry.row);if(found.length===25)break}}
  return found;
}
function consultTracksForEntry(entry,tracking){
  if(!tracking)return [];
  const rows=[],seen=new Set();
  for(const raw of [entry.row['Nota Cliente'],entry.row['Nota Lisa'],entry.query]){
    if(!/^\d+(?:\.0+)?$/.test(String(raw??'').trim()))continue;
    const key=consultDigits(raw);if(!key)continue;
    for(const item of tracking.byNote.get(key)||[]){
      const sig=[item.cte,item.carrier,item.transport_status].join('|');if(seen.has(sig))continue;
      seen.add(sig);rows.push(item);
    }
  }
  return rows.slice(0,20);
}
async function search(){
  if(consultSearchBusy)return;
  const queries=collectSearchQueries();
  if(!queries.length){searchInput?.focus();return}
  if(queries.length>25){const box=$('[data-search-result]');if(box)box.textContent='Consulte no maximo 25 numeros por vez.';return}
  const seq=++consultSearchSeq;
  consultSearchBusy=true;
  if(consultSearchButton){consultSearchButton.disabled=true;consultSearchButton.textContent='Consultando...'}
  const box=$('[data-search-result]');box.textContent='Abrindo base pre-carregada...';
  try{
    const key=selectedFileKey||'';
    const [snapshot,trackingOutcome]=await Promise.all([consultLoadBase(key),consultLoadTracking().then(data=>({data}),e=>({error:e}))]);
    if(seq!==consultSearchSeq)return;
    const tracking=trackingOutcome?.data||null;
    const entries=[],missing=[];
    for(const q of queries){const rows=consultRowsForQuery(q,snapshot);if(!rows.length)missing.push(q);for(const row of rows)entries.push({query:q,row})}
    const fields=['Pedido Cliente','Pedido MarketPlace','Carrinho ML','Nota Cliente','Nota Lisa','Status','Campanha','Nome transportadora','Nome Destinatario','Data API','Data Finalizado','Data Conferencia'];
    const html=[];
    const asOf=String(snapshot?.monitor_updated_at||'');
    const trackAsOf=String(tracking?.data?.source_updated_at||'');
    html.push(`<div style="font-size:11px;color:var(--muted);margin:0 0 10px">Base armazenada no Cloudflare${asOf?' · Monitor: '+esc(live2Time(asOf)):''}${trackAsOf?' · Transportadoras: '+esc(live2Time(trackAsOf)):''}. Pesquisas nesta tela sao locais e nao sobrecarregam o Worker.</div>`);
    entries.forEach(entry=>{
      const r=entry.row;
      const trackHtml=tracking?trackingHtml(consultTracksForEntry(entry,tracking)):'<div class="tracking-empty">Dados de rastreamento ainda nao preparados no Cloudflare. O registro do pedido continua disponivel.</div>';
      html.push(`<div class="result-card"><div style="font-size:11px;color:var(--muted);margin-bottom:8px">Consulta: ${esc(entry.query)}</div><div class="result-grid">${fields.map(k=>`<div><span>${esc(k)}</span><b>${esc(r[k]||'-')}</b></div>`).join('')}</div>${trackHtml}</div>`);
    });
    missing.forEach(q=>html.push(`<div class="result-card"><div class="tracking-empty"><b>${esc(q)}</b> — Nenhum registro encontrado na base pre-carregada.</div></div>`));
    box.innerHTML=html.length?html.join(''):'Nenhum registro encontrado.';
    consultLastEntries=entries;
  }catch(e){
    if(seq===consultSearchSeq){
      const status=e?.status;
      box.textContent=status===503?'Base de consulta ainda nao preparada no Cloudflare. Aguarde a proxima importacao do Monitor ou a execucao do cron.':(e?.message||'Nao foi possivel abrir a base pre-carregada.');
    }
  }finally{
    consultSearchBusy=false;
    if(consultSearchButton){consultSearchButton.disabled=false;consultSearchButton.textContent='Buscar'}
  }
}
// Recarregar os dados e uma acao explicita; a busca comum nunca faz uma nova chamada.
const consultRefreshBaseBtn=document.createElement('button');
consultRefreshBaseBtn.type='button';consultRefreshBaseBtn.className='ghost';consultRefreshBaseBtn.textContent='Atualizar base';consultRefreshBaseBtn.title='Ler novamente os arquivos pre-carregados no Cloudflare';
consultRefreshBaseBtn.style.cssText='font-size:11px;white-space:nowrap';
if(searchClearBtn)searchClearBtn.insertAdjacentElement('afterend',consultRefreshBaseBtn);
consultRefreshBaseBtn.addEventListener('click',async()=>{
 if(consultRefreshBaseBtn.disabled)return;
 const box=$('[data-search-result]');consultRefreshBaseBtn.disabled=true;consultRefreshBaseBtn.textContent='Atualizando...';
 try{
  await api('/consulta/tracking/refresh',{method:'POST'});
  consultResetCaches();await Promise.all([consultLoadBase(selectedFileKey||''),consultLoadTracking()]);
  if(collectSearchQueries().length)await search();
  else if(box)box.textContent='Base de rastreamento atualizada no Cloud.';
 }catch(e){if(box)box.textContent='Nao foi possivel atualizar: '+(e?.message||'falha na carga salva.');}
 finally{consultRefreshBaseBtn.disabled=false;consultRefreshBaseBtn.textContent='Atualizar base';}
});
// Le a carga pronta somente ao abrir a Consulta ou apos upload neste painel.
let consultTrackingRefreshBusy=false;
async function consultRefreshTrackingBackground(){
 if(consultTrackingRefreshBusy||!token()||document.hidden)return;
 consultTrackingRefreshBusy=true;
 try{
  const generation=consultCacheGeneration,snapshot=await api('/consulta/tracking');
  const projected=consultTrackingFromSummary(snapshot);
  if(generation!==consultCacheGeneration)return;
  const changed=consultTrackingBase?.data?.source_updated_at!==projected.source_updated_at;
  consultTrackingBase=consultIndexTracking(projected);
  if(changed&&consultLastEntries?.length&&!consultSearchBusy)void search();
 }catch(e){/* Mantem a ultima carga valida disponivel. */}
 finally{consultTrackingRefreshBusy=false;}
}
root.addEventListener('lisa:carrier-files-updated',()=>{void consultRefreshTrackingBackground()});



root.querySelector('[data-refresh]')?.addEventListener('click',consultResetCaches,{capture:true});
root.querySelector('[data-logout]')?.addEventListener('click',consultResetCaches,{capture:true});
for(const eventRoot of [root,popupLayer])eventRoot.addEventListener('click',async e=>{
  const btn=e.target.closest('[data-copy-tracking-link]');if(!btn)return;
  const value=btn.getAttribute('data-copy-tracking-link')||'';
  if(!value)return;
  const original=btn.textContent;
  try{await navigator.clipboard.writeText(value);btn.textContent='Copiado!';}
  catch{const ta=document.createElement('textarea');ta.value=value;document.body.appendChild(ta);ta.select();document.execCommand('copy');ta.remove();btn.textContent='Copiado!';}
  setTimeout(()=>{btn.textContent=original},1200);
});

const trackingUploadModal=$('[data-tracking-upload-modal]');
const trackingUploadOpen=$('[data-tracking-upload-open]');
const trackingUploadClose=$('[data-tracking-upload-close]');
const trackingFile=$('[data-tracking-file]');
const trackingUploadMsg=$('[data-tracking-upload-msg]');
const trackingUploadSend=$('[data-tracking-upload-send]');
const latestFilesList=$('[data-latest-files-list]');

function latestFileDate(value){
  if(!value)return '--/-- --:--';
  const d=new Date(value);if(Number.isNaN(d.getTime()))return '--/-- --:--';
  return d.toLocaleString('pt-BR',{day:'2-digit',month:'2-digit',hour:'2-digit',minute:'2-digit',hour12:false});
}
async function latestFileMeta(path){
  const r=await fetch(WORKER+path,{cache:'no-store',headers:{Authorization:'Bearer '+token()}});
  const d=await r.json().catch(()=>({}));
  if(!r.ok)throw new Error(d.error||d.detail||'Falha ao consultar arquivo.');
  return d;
}
function renderLatestFiles(items){
  if(!latestFilesList)return;
  latestFilesList.innerHTML=items.map(x=>`<button type="button" class="lisa-latest-file" data-latest-download="${x.kind}" ${x.available===false?'disabled':''}><span class="lisa-latest-file-main"><span class="lisa-latest-file-name">${x.label}</span><span class="lisa-latest-file-date">${latestFileDate(x.updated_at)}</span></span><span class="lisa-latest-file-download">↓</span></button>`).join('');
}
async function loadLatestUploadFiles(){
  if(!latestFilesList)return;
  latestFilesList.innerHTML='<div class="lisa-latest-files-loading">Carregando arquivos...</div>';
  const [monitor,reversa,rastreamento,sysempMeta,sysempPedidosMeta]=await Promise.allSettled([latestFileMeta('/version'),latestFileMeta('/reversa/meta'),latestFileMeta('/rastreamento/meta'),latestFileMeta('/sysemp/meta'),latestFileMeta('/sysemp-pedidos/meta')]);
  const val=(r)=>r.status==='fulfilled'?r.value:{};
  const sy=val(sysempMeta),sp=val(sysempPedidosMeta);
  renderLatestFiles([
    {kind:'monitor',label:'Monitor de Pedido',updated_at:val(monitor).updated_at||val(monitor).version,available:monitor.status==='fulfilled'},
    {kind:'reversa',label:'Reversar',updated_at:val(reversa).uploaded_at,available:reversa.status==='fulfilled'},
    {kind:'rastreamento',label:'Rastreamento',updated_at:val(rastreamento).updated_at,available:rastreamento.status==='fulfilled'&&val(rastreamento).available!==false},
    {kind:'sysemp',label:'SYSEMP',updated_at:sy.sysemp?.updated_at,available:!!sy.sysemp?.available},
    {kind:'sysemp-pedidos',label:'SYSEMP-PEDIDOS',updated_at:sp.sysemp_pedidos?.updated_at,available:!!sp.sysemp_pedidos?.available}
  ]);
}
function contentDispositionName(header,fallback){
  const raw=String(header||'');
  const utf=raw.match(/filename\*=UTF-8''([^;]+)/i);if(utf){try{return decodeURIComponent(utf[1])}catch{}}
  const plain=raw.match(/filename="?([^";]+)"?/i);return plain?plain[1]:fallback;
}
async function downloadLatestFile(kind,button){
  const map={monitor:{path:'/download',fallback:'monitor_pedido.txt'},reversa:{path:'/reversa/download',fallback:'reversa.xml'},rastreamento:{path:'/rastreamento/raw',fallback:'rastreamento.xlsx'},sysemp:{path:'/sysemp/raw',fallback:'sysemp.xlsx'},'sysemp-pedidos':{path:'/sysemp-pedidos/raw',fallback:'sysemp-pedidos.xlsx'}};
  const cfg=map[kind];if(!cfg)return;
  const old=button?.querySelector('.lisa-latest-file-download')?.textContent||'↓';
  if(button)button.disabled=true;
  try{
    const r=await fetch(WORKER+cfg.path,{headers:{Authorization:'Bearer '+token()}});
    if(!r.ok){const d=await r.json().catch(()=>({}));throw new Error(d.error||d.detail||'Falha ao baixar arquivo.')}
    const blob=await r.blob();const name=contentDispositionName(r.headers.get('Content-Disposition'),cfg.fallback);const url=URL.createObjectURL(blob);const a=document.createElement('a');a.href=url;a.download=name;document.body.appendChild(a);a.click();a.remove();setTimeout(()=>URL.revokeObjectURL(url),1500);
  }catch(e){if(trackingUploadMsg){trackingUploadMsg.textContent=e.message||'Não foi possível baixar o arquivo.';trackingUploadMsg.className='lisa-tracking-upload-msg is-error'}}
  finally{if(button){button.disabled=false;const icon=button.querySelector('.lisa-latest-file-download');if(icon)icon.textContent=old}}
}
function openTrackingUpload(){
  if(trackingUploadMsg){trackingUploadMsg.textContent='';trackingUploadMsg.className='lisa-tracking-upload-msg';}
  if(trackingFile)trackingFile.value='';
  if(trackingUploadModal)trackingUploadModal.hidden=false;
  loadLatestUploadFiles();
}
if(trackingUploadOpen)trackingUploadOpen.addEventListener('click',openTrackingUpload);
if(trackingUploadClose)trackingUploadClose.addEventListener('click',()=>{if(trackingUploadModal)trackingUploadModal.hidden=true});
if(trackingUploadModal)trackingUploadModal.addEventListener('click',e=>{if(e.target===trackingUploadModal)trackingUploadModal.hidden=true});
if(latestFilesList)latestFilesList.addEventListener('click',e=>{const b=e.target.closest('[data-latest-download]');if(b&&!b.disabled)downloadLatestFile(b.dataset.latestDownload,b)});

async function ensureXlsxLibrary(){
  if(window.XLSX)return window.XLSX;
  await new Promise((resolve,reject)=>{
    const existing=document.querySelector('script[data-lisa-xlsx]');
    if(existing){existing.addEventListener('load',resolve,{once:true});existing.addEventListener('error',()=>reject(new Error('Não foi possível carregar o leitor de Excel.')),{once:true});return}
    const s=document.createElement('script');
    s.src='https://cdn.jsdelivr.net/npm/xlsx@0.18.5/dist/xlsx.full.min.js';
    s.async=true;s.dataset.lisaXlsx='1';s.onload=resolve;s.onerror=()=>reject(new Error('Não foi possível carregar o leitor de Excel.'));
    document.head.appendChild(s);
  });
  if(!window.XLSX)throw new Error('Leitor de Excel indisponível.');
  return window.XLSX;
}
function excelText(v){
  if(v===null||v===undefined)return '';
  if(typeof v==='number'&&Number.isFinite(v))return Number.isInteger(v)?String(v):String(v);
  return String(v).trim();
}
async function buildTrackingIndex(file){
  const XLSX=await ensureXlsxLibrary();
  const ab=await file.arrayBuffer();
  const wb=XLSX.read(ab,{type:'array',cellDates:false,raw:true});
  const ws=wb.Sheets['BasedeDados']||wb.Sheets['Rastreamento']||wb.Sheets[wb.SheetNames[0]];
  if(!ws)throw new Error('A planilha não possui uma aba válida.');
  const rows=XLSX.utils.sheet_to_json(ws,{header:1,defval:'',raw:true,blankrows:false});
  if(!rows.length)throw new Error('A planilha está vazia.');
  const normHeader=v=>String(v??'').trim().normalize('NFD').replace(/[\u0300-\u036f]/g,'').toUpperCase().replace(/[^A-Z0-9]+/g,' ').trim();
  const headerRow=Math.max(0,rows.slice(0,10).findIndex(r=>{const hs=(r||[]).map(normHeader);return hs.includes('CTE')&&hs.includes('NOTA FISCAL')&&hs.includes('STATUS TR')}));
  const headers=(rows[headerRow]||[]).map(v=>String(v??'').trim());
  const normHeaders=headers.map(normHeader);
  const findHeader=names=>normHeaders.findIndex(h=>names.includes(h));
  const cteIx=findHeader(['CTE']);
  const nfIx=findHeader(['NOTA FISCAL','NF','NOTA']);
  const carrierIx=findHeader(['NF TRANSPORTADORA','TRANSPORTADORA']);
  const statusIx=findHeader(['STATUS TR','STATUS']);
  const previsaoIx=findHeader(['PREVISAO DE ENTREGA TR','PREVISAO ENTREGA TR','PREVISAO DE ENTREGA']);
  const ocorrenciaIx=findHeader(['OCORRENCIA TR','OCORRENCIA']);
  const tratativasIx=findHeader(['TRATATIVAS','TRATATIVA','TRATATIVAS TR','TRATATIVA TR']);
  const pedidoIx=findHeader(['PEDIDOS','PEDIDO','PEDIDO NUMERO','NUMERO PEDIDO','N PEDIDO','PEDIDO CLIENTE']);
  const coletaIx=normHeaders.findIndex(h=>h==='DATA COLETA'||h==='DATA DE COLETA'||h==='DT COLETA'||h==='COLETA'||h.includes('DATA COLETA')||h.includes('COLETA DATA'));
  const missing=[];
  if(cteIx<0)missing.push('CTE');if(nfIx<0)missing.push('Nota Fiscal');if(carrierIx<0)missing.push('NF - Transportadora');if(statusIx<0)missing.push('Status - TR');if(pedidoIx<0)missing.push('Pedido - Número');
  if(missing.length)throw new Error('Colunas obrigatórias ausentes: '+missing.join(', ')+'.');
  const clean=v=>excelText(v).replace(/\.0+$/,'').trim();
  const excelDateBR=v=>{
    if(v===null||v===undefined||v==='')return '';
    if(typeof v==='number'&&Number.isFinite(v)){const d=XLSX.SSF.parse_date_code(v);if(d&&d.y&&d.m&&d.d)return `${String(d.d).padStart(2,'0')}/${String(d.m).padStart(2,'0')}/${d.y}`;}
    const raw=String(v).trim();
    let m=raw.match(/^(\d{1,2})[\/\-.](\d{1,2})[\/\-.](\d{2,4})/);if(m){let y=Number(m[3]);if(y<100)y+=2000;return `${String(Number(m[1])).padStart(2,'0')}/${String(Number(m[2])).padStart(2,'0')}/${y}`;}
    m=raw.match(/^(\d{4})-(\d{2})-(\d{2})/);if(m)return `${m[3]}/${m[2]}/${m[1]}`;
    return raw;
  };
  const orderKeys=value=>{const raw=String(value??'').trim();if(!raw)return [];const out=new Set();for(const m of raw.match(/\d{4,}/g)||[]){const k=String(m).replace(/^0+(?=\d)/,'');if(k)out.add(k)}if(!out.size){const d=raw.replace(/\D+/g,'').replace(/^0+(?=\d)/,'');if(d)out.add(d)}return [...out]};
  const records={},by_order={};let count=0;
  const pushOrder=(key,rec)=>{if(!key)return;if(!by_order[key])by_order[key]=[];const sig=[rec.nota,rec.pedido,rec.cte,rec.carrier,rec.status,rec.previsao_entrega,rec.ocorrencia,rec.tratativas,rec.coleta].join('\u0001');if(!by_order[key].some(x=>[x.nota,x.pedido,x.cte,x.carrier,x.status,x.previsao_entrega,x.ocorrencia,x.tratativas,x.coleta].join('\u0001')===sig))by_order[key].push(rec)};
  for(let i=headerRow+1;i<rows.length;i++){
    const r=rows[i]||[];
    const nf=clean(r[nfIx]),pedido=clean(r[pedidoIx]);
    if(!nf||nf==='0'||!pedido)continue;
    const rec={nota:nf,pedido,cte:excelText(r[cteIx]),carrier:excelText(r[carrierIx]),status:excelText(r[statusIx]),previsao_entrega:previsaoIx>=0?excelDateBR(r[previsaoIx]):'',ocorrencia:ocorrenciaIx>=0?excelText(r[ocorrenciaIx]):'',tratativas:tratativasIx>=0?excelText(r[tratativasIx]):'',coleta:coletaIx>=0?excelDateBR(r[coletaIx]):''};
    if(!records[nf])records[nf]=[];
    const sig=[rec.pedido,rec.cte,rec.carrier,rec.status,rec.previsao_entrega,rec.ocorrencia,rec.tratativas,rec.coleta].join('\u0001');
    if(!records[nf].some(x=>[String(x.pedido||''),x.cte,x.carrier,x.status,x.previsao_entrega,x.ocorrencia,x.tratativas,x.coleta].join('\u0001')===sig))records[nf].push(rec);
    for(const key of orderKeys(pedido))pushOrder(key,rec);
    count++;
  }
  if(!Object.keys(records).length)throw new Error('Nenhuma Nota Fiscal válida foi encontrada.');
  if(!Object.keys(by_order).length)throw new Error('Nenhum número de pedido válido foi encontrado na coluna Pedido - Número.');
  return {version:9,source_name:file.name,created_at:new Date().toISOString(),row_count:count,collection_date_column:coletaIx>=0?headers[coletaIx]:'',previsao_entrega_column:previsaoIx>=0?headers[previsaoIx]:'',tratativas_column:tratativasIx>=0?headers[tratativasIx]:'',records,by_order};
}

function lisaNormFileName(v){return String(v||'').normalize('NFD').replace(/[\u0300-\u036f]/g,'').toLowerCase()}
function isSysempFileName(name){
  const clean=String(name||'').split(/[\\/]/).pop()||'';const dot=clean.lastIndexOf('.');if(dot<=0)return false;
  const ext=clean.slice(dot+1).toLowerCase();if(!['xlsx','xlsm','xls'].includes(ext))return false;
  return lisaNormFileName(clean.slice(0,dot)).replace(/\s+/g,'')==='sysemp';
}
function isSysempPedidosFileName(name){
  const clean=String(name||'').split(/[\/]/).pop()||'';const dot=clean.lastIndexOf('.');if(dot<=0)return false;
  const ext=clean.slice(dot+1).toLowerCase();if(!['xlsx','xlsm','xls'].includes(ext))return false;
  return lisaNormFileName(clean.slice(0,dot)).replace(/[^a-z0-9]+/g,'')==='sysemppedidos';
}
function mlHeaderNorm(v){return String(v??'').normalize('NFD').replace(/[\u0300-\u036f]/g,'').toUpperCase().replace(/[^A-Z0-9]+/g,' ').trim().replace(/\s+/g,' ')}
function mlCellText(v){if(v===null||v===undefined)return '';if(v instanceof Date)return v.toISOString();if(typeof v==='number'&&Number.isFinite(v))return Number.isInteger(v)?String(v):String(v);return String(v).trim()}



function mlNormalizeNf(v){let s=mlCellText(v).replace(/\.0+$/,'');const d=s.replace(/[^0-9]/g,'');return d||s}
async function buildSysempPedidosIndex(file){
  const XLSX=await ensureXlsxLibrary();
  const ab=await file.arrayBuffer();
  const wb=XLSX.read(ab,{type:'array',cellDates:false,raw:true});
  const ws=wb.Sheets[wb.SheetNames[0]];
  if(!ws)throw new Error('A planilha SYSEMP-PEDIDOS não possui uma aba válida.');
  const rows=XLSX.utils.sheet_to_json(ws,{header:1,defval:'',raw:true,blankrows:false});
  if(!rows.length)throw new Error('A planilha SYSEMP-PEDIDOS está vazia.');
  let headerRow=-1;
  for(let i=0;i<Math.min(rows.length,20);i++){const h=(rows[i]||[]).map(mlHeaderNorm);if(h.includes('CANAL DE VENDA')&&h.includes('MOTIVO DO BLOQUEIO')&&h.includes('PEDIDO MARKETPLACE')){headerRow=i;break}}
  if(headerRow<0)throw new Error('Cabeçalho do SYSEMP-PEDIDOS não encontrado. Verifique Canal de Venda, Motivo do Bloqueio e Pedido MarketPlace.');
  const rawHeaders=(rows[headerRow]||[]).map(v=>mlCellText(v).trim());
  const hs=rawHeaders.map(mlHeaderNorm),ix=names=>hs.findIndex(h=>names.includes(h));
  const idIx=ix(['ID']),pedidoIx=ix(['PEDIDO']),mpIx=ix(['PEDIDO MARKETPLACE']),cartIx=ix(['CARRINHO ML']),canalIx=ix(['CANAL DE VENDA']),bloqueioIx=ix(['MOTIVO DO BLOQUEIO']),dataIx=ix(['DATA PEDIDO']),parceiroIx=ix(['PARCEIRO DE NEGOCIO']),tipoIx=ix(['TIPO DE PEDIDO','TIPO PEDIDO','TIPO']),empresaIx=ix(['EMPRESA']),statusBloqueioIx=ix(['STATUS DE BLOQUEIO','STATUS BLOQUEIO']);
  if(canalIx<0||bloqueioIx<0||mpIx<0)throw new Error('O SYSEMP-PEDIDOS precisa conter Canal de Venda, Motivo do Bloqueio e Pedido MarketPlace.');
  /* Exibe no seletor SOMENTE colunas da planilha que realmente possuem ao menos um valor.
     A contagem considera todas as linhas da aba, nao apenas os canais usados pelos cards. */
  const hasRealValue=v=>v!==null&&v!==undefined&&String(v).trim()!=='';
  const nonEmptyCounts=rawHeaders.map(()=>0);
  for(let i=headerRow+1;i<rows.length;i++){
    const r=rows[i]||[];
    for(let j=0;j<rawHeaders.length;j++)if(rawHeaders[j]&&hasRealValue(r[j]))nonEmptyCounts[j]++;
  }
  const columns=rawHeaders.map((label,source_index)=>({key:'src_'+source_index,label:String(label||'').trim(),source_index,non_empty_count:nonEmptyCounts[source_index]||0})).filter(c=>c.label&&c.non_empty_count>0);
  const digits=v=>mlCellText(v).replace(/\.0+$/,'').replace(/\D+/g,'').replace(/^0+(?=\d)/,'');
  const displayCell=(v,label)=>{
    const h=mlHeaderNorm(label),n=typeof v==='number'?v:Number.NaN;
    if(h.includes('DATA')&&Number.isFinite(n)&&n>20000&&n<90000){const ms=Date.UTC(1899,11,30)+Math.round(n*86400000),d=new Date(ms),date=`${String(d.getUTCDate()).padStart(2,'0')}/${String(d.getUTCMonth()+1).padStart(2,'0')}/${d.getUTCFullYear()}`,hasTime=Math.abs(n-Math.floor(n))>0.000001;return hasTime?`${date} ${String(d.getUTCHours()).padStart(2,'0')}:${String(d.getUTCMinutes()).padStart(2,'0')}`:date}
    return mlCellText(v).trim();
  };
  const orders=[];
  for(let i=headerRow+1;i<rows.length;i++){
    const r=rows[i]||[],canal=mlCellText(r[canalIx]).trim();
    const id=idIx>=0?digits(r[idIx]):'',pedido=pedidoIx>=0?digits(r[pedidoIx]):'',marketplace=mpIx>=0?digits(r[mpIx]):'',carrinho=cartIx>=0?digits(r[cartIx]):'';
    // Pedidos manuais podem nao ter Canal de Venda. Preservar todo pedido com identificador.
    if(!id&&!pedido)continue;
    const motivo_bloqueio=bloqueioIx>=0?mlCellText(r[bloqueioIx]).trim():'',data_pedido=dataIx>=0?displayCell(r[dataIx],rawHeaders[dataIx]):'',parceiro_negocio=parceiroIx>=0?mlCellText(r[parceiroIx]).trim():'',tipo_pedido=tipoIx>=0?mlCellText(r[tipoIx]).trim():'',empresa=empresaIx>=0?mlCellText(r[empresaIx]).trim():'',status_bloqueio=statusBloqueioIx>=0?mlCellText(r[statusBloqueioIx]).trim():'';
    const cells=columns.map(c=>displayCell(r[c.source_index],c.label));
    orders.push({id,pedido,marketplace,carrinho,canal,motivo_bloqueio,data_pedido,parceiro_negocio,tipo_pedido,empresa,status_bloqueio,cells,row:i+1});
  }
  if(!orders.length)throw new Error('Nenhum pedido válido foi encontrado no SYSEMP-PEDIDOS.');
  return {version:8,kind:'sysemp_pedidos',source_name:file.name,created_at:new Date().toISOString(),row_count:orders.length,column_count:columns.length,columns,orders};
}
async function buildSysempIndex(file){
  const XLSX=await ensureXlsxLibrary();
  const ab=await file.arrayBuffer();
  const wb=XLSX.read(ab,{type:'array',cellDates:false,raw:true});
  const names=Array.isArray(wb.SheetNames)?wb.SheetNames:[];
  if(!names.length)throw new Error('A planilha SYSEMP não possui uma aba válida.');
  const invoiceNumber=value=>{
    if(typeof value==='number')return Number.isSafeInteger(value)&&value>0&&value<=999999999?String(value):'';
    if(typeof value!=='string')return '';
    let text=value.trim().replace(/\u00a0/g,' ').replace(/^(?:NF(?:[ -]?E)?|NOTA\s+FISCAL)\s*(?:(?:N[º°O.]?|N[ÚU]MERO)\s*)?[:#-]?\s*/i,'');
    if(/^\d{1,3}(?:[.,]\d{3})+$/.test(text))text=text.replace(/[.,]/g,'');
    else if(/^\d+\.0+$/.test(text))text=text.replace(/\.0+$/,'');
    if(!/^\d{1,9}$/.test(text))return '';
    text=text.replace(/^0+(?=\d)/,'');
    return text!=='0'&&text.length<=9?text:'';
  };
  const header=row=>{
    const hs=row.map(mlHeaderNorm),ix=names=>{for(const name of names){const at=hs.indexOf(name);if(at>=0)return at}return -1};
    const out={
      nf:ix(['NOTA FISCAL','NOTA FISCAL NUMERO','NOTA FISCAL N','NOTA FISCAL NRO','NOTA FISCAL NR','NUMERO NOTA FISCAL','NUMERO DA NOTA FISCAL','N NOTA FISCAL','N DA NOTA FISCAL','NR NOTA FISCAL','NRO NOTA FISCAL','NUMERONOTAFISCAL','NF','NFE','NF E','N F','N F E','NUMERO NF','NUMERO DA NF','N NF','N DA NF','N NFE','N NF E','N DA NF E','NR NF','NRO NF','NF NUMERO','NF N','NF NR','NF NRO','NF E NUMERO','NFE NUMERO','NUMERO NFE','NUMERO NF E','NUMERO NOTA','NUMERO DA NOTA','N NOTA','NOTA']),
      marketplace:ix(['PEDIDO MARKETPLACE','PEDIDO MARKET PLACE','NUMERO PEDIDO MARKETPLACE','PEDIDO NUMERO MARKETPLACE','MARKETPLACE PEDIDO']),
      carrinho:ix(['CARRINHO ML','CARRINHO MERCADO LIVRE','CARRINHO']),
      pedido:ix(['PEDIDO O S','PEDIDO OS','PEDIDO NUMERO','NUMERO PEDIDO','NUMERO DO PEDIDO','PEDIDO','N PEDIDO','NR PEDIDO','PEDIDO CLIENTE']),
      cliente:ix(['PARCEIRO DE NEGOCIO','PARCEIRO DE NEGOCIO NOME','PARCEIRO NEGOCIO','NOME DESTINATARIO','NOME DO DESTINATARIO','DESTINATARIO NOME','DESTINATARIO','CLIENTE','CLIENTE NOME','NOME CLIENTE','NOME DO CLIENTE','RAZAO SOCIAL CLIENTE','CLIENTE RAZAO SOCIAL']),
      transportadora:ix(['TRANSPORTADORA','TRANSPORTADORA NOME','NOME TRANSPORTADORA','NOME DA TRANSPORTADORA','NF TRANSPORTADORA','TRANSPORTADORA NF','NF NOME TRANSPORTADORA','NF TRANSPORTADORA NOME','TRANSPORTADORA RAZAO SOCIAL','TRANSPORTADORA NOME RAZAO SOCIAL','RAZAO SOCIAL TRANSPORTADORA','NOME FANTASIA TRANSPORTADORA']),
      uf:ix(['UF DESTINATARIO','UF DO DESTINATARIO','UF ENTREGA','UF DESTINO','ESTADO DESTINATARIO','ESTADO ENTREGA','ESTADO','UF']),
      cidade:ix(['CIDADE DESTINATARIO','MUNICIPIO DESTINATARIO','CIDADE ENTREGA','MUNICIPIO ENTREGA','MUNICIPIO','CIDADE']),
      cep:ix(['CEP DESTINATARIO','CEP DO DESTINATARIO','CEP DESTINO','CEP DE DESTINO','CEP ENTREGA','CEP DE ENTREGA','CEP CLIENTE','CEP DO CLIENTE','CEP PARCEIRO','CEP']),
      data_emissao_nf:ix(['DATA EMISSAO NF','DATA DE EMISSAO NF','DATA EMISSAO NOTA FISCAL','DATA DE EMISSAO NOTA FISCAL','NF DATA EMISSAO','NOTA FISCAL DATA EMISSAO','DATA EMISSAO','DATA DE EMISSAO','DATA DA EMISSAO','EMISSAO NF','EMISSAO','DATA NF','DATA DA NOTA','DATA NOTA FISCAL']),
      serie:ix(['SERIE NF','SERIE DA NF','SERIE NOTA FISCAL','NF SERIE','SERIE']),
      cnpj_emitente:ix(['CNPJ EMITENTE','CNPJ DO EMITENTE','EMITENTE CNPJ','CNPJ EMISSOR']),
      empresa:ix(['EMPRESA','FILIAL','BASE']),
      campanha:ix(['CAMPANHA','NOME CAMPANHA','NOME DA CAMPANHA','PEDIDO CAMPANHA']),
      vendedor:ix(['VENDEDOR','NOME VENDEDOR','VENDEDOR RESPONSAVEL','VENDEDOR INTERNO','VENDEDOR 1']),
      total_produtos:ix(['TOTAL DE PRODUTOS','TOTAL PRODUTOS','VALOR DOS PRODUTOS','TOTAL MERCADORIAS','VALOR PRODUTOS']),
      cfop:ix(['CFOP','CODIGO CFOP','CFOP NF','CFOP NOTA FISCAL','COD CFOP','CODIGO DO CFOP','COD DO CFOP','CFOP CODIGO','CFOP DA NOTA','CFOP NFE','CFOP NF E']),
      total_geral:ix(['TOTAL GERAL','VALOR TOTAL','TOTAL NOTA','VALOR TOTAL NOTA','VALOR DA NOTA']),
      valor_frete:ix(['VALOR FRETE','VALOR DO FRETE','FRETE','TOTAL FRETE']),
      canal_venda:ix(['CANAL DE VENDA','CANAL VENDA','CANAL DE VENDAS','CANAL VENDAS','PEDIDO CANAL DE VENDA','PEDIDO CANAL VENDA']),
      chave_nfe:ix(['CHAVE NFE','CHAVE NF E','CHAVE DA NFE','CHAVE DA NF E','CHAVE DE ACESSO NFE','CHAVE DE ACESSO NF E','CHAVE NOTA FISCAL','CHAVE DA NOTA FISCAL'])
    };
    if(out.cep<0)out.cep=hs.findIndex(h=>h.startsWith('CEP '));
    out.score=Object.values(out).filter(x=>x>=0).length;
    return out;
  };
  const sysempUfFromCep=value=>{
    const raw=String(value??'').replace(/\D+/g,'');
    if(!raw||raw.length<7||raw.length>8)return '';
    const d=raw.padStart(8,'0'),n=Number(d),ranges=[[1000000,19999999,'SP'],[20000000,28999999,'RJ'],[29000000,29999999,'ES'],[30000000,39999999,'MG'],[40000000,48999999,'BA'],[49000000,49999999,'SE'],[50000000,56999999,'PE'],[57000000,57999999,'AL'],[58000000,58999999,'PB'],[59000000,59999999,'RN'],[60000000,63999999,'CE'],[64000000,64999999,'PI'],[65000000,65999999,'MA'],[66000000,68899999,'PA'],[68900000,68999999,'AP'],[69000000,69299999,'AM'],[69300000,69399999,'RR'],[69400000,69899999,'AM'],[69900000,69999999,'AC'],[70000000,72799999,'DF'],[72800000,72999999,'GO'],[73000000,73699999,'DF'],[73700000,76799999,'GO'],[76800000,76999999,'RO'],[77000000,77999999,'TO'],[78000000,78899999,'MT'],[78900000,78999999,'RO'],[79000000,79999999,'MS'],[80000000,87999999,'PR'],[88000000,89999999,'SC'],[90000000,99999999,'RS']];
    for(const [a,b,uf] of ranges)if(n>=a&&n<=b)return uf;return '';
  };
  const normalizeMlNumber=value=>{
    const raw=mlCellText(value).replace(/\.0+$/,'').trim();if(!raw)return '';
    const match=raw.match(/\d{12,}/);
    return (match?match[0]:raw.replace(/\D+/g,'')).replace(/^0+(?=\d)/,'');
  };
  const by_ml=Object.create(null),by_nf=Object.create(null),by_pedido=Object.create(null),sheets=[],signatures=new WeakMap();
  const fields=['nf','pedido','cliente','transportadora','marketplace','carrinho','uf','cidade','cep','cep_normalizado','data_emissao_nf','serie','cnpj_emitente','empresa','chave_nfe','campanha','canal_venda','vendedor','total_produtos','total_geral','valor_frete','cfop'];
  const signature=rec=>{let value=signatures.get(rec);if(value===undefined){value=JSON.stringify(fields.map(key=>rec[key]||''));signatures.set(rec,value)}return value};
  const push=(map,key,rec)=>{if(!key)return;const list=map[key]||(map[key]=[]),sig=signature(rec);if(!list.some(old=>signature(old)===sig))list.push(rec)};
  let count=0,invoiceRows=0,geoCount=0,cepGeoCount=0,hasCepColumn=false,recognized=0;
  for(const name of names){
    const ws=wb.Sheets[name];if(!ws)throw new Error('Não foi possível ler uma das abas da SYSEMP.');
    const rows=XLSX.utils.sheet_to_json(ws,{header:1,defval:'',raw:true,blankrows:false});
    const sheet={name:String(name),recognized:false,accepted_rows:0};sheets.push(sheet);let selected=null;
    for(const row of rows){
      const r=Array.isArray(row)?row:[],candidate=header(r);
      if(candidate.nf>=0&&(!selected||candidate.nf===selected.nf||candidate.score>=3||r.every((value,i)=>i===candidate.nf||!mlCellText(value)))){
        if(!selected){recognized++;sheet.recognized=true}
        selected=candidate;hasCepColumn=hasCepColumn||candidate.cep>=0;continue;
      }
      if(!selected)continue;
      const get=key=>selected[key]>=0?mlCellText(r[selected[key]]):'';
      const nf=invoiceNumber(r[selected.nf]),marketplace=normalizeMlNumber(get('marketplace')),carrinho=normalizeMlNumber(get('carrinho')),pedido=get('pedido').replace(/\.0+$/,'').trim();
      if(!nf&&!marketplace&&!carrinho&&!pedido)continue;
      const cep=get('cep'),cepDigits=cep.replace(/\D+/g,''),cep_normalizado=cepDigits.length>=7&&cepDigits.length<=8?cepDigits.padStart(8,'0'):'';
      const rec={nf,pedido,cliente:get('cliente'),transportadora:get('transportadora'),marketplace,carrinho,uf:sysempUfFromCep(cep)||get('uf'),cidade:get('cidade'),cep,cep_normalizado,data_emissao_nf:get('data_emissao_nf')};
      for(const key of ['serie','cnpj_emitente','empresa','chave_nfe','campanha','canal_venda','vendedor','total_produtos','total_geral','valor_frete','cfop']){const value=get(key);if(value)rec[key]=value}
      if(rec.uf){geoCount++;if(cep_normalizado)cepGeoCount++}
      if(nf){invoiceRows++;sheet.accepted_rows++;push(by_nf,nf,rec)}
      for(const mlNumber of new Set([marketplace,carrinho].filter(Boolean)))push(by_ml,mlNumber,rec);
      push(by_pedido,pedido.replace(/\D+/g,''),rec);count++;
    }
  }
  if(!recognized)throw new Error('Nenhuma coluna de Nota Fiscal foi encontrada nas abas da SYSEMP.');
  const uniqueNotes=Object.keys(by_nf).length;
  if(!uniqueNotes)throw new Error('Nenhuma Nota Fiscal válida foi encontrada no SYSEMP.');
  const cepRows=Object.values(by_nf).flat().filter(rec=>/^\d{8}$/.test(rec.cep_normalizado)).length;
  return {version:10,kind:'sysemp',source_name:file.name,created_at:new Date().toISOString(),row_count:count,geo_row_count:geoCount,cep_geo_row_count:cepGeoCount,cep_row_count:cepRows,has_geo:geoCount>0,has_cep:hasCepColumn&&cepRows>0,geo_source:'CEP (prioridade) / UF (fallback)',invoice_manifest:{version:1,complete:true,source:'all_worksheets',sheets,invoice_rows:invoiceRows,unique_notes:uniqueNotes},by_ml,by_nf,by_pedido};
}

window.__lisaBuildSysempIndex=buildSysempIndex;

function trackingUploadKindConfig(kind){
  const map={
    monitor:{path:'/version',pick:d=>d||{}},
    reversa:{path:'/reversa/meta',pick:d=>d||{}},
    rastreamento:{path:'/rastreamento/meta',pick:d=>d||{}},
    sysemp:{path:'/sysemp/meta',pick:d=>d?.sysemp||{}},
    sysemp_pedidos:{path:'/sysemp-pedidos/meta',pick:d=>d?.sysemp_pedidos||{}}
  };
  return map[kind]||null;
}
function trackingUploadMetaTimestamp(meta){
  const raw=meta?.updated_at||meta?.uploaded_at||meta?.version||meta?.uploadedAt||'';
  const t=raw?Date.parse(raw):NaN;
  return Number.isFinite(t)?t:NaN;
}
function trackingUploadMetaName(meta){return String(meta?.file_name||meta?.source_name||meta?.sourceName||'').split(/[\\/]/).pop().trim()}
function trackingUploadCleanName(value){return String(value||'').split(/[\\/]/).pop().trim().toLowerCase()}
async function confirmTrackingUploadStored(kind,fileName,startedAt,previousTimestamp){
  const cfg=trackingUploadKindConfig(kind);if(!cfg)return false;
  const waits=[500,1500,3000,5000,8000];
  for(const wait of waits){
    if(wait)await new Promise(resolve=>setTimeout(resolve,wait));
    try{
      const raw=await latestFileMeta(cfg.path),meta=cfg.pick(raw)||{};
      const available=meta?.available!==false;
      const ts=trackingUploadMetaTimestamp(meta);
      const fresh=Number.isFinite(ts)&&Number.isFinite(previousTimestamp)&&ts>previousTimestamp;
      const remoteName=trackingUploadMetaName(meta),expected=trackingUploadCleanName(fileName);
      const sameName=!!remoteName&&!!expected&&trackingUploadCleanName(remoteName)===expected;
      if(available&&fresh&&sameName)return true;
    }catch{}
  }
  return false;
}
function trackingUploadSuccessText(kind,indexedNotes,response=null){
  const summaryTotal=response?.tracking_summary_total;
  if(kind==='sysemp'&&response?.realtime_snapshot==='ready'&&summaryTotal!==null&&summaryTotal!==undefined&&Number.isSafeInteger(Number(summaryTotal))&&Number(summaryTotal)>=0)return 'SYSEMP importada · '+Number(summaryTotal).toLocaleString('pt-BR')+' notas no resumo.';
  if(kind==='monitor')return 'Monitor de Pedido enviado com sucesso.';
  if(kind==='reversa')return 'Reversa enviada com sucesso. O arquivo anterior foi substituído.';
  if(kind==='sysemp_pedidos')return 'SYSEMP-PEDIDOS enviado com sucesso. Atualizando os painéis em segundo plano.';
  if(kind==='sysemp')return 'SYSEMP enviado com sucesso. Atualizando os painéis em segundo plano.';
  return `Rastreamento atualizado com sucesso: ${Number(indexedNotes||0).toLocaleString('pt-BR')} notas indexadas.`;
}
function refreshAfterTrackingUpload(kind){
  try{loadLatestUploadFiles()}catch{}
  if(kind==='monitor'){selectedFileKey='';Promise.allSettled([loadOrders(),loadLatestVersionInfo()]).catch(()=>{});return}
  if(kind==='reversa'){Promise.allSettled([loadReversaMeta()]).catch(()=>{});return}
  if(kind==='sysemp'||kind==='sysemp_pedidos'){setTimeout(()=>{Promise.resolve(loadSysempPedidosLive(true)).catch(()=>{})},1400)}
}

if(trackingUploadSend)trackingUploadSend.addEventListener('click',async()=>{
  if(!token())return showLogin();
  const file=trackingFile?.files?.[0];
  if(!file){trackingUploadMsg.textContent='Selecione um arquivo.';trackingUploadMsg.className='lisa-tracking-upload-msg is-error';return}
  const cleanName=String(file.name||'').split(/[\\/]/).pop()||'';
  const dot=cleanName.lastIndexOf('.');
  const ext=dot>=0?cleanName.slice(dot+1).toLowerCase():'';
  const base=dot>=0?cleanName.slice(0,dot).toLowerCase():cleanName.toLowerCase();
  const isTracking=base==='rastreamento'&&['xlsx','xlsm','xls'].includes(ext);
  const isMonitor=ext==='txt'&&base.includes('monitor_pedido');
  const isReversa=ext==='xml';
  const isSysempPedidos=isSysempPedidosFileName(cleanName);
  const isSysemp=isSysempFileName(cleanName);
  if(!isTracking&&!isMonitor&&!isReversa&&!isSysempPedidos&&!isSysemp){
    trackingUploadMsg.textContent='Arquivo inválido. Envie Rastreamento, Monitor de Pedido, Reversa, sysemp ou sysemp-pedidos.';
    trackingUploadMsg.className='lisa-tracking-upload-msg is-error';return;
  }
  const uploadKind=isMonitor?'monitor':isReversa?'reversa':isSysempPedidos?'sysemp_pedidos':isSysemp?'sysemp':'rastreamento';
  const uploadStartedAt=Date.now();
  let previousTimestamp=NaN;
  const oldText=trackingUploadSend.textContent;trackingUploadSend.disabled=true;
  try{
    const form=new FormData();
    form.append('file',file,file.name);
    if(isTracking){
      trackingUploadMsg.textContent='Lendo e preparando a planilha...';trackingUploadMsg.className='lisa-tracking-upload-msg';
      const index=await buildTrackingIndex(file);
      form.append('index',new Blob([JSON.stringify(index)],{type:'application/json'}),'rastreamento-index.json');
    }else if(isSysempPedidos){
      trackingUploadMsg.textContent='Lendo planilha SYSEMP-PEDIDOS...';trackingUploadMsg.className='lisa-tracking-upload-msg';
      const index=await buildSysempPedidosIndex(file);
      form.append('index',new Blob([JSON.stringify(index)],{type:'application/json'}),'sysemp-pedidos-index.json');
    }else if(isSysemp){
      trackingUploadMsg.textContent='Lendo planilha SYSEMP...';trackingUploadMsg.className='lisa-tracking-upload-msg';
      const index=await buildSysempIndex(file);
      form.append('index',new Blob([JSON.stringify(index)],{type:'application/json'}),'sysemp-index.json');
    }else if(isReversa){
      trackingUploadMsg.textContent='Preparando arquivo de Reversa...';trackingUploadMsg.className='lisa-tracking-upload-msg';
    }else{
      trackingUploadMsg.textContent='Preparando Monitor de Pedido...';trackingUploadMsg.className='lisa-tracking-upload-msg';
    }
    try{const cfg=trackingUploadKindConfig(uploadKind);previousTimestamp=trackingUploadMetaTimestamp(cfg.pick(await latestFileMeta(cfg.path)))}catch{}
    trackingUploadMsg.textContent='Enviando arquivo...';
    const r=await fetch(WORKER+'/rastreamento/upload',{method:'POST',headers:{Authorization:'Bearer '+token()},body:form});
    const d=await r.json().catch(()=>({}));
    if(r.status===401){sessionStorage.removeItem(tokenKey);showLogin();throw new Error('Sessão expirada.')}
    if(!r.ok)throw new Error(d.error||d.detail||(r.status>=500?'Falha temporária de rede no servidor.':'Falha ao enviar o arquivo.'));
    if(d.ignored){trackingUploadMsg.textContent='Arquivo não importado: envio fora do horário permitido (06:00–20:00).';trackingUploadMsg.className='lisa-tracking-upload-msg is-error';return}
    if(d.ok!==true)throw new Error('Resposta inválida do servidor.');
    const returnedKind=d.kind==='monitor_pedido'?'monitor':d.kind==='sysemp_pedidos'?'sysemp_pedidos':d.kind==='sysemp'?'sysemp':d.kind==='reversa'?'reversa':uploadKind;
    trackingUploadMsg.textContent=trackingUploadSuccessText(returnedKind,d.indexed_notes,d);
    trackingUploadMsg.className='lisa-tracking-upload-msg is-success';
    refreshAfterTrackingUpload(returnedKind);
  }catch(e){
    const rawMessage=String(e?.message||e||'');
    const looksLikeNetworkFailure=/failed to fetch|networkerror|network request failed|load failed|fetch failed|Falha temporária de rede|Resposta inválida/i.test(rawMessage);
    if(looksLikeNetworkFailure){
      trackingUploadMsg.textContent='Confirmando se o arquivo foi gravado...';
      trackingUploadMsg.className='lisa-tracking-upload-msg';
      const confirmed=await confirmTrackingUploadStored(uploadKind,cleanName,uploadStartedAt,previousTimestamp);
      if(confirmed){
        trackingUploadMsg.textContent=trackingUploadSuccessText(uploadKind,0);
        trackingUploadMsg.className='lisa-tracking-upload-msg is-success';
        refreshAfterTrackingUpload(uploadKind);
      }else{
        trackingUploadMsg.textContent='O servidor não confirmou o envio. Confira a data em Arquivos mais recentes antes de reenviar.';
        trackingUploadMsg.className='lisa-tracking-upload-msg is-error';
      }
    }else{
      trackingUploadMsg.textContent=rawMessage||'Erro ao enviar o arquivo.';
      trackingUploadMsg.className='lisa-tracking-upload-msg is-error';
    }
  }finally{trackingUploadSend.disabled=false;trackingUploadSend.textContent=oldText}
});

restoreAccountSession();
})();

;

{
  const initLisaCteModule = function(){
  'use strict';
  let lisa2Attempts=0;
  function initLisa2(){
    const panel=document.getElementById('lisa-app-vg-native') || document.querySelector('.lisa-app[data-worker]');
    const openBtn=panel ? panel.querySelector('[data-cte-open]') : null;
    if(!panel||!openBtn){if(lisa2Attempts++<200)setTimeout(initLisa2,100);return}
    const previousWrap=document.getElementById('lisacte-vg-native');
    if(previousWrap)previousWrap.remove();
  const wrap=document.createElement('div');
  wrap.id='lisacte-vg-native';
  wrap.innerHTML=`
    <div class="cte-modal" data-cte-modal hidden>
      <div class="cte-card">
        <button type="button" class="cte-close" data-cte-close aria-label="Fechar">×</button>
        <div class="cte-loading" data-cte-loading><div class="cte-spinner"></div><strong>Carregando CTE...</strong><span>Buscando Monitor de Pedido e CTEs salvos.</span></div>
        <div class="cte-head" data-cte-content hidden>
          <div><h3>Relatório CTE</h3><span class="cte-head-meta" data-cte-meta>Monitor de Pedido × Rastreamento</span></div>
          <div class="cte-head-actions"><button type="button" class="cte-action" data-cte-copy disabled>Copiar</button><button type="button" class="cte-action" data-cte-download disabled>Baixar</button></div>
        </div>
        <div class="cte-summary" data-cte-content hidden>
          <button type="button" class="cte-summary-btn is-active" data-cte-view="without"><span>Sem CTE</span><strong data-cte-count-without>0</strong></button>
          <button type="button" class="cte-summary-btn" data-cte-view="late"><span>Atraso +24h</span><strong data-cte-count-late>0</strong></button>
          <button type="button" class="cte-summary-btn" data-cte-view="transit"><span>Em trajeto</span><strong data-cte-count-transit>0</strong></button>
          <button type="button" class="cte-summary-btn" data-cte-view="nostatus"><span>Sem status</span><strong data-cte-count-nostatus>0</strong></button>
          <button type="button" class="cte-summary-btn" data-cte-view="awaiting"><span>Aguard. retirada</span><strong data-cte-count-awaiting>0</strong></button>
          <button type="button" class="cte-summary-btn" data-cte-view="returning"><span>Devolvendo</span><strong data-cte-count-returning>0</strong></button>
          <button type="button" class="cte-summary-btn" data-cte-view="returned"><span>Devolvido</span><strong data-cte-count-returned>0</strong></button>
          <button type="button" class="cte-summary-btn" data-cte-view="overdue"><span>Atrasado</span><strong data-cte-count-overdue>0</strong></button>
          <button type="button" class="cte-summary-btn" data-cte-view="delivered"><span>Entregue</span><strong data-cte-count-delivered>0</strong></button>
        </div>
        <div class="cte-campaigns" data-cte-campaigns data-cte-campaign-mode="without" data-cte-content hidden></div>
        <div class="cte-status" data-cte-status data-cte-content hidden>Carregando relatório...</div>
        <div class="cte-table-wrap" data-cte-content hidden>
          <table class="cte-table">
            <thead><tr>
              <th data-cte-sort="data_api" data-sort-type="date"><button type="button" class="cte-sort-btn">Data API <span class="cte-sort-arrows">↑↓</span></button><input class="cte-filter" data-cte-filter="data_api" type="date"></th>
              <th data-cte-sort="nota" data-sort-type="number"><button type="button" class="cte-sort-btn">Nota <span class="cte-sort-arrows">↑↓</span></button><input class="cte-filter" data-cte-filter="nota" type="text" inputmode="numeric" placeholder="Filtrar"></th>
              <th data-cte-sort="destinatario" data-sort-type="text"><button type="button" class="cte-sort-btn">Destinatário <span class="cte-sort-arrows">↑↓</span></button><input class="cte-filter" data-cte-filter="destinatario" type="text" placeholder="Filtrar"></th>
              <th data-cte-sort="campanha" data-sort-type="text"><button type="button" class="cte-sort-btn">Campanha <span class="cte-sort-arrows">↑↓</span></button><select class="cte-filter" data-cte-filter="campanha"><option value="">Todos</option></select></th>
              <th data-cte-sort="status" data-sort-type="text"><button type="button" class="cte-sort-btn">Status LISA <span class="cte-sort-arrows">↑↓</span></button><select class="cte-filter" data-cte-filter="status"><option value="">Todos</option></select></th>
              <th data-cte-sort="status_tr" data-sort-type="text"><button type="button" class="cte-sort-btn">Status -TR <span class="cte-sort-arrows">↑↓</span></button><select class="cte-filter" data-cte-filter="status_tr"><option value="">Todos</option></select></th>
              <th data-cte-sort="previsao_entrega_tr" data-sort-type="date"><button type="button" class="cte-sort-btn">Previsão de entrega - TR <span class="cte-sort-arrows">↑↓</span></button><input class="cte-filter" data-cte-filter="previsao_entrega_tr" type="date"></th>
              <th data-cte-sort="transportadora" data-sort-type="text"><button type="button" class="cte-sort-btn">Transportadora <span class="cte-sort-arrows">↑↓</span></button><select class="cte-filter" data-cte-filter="transportadora"><option value="">Todos</option></select></th>
              <th data-cte-sort="cte" data-sort-type="text"><button type="button" class="cte-sort-btn">CTE <span class="cte-sort-arrows">↑↓</span></button><input class="cte-filter" data-cte-filter="cte" type="text" placeholder="Filtrar"></th>
              <th data-cte-sort="data_romaneio" data-sort-type="date"><button type="button" class="cte-sort-btn">Data Romaneio <span class="cte-sort-arrows">↑↓</span></button><input class="cte-filter" data-cte-filter="data_romaneio" type="date"></th>
              <th data-cte-sort="data_finalizado" data-sort-type="date"><button type="button" class="cte-sort-btn">Data Finalizado <span class="cte-sort-arrows">↑↓</span></button><input class="cte-filter" data-cte-filter="data_finalizado" type="date"></th>
            </tr></thead>
            <tbody data-cte-body></tbody>
          </table>
          <div class="cte-empty" data-cte-empty hidden>Nenhuma nota encontrada.</div>
        </div>
        <div class="cte-footer" data-cte-content hidden><span data-cte-result-count>0 notas</span><div class="cte-pages"><button class="cte-page-btn" type="button" data-cte-prev>Anterior</button><span data-cte-page>Página 1 de 1</span><button class="cte-page-btn" type="button" data-cte-next>Próxima</button></div></div>
      </div>
    </div>`;
  document.body.appendChild(wrap);

  const $=s=>wrap.querySelector(s),$$=s=>[...wrap.querySelectorAll(s)];
  const modal=$('[data-cte-modal]'),body=$('[data-cte-body]'),empty=$('[data-cte-empty]'),status=$('[data-cte-status]');
  const copyBtn=$('[data-cte-copy]'),downloadBtn=$('[data-cte-download]'),loading=$('[data-cte-loading]'),contentParts=$$('[data-cte-content]');
  function showLoading(){if(loading)loading.hidden=false;for(const el of contentParts)el.hidden=true}
  function showContent(){if(loading)loading.hidden=true;for(const el of contentParts)el.hidden=false}
  let allItems=[],filtered=[],view='without',sort={key:'data_api',type:'date',dir:'desc'},page=1;
  const pageSize=200;
  const esc=s=>String(s??'').replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
  const norm=s=>String(s??'').normalize('NFD').replace(/[\u0300-\u036f]/g,'').toUpperCase().trim();
  const worker=()=>String(panel.dataset.worker||'').replace(/\/$/,'');
  const token=()=>sessionStorage.getItem('lisa_panel_token')||'';
  function dateKey(v){const s=String(v||'').trim();let m=s.match(/(\d{2})\/(\d{2})\/(\d{4})/);if(m)return `${m[3]}-${m[2]}-${m[1]}`;m=s.match(/(\d{4})-(\d{2})-(\d{2})/);return m?`${m[1]}-${m[2]}-${m[3]}`:''}
  function dateValue(v){const k=dateKey(v);return k?Date.parse(k+'T00:00:00'):0}
  function finalizadoMs(v){const s=String(v||'').trim();if(!s)return 0;let m=s.match(/(\d{2})\/(\d{2})\/(\d{4})(?:[^\d]+(\d{1,2}):(\d{2})(?::(\d{2}))?)?/);if(m){const hh=String(m[4]||'00').padStart(2,'0'),mm=m[5]||'00',ss=m[6]||'00';const t=Date.parse(`${m[3]}-${m[2]}-${m[1]}T${hh}:${mm}:${ss}-03:00`);return Number.isNaN(t)?0:t}const t=Date.parse(s);return Number.isNaN(t)?0:t}
  function isCteLate(x){const t=finalizadoMs(x?.data_finalizado);return !x?.has_cte&&t>0&&(Date.now()-t)>86400000}
  function numValue(v){const d=String(v??'').replace(/\D+/g,'');return d?Number(d):0}
  function sortValue(v,type){if(type==='date')return dateValue(v);if(type==='number')return numValue(v);return norm(v)}
  function uniqueValues(key){return [...new Set(allItems.map(x=>String(x?.[key]||'').trim()).filter(Boolean))].sort((a,b)=>a.localeCompare(b,'pt-BR',{numeric:true,sensitivity:'base'}))}
  function fillSelect(key){const el=$(`[data-cte-filter="${key}"]`);if(!el||el.tagName!=='SELECT')return;const cur=el.value;el.innerHTML='<option value="">Todos</option>'+uniqueValues(key).map(v=>`<option value="${esc(v)}">${esc(v)}</option>`).join('');if([...el.options].some(o=>o.value===cur))el.value=cur}
  function resetFilters(){for(const el of $$('.cte-filter'))el.value=''}
  function isCteDelivered(x){const v=norm(x?.status_tr);return !!x?.has_cte&&(v.includes('ENTREGUE')||v.includes('ENTREGA REALIZADA')||v==='RECEBIDO'||v.includes('RECEBIDO PELO DESTINATARIO'))}
  function isCteAwaitingPickup(x){const v=norm(x?.status_tr);return !!x?.has_cte&&(v.includes('AGUARDANDO RETIRADA')||v.includes('RETIRADA NO ENDERECO INDICADO'))}
  function isCteReturned(x){const v=norm(x?.status_tr);return !!x?.has_cte&&(v.includes('DEVOLVIDO')||v.includes('DEVOLVIDA')||v.includes('ENTREGUE AO REMETENTE')||v.includes('DEVOLUCAO CONCLUIDA')||v.includes('DEVOLUCAO REALIZADA'))}
  function isCteReturning(x){const v=norm(x?.status_tr);return !!x?.has_cte&&!isCteReturned(x)&&(v.includes('DEVOLVENDO')||v.includes('DEVOLUCAO')||v.includes('EM ROTA DE DEVOLUCAO')||v.includes('EM PROCESSO DE DEVOLUCAO')||v.includes('EM TRANSFERENCIA DEVOLUCAO')||v.includes('AGUARDANDO FREQUENCIA DE DEVOLUCAO')||v.includes('EMBARQUE DE DEVOLUCAO')||v.includes('DOCUMENTO DE DEVOLUCAO'))}
  function isCteTransit(x){return !!x?.has_cte}
  function isCteNoStatus(x){return !!x?.has_cte&&!String(x?.status_tr||'').trim()}
  function cteForecastDayMs(v){const s=String(v??'').trim();if(!s)return NaN;let m=s.match(/^(\d{1,2})[\/\-.](\d{1,2})[\/\-.](\d{4})/);if(m)return Date.UTC(Number(m[3]),Number(m[2])-1,Number(m[1]));m=s.match(/^(\d{4})-(\d{2})-(\d{2})/);if(m)return Date.UTC(Number(m[1]),Number(m[2])-1,Number(m[3]));return NaN}
  function cteTodayMs(){const d=new Date();return Date.UTC(d.getFullYear(),d.getMonth(),d.getDate())}
  function isCteOverdue(x){const ms=cteForecastDayMs(x?.previsao_entrega_tr);return !!x?.has_cte&&Number.isFinite(ms)&&ms<cteTodayMs()&&!isCteDelivered(x)}
  function cteViewItems(){return allItems.filter(x=>view==='without'?!x.has_cte:view==='late'?isCteLate(x):view==='overdue'?isCteOverdue(x):view==='delivered'?isCteDelivered(x):view==='awaiting'?isCteAwaitingPickup(x):view==='returning'?isCteReturning(x):view==='returned'?isCteReturned(x):view==='nostatus'?isCteNoStatus(x):view==='transit'?isCteTransit(x):false)}
  function renderCampaignButtons(){
    const host=$('[data-cte-campaigns]');if(!host)return;
    const base=cteViewItems(),selected=String($('[data-cte-filter="campanha"]')?.value||'');
    const counts=new Map();
    for(const x of base){const c=String(x?.campanha||'').trim();if(!c)continue;counts.set(c,(counts.get(c)||0)+1)}
    const preferred=['EDI - ABASTECE ES','MAGALU - ES','MERCADO LIVRE - ES','SHOPEE - ES','SHOPIFY','VENDAS INTERNAS - ES'];
    const campaigns=[...counts.entries()].sort((a,b)=>{const ai=preferred.indexOf(a[0]),bi=preferred.indexOf(b[0]);if(ai!==-1||bi!==-1){if(ai===-1)return 1;if(bi===-1)return -1;if(ai!==bi)return ai-bi}return a[0].localeCompare(b[0],'pt-BR',{numeric:true,sensitivity:'base'})});
    host.dataset.cteCampaignMode=view;
    const btn=(value,label,count)=>`<button type="button" class="cte-campaign-btn${selected===value?' is-active':''}" data-cte-campaign="${esc(value)}" title="${esc(label)}"><span>${esc(label)}</span><strong>${Number(count||0).toLocaleString('pt-BR')}</strong></button>`;
    host.innerHTML=btn('','TODAS',base.length)+campaigns.map(([name,count])=>btn(name,name,count)).join('');
  }
  function updateSortIndicators(){for(const th of $$('[data-cte-sort]')){th.classList.remove('is-sort-active');th.removeAttribute('data-sort-dir');if(th.dataset.cteSort===sort.key){th.classList.add('is-sort-active');th.dataset.sortDir=sort.dir}}}
  function apply(){
    const f=key=>String($(`[data-cte-filter="${key}"]`)?.value||'');
    const base=cteViewItems();
    filtered=base.filter(x=>(!f('data_api')||dateKey(x.data_api)===f('data_api'))&&(!f('nota')||norm(x.nota).includes(norm(f('nota'))))&&(!f('destinatario')||norm(x.destinatario).includes(norm(f('destinatario'))))&&(!f('campanha')||String(x.campanha||'')===f('campanha'))&&(!f('status')||String(x.status||'')===f('status'))&&(!f('status_tr')||String(x.status_tr||'')===f('status_tr'))&&(!f('previsao_entrega_tr')||dateKey(x.previsao_entrega_tr)===f('previsao_entrega_tr'))&&(!f('transportadora')||String(x.transportadora||'')===f('transportadora'))&&(!f('cte')||norm(x.cte).includes(norm(f('cte'))))&&(!f('data_romaneio')||dateKey(x.data_romaneio)===f('data_romaneio'))&&(!f('data_finalizado')||dateKey(x.data_finalizado)===f('data_finalizado')));
    const mult=sort.dir==='asc'?1:-1;
    filtered=filtered.map((item,index)=>({item,index})).sort((a,b)=>{const av=sortValue(a.item?.[sort.key],sort.type),bv=sortValue(b.item?.[sort.key],sort.type);let c=0;if(sort.type==='text')c=String(av).localeCompare(String(bv),'pt-BR',{numeric:true,sensitivity:'base'});else c=av-bv;return c===0?a.index-b.index:c*mult}).map(x=>x.item);
    const pages=Math.max(1,Math.ceil(filtered.length/pageSize));if(page>pages)page=pages;if(page<1)page=1;
    const start=(page-1)*pageSize,slice=filtered.slice(start,start+pageSize);
    body.innerHTML=slice.map(x=>{const full=String(x.status_tr||'-'),short=full.length>24?full.slice(0,24)+'…':full;return `<tr><td>${esc(x.data_api||'-')}</td><td>${esc(x.nota||'-')}</td><td>${esc(x.destinatario||'-')}</td><td>${esc(x.campanha||'-')}</td><td>${esc(x.status||'-')}</td><td><span data-cte-status-tr-cell data-short="${esc(short)}" data-full="${esc(full)}" title="${esc(full)}" style="display:inline-block;max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:middle;cursor:pointer">${esc(short)}</span></td><td>${esc(x.previsao_entrega_tr||'-')}</td><td>${esc(x.transportadora||'-')}</td><td>${esc(x.cte||'-')}</td><td>${esc(x.data_romaneio||'-')}</td><td>${esc(x.data_finalizado||'-')}</td></tr>`}).join('');
    empty.hidden=filtered.length>0;
    $('[data-cte-result-count]').textContent=`${filtered.length.toLocaleString('pt-BR')} ${filtered.length===1?'nota':'notas'}${filtered.length>pageSize?` · exibindo ${start+1}-${Math.min(start+pageSize,filtered.length)}`:''}`;
    $('[data-cte-page]').textContent=`Página ${page} de ${pages}`;$('[data-cte-prev]').disabled=page<=1;$('[data-cte-next]').disabled=page>=pages;
    copyBtn.disabled=!filtered.length;downloadBtn.disabled=!filtered.length;updateSortIndicators();
  }
  function setView(next){view=['without','late','transit','nostatus','awaiting','returning','returned','overdue','delivered'].includes(next)?next:'without';for(const b of $$('[data-cte-view]'))b.classList.toggle('is-active',b.dataset.cteView===view);page=1;resetFilters();fillSelect('campanha');fillSelect('status');fillSelect('status_tr');fillSelect('transportadora');renderCampaignButtons();apply()}
  async function load(){
    showLoading();
    status.textContent='Carregando Monitor de Pedido e CTEs salvos...';status.classList.remove('is-error');body.innerHTML='';empty.hidden=true;copyBtn.disabled=true;downloadBtn.disabled=true;
    try{
      const r=await fetch(worker()+'/cte-report',{headers:{Authorization:'Bearer '+token()}});const d=await r.json().catch(()=>({}));
      if(r.status===401)throw new Error(d.error||'Sessão expirada.');if(!r.ok)throw new Error(d.detail||d.error||'Não foi possível carregar o relatório CTE.');
      allItems=Array.isArray(d.items)?d.items:[];const lateCount=allItems.reduce((n,x)=>n+(isCteLate(x)?1:0),0),deliveredCount=allItems.reduce((n,x)=>n+(isCteDelivered(x)?1:0),0),awaitingCount=allItems.reduce((n,x)=>n+(isCteAwaitingPickup(x)?1:0),0),returningCount=allItems.reduce((n,x)=>n+(isCteReturning(x)?1:0),0),returnedCount=allItems.reduce((n,x)=>n+(isCteReturned(x)?1:0),0),overdueCount=allItems.reduce((n,x)=>n+(isCteOverdue(x)?1:0),0),transitCount=allItems.reduce((n,x)=>n+(isCteTransit(x)?1:0),0),noStatusCount=allItems.reduce((n,x)=>n+(isCteNoStatus(x)?1:0),0);$('[data-cte-count-without]').textContent=Number(d.without_cte||0).toLocaleString('pt-BR');$('[data-cte-count-late]').textContent=lateCount.toLocaleString('pt-BR');$('[data-cte-count-transit]').textContent=transitCount.toLocaleString('pt-BR');$('[data-cte-count-nostatus]').textContent=noStatusCount.toLocaleString('pt-BR');$('[data-cte-count-awaiting]').textContent=awaitingCount.toLocaleString('pt-BR');$('[data-cte-count-returning]').textContent=returningCount.toLocaleString('pt-BR');$('[data-cte-count-returned]').textContent=returnedCount.toLocaleString('pt-BR');$('[data-cte-count-overdue]').textContent=overdueCount.toLocaleString('pt-BR');$('[data-cte-count-delivered]').textContent=deliveredCount.toLocaleString('pt-BR');
      const u=d.updated_at?new Date(d.updated_at):null;$('[data-cte-meta]').textContent='Monitor de Pedido × Rastreamento'+(u&&!Number.isNaN(u.getTime())?' · atualizado '+u.toLocaleString('pt-BR'):'');
      status.textContent=`${Number(d.total||allItems.length).toLocaleString('pt-BR')} notas do Monitor · ${Number(d.without_cte||0).toLocaleString('pt-BR')} sem CTE · ${lateCount.toLocaleString('pt-BR')} em atraso +24h · ${transitCount.toLocaleString('pt-BR')} com CTE · ${noStatusCount.toLocaleString('pt-BR')} sem status · ${awaitingCount.toLocaleString('pt-BR')} aguardando retirada · ${returningCount.toLocaleString('pt-BR')} devolvendo · ${returnedCount.toLocaleString('pt-BR')} devolvidos · ${overdueCount.toLocaleString('pt-BR')} atrasados · ${deliveredCount.toLocaleString('pt-BR')} entregues`;
      fillSelect('campanha');fillSelect('status');fillSelect('status_tr');fillSelect('transportadora');setView(view);showContent();
    }catch(e){allItems=[];filtered=[];status.textContent=e.message||'Erro ao carregar o relatório CTE.';status.classList.add('is-error');$('[data-cte-count-without]').textContent='0';$('[data-cte-count-late]').textContent='0';$('[data-cte-count-transit]').textContent='0';$('[data-cte-count-nostatus]').textContent='0';$('[data-cte-count-awaiting]').textContent='0';$('[data-cte-count-returning]').textContent='0';$('[data-cte-count-returned]').textContent='0';$('[data-cte-count-overdue]').textContent='0';$('[data-cte-count-delivered]').textContent='0';renderCampaignButtons();apply();showContent()}
  }
  function open(){modal.hidden=false;view='without';for(const b of $$('[data-cte-view]'))b.classList.toggle('is-active',b.dataset.cteView==='without');resetFilters();page=1;showLoading();load()}
  function close(){modal.hidden=true;showLoading()}
  async function copy(){if(!filtered.length)return;const H=['Data API','Nota','Destinatário','Campanha','Status LISA','Status -TR','Previsão de entrega - TR','Transportadora','CTE','Data Romaneio','Data Finalizado'];const txt=[H.join('\t'),...filtered.map(x=>[x.data_api,x.nota,x.destinatario,x.campanha,x.status,x.status_tr,x.previsao_entrega_tr,x.transportadora,x.cte,x.data_romaneio,x.data_finalizado].map(v=>String(v??'')).join('\t'))].join('\n');try{await navigator.clipboard.writeText(txt)}catch{const ta=document.createElement('textarea');ta.value=txt;ta.style.position='fixed';ta.style.left='-9999px';document.body.appendChild(ta);ta.select();document.execCommand('copy');ta.remove()}const old=copyBtn.textContent;copyBtn.textContent='Copiado!';setTimeout(()=>copyBtn.textContent=old,1100)}
  async function ensureXlsx(){if(window.XLSX)return window.XLSX;await new Promise((res,rej)=>{const old=document.querySelector('script[data-cte-xlsx]');if(old){if(window.XLSX)return res();old.addEventListener('load',res,{once:true});old.addEventListener('error',rej,{once:true});return}const s=document.createElement('script');s.src='https://cdn.jsdelivr.net/npm/xlsx@0.18.5/dist/xlsx.full.min.js';s.async=true;s.dataset.cteXlsx='1';s.onload=res;s.onerror=rej;document.head.appendChild(s)});if(!window.XLSX)throw new Error('Leitor de Excel indisponível.');return window.XLSX}
  async function download(){if(!filtered.length)return;const old=downloadBtn.textContent;downloadBtn.disabled=true;downloadBtn.textContent='Gerando...';try{const XLSX=await ensureXlsx();const H=['Data API','Nota','Destinatário','Campanha','Status LISA','Status -TR','Previsão de entrega - TR','Transportadora','CTE','Data Romaneio','Data Finalizado'];const A=[H,...filtered.map(x=>[x.data_api,x.nota,x.destinatario,x.campanha,x.status,x.status_tr,x.previsao_entrega_tr,x.transportadora,x.cte,x.data_romaneio,x.data_finalizado].map(v=>String(v??'')))];const ws=XLSX.utils.aoa_to_sheet(A);const rg=XLSX.utils.decode_range(ws['!ref']);for(let R=rg.s.r;R<=rg.e.r;R++)for(let C=rg.s.c;C<=rg.e.c;C++){const a=XLSX.utils.encode_cell({r:R,c:C}),cell=ws[a];if(cell){cell.t='s';cell.v=String(cell.v??'');cell.z='@'}}ws['!cols']=[{wch:18},{wch:16},{wch:34},{wch:24},{wch:18},{wch:24},{wch:22},{wch:28},{wch:24},{wch:18},{wch:18}];const wb=XLSX.utils.book_new();XLSX.utils.book_append_sheet(wb,ws,'CTE');XLSX.writeFile(wb,view==='transit'?'notas_cte_em_trajeto.xlsx':view==='nostatus'?'notas_cte_sem_status.xlsx':view==='awaiting'?'notas_cte_aguardando_retirada.xlsx':view==='returning'?'notas_cte_devolvendo.xlsx':view==='returned'?'notas_cte_devolvidos.xlsx':view==='overdue'?'notas_cte_atrasados.xlsx':view==='delivered'?'notas_cte_entregues.xlsx':view==='late'?'notas_cte_atraso_24h.xlsx':'notas_sem_cte.xlsx',{compression:true})}catch(e){alert(e.message||'Não foi possível gerar o XLSX.')}finally{downloadBtn.disabled=false;downloadBtn.textContent=old}}
  body.addEventListener('click',e=>{const el=e.target.closest('[data-cte-status-tr-cell]');if(!el)return;const expanded=el.dataset.expanded==='1';el.dataset.expanded=expanded?'0':'1';el.textContent=expanded?el.dataset.short:el.dataset.full;el.style.maxWidth=expanded?'150px':'360px';el.style.whiteSpace=expanded?'nowrap':'normal';el.style.overflow=expanded?'hidden':'visible';el.style.textOverflow=expanded?'ellipsis':'clip'});
  if(openBtn.__lisaCteOpenHandler)openBtn.removeEventListener('click',openBtn.__lisaCteOpenHandler);openBtn.__lisaCteOpenHandler=open;openBtn.addEventListener('click',open);$('[data-cte-close]').addEventListener('click',close);modal.addEventListener('click',e=>{if(e.target===modal)close()});for(const b of $$('[data-cte-view]'))b.addEventListener('click',()=>setView(b.dataset.cteView));const campaignHost=$('[data-cte-campaigns]');if(campaignHost)campaignHost.addEventListener('click',e=>{const b=e.target.closest('[data-cte-campaign]');if(!b)return;const sel=$('[data-cte-filter="campanha"]');if(sel)sel.value=String(b.dataset.cteCampaign||'');page=1;renderCampaignButtons();apply()});for(const el of $$('.cte-filter'))el.addEventListener(el.tagName==='SELECT'?'change':'input',()=>{page=1;if(el.dataset.cteFilter==='campanha')renderCampaignButtons();apply()});const head=$('.cte-table thead');head.addEventListener('click',e=>{const btn=e.target.closest('.cte-sort-btn');if(!btn)return;const th=btn.closest('[data-cte-sort]');if(!th)return;const key=th.dataset.cteSort,type=th.dataset.sortType||'text';if(sort.key!==key)sort={key,type,dir:type==='number'?'desc':'asc'};else sort={key,type,dir:sort.dir==='asc'?'desc':'asc'};page=1;apply()});$('[data-cte-prev]').addEventListener('click',()=>{if(page>1){page--;apply()}});$('[data-cte-next]').addEventListener('click',()=>{const pages=Math.max(1,Math.ceil(filtered.length/pageSize));if(page<pages){page++;apply()}});copyBtn.addEventListener('click',copy);downloadBtn.addEventListener('click',download);
  }
  initLisa2();
  };
  initLisaCteModule();
}

;

/* Refração dos cartões sobre o wallpaper: lente arredondada, normal da superfície
   e direção refratada. Implementação local; nenhuma biblioteca externa. */
(()=>{
 const root=document.getElementById('lisa-app-vg-native');
 const login=root?.querySelector('[data-login]');if(!login||login.__liquidOptics)return;
 login.__liquidOptics=true;
 const reduced=matchMedia('(prefers-reduced-transparency: reduce)');
 const img=new Image();img.crossOrigin='anonymous';
 const source=document.createElement('canvas');const ctx=source.getContext('2d',{willReadFrequently:true});
 if(!ctx)return;
 let sourceData=null,sourceW=0,sourceH=0,viewW=0,viewH=0,ratio=1,raf=0;
 const entries=new Map();
 const clamp=(v,a,b)=>Math.min(b,Math.max(a,v));
 function background(w,h){
  ratio=Math.min(devicePixelRatio||1,1.5,2200/Math.max(w,h));
  sourceW=Math.max(1,Math.round(w*ratio));sourceH=Math.max(1,Math.round(h*ratio));
  source.width=sourceW;source.height=sourceH;ctx.setTransform(ratio,0,0,ratio,0,0);
  const cover=Math.max(w/img.naturalWidth,h/img.naturalHeight),iw=img.naturalWidth*cover,ih=img.naturalHeight*cover;
  if(/iPhone|iPod/.test(navigator.userAgent))ctx.drawImage(img,0,0,w,h);
  else ctx.drawImage(img,(w-iw)/2,(h-ih)/2,iw,ih);
  // Mesmo gradiente de iluminação do wallpaper do login (120 graus).
  const dx=Math.sin(2*Math.PI/3),dy=-Math.cos(2*Math.PI/3),length=Math.abs(w*dx)+Math.abs(h*dy);
  const gradient=ctx.createLinearGradient(w/2-dx*length/2,h/2-dy*length/2,w/2+dx*length/2,h/2+dy*length/2);
  gradient.addColorStop(0,'rgba(3,15,35,.48)');gradient.addColorStop(1,'rgba(8,25,49,.16)');
  ctx.fillStyle=gradient;ctx.fillRect(0,0,w,h);sourceData=ctx.getImageData(0,0,sourceW,sourceH).data;
  viewW=w;viewH=h;
 }
 function sample(x,y,ch){
  x=clamp(x*ratio,0,sourceW-1.001);y=clamp(y*ratio,0,sourceH-1.001);
  const ix=Math.floor(x),iy=Math.floor(y),fx=x-ix,fy=y-iy,i=(iy*sourceW+ix)*4+ch;
  const a=sourceData[i]*(1-fx)+sourceData[i+4]*fx,b=sourceData[i+sourceW*4]*(1-fx)+sourceData[i+sourceW*4+4]*fx;
  return a*(1-fy)+b*fy;
 }
 function render(button,canvas,base){
  const box=button.getBoundingClientRect();if(box.width<1||box.height<1)return;
  const width=box.width,height=box.height,px=Math.max(1,Math.round(width*ratio)),py=Math.max(1,Math.round(height*ratio));
  canvas.width=px;canvas.height=py;const out=canvas.getContext('2d');if(!out)return;
  const frame=out.createImageData(px,py),pixels=frame.data;
  const radius=Math.min(parseFloat(getComputedStyle(button).borderTopLeftRadius)||26,width/2,height/2);
  const bezel=Math.min(22,width*.14,height*.14),thickness=34,eta=1/1.46;
  for(let y=0;y<py;y++)for(let x=0;x<px;x++){
   const lx=(x+.5)/ratio,ly=(y+.5)/ratio,rx=lx-width/2,ry=ly-height/2;
   const qx=Math.abs(rx)-(width/2-radius),qy=Math.abs(ry)-(height/2-radius);
   const ax=Math.max(qx,0),ay=Math.max(qy,0),len=Math.hypot(ax,ay);
   const distance=radius-len-Math.min(Math.max(qx,qy),0),i=(y*px+x)*4;
   if(distance<-.7)continue;
   let nx=0,ny=0;
   if(len>0){nx=ax/len*Math.sign(rx);ny=ay/len*Math.sign(ry)}else if(qx>qy)nx=Math.sign(rx);else ny=Math.sign(ry);
   const t=clamp(distance/bezel,0,1),h=Math.sqrt(Math.max(.015,1-(1-t)*(1-t)));
   const slope=(1-t)/(h*bezel)*13,norm=Math.sqrt(1+slope*slope),nz=1/norm;
   const k=Math.sqrt(1-eta*eta*(1-nz*nz)),factor=eta*nz-k,rz=-eta-factor*nz;
   const shift=thickness*factor*(-slope/norm)/Math.max(.1,Math.abs(rz));
   const sx=box.left-base.left+lx+nx*shift,sy=box.top-base.top+ly+ny*shift;
   const fringe=(1-t)*.65;
   const light=clamp(-nx*.65-ny*.76,0,1),fresnel=Math.pow(1-nz,3);
   const spec=clamp((fresnel*.09+Math.pow(light,6)*.045)*(1-t),0,.12);
   for(let channel=0;channel<3;channel++){
    const split=channel===0?fringe:channel===2?-fringe:0;
    const color=sample(sx+nx*split,sy+ny*split,channel);
    pixels[i+channel]=color*.95*(1-spec)+255*spec;
   }
   pixels[i+3]=Math.round(clamp(distance+.7,0,1)*255);
  }
  out.putImageData(frame,0,0);button.setAttribute('data-liquid-ready','');
 }
 function discover(){
  for(const button of login.querySelectorAll('.lisa-login-card')){
   if(entries.has(button))continue;
   const canvas=document.createElement('canvas');canvas.className='lisa-optical-surface';canvas.setAttribute('aria-hidden','true');button.prepend(canvas);entries.set(button,canvas);
   button.addEventListener('pointermove',e=>{const r=button.getBoundingClientRect();button.style.setProperty('--liquid-x',((e.clientX-r.left)/r.width*100)+'%');button.style.setProperty('--liquid-y',((e.clientY-r.top)/r.height*100)+'%')},{passive:true});
   button.addEventListener('pointerleave',()=>{button.style.removeProperty('--liquid-x');button.style.removeProperty('--liquid-y')});
   resize.observe(button);
  }
 }
 function draw(){
  raf=0;if(!root.isConnected||login.hidden||reduced.matches||!img.complete||!img.naturalWidth)return;
  const base=login.getBoundingClientRect();if(base.width<1||base.height<1)return;
  try{
   if(!sourceData||viewW!==base.width||viewH!==base.height)background(base.width,base.height);
   for(const [button,canvas] of entries)if(button.getClientRects().length)render(button,canvas,base);
  }catch(_){for(const [button]of entries)button.removeAttribute('data-liquid-ready')}
 }
 function schedule(){if(!raf)raf=requestAnimationFrame(draw)}
 const resize=new ResizeObserver(schedule);resize.observe(login);
 const observer=new MutationObserver(()=>{discover();schedule()});observer.observe(login,{childList:true,subtree:true,attributes:true,attributeFilter:['hidden','data-login-busy']});
 reduced.addEventListener?.('change',schedule);window.addEventListener('resize',schedule,{passive:true});login.addEventListener('scroll',schedule,{passive:true});
 img.onload=schedule;img.src='/cdn/shop/files/wallpaper-abastece-componentes-min.jpg?v=1792770242';
 discover();schedule();
 document.addEventListener('shopify:section:unload',e=>{if(e.detail?.sectionId!=='vg-native')return;observer.disconnect();resize.disconnect();cancelAnimationFrame(raf);window.removeEventListener('resize',schedule)},{once:true});
})();


;

// Safari: manter o wallpaper na área visível ao mudar barras, teclado e orientação.
(()=>{
 const root=document.getElementById('lisa-app-vg-native');
 const login=root?.querySelector('[data-login]');if(!login)return;
 const viewport=window.visualViewport;let frame=0;
 const iphone=/iPhone|iPod/.test(navigator.userAgent);
 if(iphone){
  let meta=document.querySelector('meta[name=viewport]');
  if(!meta){meta=document.createElement('meta');meta.name='viewport';meta.content='width=device-width, initial-scale=1';document.head.appendChild(meta)}
  const content=meta.content||'width=device-width, initial-scale=1';
  meta.content=/viewport-fit\s*=/i.test(content)?content.replace(/viewport-fit\s*=\s*[^,;\s]+/i,'viewport-fit=cover'):content+', viewport-fit=cover';
  login.classList.add('lisa-iphone-login');
 }
 function update(){
  frame=0;
  if(iphone){login.style.removeProperty('--lisa-login-height');login.style.removeProperty('--lisa-login-top');return}
  if(viewport&&Math.abs(viewport.scale-1)>.01)return;
  const height=viewport?.height||window.innerHeight;
  if(height>0)login.style.setProperty('--lisa-login-height',height+'px');
  login.style.setProperty('--lisa-login-top',(viewport?.offsetTop||0)+'px');
 }
 function schedule(){if(!frame)frame=requestAnimationFrame(update)}
 update();window.addEventListener('resize',schedule,{passive:true});window.addEventListener('pageshow',schedule);
 viewport?.addEventListener('resize',schedule,{passive:true});viewport?.addEventListener('scroll',schedule,{passive:true});
 function unload(e){
  if(e.detail?.sectionId!=='vg-native')return;
  cancelAnimationFrame(frame);window.removeEventListener('resize',schedule);window.removeEventListener('pageshow',schedule);
  viewport?.removeEventListener('resize',schedule);viewport?.removeEventListener('scroll',schedule);
  document.removeEventListener('shopify:section:unload',unload);
 }
 document.addEventListener('shopify:section:unload',unload);
})();

;


{const initLisaCteModule=function(){'use strict';let lisa2Attempts=0;function initLisa2(){const panel=document.getElementById('lisa-app-vg-native')||document.querySelector('.lisa-app[data-worker]');const openBtn=panel?panel.querySelector('[data-cte-open]'):null;if(!panel||!openBtn){if(lisa2Attempts++<200)setTimeout(initLisa2,100);return}const previousWrap=document.getElementById('lisacte-vg-native');if(previousWrap&&panel.__lisaTrackingOpen&&previousWrap.dataset.lisaCttVersion==='20261105.api-duplicate-tickets-v1'&&previousWrap.querySelector('[data-cte-sort="previsao_ctt"]'))return;panel.__lisaTrackingSysempDispose?.();panel.__lisaTrackingSummaryDispose?.();if(previousWrap)previousWrap.remove();const wrap=document.createElement('div');wrap.id='lisacte-vg-native';wrap.dataset.lisaCttVersion='20261105.api-duplicate-tickets-v1';wrap.innerHTML=`\n    <div class="cte-modal" data-cte-modal hidden>\n      <div class="cte-card">\n        <button type="button" class="cte-close" data-cte-close aria-label="Fechar">×</button>\n        <div class="cte-loading" data-cte-loading><div class="cte-spinner"></div><strong>Carregando rastreamento...</strong><span>Buscando Monitor de Pedido e dados de rastreamento salvos.</span></div>\n        <div class="cte-head" data-cte-content hidden>\n          <div><h3>Rastreamento</h3><span class="cte-head-meta" data-cte-meta>Monitor de Pedido × Rastreamento</span></div>\n          <div class="cte-head-actions"><input type="search" class="cte-note-search" data-cte-note-search inputmode="numeric" autocomplete="off" placeholder="Buscar nota" aria-label="Buscar número da nota">\n            <div class="cte-prazo-wrap" data-cte-prazo-wrap>\n              <button type="button" class="cte-prazo-toggle" data-cte-prazo-toggle aria-expanded="false" aria-controls="cte-prazo-menu-vg-native" title="Filtrar por Data Finalizado">Prazo <span aria-hidden="true">▾</span></button>\n              <div class="cte-prazo-menu" id="cte-prazo-menu-vg-native" data-cte-prazo-menu hidden>\n                <strong>Filtrar por Data Finalizado</strong>\n                <div class="cte-prazo-grid">\n                  <label>Data de<input type="date" data-cte-prazo-de aria-label="Data finalizado inicial"></label>\n                  <label>Data até<input type="date" data-cte-prazo-ate aria-label="Data finalizado final"></label>\n                  <label>Hora de<input type="time" data-cte-prazo-hora-de aria-label="Horário inicial (opcional)"></label>\n                  <label>Hora até<input type="time" data-cte-prazo-hora-ate aria-label="Horário final (opcional)"></label>\n                </div>\n                <div class="cte-prazo-help">Horários opcionais. Vazios = dia inteiro. Para vários dias, a faixa de horas se aplica a cada dia.</div>\n                <div class="cte-prazo-error" data-cte-prazo-error role="alert" hidden></div>\n                <div class="cte-prazo-footer"><button type="button" data-cte-prazo-limpar>Limpar</button><button type="button" data-cte-prazo-aplicar>Aplicar</button></div>\n              </div>\n            </div><button type="button" class="cte-action" data-cte-jt-reindex>Atualizar</button><button type="button" class="cte-action" data-rtrack-reports-open>Relatórios</button><button type="button" class="cte-action carrier-files-open" data-transportadoras-open>Transportadoras</button><button type="button" class="cte-action" data-cte-copy disabled>Copiar</button><button type="button" class="cte-action" data-cte-download disabled>Baixar</button></div>\n        </div>\n        <div class="cte-summary" data-cte-content role="group" aria-label="Indicadores de rastreamento. Deslize para o lado para ver mais categorias." hidden>\n          <button type="button" class="cte-summary-btn is-active" data-cte-view="all"><span>Todos</span><strong data-cte-count-all>0</strong></button>\n          <button type="button" class="cte-summary-btn" data-cte-view="without"><span>Sem CTe</span><strong data-cte-count-without>0</strong></button>\n          \n          <button type="button" class="cte-summary-btn" data-cte-view="transit"><span>Em trajeto</span><strong data-cte-count-transit>0</strong></button>\n          <button type="button" class="cte-summary-btn" data-cte-view="nostatus"><span>Sem status</span><strong data-cte-count-nostatus>0</strong></button>\n          <button type="button" class="cte-summary-btn" data-cte-view="awaiting"><span>AGUARD. RETIRADA</span><strong data-cte-count-awaiting>0</strong></button>\n          <button type="button" class="cte-summary-btn" data-cte-view="returning"><span>DEVOLUÇÃO</span><strong data-cte-count-returning>0</strong></button>\n          <button type="button" class="cte-summary-btn" data-cte-view="overdue"><span>Atrasado</span><strong data-cte-count-overdue>0</strong></button>\n          <button type="button" class="cte-summary-btn cte-auto-risk-card" data-cte-view="autorisk" title="Alerta preventivo: cruza previsão futura com a etapa e ocorrências do transporte. Não é previsão estatística de atraso."><span>ATRAS.AUTO</span><strong data-cte-count-autorisk>0</strong></button>\n          <button type="button" class="cte-summary-btn" data-cte-view="occurrence"><span>Ocorrência</span><strong data-cte-count-occurrence>0</strong></button>\n          <button type="button" class="cte-summary-btn" data-cte-view="delivered"><span>Entregue</span><strong data-cte-count-delivered>0</strong></button>\n          <button type="button" class="cte-summary-btn" data-cte-view="ticket"><span>Ticket</span><strong data-cte-count-ticket>0</strong></button>\n        </div>\n        <div class="cte-campaigns" data-cte-campaigns data-cte-campaign-mode="without" data-cte-content hidden></div>\n        <div class="cte-status" data-cte-status data-cte-content hidden>Carregando relatório...</div>\n        <div class="cte-auto-risk-explain" data-cte-auto-risk-explain hidden>⚠ Alerta preventivo: prazo de entrega ainda NÃO vencido, mas a etapa ou a ocorrência informada pela transportadora sugere risco. <strong>Não é uma probabilidade estatística.</strong> Sem previsão, já entregue, devolvido e prazo vencido não entram. Usa os dados do snapshot salvo (sem novas buscas).</div>\n        <div class="cte-table-wrap" data-cte-content hidden>\n          <table class="cte-table">\n            <thead><tr>\n              <th data-cte-contact-head data-cte-sort="contact_text" data-sort-type="text" hidden><button type="button" class="cte-sort-btn">Contato <span class="cte-sort-arrows">↑↓</span></button><input class="cte-filter" data-cte-filter="contact_text" type="text" placeholder="Filtrar"></th>\n              <th data-cte-sort="data_api" data-sort-type="date"><button type="button" class="cte-sort-btn">Data API <span class="cte-sort-arrows">↑↓</span></button><input class="cte-filter" data-cte-filter="data_api" type="date"></th>\n              <th data-cte-sort="nota" data-sort-type="number"><button type="button" class="cte-sort-btn">Nota Fiscal <span class="cte-sort-arrows">↑↓</span></button><input class="cte-filter" data-cte-filter="nota" type="text" inputmode="numeric" placeholder="Filtrar"></th>\n              <th data-cte-sort="destinatario" data-sort-type="text"><button type="button" class="cte-sort-btn">Destinatário <span class="cte-sort-arrows">↑↓</span></button><input class="cte-filter" data-cte-filter="destinatario" type="text" placeholder="Filtrar"></th>\n              <th data-cte-sort="campanha" data-sort-type="text"><button type="button" class="cte-sort-btn">Campanha <span class="cte-sort-arrows">↑↓</span></button><select class="cte-filter" data-cte-filter="campanha"><option value="">Todos</option></select></th>\n              <th data-cte-sort="status" data-sort-type="text"><button type="button" class="cte-sort-btn">Status LISA <span class="cte-sort-arrows">↑↓</span></button><select class="cte-filter" data-cte-filter="status"><option value="">Todos</option></select></th>\n              <th data-cte-sort="status_tr" data-sort-type="text"><button type="button" class="cte-sort-btn">Status Transp. <span class="cte-sort-arrows">↑↓</span></button><select class="cte-filter" data-cte-filter="status_tr"><option value="">Todos</option></select></th>\n              <th data-cte-sort="status_source" data-sort-type="text"><button type="button" class="cte-sort-btn">Origem status <span class="cte-sort-arrows">↑↓</span></button><select class="cte-filter" data-cte-filter="status_source"><option value="">Todos</option></select></th>\n              <th data-cte-sort="previsao_entrega_tr" data-sort-type="date"><button type="button" class="cte-sort-btn">Previsão de entrega <span class="cte-sort-arrows">↑↓</span></button><input class="cte-filter" data-cte-filter="previsao_entrega_tr" type="date"></th>\n              <th data-cte-sort="previsao_ctt" data-sort-type="date"><button type="button" class="cte-sort-btn">Previsão CTT <span class="cte-sort-arrows">↑↓</span></button><input class="cte-filter" data-cte-filter="previsao_ctt" type="date"></th>\n              <th data-cte-overdue-days-head data-cte-sort="dias_atraso" data-sort-type="number" hidden><button type="button" class="cte-sort-btn">Dias atraso <span class="cte-sort-arrows">↑↓</span></button></th>\n              <th data-cte-auto-risk-head data-cte-sort="risco_auto_peso" data-sort-type="number" hidden><button type="button" class="cte-sort-btn">Risco antecipado <span class="cte-sort-arrows">↑↓</span></button><select class="cte-filter" data-cte-filter="risco_auto_nivel"><option value="">Todos</option><option value="ALTO">Alto</option><option value="ATENÇÃO">Atenção</option></select></th>\n              <th data-cte-sort="transportadora" data-sort-type="text"><button type="button" class="cte-sort-btn">Transportadora <span class="cte-sort-arrows">↑↓</span></button><select class="cte-filter" data-cte-filter="transportadora"><option value="">Todos</option></select></th>\n              <th data-cte-sort="cte" data-sort-type="text"><button type="button" class="cte-sort-btn">CTE <span class="cte-sort-arrows">↑↓</span></button><input class="cte-filter" data-cte-filter="cte" type="text" placeholder="Filtrar"></th>\n              <th data-cte-sort="data_romaneio" data-sort-type="date"><button type="button" class="cte-sort-btn">Data Romaneio <span class="cte-sort-arrows">↑↓</span></button><input class="cte-filter" data-cte-filter="data_romaneio" type="date"></th>\n              <th data-cte-sort="data_finalizado" data-sort-type="date"><button type="button" class="cte-sort-btn">Data Finalizado <span class="cte-sort-arrows">↑↓</span></button><input class="cte-filter" data-cte-filter="data_finalizado" type="date"></th>\n              <th data-cte-sort="descricao_ticket" data-sort-type="text"><button type="button" class="cte-sort-btn">Ticket <span class="cte-sort-arrows">↑↓</span></button><input class="cte-filter" data-cte-filter="descricao_ticket" type="text" placeholder="Filtrar"></th>\n              <th data-cte-ticket-updates-head data-cte-sort="ticket_updates_text" data-sort-type="text" hidden><button type="button" class="cte-sort-btn">Atualização de ticket <span class="cte-sort-arrows">↑↓</span></button><input class="cte-filter" data-cte-filter="ticket_updates_text" type="text" placeholder="Filtrar"></th>\n            </tr></thead>\n            <tbody data-cte-body></tbody>\n          </table>\n          <div class="cte-empty" data-cte-empty hidden>Nenhuma nota encontrada.</div>\n        </div>\n        <div class="cte-footer" data-cte-content hidden><span data-cte-result-count>0 notas</span><div class="cte-pages"><button class="cte-page-btn" type="button" data-cte-prev>Anterior</button><span data-cte-page>Página 1 de 1</span><button class="cte-page-btn" type="button" data-cte-next>Próxima</button></div></div>\n      </div>\n    </div>`;document.body.appendChild(wrap);const popupPrazoPortal=wrap.querySelector('[data-cte-prazo-menu]');if(popupPrazoPortal)wrap.appendChild(popupPrazoPortal);const $=s=>wrap.querySelector(s),$$=s=>[...wrap.querySelectorAll(s)];const modal=$('[data-cte-modal]'),body=$('[data-cte-body]'),empty=$('[data-cte-empty]'),status=$('[data-cte-status]');const copyBtn=$('[data-cte-copy]'),downloadBtn=$('[data-cte-download]'),noteSearch=$('[data-cte-note-search]'),loading=$('[data-cte-loading]'),contentParts=$$('[data-cte-content]');const prazoWrap=$('[data-cte-prazo-wrap]'),prazoToggle=$('[data-cte-prazo-toggle]'),prazoMenu=$('[data-cte-prazo-menu]');const prazoInputs={de:$('[data-cte-prazo-de]'),ate:$('[data-cte-prazo-ate]'),horaDe:$('[data-cte-prazo-hora-de]'),horaAte:$('[data-cte-prazo-hora-ate]')};let prazoAplicado={de:'',ate:'',horaDe:'',horaAte:''};let resumoBaseStatus='',resumoBaseCounts=null;const oldTicketPencil=document.getElementById('lisacte-ticket-pencil-vg-native');if(oldTicketPencil)oldTicketPencil.remove();const ticketPencil=document.createElement('div');ticketPencil.id='lisacte-ticket-pencil-vg-native';ticketPencil.className='cte-ticket-pencil';ticketPencil.textContent='✎';ticketPencil.hidden=true;document.body.appendChild(ticketPencil);function hideTicketPencil(){ticketPencil.hidden=true}function showLoading(){if(loading)loading.hidden=false;for(const el of contentParts)el.hidden=true}function showContent(){if(loading)loading.hidden=true;for(const el of contentParts)el.hidden=false}let allItems=[],ticketItems=[],ticketByNote=new Map,filtered=[],view='all',sort={key:'data_api',type:'date',dir:'desc'},page=1,loadedSnapshotAt='',snapshotLoadPromise=null,preloadedSnapshotData=null,preloadRetryTimer=null,snapshotLoadError='',snapshotRefreshRetries=0;const pageSize=100;const esc=s=>String(s??'').replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));const norm=s=>String(s??'').normalize('NFD').replace(/[\u0300-\u036f]/g,'').toUpperCase().trim();const worker=()=>String(panel.dataset.worker||'').replace(/\/$/,'');const token=()=>sessionStorage.getItem('lisa_panel_token')||'';
const jtReindexButton=wrap.querySelector('[data-cte-jt-reindex]');
let trackingUpdateBusy=false;
function trackingUpdateProgress(value,label){
jtReindexButton.style.setProperty('--tracking-update-progress',value+'%');
jtReindexButton.style.setProperty('background','linear-gradient(to right,#78cf96 0%,#78cf96 '+value+'%,#ffffff '+value+'%,#ffffff 100%)','important');
jtReindexButton.style.setProperty('opacity','1','important');jtReindexButton.style.setProperty('color','#174c2c','important');
jtReindexButton.textContent=label;jtReindexButton.setAttribute('aria-label',label);
}
jtReindexButton?.addEventListener('click',async()=>{if(trackingUpdateBusy)return;trackingUpdateBusy=true;jtReindexButton.disabled=true;jtReindexButton.classList.add('is-updating');try{trackingUpdateProgress(1,'Atualizando 1%');const keys=['ALFA','BRASPRESS','CONTINENTAL','CORREIOS_VIPP','FAVORITA','JAMEF','J_T','LATAM','PATRUS','TOTAL_EXPRESS','TNT_FEDEX','RODOMAX'];const result=await panel.__lisaPreparedPublish(keys,(p,label)=>{trackingUpdateProgress(p,'Atualizando '+p+'%');status.textContent=label;});trackingUpdateProgress(100,result.failures?.length?'Concl. parc.':'Concluído');if(result.failures?.length){status.textContent=result.failures.join('\n');status.classList.add('is-error');}else status.classList.remove('is-error');}catch(e){status.textContent=String(e.message||e);status.classList.add('is-error');trackingUpdateProgress(0,'Tentar novamente');}finally{trackingUpdateBusy=false;jtReindexButton.disabled=false;}});

function sideCount(key,value){const el=panel.querySelector(`[data-tracking-count="${key}"]`);if(el)el.textContent=Number(value||0).toLocaleString('pt-BR')}function syncSideCounts(c){for(const[k,v]of Object.entries(c||{}))sideCount(k,v)}function dateKey(v){const s=String(v||'').trim();let m=s.match(/(\d{2})\/(\d{2})\/(\d{4})/);if(m)return`${m[3]}-${m[2]}-${m[1]}`;m=s.match(/(\d{4})-(\d{2})-(\d{2})/);return m?`${m[1]}-${m[2]}-${m[3]}`:''}function dateValue(v){const k=dateKey(v);return k?Date.parse(k+'T00:00:00'):0}function finalizadoMs(v){const s=String(v||'').trim();if(!s)return 0;let m=s.match(/(\d{2})\/(\d{2})\/(\d{4})(?:[^\d]+(\d{1,2}):(\d{2})(?::(\d{2}))?)?/);if(m){const hh=String(m[4]||'00').padStart(2,'0'),mm=m[5]||'00',ss=m[6]||'00';const t=Date.parse(`${m[3]}-${m[2]}-${m[1]}T${hh}:${mm}:${ss}-03:00`);return Number.isNaN(t)?0:t}const t=Date.parse(s);return Number.isNaN(t)?0:t}const prazoSaoPauloFormatter=new Intl.DateTimeFormat('en-GB',{timeZone:'America/Sao_Paulo',year:'numeric',month:'2-digit',day:'2-digit',hour:'2-digit',minute:'2-digit',hourCycle:'h23'});function prazoDataHora(valor){const s=String(valor??'').trim();if(!s)return null;let m=s.match(/^(\d{1,2})[\/.-](\d{1,2})[\/.-](\d{4})(?:[^\d]+(\d{1,2}):(\d{2}))?/);if(m){const ano=+m[3],mes=+m[2],dia=+m[1],hora=+(m[4]||0),min=+(m[5]||0);if(!prazoDataValida(ano,mes,dia,hora,min))return null;return{data:`${ano}-${String(mes).padStart(2,'0')}-${String(dia).padStart(2,'0')}`,minutos:hora*60+min}}m=s.match(/^(\d{4})-(\d{2})-(\d{2})(?:[T\s]+(\d{1,2}):(\d{2}))?/);if(!m)return null;const ano=+m[1],mes=+m[2],dia=+m[3],hora=+(m[4]||0),min=+(m[5]||0);if(!prazoDataValida(ano,mes,dia,hora,min))return null;if(/[Zz]$|[+\-]\d{2}:?\d{2}$/.test(s)&&m[4]!==undefined){const dt=new Date(s);if(!Number.isNaN(dt.getTime())){const p=Object.fromEntries(prazoSaoPauloFormatter.formatToParts(dt).filter(v=>v.type!=='literal').map(v=>[v.type,v.value]));return{data:`${p.year}-${p.month}-${p.day}`,minutos:Number(p.hour)*60+Number(p.minute)}}}return{data:`${ano}-${String(mes).padStart(2,'0')}-${String(dia).padStart(2,'0')}`,minutos:hora*60+min}}function prazoDataValida(a,m,d,h,min){return m>=1&&m<=12&&d>=1&&d<=31&&h>=0&&h<=23&&min>=0&&min<=59&&new Date(Date.UTC(a,m-1,d)).getUTCDate()===d}function prazoAtivo(){return!!(prazoAplicado.de||prazoAplicado.ate||prazoAplicado.horaDe||prazoAplicado.horaAte)}function prazoCombina(x){if(!prazoAtivo())return true;const dt=prazoDataHora(x?.data_finalizado);if(!dt)return false;if(prazoAplicado.de&&dt.data<prazoAplicado.de)return false;if(prazoAplicado.ate&&dt.data>prazoAplicado.ate)return false;if(prazoAplicado.horaDe&&dt.minutos<Number(prazoAplicado.horaDe.slice(0,2))*60+Number(prazoAplicado.horaDe.slice(3,5)))return false;if(prazoAplicado.horaAte&&dt.minutos>Number(prazoAplicado.horaAte.slice(0,2))*60+Number(prazoAplicado.horaAte.slice(3,5)))return false;return true}function prazoBase(){return prazoAtivo()?allItems.filter(prazoCombina):allItems}function prazoTickets(){return prazoAtivo()?ticketItems.filter(prazoCombina):ticketItems}function prazoContagens(){const a=prazoBase(),t=prazoTickets();const counts={all:a.length,without:0,late:0,transit:0,nostatus:0,awaiting:0,returning:0,overdue:0,autorisk:0,occurrence:0,delivered:0,ticket:t.length};for(const x of a){if(isCteWithout(x))counts.without++;if(isCteLate(x))counts.late++;if(isCteTransit(x))counts.transit++;if(isCteNoStatus(x))counts.nostatus++;if(isCteAwaitingPickup(x))counts.awaiting++;if(isCteReturning(x)||isCteReturned(x))counts.returning++;if(isCteOverdue(x))counts.overdue++;if(x.risco_auto_nivel)counts.autorisk++;if(isCteOccurrence(x))counts.occurrence++;if(isCteDelivered(x))counts.delivered++}return counts}function prazoAtualizarCards(){if(!prazoAtivo()){if(resumoBaseStatus)status.textContent=resumoBaseStatus;for(const[key,val]of Object.entries(resumoBaseCounts||{})){const el=$(`[data-cte-count-${key}]`);if(el)el.textContent=Number(val||0).toLocaleString('pt-BR')}return}const counts=prazoContagens();for(const[key,val]of Object.entries(counts)){const el=$(`[data-cte-count-${key}]`);if(el)el.textContent=val.toLocaleString('pt-BR')}status.textContent=`Prazo (Data Finalizado): ${counts.all.toLocaleString('pt-BR')} notas · ${counts.without} sem CTE · ${counts.transit} em trajeto · ${counts.nostatus} sem status · ${counts.awaiting} aguardando retirada · ${counts.returning} devolução · ${counts.overdue} atrasados · ${counts.autorisk} atras.auto · ${counts.occurrence} ocorrências · ${counts.delivered} entregues · ${counts.ticket} tickets`}function prazoRotulo(){const de=prazoAplicado.de,ate=prazoAplicado.ate,hd=prazoAplicado.horaDe,ha=prazoAplicado.horaAte;return[de?de===ate?de:`${de} a ${ate||'...'}`:ate?`até ${ate}`:'',hd||ha?`${hd||'00:00'}–${ha||'23:59'}`:''].filter(Boolean).join(' · ')}function prazoAtualizarBotao(){prazoToggle.classList.toggle('is-active',prazoAtivo());prazoToggle.innerHTML=prazoAtivo()?'Prazo ✓ <span aria-hidden="true">▾</span>':'Prazo <span aria-hidden="true">▾</span>';prazoToggle.title=prazoAtivo()?`Data Finalizado: ${prazoRotulo()}`:'Filtrar por Data Finalizado'}function prazoFechar(){prazoMenu.hidden=true;prazoToggle.setAttribute('aria-expanded','false')}function prazoPosicionar(){const r=prazoToggle.getBoundingClientRect(),largura=Math.min(318,window.innerWidth-24),dist=10;const left=Math.max(12,Math.min(r.left,window.innerWidth-largura-12));prazoMenu.style.left=left+'px';prazoMenu.style.width=largura+'px';const menuAltura=Math.min(prazoMenu.scrollHeight||280,window.innerHeight-24);const abaixo=window.innerHeight-r.bottom-12,acima=r.top-12;const colocarAcima=abaixo<menuAltura&&acima>abaixo;const top=colocarAcima?Math.max(12,r.top-menuAltura-dist):Math.min(window.innerHeight-menuAltura-12,r.bottom+dist);prazoMenu.style.top=Math.max(12,top)+'px';prazoMenu.style.maxHeight=Math.max(100,window.innerHeight-24)+'px'}function resetPrazo(){for(const input of Object.values(prazoInputs))input.value='';prazoAplicado={de:'',ate:'',horaDe:'',horaAte:''};const error=$('[data-cte-prazo-error]');error.hidden=true;error.textContent='';prazoFechar();prazoAtualizarBotao()}function prazoReaplicar(){page=1;prazoAtualizarBotao();prazoAtualizarCards();renderCampaignButtons();apply()}function prazoAplicar(){const f=Object.fromEntries(Object.entries(prazoInputs).map(([k,v])=>[k,v.value||'']));if(f.de&&!f.ate)f.ate=f.de;if(f.ate&&!f.de)f.de=f.ate;const err=$('[data-cte-prazo-error]');err.hidden=true;err.textContent='';if(f.de&&f.ate&&f.de>f.ate){err.textContent='A data inicial deve ser anterior ou igual à data final.';err.hidden=false;return}if(f.horaDe&&f.horaAte&&f.horaDe>f.horaAte){err.textContent='O horário inicial deve ser anterior ou igual ao horário final.';err.hidden=false;return}prazoAplicado=f;prazoInputs.de.value=f.de;prazoInputs.ate.value=f.ate;prazoFechar();prazoReaplicar()}function isCteExcludedMarketplace(x){const c=norm(x?.campanha);return c.includes('MERCADO LIVRE')||c.includes('MERCADO ENVIOS')||c.includes('SHOPEE')}function isCteWithout(x){return norm(x?.status)==='FINALIZADO'&&!x?.has_cte&&!isCteExcludedMarketplace(x)}function isCteLate(x){const t=finalizadoMs(x?.data_finalizado);return isCteWithout(x)&&t>0&&Date.now()-t>864e5}function numValue(v){const d=String(v??'').replace(/\D+/g,'');return d?Number(d):0}function sortValue(v,type){if(type==='date')return dateValue(v);if(type==='number')return numValue(v);return norm(v)}function ticketUpdateDate(value){const s=String(value||'').trim();let m=s.match(/^(\d{4})-(\d{2})-(\d{2})/);if(m)return`${m[3]}/${m[2]}/${m[1]}`;m=s.match(/^(\d{2})[\/.-](\d{2})[\/.-](\d{4})/);return m?`${m[1]}/${m[2]}/${m[3]}`:s}function ticketNoteKey(v){return String(v??'').replace(/\D+/g,'').replace(/^0+(?=\d)/,'')}function ticketUpdatesList(x){return(Array.isArray(x?.ticket_updates)?x.ticket_updates:[]).filter(u=>String(u?.descricao||'').trim())}function ticketUpdatesText(x){return ticketUpdatesList(x).map(u=>`${ticketUpdateDate(u?.data_atualizacao||u?.created_at)} - ${String(u?.descricao||'').trim()}`).join(' | ')}function ticketUpdatesHtml(x){const list=ticketUpdatesList(x);if(!list.length)return'-';return`<div class="cte-ticket-updates-list">${list.map(u=>`<div class="cte-ticket-update-entry"><span>${esc(ticketUpdateDate(u?.data_atualizacao||u?.created_at)||'-')}</span><span>${esc(String(u?.descricao||'').trim())}</span></div>`).join('')}</div>`}let awaitingContacts=new Map,awaitingContactsLoaded=false,awaitingContactsPromise=null,activeContactItem=null;function contactIdentity(x){const nota=String(x?.nota||'').replace(/\D+/g,'').replace(/^0+(?=\d)/,''),cte=String(x?.cte||'').toUpperCase().replace(/[^A-Z0-9]+/g,'');if(nota&&cte)return`NF:${nota}|CTE:${cte}`;if(nota)return'NF:'+nota;if(cte)return'CTE:'+cte;const pedido=String(x?.pedido_cliente||x?.pedido||'').replace(/\D+/g,'').replace(/^0+(?=\d)/,'');if(pedido)return'PED:'+pedido;return'ROW:'+norm([x?.destinatario,x?.campanha,x?.data_api].join('|')).replace(/[^A-Z0-9|]+/g,'-')}function contactList(x){return(Array.isArray(x?.contact_updates)?x.contact_updates:[]).filter(u=>String(u?.descricao||'').trim())}function contactText(x){return contactList(x).map(u=>`${ticketUpdateDate(u?.data||u?.created_at)} - ${String(u?.descricao||'').trim()}`).join(' | ')}function contactHtml(x){const list=contactList(x);if(!list.length)return'<span class="cte-contact-empty">-</span>';return`<div class="cte-contact-list">${list.map(u=>`<div class="cte-contact-entry"><span>${esc(ticketUpdateDate(u?.data||u?.created_at)||'-')}</span><span>${esc(String(u?.descricao||'').trim())}</span></div>`).join('')}</div>`}function applyAwaitingContactsToItems(){for(const x of allItems){const key=contactIdentity(x),rec=awaitingContacts.get(key);x.contact_key=key;x.contact_updates=Array.isArray(rec?.updates)?rec.updates:[];x.contact_text=contactText(x)}}async function loadAwaitingContacts(force=false){if(awaitingContactsPromise)return awaitingContactsPromise;if(awaitingContactsLoaded&&!force)return awaitingContacts;const tk=token();if(!tk)return awaitingContacts;awaitingContactsPromise=(async()=>{try{const r=await fetch(worker()+'/tracking/awaiting-contacts',{headers:{Authorization:'Bearer '+tk},cache:'no-store'}),d=await r.json().catch(()=>({}));if(r.status===401)throw new Error(d.error||'Sessão expirada.');if(!r.ok)throw new Error(d.detail||d.error||'Não foi possível carregar os contatos.');awaitingContacts=new Map((Array.isArray(d?.items)?d.items:[]).map(x=>[String(x?.key||''),x]).filter(x=>x[0]));awaitingContactsLoaded=true;applyAwaitingContactsToItems();if(view==='awaiting')apply();return awaitingContacts}catch(e){console.debug('Contatos aguardando retirada:',e);return awaitingContacts}finally{awaitingContactsPromise=null}})();return awaitingContactsPromise}function contactToday(){const p=new Intl.DateTimeFormat('en-US',{timeZone:'America/Sao_Paulo',year:'numeric',month:'2-digit',day:'2-digit'}).formatToParts(new Date).reduce((a,v)=>(a[v.type]=v.value,a),{});return`${p.year}-${p.month}-${p.day}`}function ensureContactModal(){let w=document.getElementById('lisacte-contact-vg-native');if(w)return w;w=document.createElement('div');w.id='lisacte-contact-vg-native';w.innerHTML=`<div class="cte-contact-modal" data-cte-contact-modal hidden><div class="cte-contact-card"><button type="button" class="cte-contact-close" data-cte-contact-close aria-label="Fechar">×</button><div class="cte-contact-head"><h3>Adicionar contato</h3><span data-cte-contact-meta></span></div><div class="cte-contact-form"><label><span>Data</span><input type="date" data-cte-contact-date></label><label class="is-wide"><span>Descrição</span><textarea data-cte-contact-description placeholder="Descreva o contato realizado ou a orientação passada."></textarea></label></div><div class="cte-contact-history" data-cte-contact-history hidden><strong>Histórico de contatos</strong><div data-cte-contact-history-list></div></div><div class="cte-contact-msg" data-cte-contact-msg></div><div class="cte-contact-actions"><button type="button" class="cte-contact-cancel" data-cte-contact-cancel>Cancelar</button><button type="button" class="cte-contact-save" data-cte-contact-save>Salvar</button></div></div></div>`;document.body.appendChild(w);const m=w.querySelector('[data-cte-contact-modal]'),close=()=>{m.hidden=true;activeContactItem=null;w.querySelector('[data-cte-contact-description]').value='';w.querySelector('[data-cte-contact-msg]').textContent=''};w.querySelector('[data-cte-contact-close]').onclick=close;w.querySelector('[data-cte-contact-cancel]').onclick=close;m.addEventListener('click',e=>{if(e.target===m)close()});w.querySelector('[data-cte-contact-save]').addEventListener('click',async()=>{const item=activeContactItem;if(!item)return;const date=String(w.querySelector('[data-cte-contact-date]').value||''),descricao=String(w.querySelector('[data-cte-contact-description]').value||'').trim(),msg=w.querySelector('[data-cte-contact-msg]'),save=w.querySelector('[data-cte-contact-save]');msg.textContent='';msg.className='cte-contact-msg';if(!/^\d{4}-\d{2}-\d{2}$/.test(date)){msg.textContent='Informe a data do contato.';msg.classList.add('is-error');return}if(!descricao){msg.textContent='Digite a descrição do contato.';msg.classList.add('is-error');return}const old=save.textContent;save.disabled=true;save.textContent='Salvando...';try{const payload={key:contactIdentity(item),nota:String(item?.nota||''),cte:String(item?.cte||''),destinatario:String(item?.destinatario||''),campanha:String(item?.campanha||''),data,descricao};const r=await fetch(worker()+'/tracking/awaiting-contacts',{method:'POST',headers:{'Content-Type':'application/json','Authorization':'Bearer '+token()},body:JSON.stringify(payload)}),d=await r.json().catch(()=>({}));if(r.status===401)throw new Error(d.error||'Sessão expirada.');if(!r.ok)throw new Error(d.detail||d.error||'Não foi possível salvar o contato.');const rec=d?.item;if(rec?.key)awaitingContacts.set(String(rec.key),rec);awaitingContactsLoaded=true;applyAwaitingContactsToItems();apply();msg.textContent='Contato registrado com sucesso.';msg.classList.add('is-ok');setTimeout(close,420)}catch(e){msg.textContent=e.message||'Não foi possível salvar o contato.';msg.classList.add('is-error')}finally{save.disabled=false;save.textContent=old}});document.addEventListener('keydown',e=>{if(e.key==='Escape'&&!m.hidden)close()});return w}function openContactModal(item){const w=ensureContactModal(),m=w.querySelector('[data-cte-contact-modal]'),list=contactList(item);activeContactItem=item;w.querySelector('[data-cte-contact-date]').value=contactToday();w.querySelector('[data-cte-contact-description]').value='';w.querySelector('[data-cte-contact-msg]').textContent='';w.querySelector('[data-cte-contact-msg]').className='cte-contact-msg';w.querySelector('[data-cte-contact-meta]').textContent=`Nota ${item?.nota||'-'} · ${item?.destinatario||'-'}`;const hist=w.querySelector('[data-cte-contact-history]'),histList=w.querySelector('[data-cte-contact-history-list]');hist.hidden=!list.length;histList.innerHTML=list.map(u=>`<div class="cte-contact-history-entry"><span>${esc(ticketUpdateDate(u?.data||u?.created_at)||'-')}</span><span>${esc(String(u?.descricao||'').trim())}</span></div>`).join('');m.hidden=false;setTimeout(()=>w.querySelector('[data-cte-contact-description]').focus(),30)}function filterMatches(x,skipKey=''){const f=key=>key===skipKey?'':String($(`[data-cte-filter="${key}"]`)?.value||'');return(!f('risco_auto_nivel')||String(x.risco_auto_nivel||'')===f('risco_auto_nivel'))&&(!f('data_api')||dateKey(x.data_api)===f('data_api'))&&(!f('nota')||norm(x.nota).includes(norm(f('nota'))))&&(!f('destinatario')||norm(x.destinatario).includes(norm(f('destinatario'))))&&(!f('campanha')||String(x.campanha||'')===f('campanha'))&&(!f('status')||String(x.status||'')===f('status'))&&(!f('status_tr')||String(x.status_tr||'')===f('status_tr'))&&(!f('status_source')||String(x.status_source||'')===f('status_source'))&&(!f('previsao_entrega_tr')||dateKey(x.previsao_entrega_tr)===f('previsao_entrega_tr'))&&(!f('previsao_ctt')||dateKey(x.previsao_ctt||x.previsao_ctt_iso)===f('previsao_ctt'))&&(!f('transportadora')||String(x.transportadora||'')===f('transportadora'))&&(!f('cte')||norm(x.cte).includes(norm(f('cte'))))&&(!f('data_romaneio')||dateKey(x.data_romaneio)===f('data_romaneio'))&&(!f('data_finalizado')||dateKey(x.data_finalizado)===f('data_finalizado'))&&(!f('descricao_ticket')||norm(x.descricao_ticket).includes(norm(f('descricao_ticket'))))&&(!f('ticket_updates_text')||norm(x.ticket_updates_text).includes(norm(f('ticket_updates_text'))))&&(!f('contact_text')||norm(x.contact_text).includes(norm(f('contact_text'))))}function uniqueValues(key){const src=cteViewItems().filter(x=>filterMatches(x,key));return[...new Set(src.map(x=>String(x?.[key]||'').trim()).filter(Boolean))].sort((a,b)=>a.localeCompare(b,'pt-BR',{numeric:true,sensitivity:'base'}))}function fillSelect(key){const el=$(`[data-cte-filter="${key}"]`);if(!el||el.tagName!=='SELECT')return;const cur=el.value,vals=uniqueValues(key);el.innerHTML='<option value="">Todos</option>'+vals.map(v=>`<option value="${esc(v)}">${esc(v)}</option>`).join('');if(vals.includes(cur))el.value=cur;else if(cur)el.value=''}function refreshSelects(){for(const key of['campanha','status','status_tr','status_source','transportadora'])fillSelect(key)}function resetFilters(){for(const el of $$('.cte-filter'))el.value=''}function cteApiCategory(x){return String(x?.api_category||'').trim()}function cteUsesMarketplaceApi(x){return!x?.status_from_carrier_sheet&&(!!x?.marketplace_api||isCteExcludedMarketplace(x))}function cteDeliveredStatus(value){const v=norm(value);return!!v&&(v.includes('ENTREGUE')||v.includes('ENTREGA REALIZADA')||v.includes('MERCADORIA ENTREGUE')||v.includes('OBJETO ENTREGUE')||v.includes('ENTREGA EFETUADA')||v.includes('ENTREGA CONCLUIDA')||v==='RECEBIDO'||v.includes('RECEBIDO PELO DESTINATARIO'))}function isCteDelivered(x){if(cteDeliveredStatus(x?.status_tr))return true;if(cteUsesMarketplaceApi(x))return cteApiCategory(x)==='delivered';return false}function isCteAwaitingPickup(x){if(cteUsesMarketplaceApi(x))return cteApiCategory(x)==='awaiting_pickup';const v=norm(x?.status_tr);return!!x?.has_cte&&(v.includes('AGUARDANDO RETIRADA')||v.includes('RETIRADA NO ENDERECO INDICADO'))}function isCteReturned(x){if(cteUsesMarketplaceApi(x)&&cteApiCategory(x)==='returned')return true;const v=norm(x?.status_tr);return!!v&&(v.includes('DEVOLVIDO')||v.includes('DEVOLVIDA')||v.includes('ENTREGUE AO REMETENTE')||v.includes('DEVOLUCAO CONCLUIDA')||v.includes('DEVOLUCAO REALIZADA'))}function isCteReturning(x){if(cteUsesMarketplaceApi(x)&&cteApiCategory(x)==='returning')return true;const v=norm(x?.status_tr);return!!v&&!isCteReturned(x)&&(v.includes('DEVOLVENDO')||v.includes('DEVOLUCAO')||v.includes('EM ROTA DE DEVOLUCAO')||v.includes('EM PROCESSO DE DEVOLUCAO')||v.includes('EM TRANSFERENCIA DEVOLUCAO')||v.includes('AGUARDANDO FREQUENCIA DE DEVOLUCAO')||v.includes('EMBARQUE DE DEVOLUCAO')||v.includes('DOCUMENTO DE DEVOLUCAO'))}const cteOccurrenceTerms=['DESTINATARIO MUDOU-SE','EMBALAGEM EM ANALISE','ENDERECO DESTINATARIO NAO LOCALIZADO','EXTRAVIO','FALTA BLOCO DO EDIFICIO / SALA','SINISTRO LIQUIDADO','VOLUME FALTANTE AINDA NAO COLETADO'];function isCteOccurrence(x){if(isCteDelivered(x)||isCteReturning(x)||isCteReturned(x))return false;const v=norm(x?.status_tr||x?.ocorrencia||'');return!!v&&cteOccurrenceTerms.some(term=>v.includes(term))}function isCteFinalized(x){return norm(x?.status)==='FINALIZADO'||x?.shipment_from_invoice===true}function isCteTransit(x){if(cteUsesMarketplaceApi(x))return isCteFinalized(x)&&cteApiCategory(x)==='in_transit';return isCteFinalized(x)&&!!x?.has_cte&&!isCteDelivered(x)}function isCteNoStatus(x){return!cteUsesMarketplaceApi(x)&&isCteFinalized(x)&&!!x?.has_cte&&!String(x?.status_tr||'').trim()}function cteForecastDayMs(v){const s=String(v??'').trim();if(!s)return NaN;let m=s.match(/^(\d{1,2})[\/\-.](\d{1,2})[\/\-.](\d{4})/);if(m)return Date.UTC(Number(m[3]),Number(m[2])-1,Number(m[1]));m=s.match(/^(\d{4})-(\d{2})-(\d{2})/);if(m)return Date.UTC(Number(m[1]),Number(m[2])-1,Number(m[3]));return NaN}function cteTodayMs(){const p=new Intl.DateTimeFormat('en-CA',{timeZone:'America/Sao_Paulo',year:'numeric',month:'2-digit',day:'2-digit'}).formatToParts(new Date).reduce((a,x)=>(a[x.type]=x.value,a),{});return Date.UTC(Number(p.year),Number(p.month)-1,Number(p.day))}function isCteMagaluApi(x){const c=norm(x?.campanha),src=norm(x?.status_source);return c.includes('MAGALU')||src.includes('MAGALU')}function isCteOverdue(x){if(isCteMagaluApi(x)||isCteDelivered(x)||isCteReturning(x)||isCteReturned(x)||isCteOccurrence(x))return false;const ms=cteForecastDayMs(x?.previsao_entrega_tr);return Number.isFinite(ms)&&ms<cteTodayMs()&&!cteDeliveredStatus(x?.status_tr)}let cteAutoRiskDay=NaN;function cteAutoRisk(x,today){if(!x?.has_cte||isCteDelivered(x)||isCteReturned(x)||isCteReturning(x))return null;const state=norm(x?.status||''),raw=norm(x?.status_tr||''),carrier=norm(x?.transportadora||x?.status_source||'');if(/CANCELAD|INUTILIZAD|DEVOLUCAO|DEVOLVID|REEMBOLS|RESSARCIMENTO/.test(state+' '+raw))return null;if(/MERCADO ENVIOS|EBAZAR|MAGALU/.test(carrier)||/^(J&T|J T|LATAM)(?:$|\s)/.test(carrier))return null;const due=cteForecastDayMs(x?.previsao_entrega_tr);if(!Number.isFinite(due))return null;const days=Math.round((due-today)/864e5);if(days<0)return null;if(Number.isFinite(cteForecastDayMs(x?.data_entrega_tr)))return null;const issue=/PACOTE PROBLEMATIC|PROBLEMA|PENDENTE|AVARIA|EXTRAVI|SINISTRO|RETID|ENDERECO|DESTINATARIO MUDOU|CLIENTE AUSENTE|ENTREGA NAO REALIZADA|AINDA NAO CHEGOU|AINDA NAO SAIU|FALTA BLOCO|EMBALAGEM EM ANALISE|AGUARDANDO RETIRADA|AGUARDANDO FREQUENCIA|REENTREGA|OCORRENCIA|TICKET CRIADO|VERIFICANDO ENTREGA|SAIU PARA ENTREGA E NAO/.test(raw);const early=/ARQUIVO RECEBIDO|CTRC EMITIDO|COLETA RECEBIDA|ENCOMENDA RECEBIDA|OBJETO POSTADO|RECEBIDO CD|RECEBIDA E PROCESSADA|CHEGADAS AO CENTRO|RETIRADO DA CAF|EM PROCESSO DE CARREGAMENTO|EM CARREGAMENTO|COLETA EM|ASSINATURA DE COLETA/.test(raw);const moving=/TRANSFERENCIA|TRANSBORDO|EMBARCAD|EMBARQUE|EM VIAGEM|A CAMINHO|AGUARDANDO DESEMBARQUE|DESCARREGADO|SAIDA DA UNIDADE|ENVIO DO CENTRO|ENCOMENDA CARREGADA|OBJETO EM TRANSFERENCIA|AWB EM VIAGEM|MALOTE LIBERADO|EM ROTA/.test(raw);const lastMile=/PROCESSO DE ENTREGA|DISPONIVEL PARA ENTREGA|CHEGADA NA FILIAL RESPONSAVEL|A ENTREGAR|EM ENTREGA/.test(raw);if(/BIPE DE SAIDA PARA ENTREGA|SAIU PARA ENTREGA|SAIDA PARA ENTREGA/.test(raw)&&!issue)return null;let level='',reason='';if(issue&&days<=5){level=days<=2?'ALTO':'ATENÇÃO';reason='Ocorrência operacional registrada pela transportadora'}else if(early&&days<=2){level=days<=1?'ALTO':'ATENÇÃO';reason='Envio ainda em coleta/centro de distribuição, com prazo próximo'}else if(moving&&days<=1){level=days===0&&!/EM ROTA/.test(raw)?'ALTO':'ATENÇÃO';reason=/EM ROTA/.test(raw)?'Em rota, mas sem confirmação de entrega e com prazo próximo':'Transferência/viagem ainda em andamento perto da entrega'}else if(lastMile&&days===0){level='ATENÇÃO';reason='Etapa final sem confirmação de saída para entrega na data prevista'}else if(!raw&&days<=1){level='ATENÇÃO';reason='Sem status de transporte disponível e prazo muito próximo'}if(!level)return null;return{level,days,weight:(level==='ALTO'?200:100)+Math.max(0,6-days),reason}}function cteRefreshAutoRisk(){const today=cteTodayMs();if(cteAutoRiskDay===today)return;cteAutoRiskDay=today;for(const x of allItems){const r=cteAutoRisk(x,today);x.risco_auto_nivel=r?.level||'';x.risco_auto_motivo=r?.reason||'';x.risco_auto_dias=r?.days??-1;x.risco_auto_peso=r?.weight||0}}function cteDiasAtraso(x){const ms=cteForecastDayMs(x?.previsao_entrega_tr);return Number.isFinite(ms)?Math.max(0,Math.floor((cteTodayMs()-ms)/864e5)):0}function cteDeliveredTiming(x){if(view!=='delivered')return{cls:'',label:''};const forecast=cteForecastDayMs(x?.previsao_entrega_tr),delivered=cteForecastDayMs(x?.data_entrega_tr);if(!Number.isFinite(forecast)||!Number.isFinite(delivered))return{cls:'',label:''};return delivered<=forecast?{cls:'cte-delivered-on-time',label:'Entregue dentro do prazo'}:{cls:'cte-delivered-late',label:'Entregue após o prazo'}}function cteViewItems(){if(view==='all')return prazoBase();if(view==='ticket')return prazoTickets();return prazoBase().filter(x=>view==='without'?isCteWithout(x):view==='late'?isCteLate(x):view==='overdue'?isCteOverdue(x):view==='autorisk'?!!x.risco_auto_nivel:view==='occurrence'?isCteOccurrence(x):view==='delivered'?isCteDelivered(x):view==='awaiting'?isCteAwaitingPickup(x):view==='returning'?isCteReturning(x)||isCteReturned(x):view==='nostatus'?isCteNoStatus(x):view==='transit'?isCteTransit(x):false)}function renderCampaignButtons(){const host=$('[data-cte-campaigns]');if(!host)return;const base=cteViewItems(),selected=String($('[data-cte-filter="campanha"]')?.value||'');const counts=new Map;for(const x of base){const c=String(x?.campanha||'').trim();if(!c)continue;counts.set(c,(counts.get(c)||0)+1)}const preferred=['EDI - ABASTECE ES','MAGALU - ES','MERCADO LIVRE - ES','SHOPEE - ES','SHOPIFY','VENDAS INTERNAS - ES'];const campaigns=[...counts.entries()].sort((a,b)=>{const ai=preferred.indexOf(a[0]),bi=preferred.indexOf(b[0]);if(ai!==-1||bi!==-1){if(ai===-1)return 1;if(bi===-1)return-1;if(ai!==bi)return ai-bi}return a[0].localeCompare(b[0],'pt-BR',{numeric:true,sensitivity:'base'})});host.dataset.cteCampaignMode=view;const btn=(value,label,count)=>`<button type="button" class="cte-campaign-btn${selected===value?' is-active':''}" data-cte-campaign="${esc(value)}" title="${esc(label)}"><span>${esc(label)}</span><strong>${Number(count||0).toLocaleString('pt-BR')}</strong></button>`;host.innerHTML=btn('','TODAS',base.length)+campaigns.map(([name,count])=>btn(name,name,count)).join('')}function updateSortIndicators(){for(const th of $$('[data-cte-sort]')){th.classList.remove('is-sort-active');th.removeAttribute('data-sort-dir');if(th.dataset.cteSort===sort.key){th.classList.add('is-sort-active');th.dataset.sortDir=sort.dir}}}function apply(){const f=key=>String($(`[data-cte-filter="${key}"]`)?.value||'');const noteQuery=String(noteSearch?.value||'').trim();const noteDigits=noteQuery.replace(/\D+/g,'');const noteMatch=x=>{const raw=String(x?.nota||'').trim(),digits=raw.replace(/\D+/g,'');return noteDigits?digits.includes(noteDigits):norm(raw).includes(norm(noteQuery))};const base=noteQuery&&view!=='autorisk'?prazoBase().filter(noteMatch):cteViewItems();filtered=noteQuery&&view!=='autorisk'&&view!=='all'?base:base.filter(x=>filterMatches(x)).filter(x=>!noteQuery||noteMatch(x));const mult=sort.dir==='asc'?1:-1;filtered=filtered.map((item,index)=>({item,index})).sort((a,b)=>{const av=sort.key==='dias_atraso'?cteDiasAtraso(a.item):sortValue(sort.key==='previsao_ctt'?a.item?.previsao_ctt||a.item?.previsao_ctt_iso:a.item?.[sort.key],sort.type),bv=sort.key==='dias_atraso'?cteDiasAtraso(b.item):sortValue(sort.key==='previsao_ctt'?b.item?.previsao_ctt||b.item?.previsao_ctt_iso:b.item?.[sort.key],sort.type);let c=0;if(sort.type==='text')c=String(av).localeCompare(String(bv),'pt-BR',{numeric:true,sensitivity:'base'});else c=av-bv;return c===0?view==='autorisk'?(a.item.risco_auto_dias??99)-(b.item.risco_auto_dias??99)||a.index-b.index:a.index-b.index:c*mult}).map(x=>x.item);const pages=Math.max(1,Math.ceil(filtered.length/pageSize));page=Math.min(Math.max(1,page),pages);const start=(page-1)*pageSize,slice=filtered.slice(start,start+pageSize);body.innerHTML=slice.map(x=>{const full=String(x.status_tr||'-'),short=full.length>24?full.slice(0,24)+'…':full;const source=String(x.status_source||'RASTREAMENTO · CTE'),timing=cteDeliveredTiming(x);const note=String(x?.nota||x?.nota_cliente||'').trim();const ticketCard=view==='ticket';const linkedTicket=ticketCard?x:ticketByNote.get(ticketNoteKey(x?.nota))||ticketByNote.get(ticketNoteKey(x?.nota_cliente));const ticketId=String(linkedTicket?.ticket_id||'').trim();const isTicketEditable=!!(ticketId||note);const ticketTip=ticketId?'Clique para adicionar uma atualização ao ticket':'Clique para adicionar um ticket para esta nota';const isContactRow=view==='awaiting',hasContact=isContactRow&&contactList(x).length>0;const classes=[timing.cls,isTicketEditable?'cte-ticket-edit-row':'',isContactRow?'cte-contact-edit-row':'',hasContact?'cte-contact-done-row':''].filter(Boolean);const rowClass=classes.length?` class="${classes.join(' ')}"`:'';const rowTitle=isContactRow?` title="Clique na linha para registrar contato; na coluna Ticket para criar ou atualizar ticket"`:timing.label?` title="${esc(timing.label)}"`:'';const ticketAttr=isTicketEditable?` data-cte-ticket-edit="1" data-cte-ticket-id="${esc(ticketId)}" data-cte-ticket-note="${esc(note)}"`:'';const contactAttr=isContactRow?` data-cte-contact-key="${esc(contactIdentity(x))}"`:'';const ticketCellClass=isTicketEditable?' cte-ticket-edit-cell':'';const ticketCellTip=isTicketEditable?` title="${esc(ticketTip)}"`:'';return`<tr${rowClass}${rowTitle}${ticketAttr}${contactAttr}>${isContactRow?`<td class="cte-contact-cell">${contactHtml(x)}</td>`:''}<td>${esc(x.data_api||'-')}</td><td>${esc(x.nota||'-')}</td><td>${esc(x.destinatario||'-')}</td><td>${esc(x.campanha||'-')}</td><td>${esc(x.status||'-')}</td><td><span data-cte-status-tr-cell data-short="${esc(short)}" data-full="${esc(full)}" title="${esc(full)}" style="display:inline-block;max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:middle;cursor:pointer">${esc(short)}</span></td><td><span class="cte-source-badge">${esc(source)}</span></td><td>${esc(x.previsao_entrega_tr||'-')}</td><td class="cte-ctt-forecast-cell" title="${esc(cttCellTitle(x))}">${esc(cttCellText(x))}</td>${view==='overdue'?`<td class="cte-days-overdue-cell">${cteDiasAtraso(x)}</td>`:''}${view==='autorisk'?`<td class="cte-auto-risk-cell"><strong class="cte-auto-risk-badge ${x.risco_auto_nivel==='ALTO'?'is-high':'is-watch'}">${esc(x.risco_auto_nivel)}</strong><span>${esc(x.risco_auto_motivo)} · ${x.risco_auto_dias===0?'vence hoje':x.risco_auto_dias===1?'vence amanhã':`faltam ${x.risco_auto_dias} dias`}</span></td>`:''}<td>${esc(x.transportadora||'-')}</td><td>${esc(x.cte||'-')}</td><td>${esc(x.data_romaneio||'-')}</td><td>${esc(x.data_finalizado||'-')}</td><td class="cte-ticket-description${ticketCellClass}"${ticketCellTip}>${esc(linkedTicket?.descricao_ticket||x.descricao_ticket||'-')}</td><td class="cte-ticket-updates-cell${ticketCard?ticketCellClass:''}"${ticketCard?ticketCellTip:''}${ticketCard?'':' hidden'}>${ticketUpdatesHtml(x)}</td></tr>`}).join('');empty.hidden=filtered.length>0;$('[data-cte-result-count]').textContent=`${filtered.length.toLocaleString('pt-BR')} ${filtered.length===1?'nota':'notas'}`;const pagesBox=$('.cte-pages');if(pagesBox)pagesBox.hidden=pages<=1;$('[data-cte-page]').textContent=`Página ${page} de ${pages}`;$('[data-cte-prev]').disabled=page<=1;$('[data-cte-next]').disabled=page>=pages;refreshSelects();copyBtn.disabled=!filtered.length;downloadBtn.disabled=!filtered.length;updateSortIndicators()}function setView(next){view=['all','without','late','transit','nostatus','awaiting','returning','overdue','autorisk','occurrence','delivered','ticket'].includes(next)?next:'all';if(view==='all'){resetPrazo();prazoAtualizarCards()}if(view==='autorisk')sort={key:'risco_auto_peso',type:'number',dir:'desc'};else if(sort.key==='dias_atraso'||sort.key==='risco_auto_peso')sort={key:'data_api',type:'date',dir:'desc'};for(const b of $$('[data-cte-view]'))b.classList.toggle('is-active',b.dataset.cteView===view);const updatesHead=$('[data-cte-ticket-updates-head]'),contactHead=$('[data-cte-contact-head]');if(updatesHead)updatesHead.hidden=view!=='ticket';if(contactHead)contactHead.hidden=view!=='awaiting';const overdueDaysHead=$('[data-cte-overdue-days-head]');if(overdueDaysHead)overdueDaysHead.hidden=view!=='overdue';const autoHead=$('[data-cte-auto-risk-head]'),autoHelp=$('[data-cte-auto-risk-explain]');if(autoHead)autoHead.hidden=view!=='autorisk';if(autoHelp)autoHelp.hidden=view!=='autorisk';hideTicketPencil();page=1;resetFilters();if(noteSearch)noteSearch.value='';renderCampaignButtons();refreshSelects();apply();if(view==='awaiting')loadAwaitingContacts(false)}const CTT_CLOUD_FIELDS=['previsao_ctt','previsao_ctt_iso','previsao_ctt_status','previsao_ctt_motivo','cep_ctt','prazo_ctt','data_emissao_cte_ctt','data_base_ctt','ctt_base_tipo'];function cttDisplayDate(value){const key=dateKey(value);return key?key.slice(8,10)+'/'+key.slice(5,7)+'/'+key.slice(0,4):''}function cttCellText(row){const value=cttDisplayDate(row?.previsao_ctt||row?.previsao_ctt_iso);if(value)return ['estimada_operacional','estimada_jt'].includes(String(row?.previsao_ctt_status||''))?value+' (estim.)':value;if(row?.previsao_ctt_status==='excluida')return'-';const reason=String(row?.previsao_ctt_motivo||'').trim();if(row?.previsao_ctt_status==='sem_emissao_cte'&&/sem emiss[aã]o cte/i.test(reason))return 'Aguardando data de emissão ou evento da transportadora';return reason||'-'}function cttCellTitle(row){const parts=[],value=cttDisplayDate(row?.previsao_ctt||row?.previsao_ctt_iso);if(value){parts.push('Previsão CTT: '+value);if(row.cep_ctt)parts.push('CEP '+row.cep_ctt);if(row.prazo_ctt!==null&&row.prazo_ctt!==undefined&&row.prazo_ctt!=='')parts.push(row.prazo_ctt+' dias úteis');if(row.data_emissao_cte_ctt)parts.push('Emissão CTe: '+cttDisplayDate(row.data_emissao_cte_ctt));if(['estimada_operacional','estimada_jt'].includes(String(row?.previsao_ctt_status||''))){parts.push('Estimativa operacional, não emissão fiscal CT-e');if(row.data_base_ctt)parts.push('Evento datado da transportadora: '+cttDisplayDate(row.data_base_ctt))}}else if(row?.previsao_ctt_motivo)parts.push(String(row.previsao_ctt_motivo));return parts.join(' · ')}function cttApplyCloudRecord(row,record){const hasFields=CTT_CLOUD_FIELDS.some(key=>Object.prototype.hasOwnProperty.call(record,key));const code=value=>String(value??'').toUpperCase().replace(/[^A-Z0-9]+/g,'').replace(/^0+(?=\d)/,'');if(!hasFields&&code(row?.cte)===code(record?.cte))return false;if(code(row?.cte)===code(record?.cte)&&row?.previsao_ctt_status==='calculada'&&String(row?.cep_ctt||'').replace(/\D/g,'').length===8&&record?.previsao_ctt_status==='sem_cep')return false;let changed=false;for(const key of CTT_CLOUD_FIELDS){let value=hasFields?record[key]??(key==='prazo_ctt'?null:''):key==='prazo_ctt'?null:'';if(!hasFields&&key==='previsao_ctt_status')value='sem_previsao_salva';if(!hasFields&&key==='previsao_ctt_motivo')value='Previsão não disponível para este CTe';if(row[key]!==value){row[key]=value;changed=true}}return changed}const carrierStatusIndexCache=new Map;let carrierStatusCheckAt=0,carrierStatusCheckPromise=null;function cteCarrierKey(v){const n=norm(v).replace(/[^A-Z0-9]+/g,' ').trim();if(n.includes('TOTAL EXPRESS')||n==='TOTAL'||n.startsWith('TOTAL '))return'TOTAL_EXPRESS';if(n.includes('TNT')||n.includes('FEDEX')||n.includes('MERCURIO'))return'TNT_FEDEX';if(n.includes('CORREIO')||n.includes('VIPP'))return'CORREIOS_VIPP';if(n.includes('BRASPRESS'))return'BRASPRESS';if(n.includes('CONTINENTAL'))return'CONTINENTAL';if(n.includes('FAVORITA'))return'FAVORITA';if(n.includes('JAMEF'))return'JAMEF';if(n==='JT'||n==='J T'||n.startsWith('JT ')||n.startsWith('J T ')||n.includes('J T EXPRESS'))return'J_T';if(n.includes('LATAM')||n.includes('TAM CARGO')||n.includes('TAM LINHAS'))return'LATAM';if(n.includes('PATRUS'))return'PATRUS';if(n.includes('RODOMAX'))return'RODOMAX';if(n.includes('ALFA'))return'ALFA';return''}async function carrierStatusGet(path){const ctrl=new AbortController,timeout=setTimeout(()=>ctrl.abort(),18e3);try{const resp=await fetch(worker()+path,{headers:{Authorization:'Bearer '+token()},cache:'no-store',signal:ctrl.signal});if(!resp.ok)throw new Error('Indice de transportadora indisponivel (HTTP '+resp.status+').');return await resp.json()}finally{clearTimeout(timeout)}}function carrierStatusApplyIndex(snapshot,carrier){const index=carrier?.index||{},byNote=index?.by_note||{},key=carrier.key;if(!byNote||typeof byNote!=='object')return 0;let changed=0;for(const row of snapshot.items){if(cteCarrierKey(row?.transportadora)!==key)continue;const existingUpdated=Date.parse(String(row?.carrier_sheet_updated_at||''));const indexedUpdated=Date.parse(String(index?.updated_at||carrier?.updated_at||''));if(Number.isFinite(existingUpdated)&&Number.isFinite(indexedUpdated)&&existingUpdated>indexedUpdated)continue;const sysempNote=String(row?._tracking_sysemp_nf??'').trim();const noteValues=sysempNote?[sysempNote]:[row?.nota_cliente,row?.nota_lisa,row?.nota];let rec=null;for(const raw of noteValues){const note=ticketNoteKey(raw);if(note&&Object.prototype.hasOwnProperty.call(byNote,note)){rec=byNote[note];break}}if(!rec){const ambiguous=key==='JAMEF'&&noteValues.map(ticketNoteKey).some(n=>index.ambiguous_notes?.includes(n));if(ambiguous&&String(row.status_source||'').includes('WEBHOOK')){row.status_tr='';row.cte='';row.has_cte=false;row.previsao_entrega_tr='';row.ocorrencia_tr='Mais de um documento Jamef com o mesmo número de NF.';row.data_status_tr='';row.data_entrega_tr='';row.status_source='JAMEF · NF AMBÍGUA';cttApplyCloudRecord(row,{previsao_ctt:'',previsao_ctt_status:'nota_ambigua',previsao_ctt_motivo:'Mais de um CTe para esta nota'});row.carrier_sheet_updated_at=index.updated_at;changed++}continue}const status=String(rec.status_tr??rec.status??rec.ocorrencia??'').trim();const cte=String(rec.cte??'').trim();const forecast=String(rec.previsao??rec.previsao_entrega??'').trim();const cttChanged=cttApplyCloudRecord(row,rec);if(row.status_tr!==status||String(row.cte||'')!==cte||String(row.previsao_entrega_tr||'')!==forecast||!row.status_from_carrier_sheet||cttChanged)changed++;row.status_tr=status;row.cte=cte;row.has_cte=!!cte;row.previsao_entrega_tr=key==='J_T'||key==='LATAM'?'':forecast;row.status_source=rec.source==='JAMEF_WEBHOOK'?'JAMEF · WEBHOOK':String(index.carrier||carrier.name||row.transportadora||'TRANSPORTADORA');row.status_from_carrier_sheet=true;row.ocorrencia_tr=String(rec.ocorrencia??'').trim();row.data_status_tr=String(rec.data_status||index.updated_at||carrier.updated_at||'');row.data_entrega_tr=String(rec.data_entrega??rec.data_entrega_tr??'').trim();row.carrier_sheet_updated_at=String(index.updated_at||carrier.updated_at||'')}return changed}async function carrierStatusMergeSnapshot(snapshot){if(!Array.isArray(snapshot?.items)||!snapshot.updated_at)return 0;if(carrierStatusCheckPromise)return carrierStatusCheckPromise;carrierStatusCheckPromise=(async()=>{const markers=[snapshot.updated_at,snapshot.carrier_sources_updated_at].map(v=>Date.parse(String(v||''))).filter(Number.isFinite);const since=encodeURIComponent(new Date(Math.min(...markers)).toISOString());const manifest=await carrierStatusGet('/cte-report/carrier-status/manifest?since='+since);if(!manifest?.ok||!Array.isArray(manifest.carriers))throw new Error('Manifesto das transportadoras invalido.');let count=0,lastUpdate='';for(const carrier of manifest.carriers){const key=String(carrier.key||''),stamp=String(carrier.updated_at||'');if(!key||!stamp)continue;let rec=carrierStatusIndexCache.get(key);if(!rec||rec.updated_at!==stamp){const index=await carrierStatusGet('/cte-report/carrier-status/index?carrier='+encodeURIComponent(key));if(!index||!index.by_note)continue;rec={...carrier,index,updated_at:stamp};carrierStatusIndexCache.set(key,rec)}count+=carrierStatusApplyIndex(snapshot,rec);if(!lastUpdate||stamp>lastUpdate)lastUpdate=stamp}carrierStatusCheckAt=Date.now();if(count){snapshot.counts=null;snapshot.carrier_status_updated_at=lastUpdate}snapshot.carrier_status_error='';return count})().finally(()=>{carrierStatusCheckPromise=null});return carrierStatusCheckPromise}async function refreshCarrierStatusOnOpen(){const snapshot=preloadedSnapshotData;if(!snapshot||carrierStatusCheckPromise||snapshotLoadPromise||Date.now()-carrierStatusCheckAt<12e4)return;try{const changed=await carrierStatusMergeSnapshot(snapshot);if(changed&&sysempSummaryVerifiedAudit&&sysempSummarySnapshotMatches(sysempSummaryVerifiedAudit,snapshot))applySnapshotData(snapshot)}catch(e){console.warn('Consulta aos indices atuais de transportadoras:',e);snapshot.carrier_status_error=String(e.message||e);if(sysempSummaryVerifiedAudit&&sysempSummarySnapshotMatches(sysempSummaryVerifiedAudit,snapshot))applySnapshotData(snapshot)}}function hideTrackingMarketplace(row){
return /MAGAZINELUIZA|MAGALU/.test(norm(row?.transportadora).replace(/[^A-Z]/g,''))||[row?.transportadora,row?.campanha].some(value=>/MERCADOENVIO|MERCADOLIVRE|SHOPEE/.test(norm(value).replace(/[^A-Z]/g,'')));
}
function applySnapshotData(d,syncMainCards=true){const savedFilters=$$('.cte-filter').map(el=>[el,el.value]);const sourceItems=Array.isArray(d?.items)?d.items:[];const hiddenNotes=new Set(sourceItems.filter(hideTrackingMarketplace).map(x=>ticketNoteKey(x.nota_cliente||x.nota)).filter(Boolean));ticketItems=(Array.isArray(d?.ticket_items)?d.ticket_items:[]).filter(x=>!hideTrackingMarketplace(x)&&!hiddenNotes.has(ticketNoteKey(x.nota_cliente||x.nota))).map(x=>({...x,ticket_updates_text:ticketUpdatesText(x)}));ticketByNote=new Map;for(const item of ticketItems){const k=ticketNoteKey(item?.nota_cliente||item?.nota);if(k&&String(item?.ticket_id||'').trim()&&!ticketByNote.has(k))ticketByNote.set(k,item)}allItems=sourceItems.filter(x=>!hideTrackingMarketplace(x));cteAutoRiskDay=NaN;cteRefreshAutoRisk();$('[data-cte-count-all]').textContent=allItems.length.toLocaleString('pt-BR');sideCount('all',allItems.length);applyAwaitingContactsToItems();if(token()&&!awaitingContactsLoaded)loadAwaitingContacts(false);const withoutCount=allItems.reduce((n,x)=>n+(isCteWithout(x)?1:0),0);const lateCount=allItems.reduce((n,x)=>n+(isCteLate(x)?1:0),0);const transitCount=allItems.reduce((n,x)=>n+(isCteTransit(x)?1:0),0);const noStatusCount=allItems.reduce((n,x)=>n+(isCteNoStatus(x)?1:0),0);const awaitingCount=allItems.reduce((n,x)=>n+(isCteAwaitingPickup(x)?1:0),0);const returningCount=allItems.reduce((n,x)=>n+(isCteReturning(x)||isCteReturned(x)?1:0),0);const overdueCount=allItems.reduce((n,x)=>n+(isCteOverdue(x)?1:0),0);const autoriskCount=allItems.reduce((n,x)=>n+(x.risco_auto_nivel?1:0),0);const occurrenceCount=allItems.reduce((n,x)=>n+(isCteOccurrence(x)?1:0),0);const deliveredCount=allItems.reduce((n,x)=>n+(isCteDelivered(x)?1:0),0);const ticketCount=ticketItems.length;$('[data-cte-count-without]').textContent=withoutCount.toLocaleString('pt-BR');$('[data-cte-count-transit]').textContent=transitCount.toLocaleString('pt-BR');$('[data-cte-count-nostatus]').textContent=noStatusCount.toLocaleString('pt-BR');$('[data-cte-count-awaiting]').textContent=awaitingCount.toLocaleString('pt-BR');$('[data-cte-count-returning]').textContent=returningCount.toLocaleString('pt-BR');$('[data-cte-count-overdue]').textContent=overdueCount.toLocaleString('pt-BR');$('[data-cte-count-occurrence]').textContent=occurrenceCount.toLocaleString('pt-BR');$('[data-cte-count-delivered]').textContent=deliveredCount.toLocaleString('pt-BR');$('[data-cte-count-ticket]').textContent=ticketCount.toLocaleString('pt-BR');$('[data-cte-count-autorisk]').textContent=autoriskCount.toLocaleString('pt-BR');const summaryCounts={without:withoutCount,late:lateCount,transit:transitCount,nostatus:noStatusCount,awaiting:awaitingCount,returning:returningCount,overdue:overdueCount,autorisk:autoriskCount,occurrence:occurrenceCount,delivered:deliveredCount,ticket:ticketCount};if(syncMainCards)syncSideCounts(summaryCounts);resumoBaseCounts={all:allItems.length,...summaryCounts};const u=d?.updated_at?new Date(d.updated_at):null;$('[data-cte-meta]').textContent='Resumo salvo no Cloud'+(u&&!Number.isNaN(u.getTime())?' · Atualizado '+u.toLocaleString('pt-BR'):'')+(d?.carrier_status_updated_at?' · Status transportadoras atualizado':'')+(d?.carrier_status_error?' · Status das transportadoras pode estar desatualizado':'');status.textContent=`${allItems.length.toLocaleString('pt-BR')} notas · ${withoutCount.toLocaleString('pt-BR')} sem CTE · ${transitCount.toLocaleString('pt-BR')} em trajeto · ${noStatusCount.toLocaleString('pt-BR')} sem status · ${awaitingCount.toLocaleString('pt-BR')} aguardando retirada · ${returningCount.toLocaleString('pt-BR')} devolução · ${overdueCount.toLocaleString('pt-BR')} atrasados · ${occurrenceCount.toLocaleString('pt-BR')} ocorrências · ${deliveredCount.toLocaleString('pt-BR')} entregues · ${ticketCount.toLocaleString('pt-BR')} tickets`;resumoBaseStatus=status.textContent;prazoAtualizarCards();fillSelect('campanha');fillSelect('status');fillSelect('status_tr');fillSelect('status_source');fillSelect('transportadora');for(const[el,value]of savedFilters)el.value=value;renderCampaignButtons();if(!modal.hidden){apply();showContent()}}let sysempSummaryVerifiedAudit=null;
function acceptPreloadedSnapshot(d){
if(!d||typeof d!=='object'||!Array.isArray(d.items))return false;
if(trackingSnapshotIsOlder(d))return true;
preloadedSnapshotData=d;panel.__lisaTrackingSnapshotData=d;
loadedSnapshotAt=String(d.updated_at||'');if(loadedSnapshotAt)panel.dataset.trackingSnapshotAt=loadedSnapshotAt;
if(sysempSummaryVerifiedAudit&&sysempSummarySnapshotMatches(sysempSummaryVerifiedAudit,d)){
status.classList.remove('is-error');applySnapshotData(d);
}
return true
}async function loadDirectTicketData(snapshot){
const card=wrap.querySelector('[data-cte-count-ticket]');
try{
const controller=new AbortController(),timer=setTimeout(()=>controller.abort(),20000);let r;
try{r=await fetch(worker()+'/transportadoras/tickets',{headers:{Authorization:'Bearer '+token()},cache:'no-store',signal:controller.signal});}finally{clearTimeout(timer);}
const data=await r.json();if(!r.ok)throw Error([data.error,data.detail].filter(Boolean).join(' — ')||'HTTP '+r.status);
const rows=Array.isArray(snapshot.items)?snapshot.items:[];
const byNote=new Map();for(const row of rows){const key=ticketNoteKey(row.nota_cliente||row.nota);if(!byNote.has(key))byNote.set(key,[]);byNote.get(key).push(row);}
snapshot.ticket_items=(data.items||[]).map(t=>{
const matches=byNote.get(ticketNoteKey(t.nota))||[];
const base=matches.find(x=>norm(x.transportadora)===norm(t.transportadora))||matches[0]||{};
return {...base,nota:String(t.nota||''),nota_cliente:String(t.nota||''),ticket_id:String(t.id||''),status:'TICKET ABERTO',status_source:'TICKET',transportadora:t.transportadora||base.transportadora||'',cte:t.cte||base.cte||'',has_cte:!!(t.cte||base.cte),data_api:t.data_abertura||t.created_at||base.data_api||'',descricao_ticket:t.descricao||'',ticket_updates:t.updates||[],campanha:base.campanha||t.campanha||''};
});
if(card)card.title=data.items?.length?'Tickets carregados diretamente da base salva no Cloud.':'A base de tickets abertos do Cloud retornou vazia. O historico do Google Sheets nao e lido por esta rota.';
}catch(e){if(card)card.title='Falha ao ler tickets: '+String(e?.message||e);console.warn('Tickets: carga direta',e);}
}
async function preloadTrackingSnapshot(force=false){if(!force&&preloadedSnapshotData)return preloadedSnapshotData;if(snapshotLoadPromise)return snapshotLoadPromise;const tk=token();if(!tk){snapshotLoadError='Sessão indisponível. Entre novamente no painel.';return null}snapshotLoadPromise=(async()=>{const controller=new AbortController,timer=setTimeout(()=>controller.abort(),25e3);try{const r=await fetch(worker()+'/cte-report',{headers:{Authorization:'Bearer '+tk},cache:'no-store',signal:controller.signal});const d=await r.json().catch(()=>({}));if(r.status===401)throw new Error(d.error||'Sessão expirada. Entre novamente no painel.');if(!r.ok)throw new Error(d.detail||d.error||'Falha ao carregar Rastreamento (HTTP '+r.status+').');if(!Array.isArray(d?.items))throw new Error('O Worker retornou um snapshot invalido.');if(trackingSnapshotIsOlder(d))return preloadedSnapshotData;// Todas as transportadoras ja foram cruzadas no job do Cloud.
await loadDirectTicketData(d);if(!acceptPreloadedSnapshot(d))throw new Error('O Worker retornou um snapshot inválido. Confira se o Worker atualizado foi publicado.');snapshotLoadError='';if(d.refreshing&&snapshotRefreshRetries<3){snapshotRefreshRetries++;clearTimeout(preloadRetryTimer);preloadRetryTimer=setTimeout(()=>preloadTrackingSnapshot(true),5e3)}else if(!d.refreshing){snapshotRefreshRetries=0}return d}catch(e){snapshotLoadError=e.name==='AbortError'?'O Cloud não respondeu em 25 segundos. Clique em Tentar novamente.':String(e.message||e);console.debug('Pré-carga do Rastreamento:',e);return null}finally{clearTimeout(timer);snapshotLoadPromise=null}})();return snapshotLoadPromise}function scheduleTrackingPreload(attempt=0){clearTimeout(preloadRetryTimer);if(token()){preloadTrackingSnapshot(false);return}if(attempt<3600)preloadRetryTimer=setTimeout(()=>scheduleTrackingPreload(attempt+1),1e3)}/* Conferencia do universo de NFs: o arquivo SYSEMP ja salvo no R2 e a fonte
autoritativa. O Monitor e as transportadoras complementam, nunca filtram. */
let sysempSummaryAuditPromise=null;
async function sysempSummaryRequest(path,method='GET'){
const controller=new AbortController(),timer=setTimeout(()=>controller.abort(),23000);
try{
const response=await fetch(worker()+path,{method,headers:{Authorization:'Bearer '+token()},cache:'no-store',signal:controller.signal});
const data=await response.json().catch(()=>({}));
if(!response.ok)throw new Error(response.status===404?'O Worker publicado ainda não contém a conferência SYSEMP. Substitua também o arquivo worker.txt no Cloudflare.':data.error||data.message||'Erro ao conferir as notas SYSEMP no Cloud (HTTP '+response.status+').');
if(data?.ok!==true)throw new Error(data?.error||'Resposta invalida do Worker ao conferir o arquivo SYSEMP.');
return data;
}catch(e){if(e?.name==='AbortError')throw new Error('O Cloud demorou demais para responder sobre as notas SYSEMP. Tente novamente.');throw e}
finally{clearTimeout(timer)}
}
function sysempSummarySnapshotMatches(audit,snapshot){
const summary=audit?.summary||{},scope=snapshot?.tracking_scope||{},source=audit?.sysemp||{};
return summary.current===true&&scope.complete===true&&
Array.isArray(snapshot?.items)&&Number.isSafeInteger(Number(summary.total))&&
snapshot.items.length===Number(summary.total)&&
scope.source_etag===source.source_revision&&
String(scope.raw_etag||'')===String(summary.raw_revision||'')&&
Number(scope.sysemp_notes)===Number(summary.total)&&
(source.unique_notes===null||source.unique_notes===undefined||Number(source.unique_notes)===snapshot.items.length)&&
new Set(snapshot.items.map(row=>String(row?.nota||'').trim().replace(/\.0+$/,'').replace(/^0+(?=\d)/,''))).size===snapshot.items.length;
}
function sysempSummaryUpdateLoading(message){
if(modal.hidden)return;
const label=loading?.querySelector('strong'),detail=loading?.querySelector('span');
if(label)label.textContent='Conferindo todas as notas da SYSEMP...';
if(detail)detail.textContent=message;
}

const coverageNotice=document.createElement('div');
coverageNotice.className='cte-coverage-notice';
coverageNotice.setAttribute('role','status');
coverageNotice.hidden=true;
status.before(coverageNotice);
function showCoverageNotice(message){
coverageNotice.textContent='';
coverageNotice.hidden=true;
}
function ensureSysempCompleteSummary(){
if(sysempSummaryAuditPromise)return sysempSummaryAuditPromise;
sysempSummaryAuditPromise=(async()=>{
if(!token())throw new Error('Sessão expirada. Entre novamente no painel.');
sysempSummaryUpdateLoading('Lendo dados prontos no Cloud. Nenhuma planilha será processada.');
let audit=null,auditFailure='';
try{audit=await sysempSummaryRequest('/rastreamento/sysemp-status')}
catch(e){auditFailure=String(e?.message||e)}
let snap=preloadedSnapshotData;
if(!Array.isArray(snap?.items)||!snap.items.length)snap=await preloadTrackingSnapshot(false);
if(audit?.summary?.current===true&&!sysempSummarySnapshotMatches(audit,snap)){
snap=await preloadTrackingSnapshot(true)||snap;
}
if(!Array.isArray(snap?.items)||!snap.items.length){
throw new Error(snapshotLoadError||auditFailure||audit?.preparation?.message||'Nenhum relatório detalhado foi salvo no Cloud. Confira a preparação automática.');
}
const verified=!!audit&&sysempSummarySnapshotMatches(audit,snap);
sysempSummaryVerifiedAudit=verified?audit:null;
status.classList.remove('is-error');
applySnapshotData(snap,verified);
if(verified){
showCoverageNotice(audit?.summary?.enriched===false?'Todas as notas fiscais do SYSEMP foram carregadas no Cloud. Os dados de transportadoras e previsões ainda estão sendo complementados pela rotina agendada.':'');
const meta=$('[data-cte-meta]');
if(meta)meta.textContent='SYSEMP conferido no Cloud · '+allItems.length.toLocaleString('pt-BR')+' notas fiscais · Atualizado '+(snap.updated_at?new Date(snap.updated_at).toLocaleString('pt-BR',{timeZone:'America/Sao_Paulo'}):'--');
}else{
const displayed=snap.items.length.toLocaleString('pt-BR');
const expected=Number.isSafeInteger(Number(audit?.sysemp?.unique_notes))&&audit.sysemp.unique_notes!==null?Number(audit.sysemp.unique_notes).toLocaleString('pt-BR'):null;
const detail=auditFailure||audit?.preparation?.message||'A rotina do Cloud ainda não confirmou a cobertura integral.';
showCoverageNotice('EXIBIÇÃO PROVISÓRIA — '+displayed+' registros do último rastreamento salvo no Cloud'+(expected?' de '+expected+' notas identificadas no SYSEMP':'')+'. A conferência de todas as notas fiscais ainda está pendente. '+detail+' Estes registros não representam necessariamente todas as notas do arquivo SYSEMP.');
const meta=$('[data-cte-meta]');
if(meta)meta.textContent='Snapshot disponível no Cloud · '+displayed+' registros · Cobertura SYSEMP ainda não conferida';
}
return snap;
})().finally(()=>{sysempSummaryAuditPromise=null});
return sysempSummaryAuditPromise;
}

async function load(){
showLoading();status.textContent='Lendo snapshot pré-carregado do Cloud...';
status.classList.remove('is-error');body.innerHTML='';empty.hidden=true;copyBtn.disabled=true;downloadBtn.disabled=true;
try{const data=await ensureSysempCompleteSummary();if(data)return data}
catch(e){snapshotLoadError=String(e?.message||e);console.warn('Resumo completo SYSEMP:',e)}
status.textContent=snapshotLoadError||'Não foi possível conferir as notas da SYSEMP.';
const retry=document.createElement('button');retry.type='button';retry.textContent='Consultar novamente';retry.style.marginLeft='12px';retry.addEventListener('click',()=>load());status.appendChild(retry);
const prepare=document.createElement('button');prepare.type='button';prepare.textContent='Atualizar resumo no Cloud';prepare.style.marginLeft='10px';
prepare.addEventListener('click',async()=>{
prepare.disabled=true;retry.disabled=true;
status.textContent='Solicitando preparação do resumo no Cloud. Nenhuma planilha será processada pelo navegador...';
try{
const start=await sysempSummaryRequest('/rastreamento/sysemp-prepare','POST');
if(start.ready){sysempSummaryVerifiedAudit=null;preloadedSnapshotData=null;await load();return;}
status.textContent=start.queued?'Solicitação registrada. O próximo Cron vai processar o SYSEMP já existente no Cloud; não é necessário importar novamente.':start.started?'Preparação iniciada no Cloud. Consulte o status após a rotina agendada.':'O Cloud já está preparando o resumo. Consulte novamente para verificar se foi concluído.';
const check=document.createElement('button');check.type='button';check.textContent='Consultar status';check.style.marginLeft='12px';check.addEventListener('click',()=>load());status.appendChild(check);
}catch(e){status.textContent='Falha ao iniciar a preparação: '+String(e?.message||e);const again=document.createElement('button');again.type='button';again.textContent='Tentar novamente';again.style.marginLeft='12px';again.addEventListener('click',()=>load());status.appendChild(again);}
});
status.appendChild(prepare);
status.classList.add('is-error');showContent();
for(const el of contentParts)if(el!==status)el.hidden=true;
return null
}
function open(targetView='all'){if(!trackingUpdateBusy){trackingUpdateProgress(0,'Atualizar');jtReindexButton.classList.remove('is-updating');jtReindexButton.title='Atualizar dados salvos no Cloud';}modal.hidden=false;showLoading();const label=loading?.querySelector('strong'),detail=loading?.querySelector('span');if(label)label.textContent='Carregando resumo de rastreamento...';if(detail)detail.textContent='Exibindo somente o resumo já preparado. Nenhuma planilha é processada nesta tela.';const wanted=['all','without','late','transit','nostatus','awaiting','returning','overdue','autorisk','occurrence','delivered','ticket'].includes(targetView)?targetView:'all';requestAnimationFrame(()=>setTimeout(async()=>{if(modal.hidden)return;view=wanted;page=1;for(const b of $$('[data-cte-view]'))b.classList.toggle('is-active',b.dataset.cteView===view);const updatesHead=$('[data-cte-ticket-updates-head]'),contactHead=$('[data-cte-contact-head]'),daysHead=$('[data-cte-overdue-days-head]');if(updatesHead)updatesHead.hidden=view!=='ticket';if(contactHead)contactHead.hidden=view!=='awaiting';if(daysHead)daysHead.hidden=view!=='overdue';const riskHead=$('[data-cte-auto-risk-head]'),riskHelp=$('[data-cte-auto-risk-explain]');if(riskHead)riskHead.hidden=view!=='autorisk';if(riskHelp)riskHelp.hidden=view!=='autorisk';if(view==='autorisk')sort={key:'risco_auto_peso',type:'number',dir:'desc'};if(view==='all')resetPrazo();hideTicketPencil();resetFilters();if(noteSearch)noteSearch.value='';await load();if(!modal.hidden)return;if(view==='awaiting')loadAwaitingContacts(false)},0))}function close(){prazoFechar();modal.hidden=true;hideTicketPencil();showLoading()}async function copy(){if(!filtered.length)return;const isTicket=view==='ticket',isAwaiting=view==='awaiting',H=[...isAwaiting?['Contato']:[],'Data API','Nota','Destinatário','Campanha','Status LISA','Status Transp.','Origem status','Previsão de entrega','Previsão CTT',...view==='overdue'?['Dias atraso']:[],...view==='autorisk'?['Prioridade','Motivo do alerta','Dias ate previsao']:[],'Transportadora','CTE','Data Romaneio','Data Finalizado','Ticket',...isTicket?['Atualização de ticket']:[]];const txt=[H.join('\t'),...filtered.map(x=>[...isAwaiting?[x.contact_text]:[],x.data_api,x.nota,x.destinatario,x.campanha,x.status,x.status_tr,x.status_source,x.previsao_entrega_tr,cttCellText(x),...view==='overdue'?[cteDiasAtraso(x)]:[],...view==='autorisk'?[x.risco_auto_nivel,x.risco_auto_motivo,x.risco_auto_dias]:[],x.transportadora,x.cte,x.data_romaneio,x.data_finalizado,x.descricao_ticket,...isTicket?[x.ticket_updates_text]:[]].map(v=>String(v??'')).join('\t'))].join('\n');try{await navigator.clipboard.writeText(txt)}catch{const ta=document.createElement('textarea');ta.value=txt;ta.style.position='fixed';ta.style.left='-9999px';document.body.appendChild(ta);ta.select();document.execCommand('copy');ta.remove()}const old=copyBtn.textContent;copyBtn.textContent='Copiado!';setTimeout(()=>copyBtn.textContent=old,1100)}async function ensureXlsx(){if(window.XLSX)return window.XLSX;await new Promise((res,rej)=>{const old=document.querySelector('script[data-cte-xlsx]');if(old){if(window.XLSX)return res();old.addEventListener('load',res,{once:true});old.addEventListener('error',rej,{once:true});return}const s=document.createElement('script');s.src='https://cdn.jsdelivr.net/npm/xlsx@0.18.5/dist/xlsx.full.min.js';s.async=true;s.dataset.cteXlsx='1';s.onload=res;s.onerror=rej;document.head.appendChild(s)});if(!window.XLSX)throw new Error('Leitor de Excel indisponível.');return window.XLSX}async function download(){if(!filtered.length)return;const old=downloadBtn.textContent;downloadBtn.disabled=true;downloadBtn.textContent='Gerando...';try{const XLSX=await ensureXlsx(),isTicket=view==='ticket',isAwaiting=view==='awaiting';const H=[...isAwaiting?['Contato']:[],'Data API','Nota','Destinatário','Campanha','Status LISA','Status Transp.','Origem status','Previsão de entrega','Previsão CTT',...view==='overdue'?['Dias atraso']:[],...view==='autorisk'?['Prioridade','Motivo do alerta','Dias ate previsao']:[],'Transportadora','CTE','Data Romaneio','Data Finalizado','Ticket',...isTicket?['Atualização de ticket']:[]];const A=[H,...filtered.map(x=>[...isAwaiting?[x.contact_text]:[],x.data_api,x.nota,x.destinatario,x.campanha,x.status,x.status_tr,x.status_source,x.previsao_entrega_tr,cttCellText(x),...view==='overdue'?[cteDiasAtraso(x)]:[],...view==='autorisk'?[x.risco_auto_nivel,x.risco_auto_motivo,x.risco_auto_dias]:[],x.transportadora,x.cte,x.data_romaneio,x.data_finalizado,x.descricao_ticket,...isTicket?[x.ticket_updates_text]:[]].map(v=>String(v??'')))];const ws=XLSX.utils.aoa_to_sheet(A);const rg=XLSX.utils.decode_range(ws['!ref']);for(let R=rg.s.r;R<=rg.e.r;R++)for(let C=rg.s.c;C<=rg.e.c;C++){const a=XLSX.utils.encode_cell({r:R,c:C}),cell=ws[a];if(cell){cell.t='s';cell.v=String(cell.v??'');cell.z='@'}}ws['!cols']=[...isAwaiting?[{wch:62}]:[],{wch:18},{wch:16},{wch:34},{wch:24},{wch:18},{wch:26},{wch:28},{wch:24},{wch:28},...view==='overdue'?[{wch:14}]:[],...view==='autorisk'?[{wch:15},{wch:70},{wch:19}]:[],{wch:28},{wch:24},{wch:18},{wch:18},{wch:48},...isTicket?[{wch:62}]:[]];const wb=XLSX.utils.book_new();XLSX.utils.book_append_sheet(wb,ws,'CTE');XLSX.writeFile(wb,view==='all'?'rastreamento_todos.xlsx':view==='transit'?'notas_cte_em_trajeto.xlsx':view==='nostatus'?'notas_cte_sem_status.xlsx':view==='awaiting'?'notas_cte_aguardando_retirada.xlsx':view==='returning'?'notas_cte_devolucao.xlsx':view==='overdue'?'notas_cte_atrasados.xlsx':view==='autorisk'?'notas_cte_atraso_automatico.xlsx':view==='occurrence'?'notas_cte_ocorrencias.xlsx':view==='delivered'?'notas_cte_entregues.xlsx':view==='ticket'?'tickets_cte_abertos.xlsx':view==='late'?'notas_cte_atraso_24h.xlsx':'notas_sem_cte.xlsx',{compression:true})}catch(e){alert(e.message||'Não foi possível gerar o XLSX.')}finally{downloadBtn.disabled=false;downloadBtn.textContent=old}}body.addEventListener('mousemove',e=>{const ticketCell=e.target.closest?.('td.cte-ticket-edit-cell');const ticketRow=ticketCell?.closest?.('[data-cte-ticket-edit]');const contactRow=e.target.closest?.('[data-cte-contact-key]');const active=ticketCell&&ticketRow&&body.contains(ticketCell)||view==='awaiting'&&contactRow&&body.contains(contactRow);if(active){ticketPencil.hidden=false;ticketPencil.style.left=e.clientX+14+'px';ticketPencil.style.top=e.clientY+14+'px'}else hideTicketPencil()});body.addEventListener('mouseleave',hideTicketPencil);body.addEventListener('click',e=>{const ticketCell=e.target.closest?.('td.cte-ticket-edit-cell');const ticketRow=ticketCell?.closest?.('[data-cte-ticket-edit]');if(ticketRow&&body.contains(ticketCell)){const id=String(ticketRow.dataset.cteTicketId||''),note=String(ticketRow.dataset.cteTicketNote||'');const item=id?ticketItems.find(x=>String(x?.ticket_id||'')===id):null;hideTicketPencil();e.preventDefault();e.stopPropagation();if(id){if(typeof panel.__openCarrierTicketEdit==='function')panel.__openCarrierTicketEdit(item||{ticket_id:id,nota:note});else alert('O formulário de atualização do ticket ainda não foi carregado. Recarregue o painel e tente novamente.')}else if(note){if(typeof panel.__openCarrierTicketCreate==='function'){panel.__openCarrierTicketCreate(note)}else alert('O formulário de criação de ticket ainda não foi carregado. Recarregue o painel e tente novamente.')}return}const contactRow=e.target.closest?.('[data-cte-contact-key]');if(view==='awaiting'&&contactRow&&body.contains(contactRow)){const key=String(contactRow.dataset.cteContactKey||'');const item=allItems.find(x=>contactIdentity(x)===key&&isCteAwaitingPickup(x));hideTicketPencil();e.preventDefault();e.stopPropagation();if(item)openContactModal(item);return}const el=e.target.closest('[data-cte-status-tr-cell]');if(!el)return;const expanded=el.dataset.expanded==='1';el.dataset.expanded=expanded?'0':'1';el.textContent=expanded?el.dataset.short:el.dataset.full;el.style.maxWidth=expanded?'150px':'360px';el.style.whiteSpace=expanded?'nowrap':'normal';el.style.overflow=expanded?'hidden':'visible';el.style.textOverflow=expanded?'ellipsis':'clip'});panel.__lisaPrepareSnapshot=async(d,entries)=>{for(const entry of entries){carrierStatusApplyIndex(d,entry);for(const row of d.items||[]){const note=ticketNoteKey(row._tracking_sysemp_nf||row.nota_cliente||row.nota);if(cteCarrierKey(row.transportadora)===entry.key&&entry.index.base_tombstones?.[note]){row.cte='';row.has_cte=false;row.status_tr='';row.previsao_entrega_tr='';row.status_source=(entry.index.carrier||entry.key)+' · EXCLUÍDO DA BASE';cttApplyCloudRecord(row,{});}}}await loadDirectTicketData(d);applySnapshotData(d,true);d.counts={...resumoBaseCounts};return d;};panel.__lisaAcceptPreparedSnapshot=d=>{acceptPreloadedSnapshot(d);applySnapshotData(d,true);};panel.__lisaTicketCarrierKey=cteCarrierKey;panel.__lisaTrackingOpen=open;if(openBtn.__lisaCteOpenHandler)openBtn.removeEventListener('click',openBtn.__lisaCteOpenHandler);openBtn.__lisaCteOpenHandler=e=>{e?.preventDefault?.();e?.stopPropagation?.();open('all')};openBtn.addEventListener('click',openBtn.__lisaCteOpenHandler);setInterval(()=>{if(!modal.hidden&&cteAutoRiskDay!==cteTodayMs()&&preloadedSnapshotData)applySnapshotData(preloadedSnapshotData)},12e4);let snapshotRefreshTimer=null;const queueSnapshotRefresh=()=>{clearTimeout(snapshotRefreshTimer);snapshotRefreshTimer=setTimeout(()=>preloadTrackingSnapshot(true),2500)};panel.addEventListener('lisa:carrier-files-updated',queueSnapshotRefresh);panel.addEventListener('lisa:carrier-indexes-ready',queueSnapshotRefresh);let summarySnapshotTarget=0,summarySnapshotAttempted=0,summarySnapshotDrainPromise=null,summaryEmptyRecoveryUsed=false;function trackingSnapshotVersion(value){const ms=Date.parse(String(value||''));return Number.isFinite(ms)&&ms>0?ms:0}function trackingSnapshotIsOlder(data){const loaded=trackingSnapshotVersion(loadedSnapshotAt),incoming=trackingSnapshotVersion(data?.updated_at);return!!preloadedSnapshotData&&loaded>0&&incoming<loaded}function drainSummarySnapshotRefresh(){if(summarySnapshotDrainPromise)return summarySnapshotDrainPromise;summarySnapshotDrainPromise=(async()=>{if(snapshotLoadPromise)await snapshotLoadPromise;for(;;){const target=summarySnapshotTarget,loaded=trackingSnapshotVersion(loadedSnapshotAt);if(target<=loaded||target<=summarySnapshotAttempted||!token())break;summarySnapshotAttempted=target;await preloadTrackingSnapshot(true)}})().finally(()=>{summarySnapshotDrainPromise=null;if(summarySnapshotTarget>trackingSnapshotVersion(loadedSnapshotAt)&&summarySnapshotTarget>summarySnapshotAttempted&&token())void drainSummarySnapshotRefresh()});return summarySnapshotDrainPromise}panel.addEventListener('lisa:tracking-summary-loaded',e=>{const version=trackingSnapshotVersion(e?.detail?.updated_at);if(version){if(version<=trackingSnapshotVersion(loadedSnapshotAt)||version<=summarySnapshotAttempted)return;summarySnapshotTarget=Math.max(summarySnapshotTarget,version);void drainSummarySnapshotRefresh();return}const c=e?.detail?.counts||{},operational=['without','late','transit','nostatus','awaiting','returning','overdue','delivered'].reduce((n,k)=>n+(Number(c?.[k])||0),0);const currentItems=Array.isArray(preloadedSnapshotData?.items)?preloadedSnapshotData.items.length:0;if(currentItems>0){summaryEmptyRecoveryUsed=false;return}if(operational>0&&!summaryEmptyRecoveryUsed){summaryEmptyRecoveryUsed=true;void preloadTrackingSnapshot(true)}});let savedSummaryOpenPromise=null,savedSummaryController=null,savedSummaryDisposed=false;function checkSavedSummaryOnOpen(){if(savedSummaryOpenPromise)return savedSummaryOpenPromise;if(savedSummaryDisposed||!token())return Promise.resolve(null);savedSummaryOpenPromise=(async()=>{const controller=new AbortController;savedSummaryController=controller;const timeout=setTimeout(()=>controller.abort(),18e3);try{const response=await fetch(worker()+'/rastreamento/summary',{headers:{Authorization:'Bearer '+token()},cache:'no-store',signal:controller.signal});if(!response.ok)return null;const summary=await response.json();if(savedSummaryDisposed||summary?.ok!==true||summary.available!==true)return null;if(snapshotLoadPromise)await snapshotLoadPromise;if(summarySnapshotDrainPromise)await summarySnapshotDrainPromise;if(savedSummaryDisposed)return null;const version=trackingSnapshotVersion(summary.updated_at),loaded=trackingSnapshotVersion(loadedSnapshotAt);if(version&&loaded>version)return summary;const total=Number(summary.total),scope=summary.tracking_scope;const sourceChanged=scope&&['version','source_etag','raw_etag'].some(key=>scope[key]!==undefined&&String(scope[key]||'')!==String(preloadedSnapshotData?.tracking_scope?.[key]||''));const countChanged=summary.total!==null&&summary.total!==undefined&&Number.isSafeInteger(total)&&total>=0&&total!==preloadedSnapshotData?.items?.length;if(version>loaded){summarySnapshotTarget=Math.max(summarySnapshotTarget,version);summarySnapshotAttempted=Math.min(summarySnapshotAttempted,loaded);await drainSummarySnapshotRefresh()}else if(!preloadedSnapshotData||countChanged||sourceChanged)await preloadTrackingSnapshot(true);return summary}catch(error){if(!savedSummaryDisposed)console.debug('Conferência do resumo salvo:',error);return null}finally{clearTimeout(timeout);if(savedSummaryController===controller)savedSummaryController=null}})().finally(()=>{savedSummaryOpenPromise=null});return savedSummaryOpenPromise}panel.__lisaTrackingSummaryDispose=()=>{savedSummaryDisposed=true;savedSummaryController?.abort()};const resumoBtn=panel.querySelector('[data-carrier-resumo-open]');if(resumoBtn){resumoBtn.onclick=e=>{e.preventDefault();e.stopPropagation();open('all')}}const sideHost=panel.querySelector('[data-carrier-status-table]');if(sideHost){sideHost.onclick=e=>{const b=e.target.closest?.('[data-tracking-view]');if(!b||!sideHost.contains(b))return;e.preventDefault();e.stopPropagation();open(String(b.dataset.trackingView||'without'))};sideHost.dataset.trackingBound='1'}panel.addEventListener('lisa:tracking-side-refresh',()=>{if(token()&&typeof panel.__loadTrackingSummaryCards==='function')panel.__loadTrackingSummaryCards()});scheduleTrackingPreload();$('[data-cte-close]').addEventListener('click',close);modal.addEventListener('click',e=>{if(e.target===modal)close()});prazoToggle.addEventListener('click',e=>{e.stopPropagation();const show=prazoMenu.hidden;prazoMenu.hidden=!show;prazoToggle.setAttribute('aria-expanded',String(show));if(show)prazoPosicionar()});prazoMenu.addEventListener('click',e=>e.stopPropagation());document.addEventListener('click',e=>{if(!prazoWrap.contains(e.target)&&!prazoMenu.contains(e.target))prazoFechar()});window.addEventListener('resize',()=>{if(!prazoMenu.hidden)prazoPosicionar()});window.addEventListener('scroll',()=>{if(!prazoMenu.hidden)prazoPosicionar()},true);prazoMenu.addEventListener('keydown',e=>{if(e.key==='Escape'){prazoFechar();prazoToggle.focus()}if(e.key==='Enter'&&e.target.matches('input')){e.preventDefault();prazoAplicar()}});$('[data-cte-prazo-aplicar]').addEventListener('click',prazoAplicar);$('[data-cte-prazo-limpar]').addEventListener('click',()=>{resetPrazo();prazoReaplicar()});if(noteSearch)noteSearch.addEventListener('input',()=>{page=1;if(noteSearch.value.trim()&&view!=='all')resetFilters();apply()});for(const b of $$('[data-cte-view]'))b.addEventListener('click',()=>setView(b.dataset.cteView));const campaignHost=$('[data-cte-campaigns]');if(campaignHost)campaignHost.addEventListener('click',e=>{const b=e.target.closest('[data-cte-campaign]');if(!b)return;const sel=$('[data-cte-filter="campanha"]');if(sel)sel.value=String(b.dataset.cteCampaign||'');page=1;renderCampaignButtons();apply()});for(const el of $$('.cte-filter'))el.addEventListener(el.tagName==='SELECT'?'change':'input',()=>{page=1;renderCampaignButtons();apply()});const head=$('.cte-table thead');head.addEventListener('click',e=>{const btn=e.target.closest('.cte-sort-btn');if(!btn)return;const th=btn.closest('[data-cte-sort]');if(!th)return;const key=th.dataset.cteSort,type=th.dataset.sortType||'text';if(sort.key!==key)sort={key,type,dir:type==='number'?'desc':'asc'};else sort={key,type,dir:sort.dir==='asc'?'desc':'asc'};page=1;apply()});$('[data-cte-prev]').addEventListener('click',()=>{if(page>1){page--;apply()}});$('[data-cte-next]').addEventListener('click',()=>{const pages=Math.max(1,Math.ceil(filtered.length/pageSize));if(page<pages){page++;apply()}});copyBtn.addEventListener('click',copy);downloadBtn.addEventListener('click',download);panel.addEventListener('lisa:tickets-updated',()=>{if(typeof panel.__loadTrackingSummaryCards==='function')panel.__loadTrackingSummaryCards();setTimeout(()=>preloadTrackingSnapshot(true),500)})}initLisa2()};initLisaCteModule()}

;

(()=>{'use strict';const root=document.getElementById('lisa-app-vg-native')||document.querySelector('.lisa-app[data-worker]');if(!root)return;let tries=0,workbook=null,currentCarrier='',currentMeta=null,currentSheet='';const worker=()=>String(root.dataset.worker||'').replace(/\/$/,'');const token=()=>sessionStorage.getItem('lisa_panel_token')||'';const esc=v=>String(v??'').replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));const fmtDate=v=>{if(!v)return'Nunca importado';const d=new Date(v);return Number.isNaN(d.getTime())?String(v):d.toLocaleString('pt-BR',{timeZone:'America/Sao_Paulo',day:'2-digit',month:'2-digit',year:'numeric',hour:'2-digit',minute:'2-digit'})};async function jsonReq(path,opt={}){const h={...opt.headers||{}};if(token())h.Authorization='Bearer '+token();const controller=new AbortController(),timer=setTimeout(()=>controller.abort(),45000);try{const r=await fetch(worker()+path,{...opt,headers:h,signal:controller.signal});const text=await r.text();let d=null;try{d=text?JSON.parse(text):null}catch{}if(r.status===401)throw Error('Sessão expirada.');if(!r.ok){const detail=d?.error||d?.detail||text.replace(/<[^>]*>/g,' ').replace(/\s+/g,' ').slice(0,1200)||r.statusText;throw Error('HTTP '+r.status+' em '+path+' · '+detail);}if(!d||typeof d!=='object')throw Error('Resposta inválida em '+path+' · '+text.slice(0,300));return d;}catch(e){if(e.name==='AbortError')throw Error('Tempo limite de 45 segundos em '+path+'.');if(e instanceof TypeError)throw Error('Sem resposta HTTP acessível em '+path+' · '+String(e.message||e));throw e;}finally{clearTimeout(timer);}}async function ensureTransportadorasXlsx(){if(window.XLSX)return window.XLSX;await new Promise((res,rej)=>{const old=document.querySelector('script[data-carrier-files-xlsx]');if(old){if(window.XLSX)return res();old.addEventListener('load',res,{once:true});old.addEventListener('error',rej,{once:true});return}const s=document.createElement('script');s.src='https://cdn.jsdelivr.net/npm/xlsx@0.18.5/dist/xlsx.full.min.js';s.async=true;s.dataset.carrierFilesXlsx='1';s.onload=res;s.onerror=()=>rej(new Error('Não foi possível carregar o leitor de planilhas.'));document.head.appendChild(s)});return window.XLSX}function carrierFileNorm(v){return String(v??'').normalize('NFD').replace(/[\u0300-\u036f]/g,'').toUpperCase().replace(/[^A-Z0-9]+/g,' ').trim()}function carrierCell(v){return v===null||v===undefined?'':String(v).trim()}function carrierNoteKeys(v){const s=carrierCell(v).replace(/\.0+$/,'');const hits=s.match(/\d{3,}/g)||[];return[...new Set(hits.map(x=>x.replace(/^0+(?=\d)/,'')).filter(Boolean))]}function carrierCteKey(v){return carrierFileNorm(v).replace(/\s+/g,'')}function carrierDateMs(v){if(typeof v==='number'&&Number.isFinite(v)&&v>2e4&&v<8e4)return Math.round((v-25569)*864e5);if(v instanceof Date&&!Number.isNaN(v.getTime()))return v.getTime();const s=carrierCell(v);if(!s)return 0;let m=s.match(/^(\d{1,2})[\/\-.](\d{1,2})[\/\-.](\d{2,4})(?:[^\d]+(\d{1,2}):(\d{2})(?::(\d{2}))?)?/);if(m){let y=Number(m[3]);if(y<100)y+=2e3;return new Date(y,Number(m[2])-1,Number(m[1]),Number(m[4]||0),Number(m[5]||0),Number(m[6]||0)).getTime()}m=s.match(/^(\d{4})-(\d{2})-(\d{2})/);if(m)return new Date(Number(m[1]),Number(m[2])-1,Number(m[3])).getTime();const t=Date.parse(s);return Number.isNaN(t)?0:t}function carrierDateText(v,XLSX){if(v===null||v===undefined||v==='')return'';if(typeof v==='number'&&Number.isFinite(v)){const d=XLSX?.SSF?.parse_date_code?.(v);if(d&&d.y&&d.m&&d.d){const base=`${String(d.d).padStart(2,'0')}/${String(d.m).padStart(2,'0')}/${d.y}`;return d.H||d.M||d.S?`${base} ${String(d.H||0).padStart(2,'0')}:${String(d.M||0).padStart(2,'0')}:${String(Math.floor(d.S||0)).padStart(2,'0')}`:base}}if(v instanceof Date&&!Number.isNaN(v.getTime())){const base=`${String(v.getDate()).padStart(2,'0')}/${String(v.getMonth()+1).padStart(2,'0')}/${v.getFullYear()}`;return v.getHours()||v.getMinutes()||v.getSeconds()?`${base} ${String(v.getHours()).padStart(2,'0')}:${String(v.getMinutes()).padStart(2,'0')}:${String(v.getSeconds()).padStart(2,'0')}`:base}const s=carrierCell(v);let m=s.match(/^(\d{4})-(\d{2})-(\d{2})/);if(m)return`${m[3]}/${m[2]}/${m[1]}`;m=s.match(/^(\d{1,2})[\/\-.](\d{1,2})[\/\-.](\d{4})/);if(m)return`${String(Number(m[1])).padStart(2,'0')}/${String(Number(m[2])).padStart(2,'0')}/${m[3]}`;return s}function carrierFindHeader(headers,exact=[],contains=[]){for(const n of exact){const i=headers.indexOf(n);if(i>=0)return i}for(let i=0;i<headers.length;i++){const h=headers[i];if(contains.some(x=>h.includes(x)))return i}return-1}function carrierCepText(value){const s=carrierCell(value).replace(/\.0+$/,'').replace(/[.\-\s]/g,'');return/^\d{1,8}$/.test(s)&&Number(s)>0?s.padStart(8,'0'):''}function carrierCttHeaderIndexes(headers){const cep=carrierFindHeader(headers,['CEP DESTINATARIO','CEP DO DESTINATARIO','CEP DESTINO','CEP DE DESTINO','CEP ENTREGA','CEP DE ENTREGA','CEP'],[]);const issueCte=headers.findIndex(h=>/^(?:(?:DATA|DT) )?(?:(?:DE|DA) )?EMISSAO (?:(?:DO|DE) )?(?:CT ?E|CTRC|CONHECIMENTO)(?: DE TRANSPORTE)?$/.test(h)||/^(?:CT ?E|CTRC|CONHECIMENTO) (?:DATA )?(?:DE )?EMISSAO$/.test(h));return{cep,issueCte}}function carrierCttDateYmd(y,m,d){if(!Number.isInteger(y)||y<1e3||y>9999||m<1||m>12||d<1||d>31)return'';const date=new Date(Date.UTC(y,m-1,d));if(date.getUTCFullYear()!==y||date.getUTCMonth()!==m-1||date.getUTCDate()!==d)return'';return String(y).padStart(4,'0')+'-'+String(m).padStart(2,'0')+'-'+String(d).padStart(2,'0')}function carrierCttDateIso(value,XLSX){const inSaoPaulo=date=>{if(!(date instanceof Date)||!Number.isFinite(date.getTime()))return'';const p=new Intl.DateTimeFormat('en-CA',{timeZone:'America/Sao_Paulo',year:'numeric',month:'2-digit',day:'2-digit'}).formatToParts(date).reduce((a,x)=>(a[x.type]=x.value,a),{});return carrierCttDateYmd(Number(p.year),Number(p.month),Number(p.day))};if(value instanceof Date)return inSaoPaulo(value);const raw=carrierCell(value);if(typeof value==='number'||/^\d+(?:\.\d+)?$/.test(raw)){const serial=Number(value);if(!Number.isFinite(serial)||serial<1||serial>=2958466||Math.floor(serial)===60)return'';const parsed=XLSX?.SSF?.parse_date_code?.(serial);if(parsed)return carrierCttDateYmd(Number(parsed.y),Number(parsed.m),Number(parsed.d));const day=Math.floor(serial),date=new Date(Date.UTC(1899,11,31)+(day>60?day-1:day)*864e5);return carrierCttDateYmd(date.getUTCFullYear(),date.getUTCMonth()+1,date.getUTCDate())}let m=raw.match(/^(\d{1,2})[\/.-](\d{1,2})[\/.-](\d{4})(?:[ T](\d{1,2}):(\d{2})(?::(\d{2})(?:[.,]\d+)?)?)?$/);if(m){if(Number(m[4]||0)>23||Number(m[5]||0)>59||Number(m[6]||0)>59)return'';return carrierCttDateYmd(Number(m[3]),Number(m[2]),Number(m[1]))}m=raw.match(/^(\d{4})-(\d{2})-(\d{2})(?:[Tt ](\d{2}):(\d{2})(?::(\d{2})(?:\.\d+)?)?([Zz]|[+-]\d{2}:?\d{2})?)?$/);if(!m||Number(m[4]||0)>23||Number(m[5]||0)>59||Number(m[6]||0)>59)return'';const day=carrierCttDateYmd(Number(m[1]),Number(m[2]),Number(m[3]));if(!day)return'';return m[7]?inSaoPaulo(new Date(raw)):day}function carrierCttWorkbookRecord(row,ix,carrierName,XLSX){const generatedVipp=/CORREIO|VIPP/.test(carrierFileNorm(carrierName))&&ix.h.some(h=>h.includes('CONHECIMENTO EDI'))&&ix.h.includes('HISTORICO EDI');const rawDate=ix.issueCte>=0&&!generatedVipp?row[ix.issueCte]:'';const issue=carrierCttDateIso(rawDate,XLSX);return{cep_destino:ix.cep>=0?carrierCepText(row[ix.cep]):'',data_emissao_cte:issue||''}}function carrierCttSupplementWorkbook(wb,XLSX,carrierName,by_note,by_cte){if(!wb.SheetNames.includes('Geral'))return;const fills=new WeakMap;function supplement(target,record){if(!target)return;let state=fills.get(target);if(!state){state={cepOriginal:!!target.cep_destino,dateOriginal:!!target.data_emissao_cte,ceps:new Set,dates:new Set};fills.set(target,state)}if(!state.cepOriginal&&record.cep_destino){state.ceps.add(record.cep_destino);target._ctt_cep_conflict=state.ceps.size>1;target.cep_destino=state.ceps.size===1?[...state.ceps][0]:''}if(!state.dateOriginal&&record.data_emissao_cte){state.dates.add(record.data_emissao_cte);target._ctt_emissao_conflict=state.dates.size>1;target.data_emissao_cte=state.dates.size===1?[...state.dates][0]:''}}for(const name of wb.SheetNames){if(name==='Geral')continue;const rows=XLSX.utils.sheet_to_json(wb.Sheets[name],{header:1,defval:'',raw:true,blankrows:false});let best=null;for(let i=0;i<Math.min(rows.length,30);i++){const ix=carrierHeaderIndexes(rows[i]);if(ix.nf<0||ix.cep<0&&ix.issueCte<0)continue;const score=(ix.nf>=0?6:0)+(ix.cte>=0?4:0)+(ix.cep>=0?2:0)+(ix.issueCte>=0?2:0);if(!best||score>best.score)best={row:i,ix,score}}if(!best)continue;for(let i=best.row+1;i<rows.length;i++){const row=rows[i]||[],ix=best.ix,notes=carrierNoteKeys(row[ix.nf]),code=ix.cte>=0?carrierCteKey(carrierCell(row[ix.cte]).replace(/\.0+$/,'')):'';if(!notes.length)continue;const record=carrierCttWorkbookRecord(row,ix,carrierName,XLSX);if(!record.cep_destino&&!record.data_emissao_cte)continue;for(const nf of notes){const target=by_note[nf],targetCode=carrierCteKey(target?.cte||'');if(target&&!(code&&targetCode&&code!==targetCode))supplement(target,record)}const target=code?by_cte[code]:null;if(target&&carrierNoteKeys(target.nota_fiscal).some(n=>notes.includes(n)))supplement(target,record)}}}function carrierHeaderIndexes(row){const h=(row||[]).map(carrierFileNorm);return{h,...carrierCttHeaderIndexes(h),nf:carrierFindHeader(h,['NOTA FISCAL','NF','NFE','NUMERO NF','NR NF','NRO NF','NUMERO NOTA','NOTA'],['NOTA FISCAL','NUMERO NF','NRO NF']),cte:carrierFindHeader(h,['CTE','CT E','AWB','OBJETO','RASTREIO','RASTREAMENTO','CODIGO RASTREAMENTO','CODIGO DE RASTREIO','TRACKING','REGISTRO','REGISTRO OBJETO','CODIGO OBJETO','ETIQUETA','ETIQUETA POSTAGEM'],['CTE','CONHECIMENTO','RASTRE','AWB','OBJETO','REGISTRO','ETIQUETA','CODIGO OBJETO']),status:carrierFindHeader(h,['STATUS TR','STATUS ENTREGA','STATUS','SITUACAO','SITUACAO ENTREGA','ULTIMO STATUS','ULTIMO EVENTO'],['STATUS','SITUACAO']),occ:carrierFindHeader(h,['OCORRENCIA TR','OCORRENCIA','EVENTO','ULTIMO EVENTO','DESCRICAO OCORRENCIA'],['OCORRENCIA','EVENTO']),forecast:carrierFindHeader(h,['PREVISAO DE ENTREGA','PREVISAO ENTREGA','PREVISAO','DATA PREVISTA','DATA PREVISAO','PREVISAO TR'],['PREVISAO','DATA PREVISTA']),statusDate:carrierFindHeader(h,['DATA STATUS TR','DATA DO STATUS TR','DATA STATUS','DATA OCORRENCIA','DATA DA OCORRENCIA','DATA EVENTO','ULTIMA ATUALIZACAO TR','ULTIMA ATUALIZACAO','DATA ATUALIZACAO'],['DATA STATUS','DATA OCORRENCIA','DATA EVENTO','ULTIMA ATUALIZACAO']),deliveryDate:carrierFindHeader(h,['DATA ENTREGA','DATA DE ENTREGA','ENTREGA REALIZADA EM','DT ENTREGA'],['DATA ENTREGA','DATA DE ENTREGA']),issueDate:carrierFindHeader(h,['DATA EMISSAO TR','DATA DE EMISSAO TR','DATA EMISSAO CTE','DATA DE EMISSAO CTE','DATA EMISSAO','DATA DE EMISSAO','DATA DA EMISSAO','EMISSAO','EMISSAO NF','DATA EMISSAO NF','DATA DE EMISSAO NF','DATA POSTAGEM','DATA DE POSTAGEM','DATA COLETA','DATA DE COLETA'],['DATA DE EMISSAO','DATA DA EMISSAO','DATA EMISSAO','EMISSAO','DATA POSTAGEM','DATA DE POSTAGEM','DATA COLETA','DATA DE COLETA']),prazo:carrierFindHeader(h,['PRAZO TR','PRAZO','PRAZO ENTREGA','DIAS PRAZO'],['PRAZO'])}}async function buildCarrierTrackingIndex(file,carrierName,allowEmpty=false){const XLSX=await ensureTransportadorasXlsx();const ab=await file.arrayBuffer();const wb=XLSX.read(ab,{type:'array',cellDates:false});const by_note={},by_cte={};let total=0,validSheets=0;const sheetNames=wb.SheetNames.includes('Geral')?['Geral']:[...wb.SheetNames];for(const sheetName of sheetNames){const ws=wb.Sheets[sheetName];const rows=XLSX.utils.sheet_to_json(ws,{header:1,defval:'',raw:true,blankrows:false});if(!rows.length)continue;let best=null;for(let i=0;i<Math.min(rows.length,30);i++){const ix=carrierHeaderIndexes(rows[i]);const score=(ix.nf>=0?6:0)+(ix.cte>=0?4:0)+(ix.status>=0?4:0)+(ix.occ>=0?1:0)+(ix.forecast>=0?1:0);if(!best||score>best.score)best={row:i,ix,score}}if(!best||best.ix.nf<0)continue;validSheets++;const ix=best.ix;if(carrierFileNorm(sheetName)==='GERAL'){ix.nf=0;ix.cte=1}for(let i=best.row+1;i<rows.length;i++){const row=rows[i]||[];const notes=carrierNoteKeys(row[ix.nf]);if(!notes.length)continue;const cte=ix.cte>=0?carrierCell(row[ix.cte]).replace(/\.0+$/,'').trim():'';const occ=ix.occ>=0?carrierCell(row[ix.occ]):'';const status=ix.status>=0?carrierCell(row[ix.status]):occ;const forecast=ix.forecast>=0?carrierDateText(row[ix.forecast],XLSX):'';const dataStatus=ix.statusDate>=0?carrierDateText(row[ix.statusDate],XLSX):'';const dataEntrega=ix.deliveryDate>=0?carrierDateText(row[ix.deliveryDate],XLSX):'';const dataEmissao=ix.issueDate>=0?carrierDateText(row[ix.issueDate],XLSX):'';const prazo=ix.prazo>=0?carrierCell(row[ix.prazo]):'';const cttInputs=carrierCttWorkbookRecord(row,ix,carrierName,XLSX);const score=carrierDateMs(dataStatus)||carrierDateMs(dataEntrega)||carrierDateMs(dataEmissao)||i;const rec={cte,registro:cte,status_tr:status,status,ocorrencia:occ,previsao:forecast,data_status:dataStatus,data_entrega:dataEntrega,data_emissao_tr:dataEmissao,...cttInputs,prazo,transportadora:carrierName,_score:score,_row:i,_sheet:sheetName};for(const nf of notes){if(!by_note[nf]||Number(by_note[nf]._score||0)<=score)by_note[nf]={...rec,nota_fiscal:nf}}const ck=carrierCteKey(cte);if(ck&&(!by_cte[ck]||Number(by_cte[ck]._score||0)<=score))by_cte[ck]={...rec,nota_fiscal:notes.join(' | ')};total++}}carrierCttSupplementWorkbook(wb,XLSX,carrierName,by_note,by_cte);if(!validSheets)throw new Error('Não encontrei a coluna Nota Fiscal no arquivo '+file.name+'.');if(!Object.keys(by_note).length&&!allowEmpty)throw new Error('Nenhuma Nota Fiscal válida foi encontrada em '+file.name+'.');return{version:6,total,by_note,by_cte,ctt_inputs:'cep_destino+emissao_cte_explicita',sheets:validSheets,match_by:'nota_fiscal',tracking_field:'cte_registro_rastreio',forecast_rule:'planilha_transportadora_exceto_jt_latam',issue_date_rule:'data_de_emissao_transportadora'}}root.__lisaBuildCarrierTrackingIndex=buildCarrierTrackingIndex;function detectCarrierFromFilename(fileName,items){const n=carrierFileNorm(String(fileName||'').replace(/\.[^.]+$/,''));const rules=[['CORREIOS / VIPP',['CORREIOS','VIPP']],['TOTAL EXPRESS',['TOTAL EXPRESS','TOTAL 2026','TOTAL']],['TNT / FEDEX',['TNT','FEDEX']],['BRASPRESS',['BRASPRESS']],['CONTINENTAL',['CONTINENTAL']],['FAVORITA',['FAVORITA']],['JAMEF',['JAMEF']],['J&T',['J T','JT EXPRESS','JTX','J T EXPRESS']],['LATAM',['LATAM','TAM CARGO']],['PATRUS',['PATRUS']],['RODOMAX',['RODOMAX']],['ALFA',['ALFA']]];for(const[name,keys]of rules){if(keys.some(k=>n.includes(k))){const hit=(items||[]).find(x=>carrierFileNorm(x.name)===carrierFileNorm(name));if(hit)return hit}}return null}function init(){const cteWrap=document.getElementById('lisacte-vg-native');const cte=cteWrap?.querySelector('[data-cte-modal]');const openBtn=cteWrap?.querySelector('[data-transportadoras-open]');if(!cte||!openBtn){if(tries++<300)setTimeout(init,100);return}if(cteWrap.__carrierFilesInitialized)return;cteWrap.__carrierFilesInitialized=true;document.querySelectorAll('.carrier-files-modal[data-owner="vg-native"],.carrier-files-report-modal[data-owner="vg-native"]').forEach(el=>el.remove());const modal=document.createElement('div');modal.className='carrier-files-modal';modal.dataset.owner='vg-native';modal.hidden=true;modal.innerHTML=`<section class="carrier-files-shell"><header class="carrier-files-head"><div class="carrier-files-head-main"><h3>Transportadoras</h3><p>Base acumulada por nota e transportadora. Planilhas e APIs adicionam ou atualizam os registros, preservando os dados anteriores.</p></div><div class="carrier-files-head-actions"><button type="button" class="carrier-files-download-all" data-carrier-files-download-all>BAIXAR BASE COMPLETA</button><button type="button" class="carrier-files-download-all" data-base-apis>ATUALIZAR APIs</button><button type="button" class="carrier-files-close" data-carrier-files-close>×</button></div></header><div class="carrier-files-body"><div class="carrier-files-status" data-carrier-files-status></div><div class="carrier-files-grid" data-carrier-files-grid></div><div class="carrier-files-bulk"><div class="carrier-files-drop" data-carrier-files-drop tabindex="0"><strong>UPLOAD — ARRASTE UM OU MAIS ARQUIVOS AQUI</strong><span>ou clique para selecionar. A análise e a importação dos dados começam automaticamente.</span><input type="file" data-carrier-files-multi multiple accept=".xlsx,.xlsm" hidden></div><div class="carrier-files-batch" data-carrier-files-batch></div></div></div></section>`;document.body.appendChild(modal);const reportModal=document.createElement('div');reportModal.className='carrier-files-report-modal';reportModal.dataset.owner='vg-native';reportModal.hidden=true;reportModal.innerHTML=`<section class="carrier-files-report-shell"><header class="carrier-files-head"><div class="carrier-files-head-main"><h3 data-carrier-files-report-title>Relatório</h3><div class="carrier-files-report-meta" data-carrier-files-report-meta></div></div><div class="carrier-files-report-actions"><input type="search" class="carrier-files-report-search" data-carrier-files-report-search placeholder="Buscar em todo o arquivo" autocomplete="off"><button type="button" class="carrier-files-report-download" data-carrier-files-report-download disabled>BAIXAR</button><button type="button" class="carrier-files-close" data-carrier-files-report-close>×</button></div></header><main class="carrier-files-report-main" data-carrier-files-report-main><div class="carrier-files-empty">Selecione uma transportadora.</div></main><div class="carrier-files-sheet-tabs" data-carrier-files-sheet-tabs></div></section>`;document.body.appendChild(reportModal);let currentAlfaMonth=null;const grid=modal.querySelector('[data-carrier-files-grid]'),status=modal.querySelector('[data-carrier-files-status]'),reportMain=reportModal.querySelector('[data-carrier-files-report-main]'),tabs=reportModal.querySelector('[data-carrier-files-sheet-tabs]'),reportSearch=reportModal.querySelector('[data-carrier-files-report-search]'),reportDownload=reportModal.querySelector('[data-carrier-files-report-download]');let reportSearchTimer=0,searchHits=[];let correiosManualBusy=false;async function runCorreiosNow(){status.textContent='API Correios desativada. Use Atualizar VIPP agora no programa LISA + VIPP.'}let braspressManualBusy=false;const pauseManual=ms=>new Promise(r=>setTimeout(r,ms));async function runBraspressNow(){if(braspressManualBusy){status.textContent='A atualização manual da Braspress já está em andamento.';return}braspressManualBusy=true;status.className='carrier-files-status';status.textContent='Braspress: iniciando atualização manual pela API...';const started=Date.now(),MAX_TIME=45*60*1e3;let action='start';try{while(Date.now()-started<MAX_TIME){const result=await jsonReq('/transportadoras-arquivos/braspress/run-now',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({action})});if(result.phase==='failed'||!result.ok)throw new Error(result.error||'Falha na atualização Braspress.');if(result.phase==='completed'){await loadStatus();status.textContent='Braspress atualizada com sucesso! '+Number(result.total||0).toLocaleString('pt-BR')+' notas consultadas.';status.className='carrier-files-status is-ok';root.dispatchEvent(new CustomEvent('lisa:carrier-files-updated'));await refreshTrackingCounts();return}const processed=Number(result.processed||0),total=Number(result.total||0);status.textContent=total?`Braspress: consultando notas ${processed.toLocaleString('pt-BR')} / ${total.toLocaleString('pt-BR')}...`:'Braspress: preparando notas do Monitor de Pedidos...';action='continue';await pauseManual(result.phase==='already_processing'?2300:350)}throw new Error('A atualização ainda não terminou. Reabra a tag para retomar a execução.')}catch(err){status.textContent='Braspress: '+(err?.message||'Falha ao consultar API.');status.className='carrier-files-status is-error';await loadStatus().catch(()=>{});status.textContent='Braspress: '+(err?.message||'Falha ao consultar API.');status.className='carrier-files-status is-error'}finally{braspressManualBusy=false}}function isAltGrClick(event){return!!(event.getModifierState?.('AltGraph')||event.ctrlKey&&event.altKey)}function carrierSaoPauloDay(value){const dt=new Date(value);if(!Number.isFinite(dt.getTime()))return'';const parts=new Intl.DateTimeFormat('en-US',{timeZone:'America/Sao_Paulo',year:'numeric',month:'2-digit',day:'2-digit'}).formatToParts(dt);const date={};for(const item of parts)date[item.type]=item.value;return date.year+'-'+date.month+'-'+date.day}function carrierCardDateClass(carrier){if(!carrier?.available||!carrier?.updated_at)return'';const updated=new Date(carrier.updated_at).getTime();if(!Number.isFinite(updated))return'';const age=Date.now()-updated;if(age>24*60*60*1e3)return'is-stale';if(age>=-5*60*1e3&&carrierSaoPauloDay(updated)===carrierSaoPauloDay(Date.now()))return'is-updated-today';return''}let patrusSyncBusy=false;async function syncPatrus(button){if(patrusSyncBusy)return;patrusSyncBusy=true;const old=button.textContent;button.disabled=true;button.textContent='API...';try{let d=await jsonReq('/patrus/sync',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({action:'start'})});for(let step=0;!d.done;step++){if(step>4e3)throw Error('A carga continua em segundo plano. Consulte o andamento novamente.');if(!d.run_id){if(d.busy){await new Promise(r=>setTimeout(r,1500));d=await jsonReq('/patrus/sync',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({action:'start'})});continue}throw Error('Não foi possível iniciar a carga Patrus.')}status.className='carrier-files-status';status.textContent='Patrus: consultando '+(d.processed||0)+' de '+(d.total||0)+' notas...';if(d.busy)await new Promise(r=>setTimeout(r,1500));d=await jsonReq('/patrus/sync',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({action:'step',run_id:d.run_id})})}if(d.phase==='failed')throw Error(d.last_error||'A API não retornou dados confirmados. Relatório anterior preservado.');await loadStatus();root.dispatchEvent(new CustomEvent('lisa:carrier-files-updated'));await refreshTrackingCounts();status.textContent='Patrus: '+d.found+' notas atualizadas pela API.'+(d.not_found?' '+d.not_found+' sem rastreamento; dados anteriores preservados.':'')+(d.errors?' '+d.errors+' consultas com erro; dados anteriores preservados.':'');status.className='carrier-files-status '+(d.errors?'is-error':'is-ok')}catch(e){status.textContent='Patrus: '+(e.message||'Falha na atualização.');status.className='carrier-files-status is-error'}finally{patrusSyncBusy=false;button.disabled=false;button.textContent=old}}let jamefSyncBusy=false;async function syncJamef(button){if(jamefSyncBusy)return;jamefSyncBusy=true;const old=button.textContent;button.disabled=true;button.textContent='API...';let cursor='',processed=0,invalid=0,pages=0;try{do{status.className='carrier-files-status';status.textContent='Jamef: conferindo eventos recebidos... '+pages+' lotes lidos.';const d=await jsonReq('/jamef/webhook/reprocessar',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({cursor})});processed+=Number(d.processed||0);invalid+=Number(d.invalid||0);pages++;cursor=d.has_more?String(d.cursor||''):'';if(d.has_more&&!cursor)throw Error('Cursor de eventos Jamef ausente. Tente novamente.')}while(cursor);await loadStatus();root.dispatchEvent(new CustomEvent('lisa:carrier-files-updated'));await refreshTrackingCounts();status.textContent='Jamef atualizada. Excel com as abas Geral e Jamef preparado. '+processed+' eventos anteriores processados.'+(invalid?' '+invalid+' eventos com formato não reconhecido; originais preservados.':' As novas ocorrências serão recebidas automaticamente.');status.className='carrier-files-status '+(invalid?'is-error':'is-ok')}catch(e){status.textContent='Jamef: '+(e.message||'Falha ao sincronizar.');status.className='carrier-files-status is-error'}finally{jamefSyncBusy=false;button.disabled=false;button.textContent=old}}let jtSyncBusy=false;
async function syncJt(button){
if(jtSyncBusy)return;
jtSyncBusy=true;
const prev=button.textContent;
button.disabled=true;button.textContent='API...';
const start=Date.now(),maxTime=12*60*1000;
let batchCount=0,checked=0,matched=0,completed=false,eligible=0,empty=false;
status.className='carrier-files-status';status.textContent='J&T: iniciando atualização via API...';
try{
while(Date.now()-start<maxTime&&batchCount<500){
const d=await jsonReq('/jt/sync',{method:'POST'});
if(d.reason==='busy'||d.reason==='concurrent'){
status.textContent='J&T: outra execução está em andamento. Aguardando liberação do lote...';
await new Promise(resolve=>setTimeout(resolve,2200));continue;
}
if(!d.ok)throw Error(d.error||'A API J&T não concluiu a consulta.');
if(d.reason==='no_pending_codes'){
eligible=Number(d.eligible||0);empty=eligible===0;completed=true;break;
}
batchCount++;
eligible=Number(d.eligible||0);checked+=Number(d.scanned||0);matched+=Number(d.matched||0);
completed=d.done===true;
const cursor=Number(d.cursor||0);
status.className='carrier-files-status';
status.textContent=`J&T: ${completed?eligible:Math.min(cursor,eligible)} de ${eligible} rastreios percorridos; ${matched} eventos identificados. ${completed?'Concluído.':'Continuando em lotes de 30...'}`;
if(completed)break;
await new Promise(resolve=>setTimeout(resolve,350));
}
await loadStatus();
root.dispatchEvent(new CustomEvent('lisa:carrier-files-updated'));
await refreshTrackingCounts();
if(empty){status.textContent='J&T: não há códigos de rastreio válidos vinculados às notas do SYSEMP. Nenhuma consulta foi enviada à API.';status.className='carrier-files-status is-error'}
else if(completed){status.textContent=`J&T: atualização concluída. ${checked.toLocaleString('pt-BR')} códigos consultados, ${matched.toLocaleString('pt-BR')} eventos identificados. Dados salvos no Cloud.`;status.className='carrier-files-status is-ok'}
else{status.textContent=`J&T: ${checked.toLocaleString('pt-BR')} códigos consultados nesta tentativa. A atualização restante continua na rotina agendada; clique novamente para continuar manualmente.`;status.className='carrier-files-status'}
}catch(e){
const message='J&T: '+(e.message||'Falha ao consultar a API.');
await loadStatus().catch(()=>{});
status.textContent=message;status.className='carrier-files-status is-error';
}finally{jtSyncBusy=false;button.disabled=false;button.textContent=prev}
}
async function loadStatus(){status.textContent='Carregando base...';status.className='carrier-files-status';try{
const d=await jsonReq('/carrier-base/meta');currentMeta=d;grid.innerHTML=(d.carriers||[]).map(x=>`<article class="carrier-files-card ${carrierCardDateClass(x)}"><button type="button" class="carrier-files-card-main" data-carrier-files-report="${esc(x.key)}" ${x.available?'':'disabled'}><span class="carrier-files-card-name">${esc(x.name)}${x.api?`<span class="carrier-api-dot ${x.api.active?'is-active':'is-inactive'}" role="img" aria-label="${esc(x.api.label)}" title="${esc(x.api.label+(x.api.last_success_at?' · Último sucesso: '+fmtDate(x.api.last_success_at):''))}"></span>`:''}</span><span class="carrier-files-date">${x.updated_at?'Última atualização: '+esc(fmtDate(x.updated_at)):'Sem dados importados'}</span></button><div class="carrier-files-card-actions"><button type="button" class="carrier-files-download" data-carrier-files-download="${esc(x.key)}" ${x.available?'':'disabled'}>BAIXAR</button></div></article>`).join('');status.textContent='';
}catch(e){status.textContent=e.message||'Falha ao ler base.';status.className='carrier-files-status is-error';}}
async function downloadCarrierFile(key,button){const old=button?.textContent;if(button){button.disabled=true;button.textContent='BAIXANDO...';}try{const wb=await baseWorkbook(key,true);window.XLSX.writeFile(wb,'base_'+key+'.xlsx',{compression:true});}finally{if(button){button.disabled=false;button.textContent=old||'BAIXAR';}}}
async function downloadAllCarrierFiles(button){const old=button.textContent;button.disabled=true;button.textContent='PREPARANDO...';try{const wb=await baseWorkbook('',true);window.XLSX.writeFile(wb,'BASE_TRANSPORTADORAS.xlsx',{compression:true});}catch(e){status.textContent=e.message;status.className='carrier-files-status is-error';}finally{button.disabled=false;button.textContent=old;}}
const baseColumns=[['nota_fiscal','Nota Fiscal'],['cte','CTE'],['data_emissao_tr','Data de emissão'],['data_entrega','Data de entrega'],['status_tr','Status'],['data_status','Última atualização'],['previsao','Data de previsão'],['nf_transportadora','NF - Transportadora'],['nf_sysemp','NF Sysemp'],['valor_cte','Valor CTE'],['transportadora','Transportadora'],['volumes','Volumes'],['cep_destino','CEP destino'],['data_emissao_cte','Data emissão CTe'],['previsao_atualizada','Prazo entrega atualizado'],['ocorrencia','Ocorrência / mensagem'],['ctes_alternativos','CTes alternativos'],['origem','Origem'],['arquivo_origem','Arquivo origem'],['aba_origem','Aba origem'],['linha_origem','Linha origem'],['fonte_sha256','SHA256 origem'],['fontes','Fontes'],['dados_adicionais','Dados adicionais'],['historico_registros','Histórico registros']];
function baseCarrierKey(value){const n=carrierFileNorm(value).replace(/[^A-Z0-9]/g,'');const names={CORREIOS:'CORREIOS_VIPP',CORREIOSVIPP:'CORREIOS_VIPP',TOTAL:'TOTAL_EXPRESS',TOTALEXPRESS:'TOTAL_EXPRESS',JT:'J_T',JTEXPRESS:'J_T',LATAMCARGO:'LATAM',FEDEX:'TNT_FEDEX',TNTFEDEX:'TNT_FEDEX'};return names[n]||(currentMeta?.carriers||[]).find(x=>carrierFileNorm(x.name).replace(/[^A-Z0-9]/g,'')===n)?.key||'';}
function baseInvoiceNote(value){const text=typeof value==='number'?(Number.isSafeInteger(value)?String(value):''):typeof value==='string'?value.trim():'';return /^[1-9]\d{5}$/.test(text)?text:'';}
async function baseParseFile(file,meta){
const XLSX=await ensureTransportadorasXlsx(),bytes=await file.arrayBuffer(),wb=XLSX.read(bytes,{type:'array',cellDates:false});
const sheet=wb.SheetNames.find(n=>carrierFileNorm(n)==='GERAL')||wb.SheetNames[0];if(!sheet)throw Error('Planilha vazia.');
const matrix=XLSX.utils.sheet_to_json(wb.Sheets[sheet],{header:1,defval:'',raw:true,blankrows:false});
const headers=(matrix.shift()||[]).map(carrierFileNorm),positions=new Map(headers.map((h,i)=>[h,i]));
if(!positions.has('NOTA FISCAL')||!positions.has('CTE'))throw Error('A aba Geral precisa ter as colunas Nota Fiscal e CTE.');
const aliases={'PREVISAO DE ENTREGA':'previsao','DATA DE EMISSAO':'data_emissao_tr','DATA DE ENTREGA':'data_entrega','PRAZO ENTREGA ATUALIZADO':'previsao_atualizada','MENSAGEM':'ocorrencia','DATA DE PREVISAO':'previsao','CEP DESTINO':'cep_destino','ULTIMA ATUALIZACAO':'data_status','OCORRENCIA MENSAGEM':'ocorrencia'};
for(const[k,label]of baseColumns)aliases[carrierFileNorm(label)]=k;
const sha=[...new Uint8Array(await crypto.subtle.digest('SHA-256',bytes))].map(n=>n.toString(16).padStart(2,'0')).join('');
const groups=new Map();let invalid=0;
for(let i=0;i<matrix.length;i++){
const row=matrix[i],nf=baseInvoiceNote(row[positions.get('NOTA FISCAL')]);
if(!nf){if(row.some(v=>String(v||'').trim()))invalid++;continue;}
const selected=meta?.key||baseCarrierKey(row[positions.get('TRANSPORTADORA')]);if(!selected)throw Error('Transportadora não identificada na linha '+(i+2)+'.');
const r={nota_fiscal:nf,carrier_key:selected,origem:'PLANILHA',arquivo_origem:file.name,aba_origem:sheet,linha_origem:i+2,fonte_sha256:sha},extra={};
for(let j=0;j<headers.length;j++){
const v=row[j];if(v===undefined||v===null||v==='')continue;
const field=aliases[headers[j]];if(!field){if(headers[j])extra[headers[j]]=v;continue;}
if(['data_emissao_tr','data_entrega','data_status','previsao','data_emissao_cte','previsao_atualizada'].includes(field))r[field]=carrierDateText(v,XLSX);
else if(['fontes','dados_adicionais','ctes_alternativos','historico_registros'].includes(field)){try{r[field]=typeof v==='string'?JSON.parse(v):v}catch{r[field]=field==='ctes_alternativos'?String(v).split(/\s*\|\s*/):{original:String(v)}}}
else r[field]=['valor_cte','volumes'].includes(field)&&Number.isFinite(Number(v))?Number(v):carrierCell(v);
}
r.nota_fiscal=nf;r.carrier_key=selected;
if(r.cep_destino)r.cep_destino=String(r.cep_destino).padStart(8,'0');
if(Object.keys(extra).length)r.dados_adicionais={...(r.dados_adicionais||{}),...extra};
if(!groups.has(selected))groups.set(selected,[]);groups.get(selected).push(r);
}
const historySheet=wb.SheetNames.find(n=>carrierFileNorm(n)==='HISTORICO');
if(historySheet){
const historyRows=XLSX.utils.sheet_to_json(wb.Sheets[historySheet],{header:1,defval:'',raw:true,blankrows:false}),hh=(historyRows.shift()||[]).map(carrierFileNorm),histories=new Map();
for(const row of historyRows){const r={};for(let j=0;j<hh.length;j++){const field=aliases[hh[j]],v=row[j];if(!field||v===undefined||v===null||v==='')continue;if(['data_emissao_tr','data_entrega','data_status','previsao','data_emissao_cte','previsao_atualizada'].includes(field))r[field]=carrierDateText(v,XLSX);else if(['fontes','dados_adicionais','ctes_alternativos'].includes(field)){try{r[field]=JSON.parse(v)}catch{r[field]=v}}else r[field]=v;}const key=baseCarrierKey(r.transportadora),nf=baseInvoiceNote(r.nota_fiscal);if(!key||!nf)continue;const id=key+'|'+nf;if(!histories.has(id))histories.set(id,[]);histories.get(id).push(r);}
for(const[key,rows]of groups)for(const r of rows){const h=histories.get(key+'|'+r.nota_fiscal);if(h?.length>1)r.historico_registros=h;}
}
const ckey=v=>{const s=carrierFileNorm(v).replace(/[^A-Z0-9]/g,'');return /^\d+$/.test(s)?s.replace(/^0+(?=\d)/,''):s;};
const cteRecords=new Map();for(const[key,rows]of groups)for(const r of rows)for(const c of [r.cte,...(r.ctes_alternativos||[])]){if(!c)continue;const id=key+'|'+ckey(c);if(!cteRecords.has(id))cteRecords.set(id,[]);cteRecords.get(id).push(r);}
for(const sn of wb.SheetNames){
if(sn===sheet||['HISTORICO','DUPLICADOS'].includes(carrierFileNorm(sn)))continue;
const data=XLSX.utils.sheet_to_json(wb.Sheets[sn],{header:1,defval:'',raw:true,blankrows:false}),originalHeaders=data.shift()||[],h=originalHeaders.map(carrierFileNorm);
const priority=meta?.key==='LATAM'||meta?.key==='TOTAL_EXPRESS'?['AWB','CTE','REGISTRO','NUMERO DE PEDIDO JMS','CONHECIMENTO']:['CTE','REGISTRO','AWB','NUMERO DE PEDIDO JMS','CONHECIMENTO'];
const ci=priority.map(n=>h.indexOf(n)).find(i=>i>=0),ni=['NOTA FISCAL','NOTAFISCAL','NUMERONOTAFISCAL','NRO NF','NF','NUMERO DA FATURA NFE DC DIR'].map(n=>h.indexOf(n)).find(i=>i>=0),carrierCol=h.indexOf('TRANSPORTADORA');if(ci===undefined)continue;
for(let i=0;i<data.length;i++){
const row=data[i],key=meta?.key||baseCarrierKey(row[carrierCol]);if(!key)continue;let c=carrierCell(row[ci]);if(!c)continue;
const serial=h.indexOf('SERIAL CTE');if(key==='ALFA'&&serial>=0&&row[serial]&&cteRecords.has(key+'|'+ckey(row[serial]+'/'+c)))c=row[serial]+'/'+c;
let nf=ni===undefined?'':String(row[ni]||'').replace(/\.0+$/,'').replace(/\D/g,'').replace(/^0+(?=\d)/,'');if(nf.length===44)nf=nf.slice(25,34).replace(/^0+/,'');
const candidates=cteRecords.get(key+'|'+ckey(c))||[],unique=[...new Set(candidates.map(r=>r.nota_fiscal))];
const record=nf?candidates.find(r=>r.nota_fiscal===nf):unique.length===1?candidates[0]:null;if(!record)continue;
const raw={};for(let j=0;j<h.length;j++)if(originalHeaders[j]&&row[j]!==''&&row[j]!==null&&row[j]!==undefined){let label=String(originalHeaders[j]);if(label in raw)label+=' #'+j;raw[label]=row[j];}
let detail={cte:c,arquivo:file.name,aba:sn,linha:i+2,dados:raw};
if(carrierFileNorm(sn)==='DETALHES'){const get=name=>row[h.indexOf(name)];let values=get('DADOS ADICIONAIS');try{values=JSON.parse(values)}catch{}detail={cte:c,arquivo:get('ARQUIVO ORIGEM')||file.name,aba:get('ABA ORIGEM')||sn,linha:get('LINHA ORIGEM')||i+2,dados:values||raw};}
record.dados_adicionais={...(record.dados_adicionais||{})};(record.dados_adicionais.detalhes_origem||(record.dados_adicionais.detalhes_origem=[])).push(detail);
}
}
groups.skipped_rows=invalid;return groups;
}
async function baseWorkbook(key='',includeDetails=false){
const selected=key?[key]:(currentMeta?.carriers||[]).filter(c=>c.available).map(c=>c.key),d={items:[]};for(const carrier of selected){const part={items:await root.__lisaPreparedRead(carrier,includeDetails)};if(includeDetails)await baseReadArchive(carrier,part.items||[]);d.items.push(...(part.items||[]));}const XLSX=await ensureTransportadorasXlsx();
const exportColumns=baseColumns.filter(([k])=>k!=='historico_registros');const matrix=[exportColumns.map(([,label])=>label),...(d.items||[]).map(r=>exportColumns.map(([k])=>{const v=k==='dados_adicionais'&&r[k]?Object.fromEntries(Object.entries(r[k]).filter(([name])=>name!=='detalhes_origem')):r[k];return v&&typeof v==='object'?JSON.stringify(v):v??''}))];
const wb=XLSX.utils.book_new(),ws=XLSX.utils.aoa_to_sheet(matrix);ws['!cols']=baseColumns.map(([k])=>({wch:k.includes('status')||k==='transportadora'?28:23}));XLSX.utils.book_append_sheet(wb,ws,'Geral');const history=(d.items||[]).flatMap(r=>(r.historico_registros||[]).map(h=>({...h,nota_fiscal:r.nota_fiscal,transportadora:r.transportadora})));if(history.length){const historyMatrix=[exportColumns.map(([,label])=>label),...history.map(r=>exportColumns.map(([k])=>{const v=k==='dados_adicionais'&&r[k]?Object.fromEntries(Object.entries(r[k]).filter(([name])=>name!=='detalhes_origem')):r[k];return v&&typeof v==='object'?JSON.stringify(v):v??''}))];XLSX.utils.book_append_sheet(wb,XLSX.utils.aoa_to_sheet(historyMatrix),'Historico');}const details=(d.items||[]).flatMap(r=>(r.dados_adicionais?.detalhes_origem||[]).map(h=>[r.nota_fiscal,h.cte||r.cte,r.transportadora,h.arquivo,h.aba,h.linha,JSON.stringify(h.dados||{})]));if(details.length)XLSX.utils.book_append_sheet(wb,XLSX.utils.aoa_to_sheet([['Nota Fiscal','CTE','Transportadora','Arquivo origem','Aba origem','Linha origem','Dados adicionais'],...details]),'Detalhes');return wb;
}
let activeStageId='';
async function baseStepIdentity(kind,key,items){const bytes=new TextEncoder().encode(JSON.stringify(items)),digest=[...new Uint8Array(await crypto.subtle.digest('SHA-256',bytes))].map(n=>n.toString(16).padStart(2,'0')).join('');return 'v3_'+key+'_'+kind+'_'+digest.slice(0,32);}
let confirmedImportSteps=new Set();let carrierQueueBusy=false;const carrierQueue=[];
function baseSmallBatches(items,maxCount=80,maxBytes=60000){const out=[];let batch=[],bytes=0;for(const item of items){const size=new TextEncoder().encode(JSON.stringify(item)).length+1;if(size>maxBytes)throw Error('Uma informação isolada excede o limite do lote ('+size+' bytes). Nota '+(item.note||item.nota_fiscal||'')+'.');if(batch.length&&(batch.length>=maxCount||bytes+size>maxBytes)){out.push(batch);batch=[];bytes=0;}batch.push(item);bytes+=size;}if(batch.length)out.push(batch);return out;}
async function baseSendStep(kind,key,items,source,label){
const id=await baseStepIdentity(kind,key,items),path=kind==='core'?'/carrier-base/import':'/carrier-base/archive',payload={protocol:3,carrier:key,items,id,source};if(confirmedImportSteps.has(id))return {ok:true,resumed:true};let lastError='';
for(let attempt=0;attempt<3;attempt++){
try{return await jsonReq(path,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(payload)});}
catch(e){lastError=String(e.message||e);let receipt=null;try{receipt=await jsonReq('/carrier-base/import-result?id='+encodeURIComponent(id));}catch{}
if(receipt?.phase==='completed'&&receipt.result)return {...receipt.result,confirmed_after_error:true};
const reason=receipt?.error||lastError,stage=receipt?.stage||'Sem confirmação de recebimento pelo Cloud';
if(attempt===2||receipt?.phase==='failed'&&!/ocupad|simult|timeout|limite|tempor|HTTP 5/i.test(reason))throw Error(key+' · '+label+'\n'+reason+'\nÚltima etapa: '+stage+'\nLote: '+id);
await new Promise(resolve=>setTimeout(resolve,1000*(attempt+1)));
}
}
throw Error(key+' · '+label+': '+lastError);
}
function baseArchiveUnits(rows){const units=[];for(const r of rows){const note=r.nota_fiscal;for(const value of r.fontes||[])units.push({note,type:'source',value});for(const value of r.historico_registros||[])units.push({note,type:'history',value});const extra={...(r.dados_adicionais||{})};for(const value of extra.detalhes_origem||[])units.push({note,type:'detail',value});delete extra.detalhes_origem;if(Object.keys(extra).length)units.push({note,type:'extra',value:extra});}return units;}
async function baseReadArchive(key,records){const byNote=new Map(records.map(r=>[String(r.nota_fiscal),r]));let cursor='',pages=0;do{const page=await jsonReq('/carrier-base/archive?carrier='+encodeURIComponent(key)+(cursor?'&cursor='+encodeURIComponent(cursor):''));for(const unit of page.items||[]){const record=byNote.get(String(unit.note));if(!record)continue;if(unit.type==='source')(record.fontes||(record.fontes=[])).push(unit.value);else if(unit.type==='history')(record.historico_registros||(record.historico_registros=[])).push(unit.value);else if(unit.type==='detail'){record.dados_adicionais={...(record.dados_adicionais||{})};(record.dados_adicionais.detalhes_origem||(record.dados_adicionais.detalhes_origem=[])).push(unit.value);}else if(unit.type==='extra')record.dados_adicionais={...(record.dados_adicionais||{}),...(unit.value||{})};}cursor=page.has_more?String(page.cursor||''):'';if(page.has_more&&!cursor)throw Error('Cursor ausente na leitura dos detalhes de '+key);if(++pages>10000)throw Error('Paginação inválida dos detalhes de '+key);}while(cursor);for(const record of records){for(const field of ['fontes','historico_registros'])if(Array.isArray(record[field]))record[field]=[...new Map(record[field].map(value=>[JSON.stringify(value),value])).values()];if(Array.isArray(record.dados_adicionais?.detalhes_origem))record.dados_adicionais.detalhes_origem=[...new Map(record.dados_adicionais.detalhes_origem.map(value=>[JSON.stringify(value),value])).values()];}return records;}
async function uploadCarrierFile(file,meta,onProgress=()=>{}){
const capability=await jsonReq('/carrier-base/protocol');if(capability.protocol!==3)throw Error('Publique o Worker atualizado antes de importar o arquivo.');
onProgress(0,'ANALISANDO ARQUIVO');const groups=await baseParseFile(file,meta),totals={added:0,updated:0,unchanged:0,blocked:0,warnings:[],failures:[],skipped:Number(groups.skipped_rows||0)};
if(!groups.size){onProgress(99,'ANÁLISE CONCLUÍDA · NENHUMA NOTA VÁLIDA PARA IMPORTAR');return totals;}
confirmedImportSteps=new Set();onProgress(15,'ANÁLISE CONCLUÍDA · IMPORTANDO DADOS');await new Promise(resolve=>requestAnimationFrame(()=>requestAnimationFrame(resolve)));
const all=[...groups.values()].reduce((n,rows)=>n+rows.length,0);let completedRows=0;
for(const[key,rows]of groups){
const prefix=()=>15+Math.round(completedRows/all*80);onProgress(prefix(),key+' · PREPARANDO LOTES');
try{
const core=rows.map(r=>{const {fontes,historico_registros,dados_adicionais,...main}=r;return main;}),chunks=baseSmallBatches(core,80,60000),archives=baseSmallBatches(baseArchiveUnits(rows),100,60000);
for(let i=0;i<chunks.length;i++){onProgress(15+Math.round((completedRows+rows.length*.65*i/chunks.length)/all*80),key+' · REGISTROS '+(i+1)+'/'+chunks.length);const result=await baseSendStep('core',key,chunks[i],file.name,'Registros '+(i+1)+'/'+chunks.length);if(result.resumed)totals.unchanged+=chunks[i].length;else for(const k of ['added','updated','unchanged','blocked'])totals[k]+=Number(result[k]||0);}
for(let i=0;i<archives.length;i++){onProgress(15+Math.round((completedRows+rows.length*(.65+.35*i/archives.length))/all*80),key+' · DETALHES '+(i+1)+'/'+archives.length);await baseSendStep('archive',key,archives[i],file.name,'Detalhes '+(i+1)+'/'+archives.length);}
onProgress(15+Math.round((completedRows+rows.length)/all*80),key+' · ATUALIZANDO PAINEL');

}catch(e){totals.failures.push(String(e.message||e));}
completedRows+=rows.length;
}
if(!totals.failures.length)try{const result=await root.__lisaPreparedImport(groups,activeStageId,onProgress);for(const k of ['added','updated','unchanged','blocked'])totals[k]=result[k];}catch(e){if(e.confirmedCounts)for(const k of ['added','updated','unchanged','blocked'])totals[k]=Number(e.confirmedCounts[k]||0);totals.warnings.push('Consolidação/publicação: '+String(e.message||e));}
onProgress(99,totals.failures.length||totals.warnings.length?'IMPORTAÇÃO PENDENTE':'CONFIRMANDO GRAVAÇÃO');return totals;
}

async function drainCarrierQueue(){
if(carrierQueueBusy)return;carrierQueueBusy=true;modal.querySelector('[data-base-apis]').disabled=true;
try{while(carrierQueue.length){const job=carrierQueue.shift(),label=job.row.querySelector('b'),progress=(p,text)=>{label.textContent=text||'IMPORTANDO '+p+'%';job.row.style.setProperty('--base-progress',p+'%');};try{
if(job.api){label.textContent='CONSULTANDO';await job.run();const failed=status.classList.contains('is-error');if(!failed&&job.key)await root.__lisaPreparedPublish([job.key]);job.row.classList.add(failed?'is-error':'is-ok');label.textContent=failed?'ERRO':'CONCLUÍDO';const detail=document.createElement('small');detail.textContent=status.textContent;job.row.appendChild(detail);}
else{const meta=detectCarrierFromFilename(job.file.name,currentMeta?.carriers||[]),done=await uploadCarrierFile(job.file,meta,progress),partial=done.failures.length||done.warnings.length;job.row.classList.add(partial?'is-error':'is-ok');if(!partial)progress(100);label.textContent=(partial?'CONCLUÍDO PARCIALMENTE':'CONCLUÍDO')+' · '+done.added+' novos · '+done.updated+' atualizados · '+done.unchanged+' mantidos'+(done.skipped?' · '+done.skipped+' linhas ignoradas':'');if(partial){const detail=document.createElement('small');detail.textContent=[...done.failures,...done.warnings].join('\n\n');job.row.appendChild(detail);}}
}catch(e){job.row.classList.add('is-error');label.textContent='IMPORTAÇÃO NÃO CONCLUÍDA';const detail=document.createElement('small');detail.textContent=String(e.message||e);job.row.appendChild(detail);}finally{activeStageId='';}}}
finally{carrierQueueBusy=false;modal.querySelector('[data-base-apis]').disabled=false;await loadStatus();root.dispatchEvent(new CustomEvent('lisa:carrier-indexes-ready'));root.dispatchEvent(new CustomEvent('lisa:carrier-files-updated'));await refreshTrackingCounts();status.textContent='Confira o resultado de cada arquivo abaixo.';}
}

function baseQueueRow(name){const row=document.createElement('div');row.className='carrier-files-batch-row';const title=document.createElement('span'),label=document.createElement('b');title.textContent=name;label.textContent='NA FILA';row.append(title,label);modal.querySelector('[data-carrier-files-batch]').appendChild(row);return row;}
async function handleManyFiles(fileList){for(const file of [...fileList||[]])carrierQueue.push({file,row:baseQueueRow(file.name)});void drainCarrierQueue();}
function queueBaseApis(){
const dummy=()=>({textContent:'',disabled:false});
const jobs=[['ALFA / EDI',async()=>{const d=await jsonReq('/carrier-base/api-alfa',{method:'POST'});if(d.ok===false)throw Error(d.error||'Falha Alfa');status.className='carrier-files-status is-ok';status.textContent=d.message||'Sincronização Alfa finalizada.';}],['BRASPRESS',()=>runBraspressNow()],['JAMEF',()=>syncJamef(dummy())],['J&T',()=>syncJt(dummy())],['PATRUS',()=>syncPatrus(dummy())]];
for(const[name,run]of jobs)carrierQueue.push({api:true,key:({'ALFA / EDI':'ALFA','BRASPRESS':'BRASPRESS','JAMEF':'JAMEF','J&T':'J_T','PATRUS':'PATRUS'})[name],run,row:baseQueueRow(name)});void drainCarrierQueue();
}

async function refreshTrackingCounts(){try{const d=await jsonReq('/rastreamento/summary');for(const[k,v]of Object.entries(d?.counts||{})){const el=root.querySelector(`[data-tracking-count="${k}"]`);if(el)el.textContent=Number(v||0).toLocaleString('pt-BR')}}catch{}}async function openReport(key){currentAlfaMonth=null;currentCarrier=key;workbook=null;currentSheet='';searchHits=[];clearTimeout(reportSearchTimer);reportModal.hidden=false;reportMain.innerHTML='<div class="carrier-files-empty">Carregando base...</div>';tabs.innerHTML='';if(reportSearch){reportSearch.value='';reportSearch.disabled=true;}if(reportDownload)reportDownload.disabled=true;try{workbook=await baseWorkbook(key);currentSheet='Geral';const meta=(currentMeta?.carriers||[]).find(x=>x.key===key)||{};reportModal.querySelector('[data-carrier-files-report-title]').textContent=meta.name||key;reportModal.querySelector('[data-carrier-files-report-meta]').textContent='Base acumulada · última atualização '+fmtDate(meta.updated_at);if(reportSearch)reportSearch.disabled=false;if(reportDownload)reportDownload.disabled=false;renderTabs();renderSheet();}catch(e){reportMain.innerHTML=`<div class="carrier-files-empty">${esc(e.message||e)}</div>`;}}
function renderTabs(){tabs.innerHTML=(workbook?.SheetNames||[]).map(n=>`<button type="button" class="carrier-files-sheet-tab ${n===currentSheet?'is-active':''}" data-carrier-files-sheet="${esc(n)}">${esc(n)}</button>`).join('')}function colLetter(n){let s='';for(let x=n+1;x;x=Math.floor((x-1)/26))s=String.fromCharCode(65+(x-1)%26)+s;return s}function searchValue(v){return String(v??'').normalize('NFD').replace(/[\u0300-\u036f]/g,'').toLowerCase()}function renderWorkbookSearch(query){if(!workbook){reportMain.innerHTML='<div class="carrier-files-empty">Arquivo não carregado.</div>';return}const q=searchValue(query).trim();if(!q){searchHits=[];renderSheet();return}const XLSX=window.XLSX;searchHits=[];let total=0;const MAX=750;for(const sheetName of workbook.SheetNames||[]){const ws=workbook.Sheets[sheetName];if(!ws)continue;const rows=XLSX.utils.sheet_to_json(ws,{header:1,defval:'',raw:false,blankrows:false});for(let i=0;i<rows.length;i++){const row=rows[i]||[];let match=false;for(const cell of row){if(searchValue(cell).includes(q)){match=true;break}}if(!match)continue;total++;if(searchHits.length<MAX)searchHits.push({sheet:sheetName,row:i,values:row})}}if(!total){reportMain.innerHTML=`<div class="carrier-files-empty">Nenhum resultado para “${esc(query)}”.</div>`;return}const note=total>MAX?` · exibindo os primeiros ${MAX.toLocaleString('pt-BR')}`:'';reportMain.innerHTML=`<div class="carrier-files-search-results"><div class="carrier-files-search-summary">${total.toLocaleString('pt-BR')} ${total===1?'resultado':'resultados'} em todo o arquivo${note}. Clique em uma linha para abrir a posição na planilha.</div><div class="carrier-files-search-list">${searchHits.map((hit,i)=>{const preview=(hit.values||[]).map(v=>String(v??'').trim()).filter(Boolean).join('  |  ');return`<button type="button" class="carrier-files-search-row" data-carrier-search-hit="${i}"><span class="carrier-files-search-sheet">${esc(hit.sheet)}</span><span class="carrier-files-search-line">Linha ${hit.row+1}</span><span class="carrier-files-search-content" title="${esc(preview)}">${esc(preview||'-')}</span></button>`}).join('')}</div></div>`}function renderSheet(targetRow=-1){if(!workbook||!currentSheet){reportMain.innerHTML='<div class="carrier-files-empty">Planilha vazia.</div>';return}const XLSX=window.XLSX,ws=workbook.Sheets[currentSheet],rows=XLSX.utils.sheet_to_json(ws,{header:1,defval:'',raw:false,blankrows:false});if(!rows.length){reportMain.innerHTML='<div class="carrier-files-empty">A aba não possui registros.</div>';return}let cols=0;for(const r of rows.slice(0,500))cols=Math.max(cols,(r||[]).length);cols=Math.max(cols,1);const widths=Array.from({length:cols},()=>150),tpl='50px '+widths.map(x=>x+'px').join(' '),total=50+widths.reduce((a,b)=>a+b,0);reportMain.innerHTML=`<div class="carrier-files-grid-wrap" data-carrier-files-scroll><div class="carrier-files-grid-head" style="grid-template-columns:${tpl};width:${total}px"><div class="rn">#</div>${Array.from({length:cols},(_,i)=>`<div>${colLetter(i)}</div>`).join('')}</div><div class="carrier-files-grid-body" style="height:${rows.length*28}px;width:${total}px"><div data-carrier-files-layer></div></div></div>`;const sc=reportMain.querySelector('[data-carrier-files-scroll]'),layer=reportMain.querySelector('[data-carrier-files-layer]'),ROW=28;function paint(){const start=Math.max(0,Math.floor(sc.scrollTop/ROW)-15),count=Math.ceil(sc.clientHeight/ROW)+32,end=Math.min(rows.length,start+count),out=[];for(let i=start;i<end;i++){const row=rows[i]||[];out.push(`<div class="carrier-files-grid-row ${i===targetRow?'is-search-hit':''}" style="top:${i*ROW}px;grid-template-columns:${tpl};width:${total}px"><div class="carrier-files-cell rn">${i+1}</div>${Array.from({length:cols},(_,j)=>{const v=String(row[j]??'');return`<div class="carrier-files-cell" title="${esc(v)}">${esc(v)}</div>`}).join('')}</div>`)}layer.innerHTML=out.join('')}sc.addEventListener('scroll',paint,{passive:true});if(targetRow>=0)sc.scrollTop=Math.max(0,targetRow*ROW-ROW*3);paint()}if(openBtn.__transportadorasOpenHandler)openBtn.removeEventListener('click',openBtn.__transportadorasOpenHandler);openBtn.__transportadorasOpenHandler=async()=>{modal.hidden=false;await loadStatus();};openBtn.addEventListener('click',openBtn.__transportadorasOpenHandler);modal.querySelector('[data-carrier-files-close]').onclick=()=>modal.hidden=true;modal.querySelector('[data-base-apis]').onclick=()=>queueBaseApis();modal.addEventListener('click',e=>{if(e.target===modal)modal.hidden=true;const jtBtn=e.target.closest('[data-jt-api-trigger]');if(jtBtn){e.preventDefault();e.stopPropagation();syncJt(jtBtn);return}const patrusBtn=e.target.closest('[data-patrus-api-trigger]');if(patrusBtn){e.preventDefault();e.stopPropagation();syncPatrus(patrusBtn);return}const jamefBtn=e.target.closest('[data-jamef-api-trigger]');if(jamefBtn){e.preventDefault();e.stopPropagation();syncJamef(jamefBtn);return}const correiosTrigger=e.target.closest('[data-correios-api-trigger]');if(correiosTrigger&&isAltGrClick(e)){e.preventDefault();e.stopPropagation();runCorreiosNow();return}const braspressTrigger=e.target.closest('[data-braspress-api-trigger]');if(braspressTrigger){e.preventDefault();e.stopPropagation();runBraspressNow();return}const all=e.target.closest('[data-carrier-files-download-all]');if(all){e.stopPropagation();downloadAllCarrierFiles(all);return}const dl=e.target.closest('[data-carrier-files-download]');if(dl&&!dl.disabled){e.stopPropagation();downloadCarrierFile(dl.dataset.carrierFilesDownload,dl).catch(err=>{status.textContent=err.message||'Falha ao baixar o arquivo.';status.className='carrier-files-status is-error'});return}const rp=e.target.closest('[data-carrier-files-report]');if(rp&&!rp.disabled)openReport(rp.dataset.carrierFilesReport)});modal.addEventListener('keydown',e=>{const correiosBadge=e.target.closest('[data-correios-api-trigger]');if(correiosBadge&&isAltGrClick(e)&&(e.key==='Enter'||e.key===' ')){e.preventDefault();e.stopPropagation();runCorreiosNow();return}});reportModal.querySelector('[data-carrier-files-report-close]').onclick=()=>reportModal.hidden=true;reportModal.addEventListener('click',e=>{if(e.target===reportModal)reportModal.hidden=true;const hit=e.target.closest('[data-carrier-search-hit]');if(hit){const x=searchHits[Number(hit.dataset.carrierSearchHit)];if(x){currentSheet=x.sheet;if(reportSearch)reportSearch.value='';renderTabs();renderSheet(x.row)}return}});if(reportDownload)reportDownload.addEventListener('click',()=>{if(!currentCarrier||reportDownload.disabled)return;downloadCarrierFile(currentCarrier,reportDownload,currentAlfaMonth).catch(err=>{reportMain.innerHTML=`<div class="carrier-files-empty">${esc(err.message||'Falha ao baixar o arquivo.')}</div>`})});if(reportSearch)reportSearch.addEventListener('input',()=>{clearTimeout(reportSearchTimer);reportSearchTimer=setTimeout(()=>{const q=String(reportSearch.value||'').trim();if(q)renderWorkbookSearch(q);else renderSheet()},180)});tabs.addEventListener('click',e=>{const b=e.target.closest('[data-carrier-files-sheet]');if(!b)return;currentSheet=b.dataset.carrierFilesSheet;if(reportSearch)reportSearch.value='';renderTabs();renderSheet()});const drop=modal.querySelector('[data-carrier-files-drop]'),multi=modal.querySelector('[data-carrier-files-multi]');drop.addEventListener('click',()=>multi.click());drop.addEventListener('keydown',e=>{if(e.key==='Enter'||e.key===' '){e.preventDefault();multi.click()}});multi.addEventListener('change',()=>{handleManyFiles(multi.files);multi.value=''});for(const ev of['dragenter','dragover'])drop.addEventListener(ev,e=>{e.preventDefault();e.stopPropagation();drop.classList.add('is-drag')});for(const ev of['dragleave','drop'])drop.addEventListener(ev,e=>{e.preventDefault();e.stopPropagation();drop.classList.remove('is-drag')});drop.addEventListener('drop',e=>handleManyFiles(e.dataTransfer?.files||[]))}init()})();

;

(()=>{'use strict';let tries=0,mapInstance=null,mapLayers=[],geoJsonCache=null,currentData=null,currentType='region',currentDays=60,currentGeo='',reportsBundle=null,preloadPromise=null;const UF_REGION={AC:'Norte',AP:'Norte',AM:'Norte',PA:'Norte',RO:'Norte',RR:'Norte',TO:'Norte',AL:'Nordeste',BA:'Nordeste',CE:'Nordeste',MA:'Nordeste',PB:'Nordeste',PE:'Nordeste',PI:'Nordeste',RN:'Nordeste',SE:'Nordeste',DF:'Centro-Oeste',GO:'Centro-Oeste',MT:'Centro-Oeste',MS:'Centro-Oeste',ES:'Sudeste',MG:'Sudeste',RJ:'Sudeste',SP:'Sudeste',PR:'Sul',RS:'Sul',SC:'Sul'};const STATE_NAME_UF={'ACRE':'AC','ALAGOAS':'AL','AMAPA':'AP','AMAZONAS':'AM','BAHIA':'BA','CEARA':'CE','DISTRITO FEDERAL':'DF','ESPIRITO SANTO':'ES','GOIAS':'GO','MARANHAO':'MA','MATO GROSSO':'MT','MATO GROSSO DO SUL':'MS','MINAS GERAIS':'MG','PARA':'PA','PARAIBA':'PB','PARANA':'PR','PERNAMBUCO':'PE','PIAUI':'PI','RIO DE JANEIRO':'RJ','RIO GRANDE DO NORTE':'RN','RIO GRANDE DO SUL':'RS','RONDONIA':'RO','RORAIMA':'RR','SANTA CATARINA':'SC','SAO PAULO':'SP','SERGIPE':'SE','TOCANTINS':'TO'};const esc=v=>String(v??'').replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));const norm=v=>String(v??'').normalize('NFD').replace(/[\u0300-\u036f]/g,'').toUpperCase().replace(/\s+/g,' ').trim();const fmt=n=>Number(n||0).toLocaleString('pt-BR');function init(){const root=document.getElementById('lisa-app-vg-native')||document.querySelector('.lisa-app[data-worker]');const cte=document.getElementById('lisacte-vg-native');const head=cte?.querySelector('.cte-head-actions');if(!root||!cte||!head){if(tries++<300)setTimeout(init,100);return}if(cte.__reportsInitialized)return;cte.__reportsInitialized=true;let btn=head.querySelector('[data-rtrack-reports-open]');if(!btn){btn=document.createElement('button');btn.type='button';btn.className='cte-action';btn.setAttribute('data-rtrack-reports-open','');btn.textContent='Relatórios';const transp=head.querySelector('[data-transportadoras-open]');head.insertBefore(btn,transp||head.firstChild)}document.querySelectorAll('.rtrack-reports-menu[data-owner="vg-native"],.rtrack-report-modal[data-owner="vg-native"]').forEach(x=>x.remove());const menu=document.createElement('div');menu.className='rtrack-reports-menu';menu.dataset.owner='vg-native';menu.hidden=true;menu.innerHTML='<button type="button" data-rtrack-type="geo">Região/Estado</button><button type="button" data-rtrack-type="carrier">Transportadora</button><button type="button" data-rtrack-type="date">Por data</button><button type="button" data-rtrack-type="notes">Por nota</button>';document.body.appendChild(menu);const modal=document.createElement('div');modal.className='rtrack-report-modal';modal.dataset.owner='vg-native';modal.hidden=true;modal.innerHTML=`<section class="rtrack-report-shell"><header class="rtrack-report-head"><div class="rtrack-report-title"><h3 data-rtrack-title>Por região</h3><span data-rtrack-subtitle class="rtrack-preloaded">Dados pré-carregados</span></div><div class="rtrack-report-actions"><div class="rtrack-view-switch" data-rtrack-view-switch hidden><button type="button" data-rtrack-view="region" class="is-active">REGIÃO</button><button type="button" data-rtrack-view="state">ESTADO</button></div><select class="rtrack-report-days" data-rtrack-days><option value="60">Últimos 60 dias</option><option value="30">Últimos 30 dias</option><option value="15">Últimos 15 dias</option><option value="7">Últimos 7 dias</option></select><button type="button" class="rtrack-report-download" data-rtrack-download>BAIXAR</button><button type="button" class="rtrack-report-close" data-rtrack-close>×</button></div></header><main class="rtrack-report-body" data-rtrack-body><div class="rtrack-report-loading">Preparando relatórios...</div></main></section>`;document.body.appendChild(modal);const body=modal.querySelector('[data-rtrack-body]'),title=modal.querySelector('[data-rtrack-title]'),subtitle=modal.querySelector('[data-rtrack-subtitle]'),daysSel=modal.querySelector('[data-rtrack-days]'),viewSwitch=modal.querySelector('[data-rtrack-view-switch]');const worker=()=>String(root.dataset.worker||'').replace(/\/$/,'');const token=()=>sessionStorage.getItem('lisa_panel_token')||'';let reportsRetryTimer=null,reportsRetryCount=0,geoJsonPromise=null,reportsLoadedAt=0,reportsEventTimer=null;async function api(path,opt={}){if(!token())throw new Error('Entre no painel para carregar os relatórios.');const controller=new AbortController,timer=setTimeout(()=>controller.abort(),2e4);try{const h={...opt.headers||{},Authorization:'Bearer '+token()};const r=await fetch(worker()+path,{...opt,headers:h,signal:controller.signal,cache:'no-store'}),d=await r.json().catch(()=>({}));if(!r.ok)throw new Error(d.detail||d.error||'Falha ao carregar relatório.');return d}catch(e){if(e.name==='AbortError')throw new Error('O Cloudflare demorou para responder. Tente novamente.');if(e instanceof TypeError)throw new Error('Não foi possível receber a resposta do transporte. Confira a conexão e se o Worker atualizado foi publicado; depois tente novamente.');throw e}finally{clearTimeout(timer)}}async function ensureXlsx(){if(window.XLSX)return window.XLSX;await new Promise((res,rej)=>{const old=document.querySelector('script[data-rtrack-xlsx]');if(old){if(window.XLSX)return res();old.addEventListener('load',res,{once:true});old.addEventListener('error',rej,{once:true});return}const s=document.createElement('script');s.src='https://cdn.jsdelivr.net/npm/xlsx@0.18.5/dist/xlsx.full.min.js';s.async=true;s.dataset.rtrackXlsx='1';s.onload=res;s.onerror=rej;document.head.appendChild(s)});return window.XLSX}async function ensureLeaflet(){return await Promise.race([loadLeaflet(),new Promise((_,reject)=>setTimeout(()=>reject(new Error('Não foi possível carregar a biblioteca do mapa. Tente novamente.')),15e3))])}async function loadLeaflet(){if(window.L)return window.L;if(!document.querySelector('link[data-rtrack-leaflet]')){const l=document.createElement('link');l.rel='stylesheet';l.href='https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.css';l.dataset.rtrackLeaflet='1';document.head.appendChild(l)}await new Promise((res,rej)=>{const old=document.querySelector('script[data-rtrack-leaflet]');if(old){if(window.L)return res();old.addEventListener('load',res,{once:true});old.addEventListener('error',rej,{once:true});return}const s=document.createElement('script');s.src='https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.js';s.async=true;s.dataset.rtrackLeaflet='1';s.onload=res;s.onerror=rej;document.head.appendChild(s)});return window.L}async function getGeoJson(){if(geoJsonCache)return geoJsonCache;if(geoJsonPromise)return geoJsonPromise;geoJsonPromise=api('/rastreamento/geometria-brasil').then(d=>{if(!Array.isArray(d.features)||!d.features.length)throw new Error('Geometria do Brasil inválida.');geoJsonCache=d;return d}).finally(()=>{geoJsonPromise=null});return geoJsonPromise}root.__lisaRtrackGetGeometry=getGeoJson;function featureUf(f){const p=f?.properties||{};for(const raw of[p.sigla,p.UF,p.uf,p.abbrev,p.name,p.nome]){const n=norm(raw);if(UF_REGION[n])return n;if(STATE_NAME_UF[n])return STATE_NAME_UF[n]}return''}const MAP_NEUTRAL='#bdcbd9';
const DELAY_LEVELS=[
{label:'Crítico',color:'#dc2626'},
{label:'Verificar',color:'#ea801e'},
{label:'OK',color:'#e5bd29'},
{label:'Bom',color:'#299a61'}
];
function delayRate(count,sent){return sent>0?100*count/sent:null}
function delayLevel(rate){
if(rate===null)return null;
if(rate>30)return DELAY_LEVELS[0];
if(rate>10)return DELAY_LEVELS[1];
if(rate>=3)return DELAY_LEVELS[2];
return DELAY_LEVELS[3];
}
function delayRateLabel(rate){return rate===null?'—':rate.toLocaleString('pt-BR',{maximumFractionDigits:2})+'%'}function qualityHtml(x){const q=Math.max(0,Math.min(100,Number(x?.quality||0)));return`<div class="rtrack-quality" title="Qualidade = entregues / enviados"><div class="rtrack-quality-track"><div class="rtrack-quality-fill" style="width:${q}%"></div></div><b>${q.toLocaleString('pt-BR',{maximumFractionDigits:1})}%</b></div>`}function lateText(x){return x?.late_data_available?fmt(x.late):'Não consta dados'}function periodData(days){return reportsBundle?.periods?.[String(days)]||null}function itemsForGeo(kind,key){return(currentData?.items||[]).filter(x=>kind==='region'?x.region===key:x.uf===key)}function itemsForCarrier(name,items=currentData?.items||[]){return(items||[]).filter(x=>String(x.carrier||'')===String(name||''))}function downloadRows(name,items,summary=[],lateRecords=[]){return ensureXlsx().then(XLSX=>{const wb=XLSX.utils.book_new();const rows=(items||[]).map(x=>({'Nota Fiscal':x.nf||'','Data de emissão / envio':x.issue_date||'','CEP':x.cep||'','UF':x.uf||'','Região':x.region||'','Cidade':x.cidade||'','Transportadora':x.carrier||'','CTE / Rastreio':x.cte||'','Status':x.status||'','Previsão de entrega':x.forecast||'','Data de entrega':x.delivery_date||'','Entregue':x.delivered?'SIM':'NÃO','Entregue no prazo':x.on_time?'SIM':x.late?'NÃO':'SEM DADOS'}));const ws=XLSX.utils.json_to_sheet(rows);ws['!cols']=[{wch:14},{wch:20},{wch:12},{wch:6},{wch:15},{wch:24},{wch:22},{wch:20},{wch:28},{wch:20},{wch:20},{wch:10},{wch:18}];XLSX.utils.book_append_sheet(wb,ws,'Notas');if(summary.length){const ss=XLSX.utils.json_to_sheet(summary);XLSX.utils.book_append_sheet(wb,ss,'Resumo')}if(lateRecords.length){const ls=XLSX.utils.json_to_sheet(lateRecords.map(x=>({'NF':x.nf,'Transportadora':x.carrier,'CEP':x.cep,'UF':x.uf,'Região':x.region,'Prazo previsto':x.due_date,'Entrada em atraso':x.entry_date,'Data da entrega':x.delivery_date,'Situação':x.status,'Dias de atraso':x.delay_days,'Origem previsão':x.forecast_source})));XLSX.utils.book_append_sheet(wb,ls,'Historico de atrasos')}XLSX.writeFile(wb,name+'.xlsx',{compression:true})})}let sysempRepairPromise=null,sysempRepairKey='',reportsRefreshPolls=0;async function reindexSysempIfNeeded(bundle){return}function publishReportsBundle(d){if(!d?.periods?.['60'])throw new Error('Snapshot por região inválido.');if(Array.isArray(d.items)){for(const period of Object.values(d.periods)){const from=Date.parse(period.period_start),to=Date.parse(period.period_end);period.items=d.items.filter(x=>Number(x.issue_ms)>=from&&Number(x.issue_ms)<=to)}delete d.items}reportsLoadedAt=Date.now();reportsBundle=d;reportsRetryCount=0;root.__lisaRtrackReportsBundle=d;root.__lisaRtrackReportsError='';root.dispatchEvent(new CustomEvent('lisa:rtrack-reports-loaded',{detail:{bundle:d}}));if(!modal.hidden&&setCurrentFromBundle())render();return d}function retryReports(){clearTimeout(reportsRetryTimer);if(reportsRetryCount++<4)reportsRetryTimer=setTimeout(()=>preloadReports(false).catch(()=>{}),6e4)}async function preloadReports(force=false){if(preloadPromise)return preloadPromise;if(!force&&reportsBundle&&Date.now()-reportsLoadedAt<3e3)return reportsBundle;preloadPromise=(async()=>{try{const d=await api('/rastreamento/relatorios?bundle=1&compact=1');if(d.preparing||d.available===false)throw new Error(d.message||'Snapshot por região em preparação no Cloudflare.');const result=publishReportsBundle(d);reindexSysempIfNeeded(d);if((d.refreshing||sysempRepairPromise)&&reportsRefreshPolls++<12){clearTimeout(reportsRetryTimer);reportsRetryTimer=setTimeout(()=>preloadReports(false).catch(()=>{}),5e3)}else if(!d.refreshing&&!sysempRepairPromise){reportsRefreshPolls=0}return result}catch(e){root.__lisaRtrackReportsError=e.message||String(e);root.dispatchEvent(new CustomEvent('lisa:rtrack-reports-error',{detail:{message:root.__lisaRtrackReportsError}}));retryReports();throw e}})().finally(()=>{preloadPromise=null});return preloadPromise}root.__lisaRtrackPreloadReports=preloadReports;function reportFilterNorm(value){return String(value??'').normalize('NFD').replace(/[\u0300-\u036f]/g,'').toLocaleLowerCase('pt-BR').replace(/\s+/g,' ').trim()}function reportFilterMatch(text,query){const value=reportFilterNorm(text),filter=reportFilterNorm(query);return!filter||(filter.startsWith('=')?value===filter.slice(1).trim():value.includes(filter))}function reportFilteredItems(host,selector,items){const table=host.querySelector(selector);return table?.__rtrackFilteredIndexes?table.__rtrackFilteredIndexes().map(i=>items[i]).filter(Boolean):items}function addReportColumnFilters(host,onChange=()=>{}){for(const table of host.querySelectorAll('table')){if(table.__rtrackFilteredIndexes||!table.tHead?.rows?.length||!table.tBodies?.[0])continue;const headers=[...table.tHead.rows[0].cells],rows=[...table.tBodies[0].rows],values=rows.map(row=>[...row.cells].map(cell=>cell.textContent||''));const filters=document.createElement('tr');filters.className='rtrack-column-filter-row';const inputs=headers.map((header,i)=>{const th=document.createElement('th');const label=String(header.textContent||'').trim();filters.appendChild(th);if(!label)return null;const input=document.createElement('input');input.type='text';input.placeholder='Filtrar';input.setAttribute('aria-label','Filtrar '+label);input.title='Digite parte do texto ou use = para filtrar o valor exato.';input.dataset.reportColumn=String(i);th.appendChild(input);return input});table.tHead.appendChild(filters);const meta=document.createElement('div');meta.className='rtrack-filter-meta';const count=document.createElement('span'),clear=document.createElement('button');clear.type='button';clear.textContent='LIMPAR FILTROS';meta.appendChild(count);meta.appendChild(clear);table.parentNode.insertBefore(meta,table);table.__rtrackFilteredIndexes=()=>rows.reduce((out,row,i)=>row.hidden?out:(out.push(i),out),[]);const apply=()=>{const queries=inputs.map(input=>input?.value||'');rows.forEach((row,i)=>{row.hidden=!queries.every((query,c)=>reportFilterMatch(values[i][c]||'',query))});count.textContent=`Exibindo ${fmt(table.__rtrackFilteredIndexes().length)} de ${fmt(rows.length)} registros`;onChange(table)};inputs.filter(Boolean).forEach(input=>input.addEventListener('input',apply));clear.addEventListener('click',()=>{inputs.filter(Boolean).forEach(input=>input.value='');apply()});apply()}}const oldDateModal=document.querySelector('.rtrack-date-modal[data-owner="vg-native"]');if(oldDateModal)oldDateModal.remove();const dateModal=document.createElement('div');dateModal.className='rtrack-date-modal';dateModal.dataset.owner='vg-native';dateModal.hidden=true;dateModal.innerHTML=`<section class="rtrack-date-shell"><header class="rtrack-date-head"><div><h3>Por data</h3><p>Escolha o período pela emissão da NF. Sem data fiscal, usamos a emissão/postagem do envio.</p></div><button type="button" class="rtrack-report-close" data-date-close aria-label="Fechar relatório">×</button></header><form class="rtrack-date-filters" data-date-form><label>DATA INICIAL<input type="date" data-date-start required></label><label>DATA FINAL<input type="date" data-date-end required></label><label class="rtrack-date-carrier-label">TRANSPORTADORA<select data-date-carrier><option value="">Todas as transportadoras</option></select></label><button type="submit" class="rtrack-date-generate" data-date-generate>GERAR RELATÓRIO</button><button type="button" class="rtrack-report-download" data-date-download disabled>BAIXAR</button></form><main class="rtrack-date-body"><div class="rtrack-date-status" data-date-status role="status">Escolha as datas e a transportadora para gerar o relatório.</div><div class="rtrack-date-results" data-date-results></div></main></section>`;document.body.appendChild(dateModal);const dateStart=dateModal.querySelector('[data-date-start]'),dateEnd=dateModal.querySelector('[data-date-end]'),dateCarrier=dateModal.querySelector('[data-date-carrier]'),dateStatus=dateModal.querySelector('[data-date-status]'),dateResults=dateModal.querySelector('[data-date-results]'),dateDownload=dateModal.querySelector('[data-date-download]'),dateGenerate=dateModal.querySelector('[data-date-generate]');let dateResult=null,dateOptionsReady=false,dateRequestSeq=0,dateOptionsTimer=null;function dateToday(){const p=new Intl.DateTimeFormat('en-CA',{timeZone:'America/Sao_Paulo',year:'numeric',month:'2-digit',day:'2-digit'}).formatToParts(new Date).reduce((a,x)=>(a[x.type]=x.value,a),{});return`${p.year}-${p.month}-${p.day}`}function dateDisplay(value){const s=String(value||''),m=s.match(/^(\d{4})-(\d{2})-(\d{2})/);return m?`${m[3]}/${m[2]}/${m[1]}`:s||'-'}function clearDateResult(){dateRequestSeq++;dateResult=null;dateResults.innerHTML='';dateDownload.disabled=true;dateStatus.classList.remove('is-error');dateStatus.textContent='Clique em GERAR RELATÓRIO para aplicar o período e a transportadora.';dateGenerate.disabled=!dateOptionsReady}function closeDateReport(){dateModal.hidden=true;dateRequestSeq++;clearTimeout(dateOptionsTimer)}async function loadDateOptions(){if(dateModal.hidden)return;dateGenerate.disabled=true;dateCarrier.disabled=true;try{const d=await api('/rastreamento/relatorios?date_options=1');if(dateModal.hidden)return;if(d.preparing||d.available===false){dateStatus.textContent=d.message||'Preparando os dados dos relatórios...';dateOptionsTimer=setTimeout(loadDateOptions,5e3);return}if(!Array.isArray(d.carriers))throw new Error('Não foi possível carregar as transportadoras.');const selected=dateCarrier.value;dateCarrier.innerHTML='<option value="">Todas as transportadoras</option>'+d.carriers.map(x=>`<option value="${esc(x.key)}">${esc(x.name)}</option>`).join('');if(d.carriers.some(x=>x.key===selected))dateCarrier.value=selected;dateOptionsReady=true;dateStatus.classList.remove('is-error');dateStatus.textContent=d.available_start?`Dados disponíveis nos arquivos atuais: ${dateDisplay(d.available_start)} a ${dateDisplay(d.available_end)}. Escolha o período e clique em GERAR RELATÓRIO.`:'Nenhum envio com data foi encontrado nos arquivos atuais.'}catch(e){if(dateModal.hidden)return;dateStatus.classList.add('is-error');dateStatus.textContent=e.message||'Falha ao carregar as transportadoras.';dateOptionsTimer=setTimeout(loadDateOptions,1e4)}finally{if(!dateModal.hidden){dateCarrier.disabled=!dateOptionsReady;dateGenerate.disabled=!dateOptionsReady}}}function openDateReport(){menu.hidden=true;modal.hidden=true;if(mapInstance){try{mapInstance.__rtrackResizeObserver?.disconnect()}catch{}mapInstance.remove();mapInstance=null}dateModal.hidden=false;dateOptionsReady=false;clearDateResult();if(!dateEnd.value){dateEnd.value=dateToday();dateStart.value=new Date(Date.parse(dateEnd.value+'T00:00:00Z')-59*864e5).toISOString().slice(0,10)}dateStatus.textContent='Carregando transportadoras e período disponível...';loadDateOptions()}function renderDateResult(d){const rows=Array.isArray(d.items)?d.items:[],delivered=rows.filter(x=>x.delivered).length;const undated=Array.isArray(d.undated_items)?d.undated_items:[];const missing=undated.length?`<details class="rtrack-date-missing"><summary>${fmt(undated.length)} notas sem data de emissão/postagem — fora do total do período</summary><p>Essas notas estão na base e no resumo TODOS. A data precisa estar informada no SYSEMP ou no arquivo da transportadora para confirmar o período.</p><div class="rtrack-date-table-wrap"><table class="rtrack-date-table"><thead><tr><th>NOTA FISCAL</th><th>TRANSPORTADORA</th><th>CTE / RASTREIO</th><th>STATUS</th></tr></thead><tbody>${undated.map(x=>`<tr><td>${esc(x.nf)}</td><td>${esc(x.carrier||'-')}</td><td>${esc(x.cte||'-')}</td><td>${esc(x.status||'-')}</td></tr>`).join('')}</tbody></table></div></details>`:'';dateResults.innerHTML=missing+`<div class="rtrack-date-kpis"><div><span>Notas no período</span><strong data-date-kpi-total>${fmt(rows.length)}</strong></div><div><span>Entregues</span><strong data-date-kpi-delivered>${fmt(delivered)}</strong></div><div><span>Pendentes</span><strong data-date-kpi-pending>${fmt(rows.length-delivered)}</strong></div></div>`+(rows.length?`<div class="rtrack-date-table-wrap"><table class="rtrack-date-table" data-date-main-table><thead><tr><th>NOTA FISCAL</th><th>EMISSÃO NF / ENVIO</th><th>TRANSPORTADORA</th><th>CTE / RASTREIO</th><th>STATUS</th><th>PREVISÃO</th><th>DATA DE ENTREGA</th><th>CIDADE</th><th>UF</th></tr></thead><tbody>${rows.map(x=>`<tr><td>${esc(x.nf||'-')}</td><td>${esc(dateDisplay(x.issue_date))}</td><td>${esc(x.carrier||'-')}</td><td>${esc(x.cte||'-')}</td><td>${esc(x.status||'-')}</td><td>${esc(dateDisplay(x.forecast))}</td><td>${esc(dateDisplay(x.delivery_date))}</td><td>${esc(x.cidade||'-')}</td><td>${esc(x.uf||'-')}</td></tr>`).join('')}</tbody></table></div>`:'<div class="rtrack-date-empty">Nenhum envio encontrado no período e na transportadora selecionados.</div>');addReportColumnFilters(dateResults,()=>{const visible=reportFilteredItems(dateResults,'[data-date-main-table]',d.items||[]),delivered=visible.filter(x=>x.delivered).length;dateDownload.disabled=!visible.length;const kpi=key=>dateResults.querySelector('[data-date-kpi-'+key+']');if(kpi('total'))kpi('total').textContent=fmt(visible.length);if(kpi('delivered'))kpi('delivered').textContent=fmt(delivered);if(kpi('pending'))kpi('pending').textContent=fmt(visible.length-delivered)})}dateModal.querySelector('[data-date-close]').addEventListener('click',closeDateReport);dateModal.addEventListener('click',e=>{if(e.target===dateModal)closeDateReport()});document.addEventListener('keydown',e=>{if(e.key==='Escape'&&!dateModal.hidden)closeDateReport()});for(const input of[dateStart,dateEnd,dateCarrier])input.addEventListener('change',clearDateResult);dateModal.querySelector('[data-date-form]').addEventListener('submit',async e=>{e.preventDefault();if(!dateOptionsReady)return;const start=dateStart.value,end=dateEnd.value;if(!start||!end||start>end){dateStatus.classList.add('is-error');dateStatus.textContent='Informe as duas datas. A data inicial não pode ser maior que a data final.';dateDownload.disabled=true;return}const seq=++dateRequestSeq;dateResult=null;dateResults.innerHTML='';dateGenerate.disabled=true;dateDownload.disabled=true;dateStatus.classList.remove('is-error');dateStatus.textContent='Gerando relatório do período selecionado...';try{const q=new URLSearchParams({start,end,carrier:dateCarrier.value}),d=await api('/rastreamento/relatorios?'+q.toString());if(seq!==dateRequestSeq||dateModal.hidden)return;if(d.preparing||d.available===false)throw new Error(d.message||'Os dados estão sendo preparados. Tente novamente em instantes.');if(!Array.isArray(d.items))throw new Error('Resposta do relatório inválida.');dateResult=d;renderDateResult(d);dateStatus.textContent=`${dateDisplay(d.date_start)} a ${dateDisplay(d.date_end)} · ${d.carrier_label||'Todas as transportadoras'} · ${fmt(d.items.length)} notas. Data de referência: emissão da NF, com emissão/postagem do envio como alternativa.`;dateDownload.disabled=!d.items.length}catch(error){if(seq!==dateRequestSeq||dateModal.hidden)return;dateStatus.classList.add('is-error');dateStatus.textContent=error.message||'Não foi possível gerar o relatório.'}finally{if(seq===dateRequestSeq&&!dateModal.hidden)dateGenerate.disabled=false}});dateDownload.addEventListener('click',async()=>{const d=dateResult;if(!d?.items?.length)return;const visible=reportFilteredItems(dateResults,'[data-date-main-table]',d.items);if(!visible.length)return;dateDownload.disabled=true;try{await downloadRows(`rastreamento_por_data_${d.date_start}_${d.date_end}`,visible,[{'Data inicial':dateDisplay(d.date_start),'Data final':dateDisplay(d.date_end),'Transportadora':d.carrier_label,'Notas':visible.length,'Entregues':visible.filter(x=>x.delivered).length,'Data de referência':'Emissão da NF / envio'}])}catch(e){dateStatus.classList.add('is-error');dateStatus.textContent=e.message||'Falha ao baixar o relatório.'}finally{dateDownload.disabled=dateResult!==d||!reportFilteredItems(dateResults,'[data-date-main-table]',dateResult?.items||[]).length}});function parseReportNotes(value){const notes=[],invalid=[],seen=new Set;for(const raw of String(value||'').split(/[\s,;|]+/)){let token=raw.replace(/^['"]|['"]$/g,'').trim();if(!token)continue;if(/^\d{1,3}(?:\.\d{3})+$/.test(token))token=token.replace(/\./g,'');else token=token.replace(/\.0+$/,'');if(!/^\d+$/.test(token)){invalid.push(raw);continue}token=token.replace(/^0+(?=\d)/,'');if(!seen.has(token)){notes.push(token);seen.add(token)}}return{notes,invalid}}const oldNotesModal=document.querySelector('.rtrack-notes-modal[data-owner="vg-native"]');if(oldNotesModal)oldNotesModal.remove();const notesModal=document.createElement('div');notesModal.className='rtrack-date-modal rtrack-notes-modal';notesModal.dataset.owner='vg-native';notesModal.hidden=true;notesModal.innerHTML=`<section class="rtrack-date-shell"><header class="rtrack-date-head"><div><h3>Por nota</h3><p>Digite notas e use espaço ou Enter. Você também pode colar várias linhas ou uma coluna do Excel.</p></div><button type="button" class="rtrack-report-close" data-notes-close aria-label="Fechar relatório">×</button></header><form class="rtrack-notes-form" data-notes-form><div class="rtrack-notes-entry" data-notes-entry><div class="rtrack-notes-chips" data-notes-chips></div><input type="text" inputmode="numeric" data-notes-input placeholder="Digite ou cole as notas fiscais" autocomplete="off" aria-label="Notas fiscais"></div><div class="rtrack-notes-actions"><span data-notes-count>0 notas</span><button type="button" class="rtrack-report-download" data-notes-clear>LIMPAR</button><button type="submit" class="rtrack-date-generate" data-notes-search>BUSCAR NOTAS</button><button type="button" class="rtrack-report-download" data-notes-download disabled>BAIXAR</button></div></form><main class="rtrack-date-body"><div class="rtrack-date-status" data-notes-status role="status">Adicione as notas e clique em BUSCAR NOTAS.</div><div data-notes-results></div></main></section>`;document.body.appendChild(notesModal);const notesInput=notesModal.querySelector('[data-notes-input]'),notesChips=notesModal.querySelector('[data-notes-chips]'),notesStatus=notesModal.querySelector('[data-notes-status]'),notesResults=notesModal.querySelector('[data-notes-results]'),notesSearch=notesModal.querySelector('[data-notes-search]'),notesDownload=notesModal.querySelector('[data-notes-download]');let selectedNotes=[],notesRows=[],notesRequestSeq=0;const noteColumns=[['nota','NOTA FISCAL'],['data_api','DATA API'],['data_finalizado','DATA FINALIZADO'],['data_romaneio','DATA ROMANEIO'],['transportadora','TRANSPORTADORA'],['status_lisa','STATUS LISA'],['status_tr','STATUS TRANSP.'],['previsao_entrega','PREVISÃO DE ENTREGA'],['cte','CTE / RASTREIO'],['destinatario','DESTINATÁRIO'],['consulta','CONSULTA']];function noteDateDisplay(value){const s=String(value||'');if(/^\d{4}-\d{2}-\d{2}T/.test(s)){const date=new Date(s);if(!Number.isNaN(date.getTime()))return date.toLocaleString('pt-BR',{timeZone:'America/Sao_Paulo'})}return dateDisplay(s)}function noteCell(row,key){return key==='consulta'?row.found?'Encontrada':'Não encontrada':['data_api','data_finalizado','data_romaneio','previsao_entrega'].includes(key)?noteDateDisplay(row[key]):String(row[key]||'-')}function resetNotesResult(){notesRequestSeq++;notesRows=[];notesResults.innerHTML='';notesDownload.disabled=true;notesSearch.disabled=false;notesStatus.classList.remove('is-error');notesStatus.textContent='Adicione as notas e clique em BUSCAR NOTAS.'}function renderNotesChips(){notesChips.innerHTML=selectedNotes.map(nota=>`<span class="rtrack-note-chip">${esc(nota)}<button type="button" data-notes-remove="${esc(nota)}" aria-label="Remover nota ${esc(nota)}">×</button></span>`).join('');notesModal.querySelector('[data-notes-count]').textContent=fmt(selectedNotes.length)+' notas'}function addReportNotes(value){const parsed=parseReportNotes(value);selectedNotes=[...new Set([...selectedNotes,...parsed.notes])];resetNotesResult();renderNotesChips();if(parsed.invalid.length){notesStatus.classList.add('is-error');notesStatus.textContent='Não reconheci como notas: '+parsed.invalid.slice(0,5).join(', ')}return parsed}function consumeNotesInput(force=false){const raw=notesInput.value;if(!raw)return;if(force||/[\s,;|]$/.test(raw)){notesInput.value='';addReportNotes(raw);return}const parts=raw.split(/[\s,;|]+/);if(parts.length>1){notesInput.value=parts.pop();addReportNotes(parts.join(' '))}}function closeNotesReport(){notesModal.hidden=true;notesRequestSeq++;notesSearch.disabled=false}function openNotesReport(){menu.hidden=true;modal.hidden=true;dateModal.hidden=true;if(mapInstance){try{mapInstance.__rtrackResizeObserver?.disconnect()}catch{}mapInstance.remove();mapInstance=null}notesModal.hidden=false;setTimeout(()=>notesInput.focus(),30)}notesInput.addEventListener('input',()=>consumeNotesInput(false));notesInput.addEventListener('keydown',e=>{if(e.key==='Enter'||e.key===' '){e.preventDefault();consumeNotesInput(true)}else if(e.key==='Tab')consumeNotesInput(true);else if(e.key==='Backspace'&&!notesInput.value&&selectedNotes.length){selectedNotes.pop();resetNotesResult();renderNotesChips()}});notesInput.addEventListener('paste',e=>{const text=e.clipboardData?.getData('text');if(text===undefined)return;e.preventDefault();addReportNotes([notesInput.value,text].filter(Boolean).join('\n'));notesInput.value=''});notesChips.addEventListener('click',e=>{const button=e.target.closest('[data-notes-remove]');if(!button)return;selectedNotes=selectedNotes.filter(nota=>nota!==button.dataset.notesRemove);resetNotesResult();renderNotesChips();notesInput.focus()});notesModal.querySelector('[data-notes-clear]').addEventListener('click',()=>{selectedNotes=[];notesInput.value='';resetNotesResult();renderNotesChips();notesInput.focus()});notesModal.querySelector('[data-notes-close]').addEventListener('click',closeNotesReport);notesModal.addEventListener('click',e=>{if(e.target===notesModal)closeNotesReport()});document.addEventListener('keydown',e=>{if(e.key==='Escape'&&!notesModal.hidden)closeNotesReport()});notesModal.querySelector('[data-notes-form]').addEventListener('submit',async e=>{e.preventDefault();const parsed=parseReportNotes(notesInput.value);if(parsed.invalid.length){notesStatus.classList.add('is-error');notesStatus.textContent='Confira as notas digitadas: '+parsed.invalid.slice(0,5).join(', ');return}consumeNotesInput(true);if(!selectedNotes.length){notesStatus.classList.add('is-error');notesStatus.textContent='Informe pelo menos uma nota fiscal.';return}if(selectedNotes.length>5e3){notesStatus.classList.add('is-error');notesStatus.textContent='Consulte até 5.000 notas por vez.';return}const seq=++notesRequestSeq;notesRows=[];notesResults.innerHTML='';notesDownload.disabled=true;notesSearch.disabled=true;notesStatus.classList.remove('is-error');notesStatus.textContent='Consultando '+fmt(selectedNotes.length)+' notas...';try{const d=await api('/rastreamento/relatorios/notas',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({notas:selectedNotes.slice()})});if(seq!==notesRequestSeq||notesModal.hidden)return;if(!Array.isArray(d.items))throw new Error('Resposta do relatório inválida.');notesRows=d.items;notesResults.innerHTML=`<div class="rtrack-date-table-wrap"><table class="rtrack-date-table" data-notes-table><thead><tr>${noteColumns.map(([,label])=>`<th>${esc(label)}</th>`).join('')}</tr></thead><tbody>${notesRows.map(row=>`<tr${row.found?'':' class="rtrack-note-not-found"'}>${noteColumns.map(([key])=>`<td>${esc(noteCell(row,key))}</td>`).join('')}</tr>`).join('')}</tbody></table></div>`;notesStatus.textContent=fmt(d.found)+' encontradas · '+fmt(d.not_found?.length||0)+' não encontradas. Datas não disponíveis na fonte aparecem como “-”.';addReportColumnFilters(notesResults,()=>{notesDownload.disabled=!reportFilteredItems(notesResults,'[data-notes-table]',notesRows).length})}catch(error){if(seq!==notesRequestSeq||notesModal.hidden)return;notesStatus.classList.add('is-error');notesStatus.textContent=error.message||'Falha ao consultar as notas.'}finally{if(seq===notesRequestSeq&&!notesModal.hidden)notesSearch.disabled=false}});notesDownload.addEventListener('click',async()=>{const rows=reportFilteredItems(notesResults,'[data-notes-table]',notesRows);if(!rows.length)return;notesDownload.disabled=true;try{const XLSX=await ensureXlsx(),data=rows.map(row=>Object.fromEntries(noteColumns.map(([key,label])=>[label,noteCell(row,key)]))),wb=XLSX.utils.book_new(),ws=XLSX.utils.json_to_sheet(data);ws['!cols']=noteColumns.map(([key])=>({wch:key==='destinatario'?34:key.includes('status')?28:22}));XLSX.utils.book_append_sheet(wb,ws,'Por nota');XLSX.writeFile(wb,'rastreamento_por_nota.xlsx',{compression:true})}catch(e){notesStatus.classList.add('is-error');notesStatus.textContent=e.message||'Falha ao baixar o relatório.'}finally{notesDownload.disabled=!reportFilteredItems(notesResults,'[data-notes-table]',notesRows).length}});function isMercadoEnvios(row){
return [row.carrier,row.carrier_key].some(value=>/MERCADOENVIO/.test(String(value||'').toUpperCase().replace(/[^A-Z]/g,'')));
}
function mapPeriodData(period){
const from=delayFilters.start||period.period_start?.slice(0,10);
const to=delayFilters.end||period.period_end?.slice(0,10);
const items=(period.items||[]).filter(x=>{
if(isMercadoEnvios(x)||!Number.isFinite(Number(x.issue_ms)))return false;
const date=new Date(Number(x.issue_ms)).toISOString().slice(0,10);
return (!from||date>=from)&&(!to||date<=to);
});
const regions=new Map(),states=new Map(),carriers=new Map();
const add=(group,name,item)=>{
if(!group.has(name))group.set(name,{name,sent:0,delivered:0,on_time:0,late:0,late_data_available:false,carriers:new Map()});
const row=group.get(name);
row.sent++;row.delivered+=Number(!!item.delivered);row.on_time+=Number(!!item.on_time);row.late+=Number(!!item.late);row.late_data_available||=!!item.forecast_valid;
return row;
};
let geoMissing=0;
for(const item of items){
add(carriers,item.carrier,item);
if(!item.uf||!item.region){geoMissing++;continue}
add(add(regions,item.region,item).carriers,item.carrier,item);
add(add(states,item.uf,item).carriers,item.carrier,item);
}
const finish=group=>[...group.values()].sort((a,b)=>b.sent-a.sent).map(row=>{
const list=finish(row.carriers),top=list[0];
return {...row,carriers:list,quality:100*row.delivered/row.sent,top_carrier:top?.name||'',top_carrier_pct:top?100*top.sent/row.sent:0};
});
return {...period,items,total_notes:items.length,geo_missing:geoMissing,regions:finish(regions),states:finish(states),carriers:finish(carriers)};
}
function reportNotesCount(data){return currentType==='carrier'?Number(data?.total_notes||0):Math.max(0,Number(data?.total_notes||0)-Number(data?.geo_missing||0))}function setCurrentFromBundle(){currentData=periodData(currentDays);if(!currentData)return false;if(currentType!=='carrier')currentData=mapPeriodData(currentData);const stamp=reportsBundle?.generated_at?new Date(reportsBundle.generated_at):null;subtitle.textContent=`${currentType==='carrier'?'Últimos '+currentDays+' dias · ':''}${fmt(reportNotesCount(currentData))} notas${stamp&&!Number.isNaN(stamp.getTime())?' · snapshot '+stamp.toLocaleString('pt-BR',{timeZone:'America/Sao_Paulo',day:'2-digit',month:'2-digit',hour:'2-digit',minute:'2-digit'}):''}`;return true}async function loadData(){if(!reportsBundle){body.innerHTML='<div class="rtrack-report-loading">Abrindo dados pré-carregados...</div>';await preloadReports(false)}if(!setCurrentFromBundle())throw new Error('Snapshot de relatórios indisponível.');render()}if(!document.getElementById('rtrack-delay-styles')){const style=document.createElement('style');style.id='rtrack-delay-styles';style.textContent='\n/* REGIAO/ESTADO - HISTORICO DE ATRASOS */\n.rtrack-delay-dashboard{padding:14px 16px;background:var(--lisa-card-bg,#fff);border:1px solid #d6e0ee;border-radius:15px;margin:0 0 14px;color:#173b67}\n.rtrack-delay-title{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:11px;flex-wrap:wrap}\n.rtrack-delay-title strong{font-size:15px}\n.rtrack-delay-title small{color:#66798e}\n.rtrack-delay-filters{display:flex;align-items:flex-end;flex-wrap:wrap;gap:10px;margin-bottom:11px}\n.rtrack-delay-filters label{display:flex;flex-direction:column;gap:4px;font-size:11px;color:#445e7a;min-width:110px}\n.rtrack-delay-filters input,.rtrack-delay-filters select{box-sizing:border-box;min-height:35px;border:1px solid #cddbec;background:#fff;color:#16395c;border-radius:8px;padding:5px 9px;max-width:182px}\n.rtrack-delay-filters button{min-height:35px;padding:5px 12px;border:1px solid #cddbec;background:#eff5fd;border-radius:8px;color:#143b66;cursor:pointer}\n.rtrack-delay-kpis{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px}\n.rtrack-delay-kpis div{background:#f0f5fc;border-radius:10px;padding:10px 13px}\n.rtrack-delay-kpis span{display:block;font-size:11px;color:#60758f}\n.rtrack-delay-kpis strong{font-size:19px;color:#143b66}\n.rtrack-delay-area{font-size:11px;color:#a34d00;font-weight:700;margin-top:4px}\n.rtrack-delay-table-wrap{overflow:auto;border:1px solid #d9e2ee;border-radius:12px;margin:12px 0 0;max-height:330px}\n.rtrack-delay-table{width:100%;border-collapse:collapse;white-space:nowrap;font-size:12px}\n.rtrack-delay-table th,.rtrack-delay-table td{padding:9px 12px;text-align:left;border-bottom:1px solid #e4ebf4}\n.rtrack-delay-table th{background:#eef4fa;position:sticky;top:0;color:#233f63;z-index:1}\n.rtrack-delay-warning{font-size:11px;color:#756033;margin-top:8px}\n@media(max-width:850px){.rtrack-delay-kpis{grid-template-columns:repeat(2,minmax(0,1fr))}.rtrack-delay-filters label{min-width:calc(50% - 8px)}}\n';document.head.appendChild(style)}let delayFiltersExpanded=false,delayFilters={start:'',end:'',basis:'entry',status:'all'};
function delayHistoryForPeriod(){
const all=reportsBundle?.delay_history||[];
const shipments=new Set((currentData?.items||[]).map(x=>JSON.stringify([String(x.nf),x.carrier,x.uf])));

const from=delayFilters.start||String(currentData?.period_start||'').slice(0,10);
const until=delayFilters.end||String(currentData?.period_end||'').slice(0,10);
return all.filter(x=>{
if(isMercadoEnvios(x)||!shipments.has(JSON.stringify([String(x.nf),x.carrier,x.uf])))return false;
const day=delayFilters.basis==='due'?x.due_date:x.entry_date;
return day&&(!from||day>=from)&&(!until||day<=until)&&(delayFilters.status==='all'||x.status===delayFilters.status);
});
}
function delayAreaRecords(kind,area,all){return(all||delayHistoryForPeriod()).filter(x=>(kind==='region'?x.region:x.uf)===area)}
function delayKpis(records){const open=records.filter(x=>x.status==='EM_ABERTO').length,delivered=records.filter(x=>x.status==='ENTREGUE_ATRASADO').length;return`<div class="rtrack-delay-kpis"><div><span>Total de atrasos</span><strong>${fmt(records.length)}</strong></div><div><span>Atrasos em aberto</span><strong>${fmt(open)}</strong></div><div><span>Entregues com atraso</span><strong>${fmt(delivered)}</strong></div></div>`}
function delayControls(){const set=(v)=>v||'';return`<section class="rtrack-delay-dashboard"><div class="rtrack-delay-title"><strong>Histórico de atrasos · ${currentType==='region'?'Região':'Estado'}</strong><small>CEP do SYSEMP + previsão da transportadora/CTT</small></div><details data-delay-details ${delayFiltersExpanded?'open':''}><summary class="rtrack-delay-toggle"><span>Filtros de data</span><span class="rtrack-delay-toggle-open">Expandir</span><span class="rtrack-delay-toggle-close">Recolher</span><span class="rtrack-delay-arrow" aria-hidden="true"></span></summary><div class="rtrack-delay-filters"><label>Data inicial<input data-delay-from type="date" value="${esc(set(delayFilters.start))}"></label><label>Data final<input data-delay-to type="date" value="${esc(set(delayFilters.end))}"></label><label>Filtrar por<select data-delay-basis><option value="entry" ${delayFilters.basis==='entry'?'selected':''}>Entrada em atraso</option><option value="due" ${delayFilters.basis==='due'?'selected':''}>Prazo previsto</option></select></label><label>Situação<select data-delay-status><option value="all">Todas</option><option value="EM_ABERTO" ${delayFilters.status==='EM_ABERTO'?'selected':''}>Em aberto</option><option value="ENTREGUE_ATRASADO" ${delayFilters.status==='ENTREGUE_ATRASADO'?'selected':''}>Entregue atrasado</option></select></label><button type="button" data-delay-apply>Aplicar</button><button type="button" data-delay-reset>Limpar</button></div></details>${delayKpis(delayHistoryForPeriod())}<div class="rtrack-delay-warning">${Array.isArray(reportsBundle?.delay_history)?'Sem datas informadas, o mapa usa os dados disponíveis no snapshot. Mercado Envios fica fora do mapa. Histórico registrado nos snapshots do Cloud. Sem prazo confiável, o registro não entra no indicador.':'Histórico ainda não disponível neste snapshot. O Worker precisa concluir a primeira preparação; não interprete zero como ausência de atrasos.'}</div></section>`}
function delayDetail(kind,area){const rows=delayAreaRecords(kind,area).sort((a,b)=>String(b.entry_date).localeCompare(String(a.entry_date)));return`<section class="rtrack-delay-dashboard"><div class="rtrack-delay-title"><strong>Atrasos registrados · ${esc(area||'-')}</strong><small>${fmt(rows.length)} ocorrências no filtro</small></div><div class="rtrack-delay-table-wrap"><table class="rtrack-delay-table"><thead><tr><th>NF</th><th>Transportadora</th><th>CEP</th><th>Prazo</th><th>Entrada atraso</th><th>Situação</th><th>Dias de atraso</th></tr></thead><tbody>${rows.length?rows.map(x=>`<tr><td>${esc(x.nf)}</td><td>${esc(x.carrier)}</td><td>${esc(x.cep||'-')}</td><td>${esc(x.due_date||'-')}</td><td>${esc(x.entry_date||'-')}</td><td>${x.status==='EM_ABERTO'?'Em aberto':'Entregue atrasado'}</td><td>${fmt(x.delay_days)}</td></tr>`).join(''):'<tr><td colspan="7">Nenhum atraso registrado para esta área e filtro.</td></tr>'}</tbody></table></div></section>`}
function bindDelayControls(kind){
const details=body.querySelector('[data-delay-details]');details?.addEventListener('toggle',()=>{if(details.isConnected)delayFiltersExpanded=details.open});
const set=(k,sel)=>{const node=body.querySelector(sel);delayFilters[k]=node?.value||''};
body.querySelector('[data-delay-apply]')?.addEventListener('click',()=>{set('start','[data-delay-from]');set('end','[data-delay-to]');set('basis','[data-delay-basis]');set('status','[data-delay-status]');if(delayFilters.start&&delayFilters.end&&delayFilters.start>delayFilters.end){alert('A data inicial deve ser anterior ou igual à data final.');return}setCurrentFromBundle();renderGeo(kind)});
body.querySelector('[data-delay-reset]')?.addEventListener('click',()=>{delayFilters={start:'',end:'',basis:'entry',status:'all'};setCurrentFromBundle();renderGeo(kind)});
}
function renderKpis(){const d=currentData||{};return`<div class="rtrack-kpis"><div class="rtrack-kpi"><span>Notas no período</span><strong>${fmt(reportNotesCount(d))}</strong></div><div class="rtrack-kpi"><span>Regiões atendidas</span><strong>${fmt((d.regions||[]).length)}</strong></div><div class="rtrack-kpi"><span>Estados atendidos</span><strong>${fmt((d.states||[]).length)}</strong></div><div class="rtrack-kpi"><span>Transportadoras</span><strong>${fmt((d.carriers||[]).length)}</strong></div></div>`}function renderCarrierDetail(row){const list=row?.carriers||[];return`<section class="rtrack-carrier-detail"><div class="rtrack-carrier-detail-head"><h4>Transportadoras · ${esc(row?.name||'-')}</h4><span>${fmt(row?.sent)} notas</span></div><div class="rtrack-carrier-grid">${list.length?list.map(c=>`<div class="rtrack-carrier-row"><strong>${esc(c.name)}</strong><span>Enviadas<br><b>${fmt(c.sent)}</b></span><span>No prazo<br><b>${fmt(c.on_time)}</b></span><span>Atrasadas<br><b>${esc(lateText(c))}</b></span>${qualityHtml(c)}</div>`).join(''):'<div class="rtrack-report-loading" style="min-height:80px">Sem transportadoras para este filtro.</div>'}</div></section>`}function legendHtml(){return DELAY_LEVELS.map(({label,color})=>`<span class="rtrack-legend-chip"><i class="rtrack-legend-dot" style="background:${color}"></i>${label}</span>`).join('')}function renderGeo(kind){const rows=(kind==='region'?currentData?.regions||[]:currentData?.states||[]).slice(),historicalAreas=new Set(delayHistoryForPeriod().map(x=>kind==='region'?x.region:x.uf).filter(Boolean));for(const name of historicalAreas)if(!rows.some(x=>x.name===name))rows.push({name,uf:kind==='state'?name:'',sent:0,delivered:0,on_time:0,late:0,carriers:[],top_carrier:'',top_carrier_pct:0});const delayCounts=new Map();for(const record of delayHistoryForPeriod()){const key=kind==='region'?record.region:record.uf;delayCounts.set(key,(delayCounts.get(key)||0)+1)}
const rateFor=row=>Array.isArray(reportsBundle?.delay_history)?delayRate(delayCounts.get(row?.name)||0,Number(row?.sent||0)):null;
const label=kind==='region'?'região':'estado';if(!currentGeo||!rows.some(x=>x.name===currentGeo))currentGeo=rows[0]?.name||'';const selected=rows.find(x=>x.name===currentGeo)||rows[0]||null;let warn='';if(Number(currentData?.geo_missing)>0)warn=`<div class="rtrack-report-warning">${fmt(currentData.geo_missing)} de ${fmt(currentData.total_notes)} notas estão sem CEP/UF de destino identificado. Elas não entram na contagem do mapa. Continuam disponíveis nos relatórios e na exportação. A recuperação do SYSEMP salvo é tentada automaticamente.</div>`;else if(!currentData?.sysemp_has_cep)warn='<div class="rtrack-report-warning">O índice atual do SYSEMP ainda não contém CEP. O painel tenta reprocessar automaticamente o arquivo SYSEMP salvo.</div>';else if(!currentData?.total_notes)warn=`<div class="rtrack-report-warning">Nenhuma NF foi encontrada no período. O painel vai cruzar Nota Fiscal da transportadora com a mesma Nota Fiscal do SYSEMP e usar o CEP para localizar UF/Região. Fontes indexadas: ${fmt(currentData?.carrier_indexed_count)} transportadoras · NFs SYSEMP com CEP: ${fmt(currentData?.sysemp_cep_rows)} · vínculos NF↔SYSEMP: ${fmt(currentData?.matched_sysemp)} · registros sem Data de emissão nas bases antigas: ${fmt(currentData?.skipped_no_issue_date)}.</div>`;body.innerHTML=warn+renderKpis()+delayControls()+`<div class="rtrack-geo-layout"><section class="rtrack-map-card"><div class="rtrack-map-toolbar"><strong>Distribuição por ${label}</strong><div class="rtrack-map-legend">${legendHtml()}</div></div><div class="rtrack-map" data-rtrack-map></div><div class="rtrack-map-note">Passe o mouse em uma área para destacar. Clique para abrir o detalhamento. Use a barra lateral para zoom; em telas touch, use pinça com dois dedos.</div></section><section class="rtrack-list-card"><div class="rtrack-list-head"><strong>${kind==='region'?'Regiões':'Estados'}</strong><span>NF · transportadora principal</span></div><div class="rtrack-geo-list">${rows.map(x=>{const rate=rateFor(x),color=delayLevel(rate)?.color||MAP_NEUTRAL;return`<div class="rtrack-geo-row ${x.name===currentGeo?'is-active':''}" style="--rtrack-accent:${color}" data-rtrack-geo="${esc(x.name)}"><i class="rtrack-geo-swatch" style="background:${color}"></i><div class="rtrack-geo-name">${esc(x.name)}</div><div class="rtrack-geo-count">${fmt(x.sent)}</div><div class="rtrack-geo-top">Mais usada: <b>${esc(x.top_carrier||'-')}</b><br>${Number(x.top_carrier_pct||0).toLocaleString('pt-BR',{maximumFractionDigits:1})}% dos envios<div class="rtrack-delay-area">Atrasos: ${Array.isArray(reportsBundle?.delay_history)?fmt(delayCounts.get(x.name)||0):"—"} - ${delayRateLabel(rate)}</div></div><button type="button" class="rtrack-mini-download" data-rtrack-geo-download="${esc(x.name)}" title="Baixar relatório" aria-label="Baixar relatório de ${esc(x.name)}"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 3v11"></path><path d="m7.5 10.5 4.5 4.5 4.5-4.5"></path><path d="M5 19h14"></path></svg></button></div>`}).join('')}</div></section></div>${selected?renderCarrierDetail(selected)+delayDetail(kind,selected.name):''}<div class="rtrack-report-footnote">Geografia: o CEP do SYSEMP é a fonte principal para identificar UF e Região. O mapa de envios acompanha o período 7/15/30/60 dias; a taxa divide os atrasos do filtro pelo total de envios da área no período selecionado acima. Qualidade = percentual de notas enviadas que já constam como entregues. Sem previsão confiável, não se calcula atraso.</div>`;bindGeo(kind,rows);bindDelayControls(kind);renderMap(kind,rows,rateFor).catch(e=>{const m=body.querySelector('[data-rtrack-map]');if(m)m.innerHTML=`<div class="rtrack-report-error">${esc(e.message||'Mapa indisponível.')}</div>`})}function bindGeo(kind,rows){body.querySelectorAll('[data-rtrack-geo]').forEach(el=>el.addEventListener('click',e=>{if(e.target.closest('[data-rtrack-geo-download]'))return;currentGeo=el.dataset.rtrackGeo;renderGeo(kind)}));body.querySelectorAll('[data-rtrack-geo-download]').forEach(b=>b.addEventListener('click',e=>{e.stopPropagation();const key=b.dataset.rtrackGeoDownload,row=rows.find(x=>x.name===key),items=itemsForGeo(kind,key);downloadRows(`rastreamento_${kind}_${key}_${currentDays}dias`,items,[{'Área':key,'Notas enviadas':row?.sent||0,'Mais usada':row?.top_carrier||'','% mais usada':row?.top_carrier_pct||0,'Histórico atrasos':delayAreaRecords(kind,key).length}],delayAreaRecords(kind,key))}))}async function renderMap(kind,rows,rateFor){const el=body.querySelector('[data-rtrack-map]');if(!el)return;const L=await ensureLeaflet(),geo=await getGeoJson();if(!el.isConnected||modal.hidden||body.querySelector('[data-rtrack-map]')!==el)return;if(mapInstance){try{mapInstance.__rtrackResizeObserver?.disconnect()}catch{}mapInstance.remove();mapInstance=null}mapLayers=[];const lookup=new Map(rows.map(x=>[x.name,x]));mapInstance=L.map(el,{zoomControl:false,attributionControl:false,scrollWheelZoom:false,doubleClickZoom:true,boxZoom:true,keyboard:true,touchZoom:true,dragging:true,bounceAtZoomLimits:false,preferCanvas:false,zoomSnap:.1,zoomDelta:.25,maxBoundsViscosity:.88});const localMap=mapInstance;let geoLayer=null,zoomRange=null,showRates=false;const styleFor=f=>{const uf=featureUf(f),key=kind==='region'?UF_REGION[uf]:uf,row=lookup.get(key),level=showRates?delayLevel(rateFor(row)):null;return{color:'#ffffff',weight:1.2,fillColor:level?.color||MAP_NEUTRAL,fillOpacity:level ? .76 : .45,opacity:1}};function hoverKey(key,on){for(const x of mapLayers){if(x.__rtrackKey!==key)continue;x.setStyle(on?{weight:2.6,color:'#143b66'}:styleFor(x.feature));if(x._path)x._path.classList.toggle('rtrack-map-hover',on)}}geoLayer=L.geoJSON(geo,{style:styleFor,onEachFeature:(f,l)=>{const uf=featureUf(f),key=kind==='region'?UF_REGION[uf]:uf,row=lookup.get(key);l.__rtrackKey=key;mapLayers.push(l);l.bindTooltip(`<div class="rtrack-map-tooltip"><b>${esc(key||uf||'-')}</b><br>${fmt(row?.sent||0)} notas${row?.top_carrier?`<br>${esc(row.top_carrier)} · ${Number(row.top_carrier_pct||0).toLocaleString('pt-BR',{maximumFractionDigits:1})}%`:''}</div>`,{sticky:true,direction:'top',opacity:.97,className:'rtrack-single-tooltip'});l.on('mouseover',()=>{hoverKey(key,true);l.bringToFront?.()});l.on('mouseout',()=>hoverKey(key,false));l.on('click',()=>{if(key&&lookup.has(key)){currentGeo=key;renderGeo(kind)}})}}).addTo(localMap);requestAnimationFrame(()=>requestAnimationFrame(()=>{if(mapInstance!==localMap||!el.isConnected)return;showRates=true;geoLayer.resetStyle()}));const bounds=geoLayer.getBounds();const syncZoom=()=>{if(!zoomRange||mapInstance!==localMap)return;zoomRange.min=String(localMap.getMinZoom());zoomRange.max=String(localMap.getMaxZoom());zoomRange.value=String(localMap.getZoom())};const fitMap=()=>{if(mapInstance!==localMap)return;localMap.invalidateSize(false);localMap.setMinZoom(0);localMap.setMaxZoom(20);localMap.fitBounds(bounds,{paddingTopLeft:[46,38],paddingBottomRight:[46,38],animate:false});const base=localMap.getZoom();localMap.setMinZoom(base);localMap.setMaxZoom(base+4);localMap.setMaxBounds(bounds.pad(.42));syncZoom()};const ZoomBar=L.Control.extend({options:{position:'topleft'},onAdd(map){const c=L.DomUtil.create('div','rtrack-zoom-control');c.innerHTML='<button type="button" class="rtrack-zoom-btn" data-rtrack-zoom-in title="Aumentar zoom" aria-label="Aumentar zoom">+</button><input class="rtrack-zoom-range" data-rtrack-zoom-range type="range" step="0.1" aria-label="Nível de zoom"><button type="button" class="rtrack-zoom-btn" data-rtrack-zoom-out title="Diminuir zoom" aria-label="Diminuir zoom">−</button><button type="button" class="rtrack-zoom-btn rtrack-zoom-fit" data-rtrack-zoom-fit title="Ajustar Brasil à área" aria-label="Ajustar mapa">⌂</button>';L.DomEvent.disableClickPropagation(c);L.DomEvent.disableScrollPropagation(c);zoomRange=c.querySelector('[data-rtrack-zoom-range]');const zin=c.querySelector('[data-rtrack-zoom-in]'),zout=c.querySelector('[data-rtrack-zoom-out]'),zfit=c.querySelector('[data-rtrack-zoom-fit]');zin.addEventListener('click',()=>map.setZoom(Math.min(map.getMaxZoom(),map.getZoom()+.5)));zout.addEventListener('click',()=>map.setZoom(Math.max(map.getMinZoom(),map.getZoom()-.5)));zfit.addEventListener('click',fitMap);zoomRange.addEventListener('input',()=>map.setZoom(Number(zoomRange.value)));map.on('zoom zoomend',syncZoom);return c}});(new ZoomBar).addTo(localMap);const hint=document.createElement('div');hint.className='rtrack-zoom-hint';hint.textContent='Touch: use dois dedos para ampliar ou reduzir';el.appendChild(hint);setTimeout(()=>{if(mapInstance!==localMap)return;fitMap();const RO=window.ResizeObserver;if(RO){const ro=new RO(()=>{if(mapInstance!==localMap)return;clearTimeout(localMap.__rtrackResizeTimer);localMap.__rtrackResizeTimer=setTimeout(fitMap,90)});ro.observe(el);localMap.__rtrackResizeObserver=ro}},120)}function renderCarrier(){const rows=currentData?.carriers||[];body.innerHTML=renderKpis()+`<section class="rtrack-carrier-table-card"><table class="rtrack-carrier-table"><thead><tr><th>Transportadora</th><th>Enviadas</th><th>Entregues</th><th>No prazo</th><th>Atrasadas</th><th>Qualidade</th><th></th></tr></thead><tbody>${rows.map(c=>`<tr><td><b>${esc(c.name)}</b></td><td>${fmt(c.sent)}</td><td>${fmt(c.delivered)}</td><td>${fmt(c.on_time)}</td><td>${esc(lateText(c))}</td><td>${qualityHtml(c)}</td><td><button type="button" class="rtrack-mini-download" data-rtrack-carrier-download="${esc(c.name)}" title="Baixar relatório" aria-label="Baixar relatório de ${esc(c.name)}"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 3v11"></path><path d="m7.5 10.5 4.5 4.5 4.5-4.5"></path><path d="M5 19h14"></path></svg></button></td></tr>`).join('')}</tbody></table></section><div class="rtrack-report-footnote">Qualidade = entregues ÷ enviadas no período. J&T, LATAM e qualquer outra base sem previsão exibem “Não consta dados” em Atrasadas.</div>`;body.querySelectorAll('[data-rtrack-carrier-download]').forEach(b=>b.addEventListener('click',()=>{const name=b.dataset.rtrackCarrierDownload,c=rows.find(x=>x.name===name),items=itemsForCarrier(name);downloadRows(`rastreamento_transportadora_${name}_${currentDays}dias`,items,[{'Transportadora':name,'Enviadas':c?.sent||0,'Entregues':c?.delivered||0,'No prazo':c?.on_time||0,'Atrasadas':c?.late_data_available?c.late:'Não consta dados','Qualidade %':c?.quality||0}])}));addReportColumnFilters(body,()=>{modal.querySelector('[data-rtrack-download]').disabled=!reportFilteredItems(body,'.rtrack-carrier-table',rows).length})}function render(){if(!currentData)return;modal.querySelector('[data-rtrack-download]').disabled=!(currentData.items||[]).length;const geo=currentType!=='carrier';daysSel.hidden=geo;daysSel.style.display=geo?'none':'';if(viewSwitch)viewSwitch.hidden=!geo;if(geo){for(const b of viewSwitch?.querySelectorAll('[data-rtrack-view]')||[])b.classList.toggle('is-active',b.dataset.rtrackView===currentType);title.textContent='Região / Estado';renderGeo(currentType==='state'?'state':'region')}else{title.textContent='Transportadora';renderCarrier()}}async function open(type){if(type==='notes'){openNotesReport();return}notesModal.hidden=true;if(type==='date'){openDateReport();return}currentType=type==='carrier'?'carrier':'region';currentDays=60;currentGeo='';delayFiltersExpanded=false;daysSel.value='60';menu.hidden=true;modal.hidden=false;try{await loadData()}catch(e){body.innerHTML=`<div class="rtrack-report-error">${esc(e.message||e)}</div>`}}root.__lisaRtrackOpen=open;root.__lisaRtrackGetReportsBundle=()=>reportsBundle;if(reportsBundle)root.__lisaRtrackReportsBundle=reportsBundle;btn.addEventListener('click',e=>{e.stopPropagation();const r=btn.getBoundingClientRect();menu.style.left=Math.min(window.innerWidth-246,r.left)+'px';menu.style.top=r.bottom+6+'px';menu.hidden=!menu.hidden});menu.addEventListener('click',e=>{const b=e.target.closest('[data-rtrack-type]');if(b)open(b.dataset.rtrackType)});document.addEventListener('click',e=>{if(!menu.hidden&&!menu.contains(e.target)&&e.target!==btn)menu.hidden=true});modal.querySelector('[data-rtrack-close]').onclick=()=>{modal.hidden=true;if(mapInstance){try{mapInstance.__rtrackResizeObserver?.disconnect()}catch{}mapInstance.remove();mapInstance=null}};if(viewSwitch)viewSwitch.addEventListener('click',e=>{const b=e.target.closest('[data-rtrack-view]');if(!b||currentType==='carrier')return;currentType=b.dataset.rtrackView==='state'?'state':'region';currentGeo='';render()});modal.addEventListener('click',e=>{if(e.target===modal)modal.querySelector('[data-rtrack-close]').click()});daysSel.addEventListener('change',()=>{currentDays=Number(daysSel.value)||60;currentGeo='';if(setCurrentFromBundle())render();else loadData().catch(e=>{body.innerHTML=`<div class="rtrack-report-error">${esc(e.message||e)}</div>`})});modal.querySelector('[data-rtrack-download]').addEventListener('click',()=>{if(!currentData)return;if(currentType==='carrier'){const visibleCarriers=reportFilteredItems(body,'.rtrack-carrier-table',currentData.carriers||[]),names=new Set(visibleCarriers.map(x=>x.name));const summary=visibleCarriers.map(c=>({'Transportadora':c.name,'Enviadas':c.sent,'Entregues':c.delivered,'No prazo':c.on_time,'Atrasadas':c.late_data_available?c.late:'Não consta dados','Qualidade %':c.quality}));downloadRows(`rastreamento_transportadoras_${currentDays}dias`,(currentData.items||[]).filter(x=>names.has(x.carrier)),summary)}else{const rows=currentType==='region'?currentData.regions:currentData.states,summary=(rows||[]).map(x=>({'Área':x.name,'Notas enviadas':x.sent,'Entregues':x.delivered,'No prazo':x.on_time,'Atrasadas':x.late,'Transportadora mais usada':x.top_carrier,'% mais usada':x.top_carrier_pct,'Qualidade %':x.quality}));downloadRows(`rastreamento_${currentType==='region'?'regioes':'estados'}_${currentDays}dias`,currentData.items||[],summary,delayHistoryForPeriod())}});const refreshPreload=()=>{clearTimeout(reportsEventTimer);reportsEventTimer=setTimeout(()=>{reportsRetryCount=0;preloadReports(true).catch(e=>console.debug('Pré-carga relatórios:',e))},2500)};root.addEventListener('lisa:carrier-files-updated',refreshPreload);root.addEventListener('lisa:carrier-indexes-ready',refreshPreload);root.addEventListener('lisa:monitor-updated',refreshPreload);root.addEventListener('lisa:tracking-summary-loaded',()=>{if(!reportsBundle)startPreload()});function startPreload(){if(!token()||!worker()){clearTimeout(reportsRetryTimer);reportsRetryTimer=setTimeout(startPreload,1e3);return}preloadReports(false).catch(e=>console.debug('Pré-carga relatórios:',e));getGeoJson().catch(e=>console.debug('Pré-carga geometria:',e))}setTimeout(startPreload,450)}init()})();

;

(()=>{let initialized=false;function init(){const panel=document.getElementById('lisa-app-vg-native')||document.querySelector('.lisa-app[data-worker]'),wrap=document.getElementById('lisacte-vg-native');if(!panel||!wrap){setTimeout(init,200);return;}if(initialized)return;initialized=true;
const esc=s=>String(s??'').replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
const occurrence=wrap.querySelector('[data-cte-view="occurrence"]'),card=document.createElement('button');card.type='button';card.className='cte-summary-btn';card.dataset.cteDuplicates='';card.innerHTML='<span>Duplicado</span><strong>0</strong>';if(occurrence)occurrence.after(card);
const cacheKey='lisa:duplicados:v1:'+String(panel.dataset.worker||'');let groups=[],tickets=[],loading=null,loaded=false;try{const cached=JSON.parse(localStorage.getItem(cacheKey)||'null');if(cached&&Date.now()-cached.at<86400000)card.querySelector('strong').textContent=Number(cached.total||0).toLocaleString('pt-BR');}catch{}
const modal=document.createElement('div');modal.className='base-duplicates-modal';modal.hidden=true;modal.innerHTML='<section class="base-duplicates-box"><header class="base-duplicates-head"><h3>Notas duplicadas</h3><button type="button" aria-label="Fechar">×</button></header><div class="base-duplicates-body"><p>Passe o mouse na coluna Ticket para adicionar ou atualizar as informações da nota nessa transportadora.</p><div class="base-duplicates-feedback" role="status"></div><div data-base-duplicates-list></div></div></section>';document.body.appendChild(modal);
const feedback=modal.querySelector('.base-duplicates-feedback'),list=modal.querySelector('[data-base-duplicates-list]');
async function req(path,body){const controller=new AbortController(),timer=setTimeout(()=>controller.abort(),45000);try{const r=await fetch(String(panel.dataset.worker||'').replace(/\/$/,'')+path,{method:body?'POST':'GET',headers:{Authorization:'Bearer '+(sessionStorage.getItem('lisa_panel_token')||''),...(body?{'Content-Type':'application/json'}:{})},...(body?{body:JSON.stringify(body)}:{}),cache:'no-store',signal:controller.signal});const d=await r.json();if(!r.ok)throw Error(d.error||'Falha ao consultar duplicados');return d;}finally{clearTimeout(timer);}}
function count(total){card.querySelector('strong').textContent=Number(total||0).toLocaleString('pt-BR');card.title='Quantidade de notas com mais de uma transportadora';try{localStorage.setItem(cacheKey,JSON.stringify({total:Number(total||0),at:Date.now()}));}catch{}}
function date(value){const text=String(value||'').trim();if(/^\d{1,2}\/\d{1,2}\/\d{4}/.test(text))return text;if(/^\d{4}-\d{2}-\d{2}$/.test(text))return text.slice(8,10)+'/'+text.slice(5,7)+'/'+text.slice(0,4);if(!/^\d{4}-\d{2}-\d{2}/.test(text))return'—';const d=new Date(text);return Number.isNaN(d.getTime())?'—':d.toLocaleString('pt-BR',{timeZone:'America/Sao_Paulo'});}
function carrierKey(value){return typeof panel.__lisaTicketCarrierKey==='function'?panel.__lisaTicketCarrierKey(value):String(value||'').toUpperCase().replace(/[^A-Z0-9]/g,'');}
function ticketFor(note,row){const nf=v=>String(v||'').replace(/\D/g,'').replace(/^0+(?=\d)/,''),cte=v=>String(v||'').toUpperCase().replace(/[^A-Z0-9]/g,'');return tickets.find(t=>nf(t.nota)===nf(note)&&carrierKey(t.carrier_key||t.transportadora)===carrierKey(row.carrier_key||row.transportadora)&&(!t.cte||cte(t.cte)===cte(row.cte)))||null;}
function render(){list.innerHTML=groups.length?groups.map(g=>'<section class="base-duplicate-note"><h4>Nota '+esc(g.nota)+'</h4><div class="base-duplicate-scroll"><table><thead><tr><th>Transportadora</th><th>CTe</th><th>Status</th><th>Emissão CTe</th><th>Ticket</th></tr></thead><tbody>'+g.items.map(r=>{const ticket=ticketFor(g.nota,r),updates=ticket?.updates||[],text=updates.length?updates[updates.length-1].descricao:ticket?.descricao;return '<tr><td>'+esc(r.transportadora)+'</td><td>'+esc(r.cte||'—')+'</td><td>'+esc(r.status_tr||'—')+'</td><td>'+esc(date(r.data_emissao_cte))+'</td><td class="base-duplicate-ticket-td"><button type="button" class="base-duplicate-ticket-cell" data-base-ticket="'+esc(r.carrier_key)+'" data-base-note="'+esc(g.nota)+'" data-base-cte="'+esc(r.cte||'')+'" title="'+(ticket?'Atualizar ticket':'Adicionar ticket')+'"><span>'+esc(text||(ticket?'Atualizar ticket':'Adicionar ticket'))+'</span><span class="base-duplicate-pencil" aria-hidden="true">✎</span></button></td></tr>';}).join('')+'</tbody></table></div></section>').join(''):'<p>Nenhuma nota com duas transportadoras na base.</p>';}
async function load(){if(loading)return loading;loading=(async()=>{const [d,t]=await Promise.all([req('/carrier-base/duplicates'),req('/transportadoras/tickets')]);groups=d.groups||[];tickets=t.items||[];loaded=true;count(d.total);render();})().catch(e=>{card.title=String(e.message||e);feedback.textContent=String(e.message||e);}).finally(()=>loading=null);return loading;}
async function preloadCount(){try{const d=await req('/carrier-base/duplicates-count');if(d.available)count(d.total);if(!d.available||d.refreshing)await load();}catch{await load();}}
card.onclick=()=>{modal.hidden=false;feedback.textContent='';if(loaded)render();else list.textContent='Carregando duplicados...';void load();};modal.querySelector('.base-duplicates-head button').onclick=()=>modal.hidden=true;
modal.addEventListener('click',e=>{if(e.target===modal){modal.hidden=true;return;}const button=e.target.closest('[data-base-ticket]');if(!button)return;const group=groups.find(g=>String(g.nota)===button.dataset.baseNote),row=group?.items.find(r=>r.carrier_key===button.dataset.baseTicket&&String(r.cte||'')===button.dataset.baseCte);if(!row)return;const ticket=ticketFor(group.nota,row);if(ticket){if(typeof panel.__openCarrierTicketEdit==='function')panel.__openCarrierTicketEdit({...ticket,ticket_id:ticket.id,nota:group.nota});else feedback.textContent='Recarregue o painel para abrir o formulário de ticket.';}else{if(typeof panel.__openCarrierTicketCreate==='function')panel.__openCarrierTicketCreate(group.nota,row);else feedback.textContent='Recarregue o painel para abrir o formulário de ticket.';}});
panel.addEventListener('lisa:tickets-updated',()=>void load());
document.addEventListener('keydown',e=>{if(e.key==='Escape')modal.hidden=true;});let refreshTimer;for(const type of ['lisa:carrier-indexes-ready','lisa:carrier-files-updated'])panel.addEventListener(type,()=>{clearTimeout(refreshTimer);refreshTimer=setTimeout(()=>void preloadCount(),500);});let attempts=0;function preload(){if(sessionStorage.getItem('lisa_panel_token'))void preloadCount();else if(attempts++<120)setTimeout(preload,1000);}preload();}init();})();

;

(()=>{'use strict';
const names={ALFA:'ALFA',BRASPRESS:'BRASPRESS',CONTINENTAL:'CONTINENTAL',CORREIOS_VIPP:'CORREIOS / VIPP',FAVORITA:'FAVORITA',JAMEF:'JAMEF',J_T:'J&T',LATAM:'LATAM',PATRUS:'PATRUS',TOTAL_EXPRESS:'TOTAL EXPRESS',TNT_FEDEX:'TNT / FEDEX',RODOMAX:'RODOMAX'};
const tcNote=v=>String(v??'').replace(/\.0+$/,'').replace(/\D/g,'').replace(/^0+(?=\d)/,''),tcCarrierDisplayByKey=k=>names[k]||k,tcNorm=v=>String(v??'').normalize('NFD').replace(/[\u0300-\u036f]/g,'').toUpperCase().trim(),tcDateMs=v=>Date.parse(v),trackingNormalizeText=v=>tcNorm(v).replace(/[^A-Z0-9]+/g,' ').trim();
function cbFields(r,nf,key){
 const x={...r,nota_fiscal:tcNote(nf||r.nota_fiscal||r.nota),transportadora:tcCarrierDisplayByKey(key),carrier_key:key};
 const map={status:'status_tr',data_emissao:'data_emissao_tr',data_atualizacao:'data_status',data_previsao:'previsao',nf:'nota_fiscal'};
 for(const[a,b]of Object.entries(map))if(!x[b]&&x[a])x[b]=x[a];
 if(x.previsao_atualizada)x.previsao=x.previsao_atualizada;
 for(const k of ['cte','nota_fiscal','cep_destino'])if(x[k]!==undefined)x[k]=String(x[k]??'').trim();
 for(const k of ['data_status','data_entrega','data_emissao_tr','data_emissao_cte','previsao','previsao_atualizada']){
  if(typeof x[k]==='number'){const d=new Date((x[k]-25569)*86400000);x[k]=Number.isFinite(d.getTime())?d.toISOString().replace('Z',''):'';}
 }
 return x;
}
function cbEventTime(r){return Math.max(0,...['data_status','data_entrega'].map(k=>{const v=r?.[k];if(!v)return 0;const s=String(v),m=s.match(/^(\d{1,2})\/(\d{1,2})\/(\d{4})(?:[^\d]+(\d{1,2}):(\d{2})(?::(\d{2}))?)?/);const t=m?Date.UTC(+m[3],+m[2]-1,+m[1],+(m[4]||0),+(m[5]||0),+(m[6]||0)):/^\d{4}-/.test(s)?Date.parse(s):tcDateMs(v);return Number.isFinite(t)?t:0}));}
function cbPresent(v){return v!==undefined&&v!==null&&v!==''&&v!=='-'&&v!=='—';}
function cbMergeRecord(old,incoming,key,now){
 const r=cbFields(incoming,incoming.nota_fiscal,key),previous=old||{},out={...previous};
 const newer=!old||cbEventTime(r)>0&&cbEventTime(r)>=cbEventTime(old)||cbEventTime(old)===0;
 // Cada campo guarda o timestamp do evento. Um campo vazio nunca limpa o anterior.
 const stamps={...(previous._field_times||{})},incomingAt=cbEventTime(r);
 for(const[k,v]of Object.entries(r)){
  if(k.startsWith('_')||['fontes','ctes_alternativos','historico_registros'].includes(k)||!cbPresent(v))continue;
  const prior=Number(stamps[k]||0);
  if(!cbPresent(out[k])||(incomingAt>0?incomingAt>=prior:prior===0)&&newer){
   if(v&&typeof v==='object'&&!Array.isArray(v))out[k]={...(out[k]||{}),...v};else out[k]=v;
   stamps[k]=incomingAt;
  }
 }
 const details=[...(previous.dados_adicionais?.detalhes_origem||[]),...(r.dados_adicionais?.detalhes_origem||[])];
 if(details.length)out.dados_adicionais={...(out.dados_adicionais||{}),detalhes_origem:[...new Map(details.map(x=>[JSON.stringify(x),x])).values()]};
 const allCtes=[...(previous.ctes_alternativos||[]),previous.cte,...(r.ctes_alternativos||[]),r.cte].filter(cbPresent).map(String);
 out.ctes_alternativos=[...new Set(allCtes)].filter(c=>c!==String(out.cte||''));
 const sources=[...(previous.fontes||[]),...(r.fontes||[]),...(r.arquivo_origem?[{arquivo:r.arquivo_origem,aba:r.aba_origem||'Geral',linha:r.linha_origem||'',sha256:r.fonte_sha256||''}]:[])];
 const history=[...(previous.historico_registros||[]),...(r.historico_registros||[])];
 if(history.length)out.historico_registros=[...new Map(history.map(x=>[JSON.stringify(x),x])).values()];
 out.fontes=[...new Map(sources.map(x=>[JSON.stringify(x),x])).values()];
 out.nota_fiscal=r.nota_fiscal;out.carrier_key=key;out.transportadora=tcCarrierDisplayByKey(key);
 out._field_times=stamps;out.criado_em=previous.criado_em||now;
 const meaningful=JSON.stringify({...out,atualizado_em:undefined})!==JSON.stringify({...previous,atualizado_em:undefined});
 out.atualizado_em=meaningful?now:previous.atualizado_em||now;
 return out;
}
function cbIndexRecords(index,key){
 const records={};
 for(const[nf,value]of Object.entries(index?.by_note||{})){
  const list=Array.isArray(value)?value:[value];
  for(const rec of list){if(!rec||typeof rec!=='object')continue;const note=tcNote(nf);if(!note)continue;const r=cbFields(rec,note,key);records[note]=index.base_schema&&rec._field_times?{...r}:cbMergeRecord(records[note],r,key,index.updated_at||new Date().toISOString());}
 }
 return records;
}

function trackingCarrierInfo(carrier, cte) {
  const n = trackingNormalizeText(carrier);
  const rawCte = String(cte || '').trim();
  let displayCte = rawCte;
  let link = '';
  let label = 'Link não configurado';
  let internal = false;

  if (n.includes('ALFA TRANSP')) {
    link = 'https://alfatransportes.com.br';
  } else if (n.includes('ASAP')) {
    link = 'https://rastreio.asaplog.com.br';
  } else if (n.includes('BRASPRESS')) {
    link = 'https://www.braspress.com/rastreie-sua-encomenda/';
  } else if (n.includes('CONTINENTAL')) {
    internal = true; label = 'Rastreio interno';
  } else if (n.includes('CORREIO PPN ES')) {
    link = 'https://rastreamento.correios.com.br/app/index.ph';
  } else if (n.includes('CORREIO')) {
    link = 'https://rastreamento.correios.com.br/app/index.php';
  } else if (n.includes('EBAZAR') || n.includes('MERCADO ENVIOS') || n.includes('MERCADO LIVRE')) {
    internal = true; label = 'Rastreio interno';
  } else if (n.includes('FAVORITA')) {
    link = 'https://cliente.favorita.com.br/rastreamento';
  } else if (n === 'J T' || n === 'JT' || n.includes('J T EXPRESS') || n.includes('JT EXPRESS')) {
    link = 'https://www.jtexpress.com.br';
  } else if (n.includes('JAMEF')) {
    link = 'https://www.jamef.com.br';
  } else if (n.includes('LATAM') || n.includes('TAM CARGO') || n.includes('TAM LINHAS') || n.includes('TAM VIX')) {
    // LATAM Cargo usa AWB/CTE no formato PPPNNNNNNNN: os 3 primeiros digitos
    // sao o prefixo da companhia e o restante e o numero do documento.
    // Ex.: 95739839866 -> docPrefix=957 e docNumber=39839866.
    const latamDigits = rawCte.replace(/\D+/g,'');
    if (latamDigits.length >= 4) {
      const latamPrefix = latamDigits.slice(0,3);
      const latamNumber = latamDigits.slice(3);
      displayCte = latamDigits;
      link = 'https://www.latamcargo.com/en/trackshipment?docNumber=' + encodeURIComponent(latamNumber) + '&docPrefix=' + encodeURIComponent(latamPrefix) + '&soType=SO';
      label = 'Rastrear na LATAM Cargo';
    }
  } else if (n.includes('MAGALU ENTREGAS') || n.includes('MAGAZINE LUIZA')) {
    internal = true; label = 'Rastreio interno';
  } else if (n.includes('PATRUS')) {
    link = 'https://patrus.com.br/rastreie-sua-carga/';
  } else if (n.includes('SHOPEE') || n.includes('SHPS TECNOLOGIA')) {
    internal = true; label = 'Rastreio interno';
  } else if (n.includes('TOTAL EXPRESS')) {
    link = 'https://totalconecta.totalexpress.com.br/rastreamento/';
  } else if (n.includes('TNT')) {
    link = 'https://radar.tntbrasil.com.br/radar/public/localizacaoSimplificada.do';
  }

  return { cte_display: displayCte, tracking_link: link, tracking_label: label, tracking_internal: internal };
}

async function request(root,path,opt={}){
 const method=opt.method||'GET',safeRead=method==='GET',attempts=safeRead?3:1;
 for(let attempt=0;attempt<attempts;attempt++){
 const controller=new AbortController(),timer=setTimeout(()=>controller.abort(),45000);
 try{const response=await fetch(String(root.dataset.worker||'').replace(/\/$/,'')+path,{...opt,headers:{Authorization:'Bearer '+(sessionStorage.getItem('lisa_panel_token')||''),...opt.headers},cache:'no-store',signal:controller.signal});
 if(response.status===404&&opt.missing)return {data:null,etag:''};
 if(!response.ok){const text=await response.text();let detail;try{detail=JSON.parse(text).error}catch{}const e=Error('HTTP '+response.status+' em '+method+' '+path+' · '+(detail||text.replace(/<[^>]*>/g,' ').slice(0,600)));e.status=response.status;throw e;}
 const version=cleanVersion(response.headers.get('X-Lisa-Base-Version')||response.headers.get('ETag'));if(path.startsWith('/carrier-base/prepared?')&&safeRead&&!version)throw Error('A leitura da base não retornou sua versão em '+path+'. Não foi feita nenhuma tentativa de substituir os dados. Atualize também o Worker.');return {data:await response.json(),etag:version};
 }catch(e){const network=e.name==='TypeError'||e.name==='AbortError';if(safeRead&&attempt+1<attempts&&(network||e.status>=500))continue;
 if(network){const error=Error((e.name==='AbortError'?'Tempo limite de 45 segundos':'Nenhuma resposta HTTP acessível ao navegador')+' em '+method+' '+path+' · tentativa '+(attempt+1)+'/'+attempts+' · detalhe original: '+String(e.message||e)+'. A mensagem do navegador não permite distinguir rede, CORS ou interrupção do Worker. O arquivo no Cloud foi mantido.');error.network=true;throw error;}throw e;
 }finally{clearTimeout(timer);}
 }
}

const cleanVersion=v=>String(v||'').trim().replace(/^W\//i,'').replace(/^"|"$/g,'');
const path=(target,key='')=>'/carrier-base/prepared?target='+target+(key?'&carrier='+encodeURIComponent(key):'');
async function write(root,target,key,value,etag,count){
 const text=JSON.stringify(value),hash=[...new Uint8Array(await crypto.subtle.digest('SHA-256',new TextEncoder().encode(text)))].map(n=>n.toString(16).padStart(2,'0')).join('');
 const url=path(target,key)+'&etag='+encodeURIComponent(etag)+'&stamp='+encodeURIComponent(value.updated_at||value.built_at)+'&count='+count+'&commit='+hash;
 let last;for(let i=0;i<2;i++)try{return (await request(root,url,{method:'PUT',headers:{'Content-Type':'application/json'},body:text})).data;}catch(e){last=e;if(e.status&&e.status<500)throw e;}throw last;
}
function canonical(old,rows,key){
 const stamp=new Date().toISOString(),records=cbIndexRecords(old,key),tombstones=old?.base_tombstones||{},result={added:0,updated:0,unchanged:0,blocked:0};
 for(const row of rows){const note=tcNote(row.nota_fiscal);if(!note)throw Error('Nota inválida na preparação de '+key);if(tombstones[note]){result.blocked++;continue;}const previous=records[note],{fontes,historico_registros,dados_adicionais,...core}=row,merged=cbMergeRecord(previous,core,key,stamp);records[note]=merged;if(!previous)result.added++;else if(merged.atualizado_em===previous.atualizado_em)result.unchanged++;else result.updated++;}
 for(const note of Object.keys(tombstones))delete records[note];
 const by_cte={};for(const rec of Object.values(records)){if(!rec.cte)continue;const {fontes,historico_registros,dados_adicionais,ctes_alternativos,_field_times,...projection}=rec;by_cte[tcNorm(rec.cte).replace(/\s/g,'')]=projection;}
 return {index:{...(old||{}),kind:'carrier',carrier:names[key],version:12,base_schema:1,by_note:records,by_cte,total:Object.keys(records).length,base_tombstones:tombstones,updated_at:stamp,prepared_protocol:3},result};
}
async function saveCarrier(root,key,rows){
 for(let i=0;i<3;i++){const old=await request(root,path('carrier',key),{missing:true}),built=canonical(old.data,rows,key);try{const saved=await write(root,'carrier',key,built.index,old.etag,built.index.total);return {key,index:built.index,etag:saved.etag,result:built.result};}catch(e){if(e.status!==409||i===2)throw e;}}
}
async function publish(root,carriers,stageId='',progress=()=>{}){
 if(!root.__lisaPrepareSnapshot)throw Error('Módulo de rastreamento ainda não inicializado. Reabra o painel.');
 for(let attempt=0;attempt<3;attempt++){
  progress(97,'PREPARANDO CONSULTA E RASTREAMENTO');
  const summary=await request(root,path('summary')),consult=await request(root,path('consult'),{missing:true});
  if(!Array.isArray(summary.data?.items))throw Error('Resumo salvo não está disponível. As páginas importadas foram preservadas.');
  const entries=[],failures=[];for(const carrier of carriers)try{const current=await request(root,path('carrier',carrier.key),{missing:true});if(!current.data){if(stageId)throw Error('Base de '+carrier.key+' não confirmada.');continue;}entries.push({key:carrier.key,index:current.data,etag:current.etag});}catch(e){if(stageId)throw e;failures.push(carrier.key+' · '+String(e.message||e));}if(carriers.length&&!entries.length&&failures.length)throw Error(failures.join('\n'));
  const stamp=new Date().toISOString(),snapshot=await root.__lisaPrepareSnapshot(summary.data,entries);snapshot.updated_at=stamp;snapshot.carrier_sources_updated_at=stamp;snapshot.carrier_status_updated_at=stamp;snapshot.with_cte=snapshot.items.filter(r=>r.has_cte).length;snapshot.without_cte=snapshot.items.length-snapshot.with_cte;
  const keys=new Set(entries.map(e=>e.key)),items=(consult.data?.items||[]).filter(r=>!keys.has(r.source_key));
  for(const {key,index}of entries)for(const [nf,record]of Object.entries(index.by_note||{})){
   const cte=String(record.cte||record.registro||'').trim(),status=String(record.status_tr||record.status||record.ocorrencia||'').trim();if(!cte&&!status)continue;
   items.push({aliases:[nf],cte,carrier:index.carrier||names[key],source_key:key,transport_status:status,ocorrencia:String(record.ocorrencia||''),previsao_entrega:String(record.previsao||record.previsao_entrega||''),data_entrega:String(record.data_entrega||''),source_updated_at:index.updated_at,...trackingCarrierInfo(index.carrier||names[key],cte)});
  }
  const consultation={...(consult.data||{}),ok:true,kind:'consulta_tracking',version:2,built_at:stamp,source_updated_at:stamp,items};
  try{
   progress(98,'SALVANDO CONSULTA');const consultSaved=await write(root,'consult','',consultation,consult.etag,items.length);
   progress(99,'SALVANDO RASTREAMENTO');const summarySaved=await write(root,'summary','',snapshot,summary.etag,snapshot.items.length);
   const light={ok:true,available:true,updated_at:stamp,monitor_updated_at:snapshot.monitor_updated_at||null,tracking_updated_at:snapshot.tracking_updated_at||null,carrier_sources_updated_at:stamp,ctt_version:snapshot.ctt_version||null,ctt_schema_version:snapshot.ctt_schema_version||null,tracking_scope:snapshot.tracking_scope||null,total:snapshot.counts.all,counts:snapshot.counts,sidebar_scope_version:3,snapshot_version:Number(snapshot.snapshot_version||7),refreshing:false};
   let confirmed;for(const entry of entries.length?entries:[null])confirmed=await request(root,'/carrier-base/prepared-confirm',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({stage_id:stageId,carriers:entry?[{key:entry.key,etag:entry.etag}]:[],summary_etag:summarySaved.etag,consult_etag:consultSaved.etag,light})});
   root.__lisaAcceptPreparedSnapshot?.(snapshot);root.dispatchEvent(new CustomEvent('lisa:carrier-indexes-ready'));return {...confirmed.data,failures};
  }catch(e){if(e.status!==409||attempt===2)throw e;}
 }
}
function init(){const root=document.getElementById('lisa-app-vg-native')||document.querySelector('.lisa-app[data-worker]');if(!root){setTimeout(init,100);return;}
 root.__lisaPreparedReceipts=async steps=>{const done=new Set();root.__lisaReceiptWarning='';for(let i=0;i<steps.length;i+=5){try{const r=await request(root,'/carrier-base/import-receipts',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({steps:steps.slice(i,i+5)})});for(const id of r.data.completed||[])done.add(id);}catch(e){root.__lisaReceiptWarning='Consulta auxiliar dos comprovantes indisponível; a importação seguirá verificando cada lote individualmente. '+String(e.message||e);console.warn(root.__lisaReceiptWarning);break;}}return done;};
 root.__lisaPreparedRead=async(key,details=false)=>{const d=await request(root,path('carrier',key),{missing:true}),records=cbIndexRecords(d.data,key);if(details)for(let i=0;i<32;i++){const page=await request(root,'/carrier-base/prepared-detail?carrier='+key+'&shard='+String(i).padStart(2,'0'),{missing:true});for(const[note,items]of Object.entries(page.data||{})){const row=records[note];if(row)row.dados_adicionais={...(row.dados_adicionais||{}),detalhes_origem:[...(row.dados_adicionais?.detalhes_origem||[]),...items]};}}return Object.values(records).filter(r=>!d.data?.base_tombstones?.[r.nota_fiscal]);};
 root.__lisaPreparedImport=async(groups,stageId,progress)=>{const carriers=[],result={added:0,updated:0,unchanged:0,blocked:0};for(const[key,rows]of groups){progress(96,key+' · CONSOLIDANDO BASE');let saved;try{saved=await saveCarrier(root,key,rows);}catch(e){e.confirmedCounts={...result};throw e;}carriers.push(saved);for(const k of Object.keys(result))result[k]+=saved.result[k];}try{await publish(root,carriers,stageId,progress);}catch(e){e.confirmedCounts={...result};throw e;}return result;};
 root.__lisaPreparedPublish=async(keys,progress=()=>{})=>publish(root,keys.map(key=>({key})), '',progress);
 root.__lisaPreparedCanonical=canonical;
}
if(document.readyState==='loading')document.addEventListener('DOMContentLoaded',init,{once:true});else init();
})();

;

{
  const initLisaSiteModule = function(){
  'use strict';
  let lisaSiteAttempts=0;
  function initLisaSite(){
    const panel=document.getElementById('lisa-app-vg-native') || document.querySelector('.lisa-app[data-worker]');
    if(!panel){if(lisaSiteAttempts++<300)setTimeout(initLisaSite,100);return}
    const searchBtn=panel.querySelector('[data-open-search]');
    let openBtn=panel.querySelector('[data-ls-site-open]');
    if(!openBtn&&searchBtn){
      openBtn=document.createElement('button');openBtn.type='button';openBtn.className='ghost lisa-quick-btn';openBtn.setAttribute('data-ls-site-open','');openBtn.textContent='SITE';
      const quickTools=panel.querySelector('[data-lisa-quick-tools]');if(quickTools)quickTools.appendChild(openBtn);else searchBtn.insertAdjacentElement('beforebegin',openBtn);
    }
    if(!openBtn){if(lisaSiteAttempts++<300)setTimeout(initLisaSite,100);return}
    const previousWrap=document.getElementById('lisasite-vg-native');
    if(previousWrap)previousWrap.remove();
  const wrap=document.createElement('div');wrap.id='lisasite-vg-native';wrap.innerHTML=`
    <div class="ls-modal" data-ls-modal hidden>
      <div class="ls-shell">
        <div class="ls-top"><div><h3>Painel do site</h3><span data-ls-updated>Última atualização: aguardando primeira carga</span><div data-ls-progress-box hidden role="status" aria-live="polite" style="margin-top:6px"><progress data-ls-progress max="100" value="0" aria-label="Progresso estimado da atualização" style="width:160px;vertical-align:middle"></progress> <span data-ls-progress-label></span></div></div><div style="display:flex;align-items:center;gap:10px"><button type="button" data-ls-refresh style="cursor:pointer;border:1px solid #a8bdd6;border-radius:8px;padding:8px 12px;background:#edf4ff;color:#163d70">Atualizar pedidos</button><button class="ls-x" data-ls-close type="button">×</button></div></div>
        <div class="ls-loading" data-ls-loading><div class="ls-spinner"></div><b>Carregando dados do site...</b><span>Consultando Shopify e cruzando com Monitor de Pedido e Rastreamento.</span></div>
        <div class="ls-body" data-ls-body hidden><div class="ls-error" data-ls-error hidden></div>
          <div class="ls-site-search"><input data-ls-main-search type="text" inputmode="numeric" placeholder="Buscar por número do pedido ou nota"><button type="button" data-ls-main-search-btn>Buscar</button></div>
          <div class="ls-section-title">Pedidos — últimos 30 dias</div><div class="ls-grid" data-ls-orders-grid></div>
          <div class="ls-section-title">Rastreamento</div><div class="ls-grid tracking" data-ls-tracking-grid></div>
          <div class="ls-section-title">Pedidos por dia</div><div class="ls-calendar"><div class="ls-calendar-week"><span>Dom</span><span>Seg</span><span>Ter</span><span>Qua</span><span>Qui</span><span>Sex</span><span>Sáb</span></div><div class="ls-calendar-grid" data-ls-calendar></div></div>
        </div>
      </div>
    </div>
    <div class="ls-report-modal" data-ls-report-modal hidden><div class="ls-report"><button class="ls-x" data-ls-report-close type="button" style="position:absolute;right:13px;top:10px">×</button><div class="ls-report-head"><div><h3 data-ls-report-title>Pedidos</h3><span data-ls-report-count>0 pedidos</span></div><div class="ls-report-actions"><button class="ls-btn" data-ls-refresh-tracking type="button" hidden>Atualizar rastreio</button><button class="ls-btn ls-batch-send" data-ls-batch-send type="button" hidden disabled>ENVIAR SELECIONADOS</button><button class="ls-btn" data-ls-copy type="button">Copiar</button><button class="ls-btn" data-ls-download type="button">Baixar</button></div></div><div class="ls-table-wrap"><table class="ls-table"><thead><tr>
<th data-ls-select-head hidden><input type="checkbox" data-ls-select-all title="Selecionar linhas elegíveis"></th>
<th data-ls-sort="pedido" data-sort-type="number"><button type="button" class="ls-sort-btn">Pedido <span class="ls-sort-arrows">↑↓</span></button><input class="ls-filter" data-ls-f-pedido placeholder="Filtrar"></th>
<th data-ls-sort="data" data-sort-type="date"><button type="button" class="ls-sort-btn">Data <span class="ls-sort-arrows">↑↓</span></button><input class="ls-filter" data-ls-f-data type="date"></th>
<th data-ls-sort="cliente" data-sort-type="text"><button type="button" class="ls-sort-btn">Cliente <span class="ls-sort-arrows">↑↓</span></button><input class="ls-filter" data-ls-f-cliente placeholder="Filtrar"></th>
<th data-ls-hide-compact data-ls-sort="valor" data-sort-type="number"><button type="button" class="ls-sort-btn">Valor <span class="ls-sort-arrows">↑↓</span></button><input class="ls-filter" data-ls-f-valor placeholder="Filtrar"></th>
<th data-ls-hide-compact data-ls-sort="status_shopify" data-sort-type="text"><button type="button" class="ls-sort-btn">Status Shopify <span class="ls-sort-arrows">↑↓</span></button><select class="ls-filter" data-ls-f-status-shopify><option value="">Todos</option></select></th>
<th data-ls-hide-compact data-ls-sort="archived" data-sort-type="text"><button type="button" class="ls-sort-btn">Arquivado <span class="ls-sort-arrows">↑↓</span></button><select class="ls-filter" data-ls-f-archived><option value="">Todos</option><option value="sim">Sim</option><option value="nao">Não</option></select></th>
<th data-ls-hide-compact data-ls-sort="cancelled" data-sort-type="text"><button type="button" class="ls-sort-btn">Cancelado <span class="ls-sort-arrows">↑↓</span></button><select class="ls-filter" data-ls-f-cancelled><option value="">Todos</option><option value="sim">Sim</option><option value="nao">Não</option></select></th>
<th data-ls-hide-compact data-ls-sort="imported" data-sort-type="text"><button type="button" class="ls-sort-btn">Importado <span class="ls-sort-arrows">↑↓</span></button><select class="ls-filter" data-ls-f-imported><option value="">Todos</option><option value="sim">Sim</option><option value="nao">Não</option></select></th>
<th data-ls-sort="nota" data-sort-type="number"><button type="button" class="ls-sort-btn">Nota <span class="ls-sort-arrows">↑↓</span></button><input class="ls-filter" data-ls-f-nota placeholder="Filtrar"></th>
<th data-ls-sort="transportadora" data-sort-type="text"><button type="button" class="ls-sort-btn">Transportadora <span class="ls-sort-arrows">↑↓</span></button><select class="ls-filter" data-ls-f-carrier><option value="">Todos</option></select></th>
<th data-ls-sort="lisa_status" data-sort-type="text"><button type="button" class="ls-sort-btn">Status LISA <span class="ls-sort-arrows">↑↓</span></button><select class="ls-filter" data-ls-f-lisa-status><option value="">Todos</option></select></th>
<th data-ls-sort="rastreio" data-sort-type="text"><button type="button" class="ls-sort-btn">Rastreio/CTE <span class="ls-sort-arrows">↑↓</span></button><input class="ls-filter" data-ls-f-rastreio placeholder="Filtrar"></th>
<th data-ls-sort="delivery_status" data-sort-type="text"><button type="button" class="ls-sort-btn">Status entrega <span class="ls-sort-arrows">↑↓</span></button><select class="ls-filter" data-ls-f-delivery><option value="">Todos</option></select></th>
<th data-ls-delivered-status-head hidden>Status Shopify</th>
</tr></thead><tbody data-ls-report-body></tbody></table><div class="ls-empty" data-ls-report-empty hidden>Nenhum pedido encontrado.</div></div></div></div>`;
  document.body.appendChild(wrap);
  // Sem snapshot, nao mostrar zeros como se fossem pedidos reais.
  const siteUnavailableStyle=document.createElement('style');
  siteUnavailableStyle.textContent='.ls-site-unavailable .ls-section-title,.ls-site-unavailable .ls-grid,.ls-site-unavailable .ls-calendar,.ls-site-unavailable .ls-site-search{display:none!important}.ls-site-unavailable .ls-error{display:block!important}.ls-snapshot-actions{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-top:12px}.ls-snapshot-actions button{border:1px solid #c9d7e9;padding:8px 12px;background:#fff;border-radius:7px;color:#18437b;cursor:pointer;font-size:12px}.ls-snapshot-actions button:disabled{opacity:.6;cursor:wait}';
  wrap.appendChild(siteUnavailableStyle);
  const $=s=>wrap.querySelector(s);let state={items:[],summary:{},calendar:[]},reportItems=[],reportBaseItems=[],lsReportKey='',lsReportSort={key:'',type:'text',dir:''},lsRawTrackingMap=new Map(),lsTrackingRequestBusy=false,lsBatchSending=false;const lsSelectedTracking=new Set();
  const LS_SITE_CACHE_TTL=60*60*1000;
  const LS_SITE_CACHE_KEY='lisa_site_dashboard_cache_v14';
  let lsSiteRefreshTimer=null,lsSitePreloadPromise=null;
  const modal=$('[data-ls-modal]'),loading=$('[data-ls-loading]'),body=$('[data-ls-body]'),err=$('[data-ls-error]');
  const worker=()=>String(panel.dataset.worker||'').replace(/\/$/,'');const auth=()=>sessionStorage.getItem('lisa_panel_token')||'';
  const esc=s=>String(s??'').replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
  const yn=v=>v?'Sim':'Não';
  function setLoading(on){loading.hidden=!on;body.hidden=on}
  const lsNormHeader=v=>String(v??'').trim().normalize('NFD').replace(/[\u0300-\u036f]/g,'').toUpperCase().replace(/[^A-Z0-9]+/g,' ').trim();
  const lsOrderKey=v=>{const s=String(v??'').trim(),m=s.match(/\d{4,}/);return m?m[0].replace(/^0+(?=\d)/,''):s.replace(/\D+/g,'').replace(/^0+(?=\d)/,'')};
  const lsNoteKeys=v=>{const raw=String(v??'').trim().replace(/\.0+$/,'');const d=raw.replace(/\D+/g,'').replace(/^0+(?=\d)/,'');return [...new Set([raw,d].filter(Boolean))]};
  const lsHigherNote=(a,b)=>{const A=String(a??'').replace(/\D+/g,'').replace(/^0+(?=\d)/,''),B=String(b??'').replace(/\D+/g,'').replace(/^0+(?=\d)/,'');if(!A)return false;if(!B)return true;return A.length!==B.length?A.length>B.length:A>B};
  function lsTrackingCategory(status,cte){
    const s=String(status||'').normalize('NFD').replace(/[\u0300-\u036f]/g,'').toUpperCase().replace(/_/g,' ').replace(/\s+/g,' ').trim();
    const delivered=['CARGA ENTREGUE','CARGA ENTREGUE PELA BRASPRESS','CARGA ENTREGUE PELA CONTINENTAL','ENTREGUE','ENTREGA REALIZADA','ENTREGA REALIZADA NORMALMENTE','MERCADORIA ENTREGUE','MERCADORIA ENTREGUE - AGUARDANDO CANHOTO','MERCADORIA ENTREGUE - CONHECIMENTO RETIDO PELO CLIENTE','OBJETO ENTREGUE AO DESTINATARIO','OBJETO ENTREGUE NA CAIXA DE CORREIOS INTELIGENTE'];
    if(delivered.includes(s))return 'delivered';
    if(/AGUARDANDO RETIRADA|OBJETO AGUARDANDO RETIRADA NO ENDERECO INDICADO/.test(s))return 'awaiting_pickup';
    if(/DEVOLU|DEVOLVID|DEVOLUCAO|EM ROTA DE DEVOLUCAO|EM TRANSFERENCIA DEVOLUCAO|EM PROCESSO DE DEVOLUCAO|DOCUMENTO DE DEVOLUCAO|NF DEVOLVIDA|OBJETO ENTREGUE AO REMETENTE|MERCADORIA DEVOLVIDA AO REMETENTE|MERCADORIA DEVOLVIDA AO CLIENTE DE ORIGEM|AGUARDANDO FREQUENCIA DE DEVOLUCAO|EMBARQUE DE DEVOLUCAO/.test(s))return 'returned';
    if(/ABRIR TICKET|TICKET EM ANDAMENTO|TICKET CRIADO|AG\. PAGAMENTO DO TERMO|AVARIA|AVARIADO|PACOTE PROBLEMATICO|CARTA DE CORRECAO|ENTREGA NAO REALIZADA|EXTRAVI|EXTRAVIO|FALE CONOSCO|FALTA DE COMPLEMENTO|CARGA SUMIU|FURTADO|INUTILIZADA|EXCECAO ANORMAL|NAO CONFORMIDADE|OBJETO NAO ENTREGUE|OCORRENCIA DE ENTREGA|RECUSAD|RECUSADO|RESSARCIMENTO|INDENIZATORIO|RESSALVA EM MAOS|RETIDO|SAIU PARA ENTREGA E AINDA NAO CHEGOU|SAIU PARA ENTREGA E NAO ENTREGOU AINDA|SEM COMPROVANTE|SEM CTE|SEM REGISTRO NA LISA|SINISTRO|SUSPENSAO DE ENTREGA|TENTATIVA DE ENTREGA NAO EFETUADA|TERMO EM MAOS|VERIFICANDO ENTREGA|EMBALAGEM EM ANALISE|AINDA NAO CHEGOU|AINDA NAO SAIU|NAO IMPORTADA|LISA SEM STATUS|PENDENTE/.test(s))return 'problem';
    if(String(cte||'').trim()||/A CAMINHO|A ENTREGAR|A EXPEDIR|AGUARDANDO DESEMBARQUE|AGUARDANDO TRANSFERENCIA|AWB EM VIAGEM|AWB NO DESTINO|BIPE DE SAIDA PARA ENTREGA|CARGA ALOCADA EM VOO|CHEGADA EM UNIDADE|CHEGADA NA FILIAL|CHEGADAS AO CENTRO|COLETA|CTRC EMITIDO|DESCARREGADO|DISPONIVEL PARA ENTREGA|EM ENTREGA|EM ROTA|EM TRANSITO|EM VIAGEM|EM TRANSFERENCIA|EMBARCAD|EMBARQUE|ENCOMENDA CARREGADA|ENCOMENDA RECEBIDA|ENTREGANDO|ENVIO DO CENTRO|NO TERMINAL|OBJETO AINDA NAO CHEGOU|OBJETO EM TRANSFERENCIA|OBJETO POSTADO|PLANEJADA|PROCESSO DE ENTREGA|RECEBIDA E PROCESSADA|RECEBIDO CD|REDESPACH|REENTREGA|RETIRADO DA CAF|TRANSFERENCIA|TRANSFERIDO/.test(s))return 'in_transit';
    return 'not_sent';
  }
  async function lsEnsureXlsx(){if(window.XLSX)return window.XLSX;await new Promise((res,rej)=>{const s=document.createElement('script');s.src='https://cdn.jsdelivr.net/npm/xlsx@0.18.5/dist/xlsx.full.min.js';s.onload=res;s.onerror=()=>rej(new Error('Leitor de Excel indisponível.'));document.head.appendChild(s)});return window.XLSX}
  
  
  function cards(){
    const defs=[['total','Total últimos 30 dias','▦','blue'],['unprocessed','Não processados','◷','orange'],['archived','Arquivados','▣','purple'],['archived_without_delivery','Sem rastreio','!','red'],['tracking_added','Rastreio adicionado','⌖','green'],['cancelled','Cancelados','×','gray'],['expired','Expirados','⌛','yellow']];
    $('[data-ls-orders-grid]').innerHTML=defs.map(([k,l,i,t])=>`<button type="button" class="ls-card" data-tone="${t}" data-ls-filter="${k}"><div class="ls-card-top"><span class="ls-icon">${i}</span><small>${l}</small></div><strong>${Number(state.summary?.[k]||0).toLocaleString('pt-BR')}</strong></button>`).join('');
    const track=[['not_sent','Não enviado','○','gray'],['in_transit','Em trajeto','➜','blue'],['awaiting_pickup','Aguardando retirada','⌛','yellow'],['problem','Problema','!','red'],['returned','Devolvido','↩','orange'],['delivered','Entregue','✓','green']];
    $('[data-ls-tracking-grid]').innerHTML=track.map(([k,l,i,t])=>`<button type="button" class="ls-card" data-tone="${t}" data-ls-filter="tracking:${k}"><div class="ls-card-top"><span class="ls-icon">${i}</span><small>${l}</small></div><strong>${Number(state.summary?.tracking?.[k]||0).toLocaleString('pt-BR')}</strong></button>`).join('');
  }
  function calendar(){const host=$('[data-ls-calendar]');const arr=Array.isArray(state.calendar)?state.calendar:[];if(!arr.length){host.innerHTML='';return}const first=new Date(arr[0].date+'T12:00:00');let html='';for(let i=0;i<first.getDay();i++)html+='<div class="ls-day is-empty"></div>';for(const d of arr){const [y,m,day]=String(d.date).split('-');const received=Number(d.received??d.total??0),expedited=Number(d.expedited||0),inTransit=Number(d.in_transit||0),delivered=Number(d.delivered||0),complete=received>0&&delivered>=received,cls=complete?'is-complete':'is-pending';const metric=(key,value,label)=>`<button type="button" class="ls-day-metric" data-ls-calendar-metric="${key}" data-ls-calendar-day="${esc(d.date)}" ${value?'':'disabled'}><span class="ls-day-number">${value}</span><small>${label}</small></button>`;html+=`<div class="ls-day ${cls}"><div class="ls-day-head"><span class="ls-day-date">${day}/${m}</span><span class="ls-day-state">${complete?'Concluído':'Pendente'}</span></div><div class="ls-day-counts">${metric('received',received,'Recebidos')}${metric('expedited',expedited,'Expedidos')}${metric('in_transit',inTransit,'Em trajeto')}${metric('delivered',delivered,'Entregues')}</div></div>`}host.innerHTML=html}
  function filterItems(key){const all=state.items||[];if(key==='total')return all;if(key==='unprocessed')return Array.isArray(state.unprocessed_items)?state.unprocessed_items:all.filter(x=>x.unprocessed);if(key==='imported')return all.filter(x=>x.imported);if(key==='archived')return all.filter(x=>x.paid&&x.archived);if(key==='archived_without_delivery')return all.filter(x=>x.archived_without_delivery);if(key==='tracking_added')return all.filter(x=>x.tracking_added);if(key==='cancelled')return all.filter(x=>x.cancelled);if(key==='expired')return all.filter(x=>x.expired);if(key.startsWith('tracking:')){const k=key.split(':')[1];if(k==='tracking_added')return all.filter(x=>x.tracking_added);return all.filter(x=>x.tracking_category===k)}return []}
  function labelFor(key){return {'total':'Total — últimos 30 dias','unprocessed':'Não processados','imported':'Importados','archived':'Arquivados','archived_without_delivery':'Sem rastreio','tracking_added':'Rastreio adicionado','cancelled':'Cancelados','expired':'Expirados','tracking:not_sent':'Não enviado','tracking:tracking_added':'Rastreio adicionado','tracking:in_transit':'Em trajeto','tracking:awaiting_pickup':'Aguardando retirada','tracking:problem':'Problema','tracking:returned':'Devolvido','tracking:delivered':'Entregue'}[key]||'Pedidos'}
  function lsNorm(v){return String(v??'').normalize('NFD').replace(/[\u0300-\u036f]/g,'').toUpperCase().trim()}
  function lsDateKey(v){const m=String(v||'').match(/(\d{2})\/(\d{2})\/(\d{4})/);return m?`${m[3]}-${m[2]}-${m[1]}`:''}
  function lsSortValue(v,type){if(type==='number'){const n=Number(String(v??'').replace(/[^\d,.-]/g,'').replace(/\./g,'').replace(',','.'));return Number.isFinite(n)?n:Number.NEGATIVE_INFINITY}if(type==='date'){const m=String(v||'').match(/(\d{2})\/(\d{2})\/(\d{4})(?:\s+(\d{1,2}):(\d{2}))?/);return m?Date.UTC(Number(m[3]),Number(m[2])-1,Number(m[1]),Number(m[4]||0),Number(m[5]||0)):0}if(typeof v==='boolean')return v?'SIM':'NAO';return lsNorm(v)}
  function lsSortItems(items){if(!lsReportSort.key||!lsReportSort.dir)return [...items];const mult=lsReportSort.dir==='asc'?1:-1;return [...items].map((item,index)=>({item,index})).sort((a,b)=>{const av=lsSortValue(a.item?.[lsReportSort.key],lsReportSort.type),bv=lsSortValue(b.item?.[lsReportSort.key],lsReportSort.type);let c=0;if(lsReportSort.type==='text')c=String(av).localeCompare(String(bv),'pt-BR',{numeric:true,sensitivity:'base'});else c=av-bv;return c===0?a.index-b.index:c*mult}).map(x=>x.item)}
  function updateLsSortIndicators(){for(const th of wrap.querySelectorAll('[data-ls-sort]')){th.classList.remove('is-sort-active');th.removeAttribute('data-sort-dir');if(th.dataset.lsSort===lsReportSort.key){th.classList.add('is-sort-active');th.dataset.sortDir=lsReportSort.dir}}}
  function resetLsSort(){lsReportSort={key:'',type:'text',dir:''};updateLsSortIndicators()}
  function toggleLsSort(key,type){if(lsReportSort.key!==key)lsReportSort={key,type,dir:type==='number'?'desc':'asc'};else lsReportSort={key,type,dir:lsReportSort.dir==='asc'?'desc':'asc'};reportItems=lsSortItems(reportItems);updateLsSortIndicators();renderLsReportRows()}
  function fillLsSelect(sel,vals){const el=$(sel);if(!el)return;el.innerHTML='<option value="">Todos</option>'+[...new Set(vals.map(v=>String(v||'').trim()).filter(Boolean))].sort((a,b)=>a.localeCompare(b,'pt-BR')).map(v=>`<option value="${esc(v)}">${esc(v)}</option>`).join('')}
  function resetLsFilters(){for(const q of ['[data-ls-f-pedido]','[data-ls-f-data]','[data-ls-f-cliente]','[data-ls-f-valor]','[data-ls-f-status-shopify]','[data-ls-f-archived]','[data-ls-f-cancelled]','[data-ls-f-imported]','[data-ls-f-nota]','[data-ls-f-carrier]','[data-ls-f-lisa-status]','[data-ls-f-rastreio]','[data-ls-f-delivery]']){const el=$(q);if(el)el.value=''}}
  function applyLsReportFilters(){const v=q=>String($(q)?.value||'');const boolMatch=(x,raw)=>!raw||(raw==='sim'?!!x:!x);reportItems=lsSortItems(reportBaseItems.filter(x=>(!v('[data-ls-f-pedido]')||lsNorm(x.pedido).includes(lsNorm(v('[data-ls-f-pedido]'))))&&(!v('[data-ls-f-data]')||lsDateKey(x.data)===v('[data-ls-f-data]'))&&(!v('[data-ls-f-cliente]')||lsNorm(x.cliente).includes(lsNorm(v('[data-ls-f-cliente]'))))&&(!v('[data-ls-f-valor]')||lsNorm(x.valor).includes(lsNorm(v('[data-ls-f-valor]'))))&&(!v('[data-ls-f-status-shopify]')||String(x.status_shopify||'')===v('[data-ls-f-status-shopify]'))&&boolMatch(x.archived,v('[data-ls-f-archived]'))&&boolMatch(x.cancelled,v('[data-ls-f-cancelled]'))&&boolMatch(x.imported,v('[data-ls-f-imported]'))&&(!v('[data-ls-f-nota]')||lsNorm(x.nota).includes(lsNorm(v('[data-ls-f-nota]'))))&&(!v('[data-ls-f-carrier]')||String(x.transportadora||'')===v('[data-ls-f-carrier]'))&&(!v('[data-ls-f-lisa-status]')||String(x.lisa_status||'')===v('[data-ls-f-lisa-status]'))&&(!v('[data-ls-f-rastreio]')||lsNorm(x.rastreio).includes(lsNorm(v('[data-ls-f-rastreio]'))))&&(!v('[data-ls-f-delivery]')||String(x.delivery_status||'')===v('[data-ls-f-delivery]'))));renderLsReportRows()}
  function lsTrackingSendEligible(x){return !!x?.archived&&!x?.shopify_tracking_added&&!!String(x?.rastreio||'').trim()&&!!String(x?.transportadora||'').trim()&&!!lsNorm(x?.delivery_status)&&lsNorm(x?.delivery_status)!=='NAO ENVIADO'}
  function lsDeliveredEligible(x){return x?.tracking_category==='delivered'&&!x?.shopify_delivered&&Array.isArray(x?.shopify_all_fulfillment_ids)&&x.shopify_all_fulfillment_ids.length>0}
  function lsIsDeliveredReport(){return lsReportKey==='tracking:delivered'}
  function lsIsSemRastreio(){return lsReportKey==='archived_without_delivery'}
  function lsRowEligible(x){return lsIsDeliveredReport()?lsDeliveredEligible(x):(lsIsSemRastreio()?lsTrackingSendEligible(x):false)}
  function lsTrackingSelectHtml(x){if(!lsIsSemRastreio()&&!lsIsDeliveredReport())return '';const pedido=String(x?.pedido||'');const eligible=lsRowEligible(x);const checked=lsSelectedTracking.has(pedido)?' checked':'';return `<td class="ls-select-cell"><input class="ls-row-select" type="checkbox" data-ls-row-select="${esc(pedido)}"${checked}${eligible?'':' disabled'}></td>`}
  function updateLsBatchButton(){const refresh=$('[data-ls-refresh-tracking]');if(refresh)refresh.hidden=!lsIsSemRastreio();const btn=$('[data-ls-batch-send]'),all=$('[data-ls-select-all]'),active=lsIsSemRastreio()||lsIsDeliveredReport();if(btn){btn.hidden=!active;btn.disabled=!active||lsSelectedTracking.size<1||lsBatchSending;const label=lsIsDeliveredReport()?'MARCAR ENTREGUE':'ENVIAR SELECIONADOS';btn.textContent=lsBatchSending?btn.textContent:`${label} (${lsSelectedTracking.size})`}if(all&&active){const eligible=reportItems.filter(lsRowEligible).map(x=>String(x.pedido||''));const selected=eligible.filter(x=>lsSelectedTracking.has(x)).length;all.checked=eligible.length>0&&selected===eligible.length;all.indeterminate=selected>0&&selected<eligible.length}}
  function lsNotaCell(x){const nota=String(x?.nota||'').trim();return nota?esc(nota):'<span style="display:inline-flex;align-items:center;padding:3px 7px;border-radius:999px;background:#fff1f0;border:1px solid #ffc9c5;color:#b42318;font-size:9px;font-weight:700;white-space:nowrap">NÃO EMITIDA</span>'}
  function renderLsReportRows(){const compact=lsIsSemRastreio()||lsIsDeliveredReport(),isDelivered=lsIsDeliveredReport();const selectHead=$('[data-ls-select-head]'),deliveredHead=$('[data-ls-delivered-status-head]');if(selectHead)selectHead.hidden=!compact;if(deliveredHead)deliveredHead.hidden=!isDelivered;for(const th of wrap.querySelectorAll('[data-ls-hide-compact]'))th.hidden=compact;$('[data-ls-report-count]').textContent=`${reportItems.length} ${reportItems.length===1?'pedido':'pedidos'}`;$('[data-ls-report-body]').innerHTML=reportItems.map(x=>{const updated=isDelivered&&x?.shopify_delivered;return `<tr data-ls-row-pedido="${esc(x.pedido||'')}"${updated?' class="ls-delivered-updated-row"':''}>${lsTrackingSelectHtml(x)}<td>${esc(x.pedido||'-')}</td><td>${esc(x.data||'-')}</td><td>${esc(x.cliente||'-')}</td>${compact?'':`<td>${esc(x.valor||'-')}</td><td>${esc(x.status_shopify||'-')}</td><td>${yn(x.archived)}</td><td>${yn(x.cancelled)}</td><td>${yn(x.imported)}</td>`}<td>${lsNotaCell(x)}</td><td>${esc(x.transportadora||'-')}</td><td>${esc(x.lisa_status||'-')}</td><td>${esc(x.rastreio||'-')}</td><td>${esc(x.delivery_status||'-')}</td>${isDelivered?`<td>${updated?'<span class="ls-delivered-updated">ATUALIZADO</span>':'PENDENTE'}</td>`:''}</tr>`}).join('');$('[data-ls-report-empty]').hidden=reportItems.length>0;updateLsBatchButton()}
  function openReport(key,customItems=null,customTitle=''){lsReportKey=String(key||'');lsSelectedTracking.clear();reportBaseItems=customItems||filterItems(key);reportItems=[...reportBaseItems];resetLsFilters();resetLsSort();fillLsSelect('[data-ls-f-status-shopify]',reportBaseItems.map(x=>x.status_shopify));fillLsSelect('[data-ls-f-carrier]',reportBaseItems.map(x=>x.transportadora));fillLsSelect('[data-ls-f-lisa-status]',reportBaseItems.map(x=>x.lisa_status));fillLsSelect('[data-ls-f-delivery]',reportBaseItems.map(x=>x.delivery_status));$('[data-ls-report-title]').textContent=customTitle||labelFor(key);renderLsReportRows();$('[data-ls-report-modal]').hidden=false}
  async function lsRefreshTracking(){
    const button=$('[data-ls-refresh-tracking]'),count=$('[data-ls-report-count]');
    if(button.disabled)return;
    button.disabled=true;button.textContent='Atualizando...';
    try{
      const response=await fetch(worker()+'/site/dashboard/tracking/refresh',{method:'POST',headers:{Authorization:'Bearer '+auth()},cache:'no-store'});
      const result=await response.json().catch(()=>({}));
      if(!response.ok||!result.ok)throw Error(result.error||'Falha ao atualizar rastreios.');
      const snapshotResponse=await fetch(worker()+'/site/dashboard',{headers:{Authorization:'Bearer '+auth()},cache:'no-store'});
      const data=await snapshotResponse.json();
      if(!snapshotResponse.ok||!Array.isArray(data.items))throw Error('Não foi possível receber a lista atualizada.');
      lsRenderSiteState(data);lsSaveSiteCache(data);
      if(lsIsSemRastreio()&&!$('[data-ls-report-modal]').hidden){
        openReport(lsReportKey);
        count.textContent+=' · '+Number(result.rows||0).toLocaleString('pt-BR')+' registros atualizados';
      }
    }catch(error){count.textContent='Falha ao atualizar rastreio · '+String(error?.message||error)}
    finally{button.disabled=false;button.textContent='Atualizar rastreio'}
  }
  $('[data-ls-refresh-tracking]').addEventListener('click',lsRefreshTracking);
  function closeReport(){$('[data-ls-report-modal]').hidden=true;reportItems=[];reportBaseItems=[];lsReportKey='';lsSelectedTracking.clear();lsBatchSending=false;resetLsSort();updateLsBatchButton()}
  function lsMarkTrackingSent(pedido){
    const item=reportBaseItems.find(x=>String(x?.pedido||'')===String(pedido||''));if(!item)return;
    const row=$(`[data-ls-row-pedido="${CSS.escape(String(pedido||''))}"]`);if(row)row.classList.add('ls-tracking-sent');
    let seconds=5;let badge=row?.querySelector('[data-ls-countdown]');
    if(!badge&&row){badge=document.createElement('span');badge.setAttribute('data-ls-countdown','');badge.className='ls-batch-countdown';const cell=row.querySelector('.ls-select-cell');if(cell)cell.appendChild(badge)}
    if(badge)badge.textContent=String(seconds);
    const finish=()=>{item.shopify_tracking_added=true;item.archived_without_delivery=false;lsSelectedTracking.delete(String(pedido||''));for(const x of (state.items||[]))if(String(x?.pedido||'')===String(pedido||'')){x.shopify_tracking_added=true;x.archived_without_delivery=false}reportBaseItems=reportBaseItems.filter(x=>String(x?.pedido||'')!==String(pedido||''));reportItems=reportItems.filter(x=>String(x?.pedido||'')!==String(pedido||''));if(state.summary)state.summary.archived_without_delivery=(state.items||[]).filter(x=>x.archived_without_delivery).length;cards();renderLsReportRows()};
    const timer=setInterval(()=>{seconds--;if(badge)badge.textContent=String(seconds);if(seconds<=0){clearInterval(timer);finish()}},1000);
  }
  function lsMarkDeliveredUpdated(pedido){const item=reportBaseItems.find(x=>String(x?.pedido||'')===String(pedido||''));if(item){item.shopify_delivered=true;item.status_shopify='ATUALIZADO'}for(const x of (state.items||[]))if(String(x?.pedido||'')===String(pedido||'')){x.shopify_delivered=true;x.status_shopify='ATUALIZADO'}lsSelectedTracking.delete(String(pedido||''));renderLsReportRows()}
  async function lsSendSelectedTracking(){
    if(lsBatchSending||lsSelectedTracking.size<1)return;
    const btn=$('[data-ls-batch-send]'),isDelivered=lsIsDeliveredReport();
    const pedidos=[...lsSelectedTracking].filter(p=>reportBaseItems.some(x=>String(x?.pedido||'')===p&&lsRowEligible(x)));
    if(!pedidos.length){updateLsBatchButton();return}
    lsBatchSending=true;btn.disabled=true;btn.textContent=isDelivered?`ATUALIZANDO ${pedidos.length}...`:`ENVIANDO ${pedidos.length}...`;
    try{
      const endpoint=isDelivered?'/site/delivery/mark-batch':'/site/tracking/send-batch';
      const r=await fetch(worker()+endpoint,{method:'POST',headers:{'Content-Type':'application/json','Authorization':'Bearer '+auth()},body:JSON.stringify({pedidos})});
      const d=await r.json().catch(()=>({}));if(r.status===401)throw new Error('Sessão expirada.');if(!r.ok)throw new Error(d.detail||d.error||'Não foi possível concluir a atualização no Shopify.');
      const results=Array.isArray(d.results)?d.results:[],success=results.filter(x=>x?.ok),failed=results.filter(x=>!x?.ok);
      for(const x of success){if(isDelivered)lsMarkDeliveredUpdated(String(x.pedido||''));else lsMarkTrackingSent(String(x.pedido||''))}
      lsSelectedTracking.clear();for(const x of failed)lsSelectedTracking.add(String(x.pedido||''));
      if(failed.length){const sample=failed.slice(0,5).map(x=>`${x.pedido}: ${x.error||'falha'}`).join('\n');alert(`${success.length} atualizado(s). ${failed.length} falha(s).${sample?'\n\n'+sample:''}`)}
    }catch(e){const raw=String(e?.message||'').trim();alert(/failed to fetch/i.test(raw)?'Não foi possível comunicar com o Worker/Shopify. Tente novamente em alguns segundos.':(raw||'Não foi possível concluir a atualização no Shopify.'))}
    finally{lsBatchSending=false;updateLsBatchButton()}
  }
  async function mainSiteSearch(){
    const q=String($('[data-ls-main-search]')?.value||'').trim();if(!q)return;
    err.hidden=true;
    const nq=lsNorm(q);
    let found=[...(state.items||[]),...(state.unprocessed_items||[])].filter((x,i,a)=>a.findIndex(y=>String(y.pedido||'')===String(x.pedido||''))===i).filter(x=>lsNorm(x.pedido).includes(nq)||lsNorm(x.nota).includes(nq));
    if(found.length){openReport('',found,`Busca: ${q}`);return}
    const shopifyQuery=q;
    try{
      const r=await fetch(worker()+'/site/search?q='+encodeURIComponent(shopifyQuery),{headers:{Authorization:'Bearer '+auth()}});
      const d=await r.json().catch(()=>({}));
      if(r.status===401)throw new Error('Sessão expirada.');
      if(!r.ok){openReport('',[],'Dado não encontrado');return}
      found=Array.isArray(d.items)?d.items:[];
      if(!found.length){openReport('',[],'Dado não encontrado');return}
      openReport('',found,`Busca: ${q}`);
    }catch(e){
      if(String(e?.message||'').toLowerCase().includes('sessão expirada')){err.textContent='Sessão expirada.';err.hidden=false;return}
      openReport('',[],'Dado não encontrado');
    }
  }
  function openCalendarMetric(date,metric){const dayItems=(state.items||[]).filter(x=>String(x.day_key||'')===String(date||''));let found=dayItems,label='Recebidos';if(metric==='expedited'){found=dayItems.filter(x=>x.lisa_finalized);label='Expedidos'}else if(metric==='in_transit'){found=dayItems.filter(x=>String(x.rastreio||'').trim());label='Em trajeto'}else if(metric==='delivered'){found=dayItems.filter(x=>x.tracking_category==='delivered');label='Entregues'}const [y,m,d]=String(date||'').split('-');openReport('',found,`${label} de ${d||'--'}/${m||'--'}/${y||'----'}`)}
  function lsReadSiteCache(){try{const raw=sessionStorage.getItem(LS_SITE_CACHE_KEY);if(!raw)return null;const c=JSON.parse(raw);return c&&c.data&&Array.isArray(c.data.items)?c:null}catch{return null}}
  function lsSaveSiteCache(d){try{const savedAt=Date.now();sessionStorage.setItem(LS_SITE_CACHE_KEY,JSON.stringify({saved_at:savedAt,data:d}));return savedAt}catch{return Date.now()}}
  function lsRenderSiteState(d){wrap.classList.remove('ls-site-unavailable');state=d||{items:[],summary:{tracking:{}},calendar:[]};for(const rows of [state.items,state.unprocessed_items])for(const row of rows||[])if(lsNorm(row.transportadora).includes('CONTINENTAL')){row.rastreio=String(row.nota??'').trim();row.tracking_added=!!row.rastreio}cards();calendar();const at=new Date(state.updated_at||Date.now());$('[data-ls-updated]').textContent='Última atualização: '+at.toLocaleString('pt-BR')}
  function lsSnapshotUnavailable(message){
    wrap.classList.add('ls-site-unavailable');
    err.hidden=false;
    err.textContent=String(message||'A base do Site ainda nao esta disponivel.');
    const actions=document.createElement('div');actions.className='ls-snapshot-actions';
    const retry=document.createElement('button');retry.type='button';retry.textContent='Tentar novamente';
    retry.addEventListener('click',()=>load(true,false,true).catch(()=>{}));
    const prepare=document.createElement('button');prepare.type='button';prepare.textContent='Solicitar preparação no Cloudflare';
    prepare.title='Processo manual: pede ao Cloudflare para atualizar os dados. A tela nao busca Shopify diretamente.';
    prepare.addEventListener('click',()=>lsRequestRefresh(prepare));
    actions.append(retry,prepare);err.appendChild(actions);
  }
  async function lsRequestRefresh(button){
    if(button.dataset.refreshing)return;
    button.dataset.refreshing='1';
    button.disabled=true;
    const box=$('[data-ls-progress-box]'),bar=$('[data-ls-progress]'),label=$('[data-ls-progress-label]');
    const headers={Authorization:'Bearer '+auth()};
    const previous=state.updated_at;
    let stage='Solicitando atualização',percent=0;
    function show(message){
      box.hidden=false;
      bar.value=percent;
      label.textContent=message;
    }
    show('Solicitando atualização · pedidos salvos continuam visíveis');
    try{
      const progressTimer=setInterval(async()=>{
        try{
          const response=await fetch(worker()+'/site/dashboard/status',{headers,cache:'no-store'});
          if(!response.ok)return;
          const current=await response.json();
          if(modal.hidden)return;
          if(current.stage){stage=current.stage;percent=Number(current.progress)||0;show(percent+'% estimado · '+stage)}
        }catch{}
      },4000);
      let r;
      try{
        r=await fetch(worker()+'/site/dashboard/prepare',{method:'POST',headers,cache:'no-store'});
      }finally{clearInterval(progressTimer)}
      const d=await r.json().catch(()=>({}));
      if(!r.ok||d.ok===false)throw new Error(d.detail||d.error||'Não foi possível iniciar a atualização (HTTP '+r.status+').');
      if(d.ok&&d.started&&!d.preparing){
        const loaded=await load(true,true,true);
        if(!loaded?.updated_at||loaded.updated_at!==d.updated_at)throw Error('Carga concluída, mas o painel ainda não recebeu os pedidos atualizados.');
        percent=100;show('100% · Pedidos atualizados');return;
      }
      if(d.preparing&&(!d.started_at||!d.progress))throw new Error('O Worker respondeu sem confirmar a partida. Confira se a versão atual foi implantada em Produção.');
      if(d.progress){percent=d.progress;stage=d.stage||stage;show(percent+'% estimado · '+stage)}
      for(let attempt=0;attempt<30&&!modal.hidden;attempt++){
        await new Promise(resolve=>setTimeout(resolve,4000));
        if(modal.hidden)return;
        const response=await fetch(worker()+'/site/dashboard/status',{headers,cache:'no-store'});
        if(!response.ok)throw new Error('Falha ao consultar o progresso (HTTP '+response.status+').');
        const result=await response.json();
        if(d.started_at&&result.last_attempt_at!==d.started_at)throw new Error('O status consultado não corresponde à atualização iniciada. Confira o Worker de Produção e o binding LISA_BUCKET.');
        stage=result.stage||stage;
        percent=Math.max(0,Math.min(100,Number(result.progress)||0));
        if(result.phase==='failed')throw new Error(result.last_error||'O Worker não concluiu a carga.');
        if(result.updated_at&&result.updated_at!==previous&&result.phase!=='running'){
          const loaded=await load(true,true,true);
          if(loaded?.updated_at!==result.updated_at)throw new Error('O Worker salvou os pedidos, mas o painel ainda não recebeu a nova carga.');
          percent=100;
          show('100% · Pedidos atualizados');
          return;
        }
        show(percent+'% estimado · '+stage);
      }
      if(!modal.hidden)show('Sem confirmação após 2 minutos · última etapa: '+stage+' ('+percent+'%). Os pedidos salvos continuam visíveis.');
    }catch(e){show('Falha em “'+stage+'” · '+String(e?.message||e))}
    finally{delete button.dataset.refreshing;button.disabled=false}
  }
  function lsScheduleSiteRefresh(savedAt=Date.now()){if(lsSiteRefreshTimer)clearTimeout(lsSiteRefreshTimer);const elapsed=Math.max(0,Date.now()-Number(savedAt||0));const wait=Math.max(30000,LS_SITE_CACHE_TTL-elapsed);lsSiteRefreshTimer=setTimeout(()=>{if(!modal.hidden&&auth())load(true,true,false).catch(()=>{})},wait)}
  let lsFirstLoadTimer=null,lsFirstLoadAttempts=0;
  function lsFirstLoadWait(){
    if(lsFirstLoadTimer)clearTimeout(lsFirstLoadTimer);
    if(modal.hidden||lsFirstLoadAttempts>=30)return;
    lsFirstLoadAttempts++;
    lsFirstLoadTimer=setTimeout(()=>{lsFirstLoadTimer=null;if(!modal.hidden&&auth())load(true,true,true).catch(()=>{})},6000);
  }
  async function load(force=false,silent=false,forceApi=false){
    const cached=lsReadSiteCache();
    if(!force&&cached&&(Date.now()-Number(cached.saved_at||0))<LS_SITE_CACHE_TTL){err.hidden=true;setLoading(false);lsRenderSiteState(cached.data);lsScheduleSiteRefresh(cached.saved_at);return cached.data}
    if(lsSitePreloadPromise&&!forceApi)return lsSitePreloadPromise;
    if(cached&&silent){lsRenderSiteState(cached.data);setLoading(false)}else if(!silent&&!modal.hidden){setLoading(true)}
    err.hidden=true;
    const task=(async()=>{try{
      const dashRes=await fetch(worker()+'/site/dashboard',{headers:{Authorization:'Bearer '+auth()},cache:'no-store'});
      const d=await dashRes.json().catch(()=>({}));if(dashRes.status===401)throw new Error('Sessão expirada.');
      if(dashRes.status===202&&d.first_load&&d.preparing){
        if(!modal.hidden){lsSnapshotUnavailable(d.message||'Preparando a primeira carga do Shopify.');lsFirstLoadWait()}
        return d;
      }
      if(!dashRes.ok||d.available===false||!Array.isArray(d.items)||(!d.items.length&&(state.items?.length||cached?.data?.items?.length)))throw new Error(d.error||d.message||'Não foi possível carregar os pedidos do site.');
      if(lsFirstLoadTimer){clearTimeout(lsFirstLoadTimer);lsFirstLoadTimer=null}lsFirstLoadAttempts=0;
      lsRenderSiteState(d);const savedAt=lsSaveSiteCache(d);lsScheduleSiteRefresh(savedAt);return d;
    }catch(e){const fallback=lsReadSiteCache();if(fallback){lsRenderSiteState(fallback.data);if(!modal.hidden){err.textContent='Não foi possível atualizar o snapshot agora. Exibindo a última carga salva.';err.hidden=false}lsScheduleSiteRefresh(fallback.saved_at);return fallback.data}if(!modal.hidden){lsSnapshotUnavailable(e.message||'Erro ao carregar o painel do site.')}throw e}finally{if(!modal.hidden)setLoading(false)}})();
    if(!forceApi)lsSitePreloadPromise=task.finally(()=>{lsSitePreloadPromise=null}).catch(()=>null);
    return task;
  }
  function startSitePreload(attempt=0){if(auth()&&worker()){load(true,true,false).catch(()=>{});return}if(attempt<1200)setTimeout(()=>startSitePreload(attempt+1),500)}
  function open(){modal.hidden=false;const cached=lsReadSiteCache();if(cached){lsRenderSiteState(cached.data);setLoading(false)}else if(Array.isArray(state.items)&&state.items.length){setLoading(false)}else setLoading(true);load(true,true,false).catch(()=>{})}
  function close(){modal.hidden=true;if(lsFirstLoadTimer){clearTimeout(lsFirstLoadTimer);lsFirstLoadTimer=null}closeReport()}
  async function copy(){if(!reportItems.length)return;const txt=reportItems.map(x=>[`Pedido: ${x.pedido||'-'}`,`Data: ${x.data||'-'}`,`Cliente: ${x.cliente||'-'}`,`Valor: ${x.valor||'-'}`,`Status Shopify: ${x.status_shopify||'-'}`,`Arquivado: ${yn(x.archived)}`,`Cancelado: ${yn(x.cancelled)}`,`Importado: ${yn(x.imported)}`,`Nota: ${x.nota||'-'}`,`Transportadora: ${x.transportadora||'-'}`,`Status LISA: ${x.lisa_status||'-'}`,`Rastreio/CTE: ${x.rastreio||'-'}`,`Status entrega: ${x.delivery_status||'-'}`].join('\n')).join('\n\n');try{await navigator.clipboard.writeText(txt)}catch{const ta=document.createElement('textarea');ta.value=txt;ta.style.position='fixed';ta.style.left='-9999px';document.body.appendChild(ta);ta.select();document.execCommand('copy');ta.remove()}const b=$('[data-ls-copy]'),old=b.textContent;b.textContent='Copiado!';setTimeout(()=>b.textContent=old,1100)}
  async function xlsx(){if(!reportItems.length)return;const b=$('[data-ls-download]'),old=b.textContent;b.disabled=true;b.textContent='Gerando...';try{if(!window.XLSX)await new Promise((res,rej)=>{const s=document.createElement('script');s.src='https://cdn.jsdelivr.net/npm/xlsx@0.18.5/dist/xlsx.full.min.js';s.onload=res;s.onerror=rej;document.head.appendChild(s)});const H=['Pedido','Data','Cliente','Valor','Status Shopify','Arquivado','Cancelado','Importado','Nota','Transportadora','Status LISA','Rastreio/CTE','Status entrega'];const A=[H,...reportItems.map(x=>[x.pedido,x.data,x.cliente,x.valor,x.status_shopify,yn(x.archived),yn(x.cancelled),yn(x.imported),x.nota,x.transportadora,x.lisa_status,x.rastreio,x.delivery_status].map(v=>String(v??'')))];const ws=XLSX.utils.aoa_to_sheet(A);const rg=XLSX.utils.decode_range(ws['!ref']);for(let R=rg.s.r;R<=rg.e.r;R++)for(let C=rg.s.c;C<=rg.e.c;C++){const a=XLSX.utils.encode_cell({r:R,c:C}),cell=ws[a];if(cell){cell.t='s';cell.v=String(cell.v??'');cell.z='@'}}ws['!cols']=[{wch:16},{wch:19},{wch:34},{wch:16},{wch:22},{wch:11},{wch:11},{wch:11},{wch:16},{wch:26},{wch:20},{wch:22},{wch:28}];const wb=XLSX.utils.book_new();XLSX.utils.book_append_sheet(wb,ws,'Pedidos');XLSX.writeFile(wb,'pedidos_site.xlsx',{compression:true})}catch(e){alert(e.message||'Não foi possível gerar o Excel.')}finally{b.disabled=false;b.textContent=old}}
  if(openBtn.__lisaSiteOpenHandler)openBtn.removeEventListener('click',openBtn.__lisaSiteOpenHandler);openBtn.__lisaSiteOpenHandler=open;openBtn.addEventListener('click',open);const refreshButton=$('[data-ls-refresh]');refreshButton?.addEventListener('click',()=>lsRequestRefresh(refreshButton));$('[data-ls-close]').addEventListener('click',close);modal.addEventListener('click',e=>{if(e.target===modal)close()});$('[data-ls-body]').addEventListener('click',e=>{const metric=e.target.closest('[data-ls-calendar-metric]');if(metric&&!metric.disabled){openCalendarMetric(metric.dataset.lsCalendarDay,metric.dataset.lsCalendarMetric);return}const c=e.target.closest('[data-ls-filter]');if(c)openReport(c.dataset.lsFilter)});$('[data-ls-report-close]').addEventListener('click',closeReport);$('[data-ls-report-modal]').addEventListener('click',e=>{if(e.target===$('[data-ls-report-modal]'))closeReport()});const lsHead=$('.ls-table thead');if(lsHead)lsHead.addEventListener('click',e=>{const btn=e.target.closest('.ls-sort-btn');if(!btn)return;const th=btn.closest('[data-ls-sort]');if(th)toggleLsSort(th.dataset.lsSort,th.dataset.sortType||'text')});$('[data-ls-copy]').addEventListener('click',copy);$('[data-ls-download]').addEventListener('click',xlsx);const lsReportBody=$('[data-ls-report-body]');if(lsReportBody){lsReportBody.addEventListener('change',e=>{const cb=e.target.closest('[data-ls-row-select]');if(!cb)return;const p=String(cb.dataset.lsRowSelect||'');if(cb.checked)lsSelectedTracking.add(p);else lsSelectedTracking.delete(p);updateLsBatchButton()})}const lsSelectAll=$('[data-ls-select-all]');if(lsSelectAll)lsSelectAll.addEventListener('change',()=>{for(const x of reportItems){const p=String(x?.pedido||'');if(!lsRowEligible(x))continue;if(lsSelectAll.checked)lsSelectedTracking.add(p);else lsSelectedTracking.delete(p)}renderLsReportRows()});const lsBatchBtn=$('[data-ls-batch-send]');if(lsBatchBtn)lsBatchBtn.addEventListener('click',lsSendSelectedTracking);$('[data-ls-main-search-btn]').addEventListener('click',mainSiteSearch);$('[data-ls-main-search]').addEventListener('keydown',e=>{if(e.key==='Enter'){e.preventDefault();mainSiteSearch()}});for(const el of wrap.querySelectorAll('.ls-filter'))el.addEventListener(el.tagName==='SELECT'?'change':'input',applyLsReportFilters);
  panel.addEventListener('lisa:carrier-files-updated',()=>{
    try{sessionStorage.removeItem(LS_SITE_CACHE_KEY)}catch{}
    if(!modal.hidden)load(true,true,true).then(()=>{
      if(lsReportKey){reportBaseItems=filterItems(lsReportKey);applyLsReportFilters()}
    }).catch(()=>{});
  });
  // Shopify snapshot is fetched on demand when the SITE button is opened.
  }
  initLisaSite();
  };
  initLisaSiteModule();
}

;

(()=>{
  const root=document.getElementById('lisa-app-vg-native');if(!root)return;
  const worker=()=>String(root.dataset.worker||'').replace(/\/$/,'');
  const auth=()=>sessionStorage.getItem('lisa_panel_token')||'';
  const esc=v=>String(v??'').replace(/[&<>"']/g,m=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[m]));
  const parseNotes=v=>{const seen=new Set(),out=[];for(const x of String(v||'').split(/[\s,;]+/)){const n=x.trim();if(!n)continue;const k=n.toUpperCase();if(seen.has(k))continue;seen.add(k);out.push(n)}return out};
  const today=()=>{const d=new Date(),y=d.getFullYear(),m=String(d.getMonth()+1).padStart(2,'0'),day=String(d.getDate()).padStart(2,'0');return `${y}-${m}-${day}`};
  async function request(path,opt={}){const h={'Content-Type':'application/json',...(opt.headers||{})};const t=auth();if(t)h.Authorization='Bearer '+t;const r=await fetch(worker()+path,{...opt,headers:h,cache:'no-store'});const d=await r.json().catch(()=>({}));if(!r.ok)throw new Error(d.detail||d.error||'Falha na comunicação.');return d}
  function ensure(){
    let wrap=document.getElementById('lisacarrierticket-vg-native');if(wrap)return wrap;
    wrap=document.createElement('div');wrap.id='lisacarrierticket-vg-native';
    wrap.innerHTML=`<div class="carrier-ticket-modal" data-ticket-modal hidden><div class="carrier-ticket-card"><button type="button" class="carrier-ticket-close" data-ticket-close aria-label="Fechar">×</button><div class="carrier-ticket-head"><h3 data-ticket-title>Adicionar ticket</h3><span data-ticket-subtitle>Registre as informações desta nota fiscal. A transportadora será identificada automaticamente.</span></div><div class="carrier-ticket-form"><input type="hidden" data-ticket-notes><label class="is-wide"><span>Data de abertura</span><input type="date" data-ticket-date></label><label class="is-wide"><span>Descrição</span><textarea data-ticket-description placeholder="Descreva o motivo e as informações do ticket."></textarea></label></div><div class="carrier-ticket-msg" data-ticket-msg></div><div class="carrier-ticket-actions"><button type="button" class="carrier-ticket-reset" data-ticket-reset>RECOMEÇAR</button><button type="button" class="carrier-ticket-save" data-ticket-save>SALVAR</button></div></div></div>`;
    document.body.appendChild(wrap);
    const modal=wrap.querySelector('[data-ticket-modal]'),notes=wrap.querySelector('[data-ticket-notes]'),date=wrap.querySelector('[data-ticket-date]'),desc=wrap.querySelector('[data-ticket-description]'),msg=wrap.querySelector('[data-ticket-msg]'),save=wrap.querySelector('[data-ticket-save]'),title=wrap.querySelector('[data-ticket-title]'),subtitle=wrap.querySelector('[data-ticket-subtitle]');
    const dateLabel=date.closest('label'),descLabel=desc.closest('label'),descLabelText=descLabel?.querySelector('span');
    wrap.__editingId='';
    const setMode=(mode,item=null)=>{
      const editing=mode==='edit';
      wrap.__editingId=editing?String(item?.ticket_id||item?.id||''):'';
      const nota=String(item?.nota_cliente||item?.nota_lisa||item?.nota_fiscal||item?.nota||item?.nf||notes.value||'').trim();
      title.textContent=(editing?'Atualizar ticket':'Adicionar ticket')+(nota?' - NF '+nota:'');
      subtitle.textContent=editing?'Registre uma nova atualização. O ticket original e as atualizações anteriores serão mantidos.':'Registre as informações desta nota fiscal. A transportadora será identificada automaticamente.';
      save.textContent=editing?'SALVAR ATUALIZAÇÃO':'SALVAR';
      wrap.querySelector('[data-ticket-reset]').textContent=editing?'CANCELAR':'RECOMEÇAR';
      if(dateLabel)dateLabel.hidden=editing;
      if(descLabel)descLabel.hidden=false;if(descLabelText)descLabelText.textContent=editing?'Atualização do ticket':'Descrição';
      desc.placeholder=editing?'Descreva a nova atualização do ticket.':'Descreva o motivo e as informações do ticket.';
    };
    const reset=(keepNote=false)=>{if(!keepNote)wrap.__ticketContext=null;const nota=keepNote?notes.value:'';notes.value=nota;setMode('create',nota?{nota}:null);desc.value='';date.value=today();msg.textContent='';msg.className='carrier-ticket-msg'};
    wrap.querySelector('[data-ticket-reset]').addEventListener('click',()=>{if(wrap.__editingId){modal.hidden=true;reset()}else reset(true)});
    wrap.querySelector('[data-ticket-close]').addEventListener('click',()=>{modal.hidden=true;reset()});
    modal.addEventListener('click',e=>{if(e.target===modal){modal.hidden=true;reset()}});
    save.addEventListener('click',async()=>{
      const notas=parseNotes(notes.value),data_abertura=date.value,descricao=desc.value.trim(),editing=String(wrap.__editingId||'');
      msg.textContent='';msg.className='carrier-ticket-msg';
      if(!editing&&!notas.length){msg.textContent='Informe ao menos uma nota fiscal.';msg.classList.add('is-error');return}
      if(!editing&&!data_abertura){msg.textContent='Escolha a data de abertura.';msg.classList.add('is-error');return}
      if(!descricao){msg.textContent=editing?'Digite a nova atualização do ticket.':'Digite a descrição do ticket.';msg.classList.add('is-error');return}
      const old=save.textContent;save.disabled=true;save.textContent='SALVANDO...';
      try{
        if(editing){
          await request('/transportadoras/tickets/update',{method:'POST',body:JSON.stringify({id:editing,descricao})});
          msg.textContent='Atualização registrada no histórico do ticket.';msg.classList.add('is-ok');
          setTimeout(()=>{modal.hidden=true;reset()},450);
          root.dispatchEvent(new CustomEvent('lisa:tickets-updated',{detail:{updated:1,ticket_id:editing}}));
        }else{
          const d=await request('/transportadoras/tickets',{method:'POST',body:JSON.stringify({notas,data_abertura,descricao,...(wrap.__ticketContext?{ticket_context:wrap.__ticketContext}:{})})});
          msg.textContent=`${Number(d.created||0).toLocaleString('pt-BR')} ${Number(d.created||0)===1?'ticket salvo':'tickets salvos'} com sucesso.`;msg.classList.add('is-ok');root.dispatchEvent(new CustomEvent('lisa:tickets-updated',{detail:{created:Number(d.created||0)}}));setTimeout(()=>{modal.hidden=true;reset()},650);
        }
      }catch(e){msg.textContent=e.message||'Não foi possível salvar o ticket.';msg.classList.add('is-error')}finally{save.disabled=false;if(!wrap.__editingId)save.textContent='SALVAR';else save.textContent='SALVAR ATUALIZAÇÃO'}
    });
    wrap.__ticket={modal,date,notes,desc,msg,reset,setMode};return wrap;
  }
  async function openCreate(notaFiscal,context=null){
    const nota=String(notaFiscal??'').trim();
    if(!nota){alert('Para adicionar um ticket, clique na coluna Ticket da nota fiscal correspondente no Rastreamento.');return}
    const wrap=ensure(),x=wrap.__ticket;
    x.reset();wrap.__ticketContext=context?{nota,carrier_key:context.carrier_key,transportadora:context.transportadora,cte:context.cte||''}:null;x.notes.value=nota;x.setMode('create',{nota});if(context)wrap.querySelector('[data-ticket-subtitle]').textContent='Transportadora: '+String(context.transportadora||context.carrier_key||'')+' · CTe: '+String(context.cte||'—');x.date.value=today();x.modal.hidden=false;
    setTimeout(()=>x.desc.focus(),30);
  }
  async function openEdit(item){const wrap=ensure(),x=wrap.__ticket;wrap.__ticketContext=null;x.notes.value=String(item?.nota_cliente||item?.nota_lisa||item?.nota_fiscal||item?.nota||'').trim();x.setMode('edit',item);x.modal.hidden=false;x.desc.value='';x.msg.textContent='';x.msg.className='carrier-ticket-msg';setTimeout(()=>x.desc.focus(),30)}
  async function deleteTicket(item){const id=String(item?.ticket_id||item?.id||'');if(!id)return;if(!confirm(`Apagar o ticket da nota ${item?.nota_cliente||item?.nota||'-'}?`))return;try{await request('/transportadoras/tickets/delete',{method:'POST',body:JSON.stringify({id})});root.dispatchEvent(new CustomEvent('lisa:tickets-updated',{detail:{deleted:1}}))}catch(e){alert(e.message||'Não foi possível apagar o ticket.')}}
  // Criacao e atualizacao pela coluna Ticket em qualquer card do Rastreamento.
  const rowOnlyStyleId='lisa-ticket-row-only-style-vg-native';
  if(!document.getElementById(rowOnlyStyleId)){
    const style=document.createElement('style');style.id=rowOnlyStyleId;
    style.textContent='#lisa-app-vg-native [data-carrier-ticket-add]{display:none!important}';
    document.head.appendChild(style);
  }
  root.__openCarrierTicketCreate=openCreate;root.__openCarrierTicketEdit=openEdit;
  document.addEventListener('click',e=>{
    const btn=e.target.closest?.('[data-carrier-ticket-add]');
    if(btn&&root.contains(btn)){e.preventDefault();e.stopPropagation();openCreate();return}
    const edit=e.target.closest?.('[data-ticket-edit]');
    if(edit&&root.contains(edit)){e.preventDefault();e.stopPropagation();const tr=edit.closest('[data-ticket-row]'),item=tr?.__ticketItem;if(item)openEdit(item);return}
    const del=e.target.closest?.('[data-ticket-delete]');
    if(del&&root.contains(del)){e.preventDefault();e.stopPropagation();const tr=del.closest('[data-ticket-row]'),item=tr?.__ticketItem;if(item)deleteTicket(item)}
  },true);
  let adminHeld=false;
  const syncAdmin=()=>root.classList.toggle('carrier-ticket-admin-mode',adminHeld);
  const isAltGr=e=>e.key==='AltGraph'||(e.altKey&&e.ctrlKey);
  document.addEventListener('keydown',e=>{if(isAltGr(e)){adminHeld=true;syncAdmin()}});
  document.addEventListener('keyup',e=>{if(e.key==='AltGraph'||e.key==='Alt'||e.key==='Control'||(!e.altKey&&!e.ctrlKey)){adminHeld=false;syncAdmin()}});
  window.addEventListener('blur',()=>{adminHeld=false;syncAdmin()});
  root.__carrierTicketReportHtml=(rows,escapeHtml)=>{const data=Array.isArray(rows)?rows:[];setTimeout(()=>{const modal=root.querySelector('[data-carrier-resumo-modal]');if(!modal)return;const trs=modal.querySelectorAll('[data-ticket-row]');trs.forEach((tr,i)=>tr.__ticketItem=data[i]||null)},0);return `<div class="carrier-resumo-report-wrap"><table class="carrier-resumo-report-table is-ticket-report"><thead><tr><th>NOTA FISCAL</th><th>DATA API</th><th>DATA ROMANEIO</th><th>STATUS TRANSPORTADORA</th><th>DESCRIÇÃO TICKET</th><th class="carrier-ticket-admin-col"></th></tr></thead><tbody>${data.map(x=>`<tr data-ticket-row data-ticket-id="${escapeHtml(x.ticket_id||'')}"><td>${escapeHtml(x.nota_cliente||x.nota_lisa||x.nota||'-')}</td><td>${escapeHtml(x.data_api||'-')}</td><td>${escapeHtml(x.data_romaneio||'-')}</td><td>${escapeHtml(x.status_entrega||x.status||'-')}</td><td class="carrier-ticket-description">${escapeHtml(x.descricao_ticket||'-')}</td><td class="carrier-ticket-admin-col"><div class="carrier-ticket-admin-actions"><button type="button" data-ticket-edit>EDITAR</button><button type="button" data-ticket-delete>APAGAR</button></div></td></tr>`).join('')}</tbody></table></div>`};
  document.addEventListener('keydown',e=>{if(e.key==='Escape'){const m=document.querySelector('#lisacarrierticket-vg-native [data-ticket-modal]');if(m)m.hidden=true}});
})();

;

(() => {
  'use strict';
  const panelRoot=document.getElementById('lisa-app-vg-native');
  if(!panelRoot||document.getElementById('lisaextra-vg-native'))return;
  const searchBtn=panelRoot.querySelector('[data-open-search]');
  if(!searchBtn)return;
  let fileBtn=panelRoot.querySelector('[data-file-menu-open]');
  if(!fileBtn){
    fileBtn=document.createElement('button');
    fileBtn.type='button';fileBtn.className='ghost lisa-quick-btn';fileBtn.setAttribute('data-file-menu-open','');fileBtn.textContent='APLICATIVOS';
    searchBtn.insertAdjacentElement('afterend',fileBtn);
  }
  const holder=document.createElement('div');holder.innerHTML=`
<div id="lisaextra-vg-native" class="lisaextra-root" aria-live="polite">
  <div class="lisa-file-tools-menu" data-file-menu hidden>
    <button type="button" data-pdf-editor-open>Editor PDF</button>
    <button type="button" data-nf-xlsx-open>Nota &gt; XLSX</button>
  </div>

  <div class="lx-modal lisa-nf-xlsx-modal" data-nf-xlsx-modal hidden>
    <div class="lx-card lisa-nf-xlsx-card">
      <button class="lx-close" data-nf-xlsx-close type="button">×</button>
      <div class="lisa-nf-xlsx-heading">
        <div><h3>Nota &gt; XLSX</h3><span>Extrai somente a tabela de produtos / serviços da DANFE</span></div>
        <div class="popup-report-actions"><button type="button" class="popup-download-btn" data-nf-xlsx-copy disabled>Copiar</button><button type="button" class="popup-download-btn" data-nf-xlsx-download disabled>Baixar</button></div>
      </div>
      <label class="lisa-nf-xlsx-file">Selecionar nota fiscal em PDF<input type="file" data-nf-xlsx-file accept="application/pdf,.pdf" hidden></label>
      <div class="lisa-nf-xlsx-file-name" data-nf-xlsx-file-name>Nenhum arquivo selecionado</div>
      <div class="lisa-nf-xlsx-status" data-nf-xlsx-status>Selecione uma nota fiscal para começar.</div>
      <div class="lisa-nf-xlsx-preview-wrap" data-nf-xlsx-preview-wrap hidden>
        <table class="lisa-nf-xlsx-preview"><thead data-nf-xlsx-head></thead><tbody data-nf-xlsx-body></tbody></table>
      </div>
    </div>
  </div>

  <div class="lx-modal lisa-pdf-editor-modal" data-pdf-editor-modal hidden>
    <div class="lisa-pdf-editor-shell">
      <div class="lisa-pdf-editor-topbar">
        <div class="lisa-pdf-editor-title"><b>Editor PDF</b><span data-pdf-file-name>Nenhum arquivo selecionado</span></div>
        <div class="lisa-pdf-editor-top-actions">
          <label class="ghost lisa-pdf-open-file" data-pdf-open-top>Abrir PDF<input type="file" data-pdf-file accept="application/pdf,.pdf" hidden></label>
          <button type="button" class="ghost" data-pdf-change hidden>Trocar PDF</button>
          <button type="button" class="primary" data-pdf-save disabled>Salvar PDF</button>
          <button type="button" class="lx-close lisa-pdf-close" data-pdf-editor-close aria-label="Fechar">×</button>
        </div>
      </div>
      <div class="lisa-pdf-editor-body">
        <aside class="lisa-pdf-toolbar">
          <div class="lisa-pdf-tool-group">
            <span class="lisa-pdf-tool-label">Ferramentas</span>
            <button type="button" class="lisa-pdf-tool is-active" data-pdf-tool="text">T Texto</button>
            <button type="button" class="lisa-pdf-tool" data-pdf-tool="highlight">▰ Marcador</button>
            <button type="button" class="lisa-pdf-tool" data-pdf-tool="erase">□ Apagar</button>
          </div>
          <div class="lisa-pdf-tool-group" data-pdf-marker-settings hidden>
            <span class="lisa-pdf-tool-label">Marcador</span>
            <label>Cor<input type="color" data-pdf-marker-color value="#fff176"></label>
            <label>Opacidade<input type="range" data-pdf-marker-opacity min="10" max="80" value="35"><span data-pdf-marker-opacity-label>35%</span></label>
          </div>
          <div class="lisa-pdf-tool-group">
            <button type="button" class="ghost lisa-pdf-undo" data-pdf-undo disabled>↶ Desfazer</button>
          </div>
        </aside>
        <section class="lisa-pdf-workspace">
          <div class="lisa-pdf-empty" data-pdf-empty>
            <b>Selecione um arquivo PDF</b>
            <span>O arquivo será aberto somente neste navegador.</span>
            <label class="primary lisa-pdf-empty-open">Escolher PDF<input type="file" data-pdf-file-empty accept="application/pdf,.pdf" hidden></label>
          </div>
          <div class="lisa-pdf-stage-wrap" data-pdf-stage-wrap hidden>
            <div class="lisa-pdf-page-nav">
              <button type="button" class="ghost" data-pdf-prev>‹</button>
              <span>Página <b data-pdf-page-current>1</b> de <b data-pdf-page-total>1</b></span>
              <button type="button" class="ghost" data-pdf-next>›</button>
              <select data-pdf-zoom aria-label="Zoom"><option value="1">100%</option><option value="1.25" selected>125%</option><option value="1.5">150%</option><option value="1.75">175%</option><option value="2">200%</option></select>
            </div>
            <div class="lisa-pdf-scroll">
              <div class="lisa-pdf-stage" data-pdf-stage>
                <canvas data-pdf-canvas></canvas>
                <div class="lisa-pdf-overlay" data-pdf-overlay></div>
              </div>
            </div>
          </div>
          <div class="lisa-pdf-status" data-pdf-status></div>
        </section>
      </div>
    </div>
  </div>
</div>
`;const extra=holder.firstElementChild;document.body.appendChild(extra);
})();

;


(() => {
  'use strict';
  const panelRoot=document.getElementById('lisa-app-vg-native');
  const root=document.getElementById('lisaextra-vg-native');
  if(!panelRoot||!root)return;
  const esc=s=>String(s??'').replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
  async function ensureXlsxLibrary(){
    if(window.XLSX)return window.XLSX;
    await new Promise((resolve,reject)=>{
      const existing=document.querySelector('script[data-lisa-filetools-xlsx]');
      if(existing){
        if(window.XLSX)return resolve();
        existing.addEventListener('load',resolve,{once:true});
        existing.addEventListener('error',()=>reject(new Error('Não foi possível carregar o leitor de Excel.')),{once:true});
        return;
      }
      const s=document.createElement('script');
      s.src='https://cdn.jsdelivr.net/npm/xlsx@0.18.5/dist/xlsx.full.min.js';
      s.async=true;s.dataset.lisaFiletoolsXlsx='1';
      s.onload=resolve;s.onerror=()=>reject(new Error('Não foi possível carregar o leitor de Excel.'));
      document.head.appendChild(s);
    });
    if(!window.XLSX)throw new Error('Leitor de Excel indisponível.');
    return window.XLSX;
  }

const fileTools=root.querySelector('[data-file-tools]');
const fileMenu=root.querySelector('[data-file-menu]');
const fileMenuBtn=panelRoot.querySelector('[data-file-menu-open]');
function closeFileMenu(){if(fileMenu)fileMenu.hidden=true}
if(fileMenuBtn)fileMenuBtn.addEventListener('click',e=>{e.preventDefault();e.stopPropagation();if(!fileMenu)return;const willOpen=fileMenu.hidden;if(willOpen){const correiosMenu=document.querySelector('#lisacorreios-vg-native [data-lc-submenu]');if(correiosMenu)correiosMenu.hidden=true;const r=fileMenuBtn.getBoundingClientRect();fileMenu.style.left=Math.max(8,Math.min(window.innerWidth-170,r.left))+'px';fileMenu.style.top=(r.bottom+7)+'px';}fileMenu.hidden=!willOpen});
if(fileMenu)fileMenu.addEventListener('click',e=>e.stopPropagation());
document.addEventListener('click',closeFileMenu);

const NF_XLSX_HEADERS=['CÓDIGO PRODUTO','DESCRIÇÃO DO PRODUTO / SERVIÇO','NCM/SH','O/CST','CFOP','UN','QUANT','VALOR UNIT','VALOR TOTAL','VALOR DESC','B.CALC ICMS','VALOR ICMS','VALOR IPI','ALIQ. ICMS','ALIQ. IPI'];
let nfXlsxState={fileName:'nota_fiscal.pdf',rows:[]};
function nfEl(s){return root.querySelector(s)}
function nfNorm(v){return String(v??'').replace(/\s+/g,' ').trim()}
function nfStatus(msg,type=''){const el=nfEl('[data-nf-xlsx-status]');if(!el)return;el.textContent=msg;el.classList.toggle('is-error',type==='error');el.classList.toggle('is-ok',type==='ok')}
function nfHeaderToken(v){return nfNorm(v).toUpperCase().normalize('NFD').replace(/[\u0300-\u036f]/g,'')}
function nfIsSeparator(v){const s=nfNorm(v);return !s||/^[-_.·•–—\s]+$/.test(s)}
function nfIsStopLine(v){return /^(CALCULO DO ISSQN|DADOS ADICIONAIS|INFORMACOES COMPLEMENTARES|RESERVADO AO FISCO)/.test(nfHeaderToken(v))}
function nfIsFooterLine(v){return /IMPRESSO EM|ARQUIVEI|POWERED BY|NFEPHP|WWW\.?ARQUIVEI/i.test(nfNorm(v))}
function nfIsProductMetaLine(v){const s=nfHeaderToken(v);return /^(NOSSO PEDIDO|PEDIDO CLIENTE|ES \d|MATERIAL CLIENTE|0[.,]00 ALIQUOTA|ALIQUOTA FCP|\(PEDIDO )/.test(s)||/FCP-ST|MATERIAL CLIENTE|PEDIDO ABASTECE/.test(s)}
function nfGroupLines(items,tol=3.2){
  const sorted=(items||[]).filter(it=>nfNorm(it.str)).map(it=>({str:nfNorm(it.str),x:Number(it.transform?.[4]||0),y:Number(it.transform?.[5]||0),w:Number(it.width||0)})).sort((a,b)=>b.y-a.y||a.x-b.x);
  const lines=[];
  for(const it of sorted){let line=lines.find(l=>Math.abs(l.y-it.y)<=tol);if(!line){line={y:it.y,items:[]};lines.push(line)}line.items.push(it)}
  for(const l of lines)l.items.sort((a,b)=>a.x-b.x);
  lines.sort((a,b)=>b.y-a.y);
  return lines;
}
function nfLineText(line){return line.items.map(x=>x.str).join(' ')}
function nfFindHeaderBand(lines){
  let best=null;
  for(let i=0;i<lines.length;i++){
    const y=lines[i].y;const near=lines.filter(l=>Math.abs(l.y-y)<=30);const joined=nfHeaderToken(near.map(nfLineText).join(' '));
    const keys=['CODIGO','DESCRICAO','NCM','CST','CFOP','QUANT','VALOR','ICMS','IPI'];
    const score=keys.reduce((s,k)=>s+(joined.includes(k)?1:0),0);
    if(score>=6&&(!best||score>best.score))best={score,topY:Math.max(...near.map(l=>l.y))+3,bottomY:Math.min(...near.map(l=>l.y))-5};
  }
  return best;
}

function nfHeaderAnchors(headerItems,pageWidth){
  const findExact=(re)=>{const a=(headerItems||[]).filter(it=>re.test(nfHeaderToken(it.str))).sort((a,b)=>a.x-b.x);return a.length?a[0].x:null};
  const valores=(headerItems||[]).filter(it=>/^VALOR$/.test(nfHeaderToken(it.str))).sort((a,b)=>a.x-b.x).map(it=>it.x);
  const aliquotas=(headerItems||[]).filter(it=>/^ALIQ/.test(nfHeaderToken(it.str))).sort((a,b)=>a.x-b.x).map(it=>it.x);
  const anchors=[
    findExact(/^CODIGO$/),
    findExact(/^DESCRICAO$/),
    findExact(/^NCM\/SH$/),
    findExact(/^O\/CST$/),
    findExact(/^CFOP$/),
    findExact(/^UN$/),
    findExact(/^QUANT$/),
    valores[0]??null,
    valores[1]??null,
    valores[2]??null,
    findExact(/^B\.CALC$|^B\.CALCULO$|^B\.CALC$/),
    valores[3]??null,
    valores[4]??null,
    aliquotas[0]??null,
    aliquotas[1]??null
  ];
  // Fallbacks proporcionais só são usados se algum cabeçalho específico não for detectado.
  const fallback=[.010,.150,.385,.444,.488,.527,.555,.608,.666,.718,.770,.827,.879,.925,.962].map(v=>pageWidth*v);
  for(let i=0;i<anchors.length;i++)if(!Number.isFinite(anchors[i]))anchors[i]=fallback[i];
  // Garante progressão crescente mesmo em PDFs cujo texto do cabeçalho venha fragmentado.
  for(let i=1;i<anchors.length;i++)if(anchors[i]<=anchors[i-1])anchors[i]=fallback[i];
  return anchors;
}
function nfColByAnchors(x,anchors){
  if(!anchors?.length)return 0;
  for(let i=0;i<anchors.length-1;i++){
    const boundary=(anchors[i]+anchors[i+1])/2;
    if(x<boundary)return i;
  }
  return anchors.length-1;
}
function nfLooksLikeProductCode(v){const s=nfNorm(v);return /^[A-Z0-9][A-Z0-9._\/-]{2,}$/i.test(s)&&!/^(TOTAL|TOTAIS|DADOS|VALOR|BASE)$/i.test(s)}
function nfNormalizeProductColumns(vals){
  const row=Array.isArray(vals)?[...vals]:Array(NF_XLSX_HEADERS.length).fill('');
  // Descrições longas podem avançar visualmente sobre a coluna NCM/SH.
  // O NCM/SH da DANFE é identificado pelo bloco numérico de 8 dígitos;
  // qualquer texto antes dele continua pertencendo à descrição.
  const ncmCell=nfNorm(row[2]);
  const ncmMatch=ncmCell.match(/^(.*?)(\d{8})(?:\s*)$/);
  if(ncmMatch){
    const descOverflow=nfNorm(ncmMatch[1]);
    if(descOverflow)row[1]=nfNorm((row[1]||'')+' '+descOverflow);
    row[2]=ncmMatch[2];
  }else{
    // Fallback: alguns geradores podem posicionar o NCM no fim da própria descrição.
    const desc=nfNorm(row[1]);
    const dm=desc.match(/^(.*?)(?:\s+)(\d{8})$/);
    if(dm){row[1]=nfNorm(dm[1]);row[2]=dm[2];}
  }

  // Alguns DANFEs comprimem campos vizinhos na mesma posição textual do PDF
  // (por exemplo: UN + QUANT, VALOR UNIT + VALOR TOTAL ou ICMS + IPI).
  // Quando a sequência inteira da parte numérica é reconhecida de forma inequívoca,
  // reconstruímos somente as colunas 3 a 15, preservando código e descrição.
  const tail=row.slice(2).map(nfNorm).filter(Boolean).join(' ');
  const num4='([0-9.]+,[0-9]{4})';
  const num2='([0-9.]+,[0-9]{2})';
  const compactRe=new RegExp(
    '^\\s*(\\d{8})\\s+(\\d{2,4})\\s+(\\d{4})\\s+([A-ZÀ-Ü]{1,5})\\s*'+
    num4+'\\s*'+num4+'\\s*'+num2+'\\s*'+num2+'\\s*'+num2+'\\s*'+num2+'\\s*'+num2+'\\s*'+num2+'\\s*'+num2+'\\s*$','i'
  );
  const m=tail.match(compactRe);
  if(m){
    for(let i=0;i<13;i++)row[i+2]=m[i+1];
  }
  return row;
}
function nfExtractPageRows(items,pageWidth){
  const lines=nfGroupLines(items);const band=nfFindHeaderBand(lines);if(!band)return [];
  const headerLines=lines.filter(l=>l.y<=band.topY&&l.y>=band.bottomY);
  const headerItems=headerLines.flatMap(l=>l.items||[]);
  const anchors=nfHeaderAnchors(headerItems,pageWidth);
  const bodyLines=lines.filter(l=>l.y<band.bottomY).sort((a,b)=>b.y-a.y);
  const rows=[];let cur=null;
  for(const line of bodyLines){
    const lineText=nfLineText(line);
    if(nfIsStopLine(lineText)||nfIsFooterLine(lineText))break;
    if(/^TOTAIS\b/i.test(nfNorm(lineText))||/TOTAIS\s+SIT\s+TRIB/i.test(nfNorm(lineText)))continue;
    if(nfIsProductMetaLine(lineText))continue;
    const vals=Array(NF_XLSX_HEADERS.length).fill('');
    for(const it of line.items){
      if(nfIsSeparator(it.str))continue;
      const c=nfColByAnchors(it.x,anchors);
      vals[c]=vals[c]?vals[c]+' '+it.str:it.str;
    }
    if(!vals.some(Boolean))continue;
    const fixed=nfNormalizeProductColumns(vals);
    const code=nfNorm(fixed[0]);
    const hasProductData=/^\d{8}$/.test(nfNorm(fixed[2]))&&/^\d{3,4}$/.test(nfNorm(fixed[3]))&&/^\d{4}$/.test(nfNorm(fixed[4]))&&!!nfNorm(fixed[5])&&!!nfNorm(fixed[6]);
    const starts=!!code&&!!nfNorm(fixed[1])&&(hasProductData||nfLooksLikeProductCode(code));
    if(starts){
      if(cur&&cur.some(Boolean))rows.push(cur);
      cur=fixed;
      continue;
    }
    if(cur){
      // Linhas quebradas da descrição (ex.: "2PC") entram somente na descrição.
      if(fixed[1]&&!fixed.slice(2).some(Boolean))cur[1]=cur[1]?cur[1]+' '+fixed[1]:fixed[1];
      else for(let c=1;c<fixed.length;c++)if(fixed[c])cur[c]=cur[c]?cur[c]+' '+fixed[c]:fixed[c];
    }
  }
  if(cur&&cur.some(Boolean))rows.push(cur);
  return rows.filter(r=>r[0]&&r[1]&&r[2]&&!nfIsFooterLine((r||[]).join(' ')));
}
async function nfPdfToRows(file){
  await ensurePdfLibraries();const bytes=new Uint8Array(await file.arrayBuffer());const task=pdfEditor.pdfjs.getDocument({data:bytes});const doc=await task.promise;const rows=[];let count=0;
  for(let p=1;p<=doc.numPages;p++){const page=await doc.getPage(p),viewport=page.getViewport({scale:1}),content=await page.getTextContent();count+=content.items.length;rows.push(...nfExtractPageRows(content.items,viewport.width))}
  if(!count)throw new Error('Este PDF não possui texto pesquisável.');
  if(!rows.length)throw new Error('Não consegui identificar a tabela de produtos desta DANFE.');
  const cleanRows=rows.filter(r=>!/IMPRESSO EM|ARQUIVEI|POWERED BY|NFEPHP|WWW\.?ARQUIVEI/i.test((r||[]).join(' ')));
  if(!cleanRows.length)throw new Error('Não consegui identificar itens válidos na tabela de produtos desta DANFE.');
  return cleanRows;
}
function renderNfPreview(){const head=nfEl('[data-nf-xlsx-head]'),body=nfEl('[data-nf-xlsx-body]'),wrap=nfEl('[data-nf-xlsx-preview-wrap]');if(head)head.innerHTML='<tr>'+NF_XLSX_HEADERS.map(h=>`<th>${esc(h)}</th>`).join('')+'</tr>';if(body)body.innerHTML=nfXlsxState.rows.map(r=>'<tr>'+NF_XLSX_HEADERS.map((_,i)=>`<td>${esc(String(r?.[i]??''))}</td>`).join('')+'</tr>').join('');if(wrap)wrap.hidden=!nfXlsxState.rows.length}
async function downloadNfXlsx(){if(!nfXlsxState.rows.length)return;const btn=nfEl('[data-nf-xlsx-download]'),old=btn?.textContent;if(btn){btn.disabled=true;btn.textContent='Gerando...'}try{const XLSX=await ensureXlsxLibrary();const aoa=[NF_XLSX_HEADERS,...nfXlsxState.rows.map(r=>NF_XLSX_HEADERS.map((_,i)=>String(r?.[i]??'')))];const ws=XLSX.utils.aoa_to_sheet(aoa);const range=XLSX.utils.decode_range(ws['!ref']);for(let R=range.s.r;R<=range.e.r;R++)for(let C=range.s.c;C<=range.e.c;C++){const addr=XLSX.utils.encode_cell({r:R,c:C}),cell=ws[addr];if(cell){cell.t='s';cell.v=String(cell.v??'');cell.z='@'}}ws['!cols']=[{wch:18},{wch:55},{wch:14},{wch:10},{wch:9},{wch:8},{wch:12},{wch:14},{wch:14},{wch:13},{wch:14},{wch:13},{wch:13},{wch:11},{wch:11}];const wb=XLSX.utils.book_new();XLSX.utils.book_append_sheet(wb,ws,'Produtos NF');const base=(nfXlsxState.fileName||'nota_fiscal.pdf').replace(/\.pdf$/i,'').replace(/[^a-zA-Z0-9_-]+/g,'_');XLSX.writeFile(wb,base+'_produtos.xlsx',{compression:true})}catch(e){alert(e.message||'Não foi possível gerar o XLSX.')}finally{if(btn){btn.disabled=false;btn.textContent=old||'Baixar'}}}
async function copyNfXlsx(){
  if(!nfXlsxState.rows.length)return;
  const lines=[NF_XLSX_HEADERS.join('\t'),...nfXlsxState.rows.map(r=>NF_XLSX_HEADERS.map((_,i)=>String(r?.[i]??'')).join('\t'))];
  const value=lines.join('\n');
  const btn=nfEl('[data-nf-xlsx-copy]');
  try{await navigator.clipboard.writeText(value)}catch(e){const ta=document.createElement('textarea');ta.value=value;ta.style.position='fixed';ta.style.left='-9999px';document.body.appendChild(ta);ta.select();document.execCommand('copy');ta.remove()}
  if(btn){const old=btn.textContent;btn.textContent='Copiado!';btn.disabled=true;setTimeout(()=>{btn.textContent=old;btn.disabled=false},1200)}
}
function openNfXlsx(){closeFileMenu();const m=nfEl('[data-nf-xlsx-modal]');if(m)m.hidden=false}
function resetNfXlsx(){nfXlsxState={fileName:'nota_fiscal.pdf',rows:[]};const name=nfEl('[data-nf-xlsx-file-name]'),status=nfEl('[data-nf-xlsx-status]'),wrap=nfEl('[data-nf-xlsx-preview-wrap]'),head=nfEl('[data-nf-xlsx-head]'),body=nfEl('[data-nf-xlsx-body]'),download=nfEl('[data-nf-xlsx-download]'),copy=nfEl('[data-nf-xlsx-copy]'),input=nfEl('[data-nf-xlsx-file]');if(name)name.textContent='Nenhum arquivo selecionado';if(status){status.textContent='Selecione uma nota fiscal para começar.';status.classList.remove('is-error','is-ok')}if(wrap)wrap.hidden=true;if(head)head.innerHTML='';if(body)body.innerHTML='';if(download)download.disabled=true;if(copy)copy.disabled=true;if(input)input.value=''}
function closeNfXlsx(){const m=nfEl('[data-nf-xlsx-modal]');if(m)m.hidden=true;resetNfXlsx()}
const nfModal=nfEl('[data-nf-xlsx-modal]');
nfEl('[data-nf-xlsx-open]')?.addEventListener('click',openNfXlsx);
nfEl('[data-nf-xlsx-close]')?.addEventListener('click',closeNfXlsx);
if(nfModal)nfModal.addEventListener('click',e=>{if(e.target===nfModal)closeNfXlsx()});
nfEl('[data-nf-xlsx-copy]')?.addEventListener('click',copyNfXlsx);
nfEl('[data-nf-xlsx-download]')?.addEventListener('click',downloadNfXlsx);
nfEl('[data-nf-xlsx-file]')?.addEventListener('change',async e=>{const file=e.target.files?.[0];if(!file)return;nfXlsxState={fileName:file.name,rows:[]};const name=nfEl('[data-nf-xlsx-file-name]'),btn=nfEl('[data-nf-xlsx-download]'),copy=nfEl('[data-nf-xlsx-copy]'),wrap=nfEl('[data-nf-xlsx-preview-wrap]');if(name)name.textContent=file.name;if(btn)btn.disabled=true;if(copy)copy.disabled=true;if(wrap)wrap.hidden=true;nfStatus('Lendo tabela da nota fiscal...');try{nfXlsxState.rows=await nfPdfToRows(file);renderNfPreview();if(btn)btn.disabled=false;if(copy)copy.disabled=false;nfStatus(`${nfXlsxState.rows.length} ${nfXlsxState.rows.length===1?'item encontrado':'itens encontrados'}. Todas as células serão exportadas como texto.`,'ok')}catch(err){console.error(err);nfStatus(err.message||'Não foi possível ler a nota fiscal.','error')}finally{e.target.value=''}});



const pdfEditor={
  pdfjs:null,pdfLib:null,srcBytes:null,pdfDoc:null,fileName:'',page:1,total:0,zoom:1.25,tool:'text',edits:[],drag:null,renderToken:0,textPoint:null
};
function pdfEl(s){return root.querySelector(s)}
function pdfStatus(msg){const el=pdfEl('[data-pdf-status]');if(!el)return;el.textContent=msg;el.classList.add('is-visible');clearTimeout(pdfStatus._t);pdfStatus._t=setTimeout(()=>el.classList.remove('is-visible'),1800)}
function loadScriptOnce(src,key){
  if(window[key])return Promise.resolve(window[key]);
  return new Promise((resolve,reject)=>{const existing=document.querySelector(`script[data-lisa-lib="${key}"]`);if(existing){existing.addEventListener('load',()=>resolve(window[key]),{once:true});existing.addEventListener('error',reject,{once:true});return}const s=document.createElement('script');s.src=src;s.async=true;s.dataset.lisaLib=key;s.onload=()=>resolve(window[key]);s.onerror=()=>reject(new Error('Não foi possível carregar '+key));document.head.appendChild(s)});
}
async function ensurePdfLibraries(){
  if(!pdfEditor.pdfjs){pdfEditor.pdfjs=await loadScriptOnce('https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js','pdfjsLib');pdfEditor.pdfjs.GlobalWorkerOptions.workerSrc='https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js'}
  if(!pdfEditor.pdfLib)pdfEditor.pdfLib=await loadScriptOnce('https://cdn.jsdelivr.net/npm/pdf-lib@1.17.1/dist/pdf-lib.min.js','PDFLib');
}
function pdfHexRgb(hex){const h=String(hex||'#000000').replace('#','');const n=parseInt(h.length===3?h.split('').map(x=>x+x).join(''):h,16);return {r:((n>>16)&255)/255,g:((n>>8)&255)/255,b:(n&255)/255}}

function pdfSetTool(tool){pdfEditor.tool=tool;for(const b of root.querySelectorAll('[data-pdf-tool]'))b.classList.toggle('is-active',b.dataset.pdfTool===tool);const ms=pdfEl('[data-pdf-marker-settings]');if(ms)ms.hidden=tool!=='highlight';if(tool!=='text')pdfHideTextPopover()}
function pdfUpdateUndo(){const b=pdfEl('[data-pdf-undo]');if(b)b.disabled=!pdfEditor.edits.length}
function pdfFontCss(font,bold){if(font==='times')return bold?'Georgia,serif':'Times New Roman,serif';if(font==='courier')return bold?'Courier New,monospace':'Courier New,monospace';return 'Arial,sans-serif'}
function pdfRenderOverlays(){
  const ov=pdfEl('[data-pdf-overlay]'),canvas=pdfEl('[data-pdf-canvas]');if(!ov||!canvas)return;
  ov.querySelectorAll('.lisa-pdf-text-item,.lisa-pdf-edit-text,.lisa-pdf-edit-rect').forEach(n=>n.remove());
  const w=canvas.width,h=canvas.height;
  pdfEditor.edits.forEach((e,index)=>{
    if(e.page!==pdfEditor.page)return;
    if(e.type==='text'){
      const item=document.createElement('div');item.className='lisa-pdf-text-item';item.style.left=(e.x*w)+'px';item.style.top=(e.y*h)+'px';item.dataset.pdfTextItem=String(index);
      const d=document.createElement('div');d.className='lisa-pdf-edit-text';d.contentEditable='true';d.spellcheck=false;d.dataset.pdfTextIndex=String(index);d.textContent=e.text||'';d.style.fontFamily=pdfFontCss(e.font,e.bold);d.style.fontWeight=e.bold?'700':'400';d.style.fontSize=(e.size*pdfEditor.zoom)+'px';d.style.color=e.color||'#000000';
      d.addEventListener('input',()=>{const cur=pdfEditor.edits[index];if(cur)cur.text=d.innerText.replace(/\u00a0/g,' ')});
      d.addEventListener('keydown',ev=>{if(ev.key==='Escape'){ev.preventDefault();d.blur()}ev.stopPropagation()});
      d.addEventListener('pointerdown',ev=>ev.stopPropagation());
      const bar=document.createElement('div');bar.className='lisa-pdf-inline-toolbar';bar.innerHTML=`<label>Tamanho <input type="number" min="6" max="72" value="${Number(e.size)||12}" data-inline-size></label><label>Cor <input type="color" value="${e.color||'#000000'}" data-inline-color></label><button type="button" class="lisa-pdf-inline-bold${e.bold?' is-active':''}" data-inline-bold><b>B</b></button><button type="button" class="lisa-pdf-inline-delete" data-inline-delete title="Excluir texto">×</button>`;
      bar.addEventListener('pointerdown',ev=>ev.stopPropagation());

      d.addEventListener('pointerdown',ev=>{
        if(ev.button!==0)return;
        ev.stopPropagation();
        const cur=pdfEditor.edits[index];if(!cur)return;
        const rect=ov.getBoundingClientRect();
        const startX=ev.clientX,startY=ev.clientY,startNormX=cur.x,startNormY=cur.y;
        let moving=false;

        const move=mv=>{
          const screenDx=mv.clientX-startX,screenDy=mv.clientY-startY;
          if(!moving&&Math.hypot(screenDx,screenDy)<4)return;
          if(!moving){
            moving=true;
            d.blur();
            item.classList.add('is-moving');
            d.contentEditable='false';
            document.body.style.userSelect='none';
          }
          mv.preventDefault();
          const dx=screenDx*(ov.offsetWidth/rect.width),dy=screenDy*(ov.offsetHeight/rect.height);
          const maxX=Math.max(0,1-(item.offsetWidth/Math.max(1,w)));
          const maxY=Math.max(0,1-(item.offsetHeight/Math.max(1,h)));
          cur.x=Math.max(0,Math.min(maxX,startNormX+dx/Math.max(1,w)));
          cur.y=Math.max(0,Math.min(maxY,startNormY+dy/Math.max(1,h)));
          item.style.left=(cur.x*w)+'px';
          item.style.top=(cur.y*h)+'px';
        };

        const up=()=>{
          window.removeEventListener('pointermove',move);
          window.removeEventListener('pointerup',up);
          document.body.style.userSelect='';
          d.contentEditable='true';
          item.classList.remove('is-moving');
          if(moving){
            pdfStatus('Texto reposicionado');
          }
        };

        window.addEventListener('pointermove',move,{passive:false});
        window.addEventListener('pointerup',up,{once:true});
      });
      bar.querySelector('[data-inline-size]')?.addEventListener('change',ev=>{const n=Math.max(6,Math.min(72,Number(ev.target.value)||12));const cur=pdfEditor.edits[index];if(cur){cur.size=n;d.style.fontSize=(n*pdfEditor.zoom)+'px'}});
      bar.querySelector('[data-inline-color]')?.addEventListener('input',ev=>{const cur=pdfEditor.edits[index];if(cur){cur.color=ev.target.value;d.style.color=ev.target.value}});
      bar.querySelector('[data-inline-bold]')?.addEventListener('click',ev=>{const cur=pdfEditor.edits[index];if(!cur)return;cur.bold=!cur.bold;ev.currentTarget.classList.toggle('is-active',cur.bold);d.style.fontWeight=cur.bold?'700':'400';d.style.fontFamily=pdfFontCss(cur.font,cur.bold)});
      bar.querySelector('[data-inline-delete]')?.addEventListener('click',()=>pdfDeleteInlineText(index));
      item.appendChild(d);item.appendChild(bar);ov.appendChild(item);
    }else{
      const d=document.createElement('div');d.className='lisa-pdf-edit-rect';d.style.left=(e.x*w)+'px';d.style.top=(e.y*h)+'px';d.style.width=(e.w*w)+'px';d.style.height=(e.h*h)+'px';d.style.background=e.type==='erase'?'#ffffff':e.color;d.style.opacity=e.type==='erase'?'1':String(e.opacity);ov.appendChild(d);
    }
  });
  pdfUpdateUndo();
}
async function pdfRenderPage(){
  if(!pdfEditor.pdfDoc)return;const token=++pdfEditor.renderToken;const page=await pdfEditor.pdfDoc.getPage(pdfEditor.page);if(token!==pdfEditor.renderToken)return;const viewport=page.getViewport({scale:pdfEditor.zoom});const canvas=pdfEl('[data-pdf-canvas]'),ctx=canvas.getContext('2d');canvas.width=Math.ceil(viewport.width);canvas.height=Math.ceil(viewport.height);canvas.style.width=canvas.width+'px';canvas.style.height=canvas.height+'px';const stage=pdfEl('[data-pdf-stage]');stage.style.width=canvas.width+'px';stage.style.height=canvas.height+'px';await page.render({canvasContext:ctx,viewport}).promise;const ov=pdfEl('[data-pdf-overlay]');ov.style.width=canvas.width+'px';ov.style.height=canvas.height+'px';pdfEl('[data-pdf-page-current]').textContent=pdfEditor.page;pdfEl('[data-pdf-page-total]').textContent=pdfEditor.total;pdfEl('[data-pdf-prev]').disabled=pdfEditor.page<=1;pdfEl('[data-pdf-next]').disabled=pdfEditor.page>=pdfEditor.total;pdfRenderOverlays();
}
async function pdfOpenFile(file){
  if(!file)return;if(!/\.pdf$/i.test(file.name)&&file.type!=='application/pdf'){alert('Selecione um arquivo PDF.');return}
  try{pdfStatus('Abrindo PDF...');await ensurePdfLibraries();const bytes=new Uint8Array(await file.arrayBuffer());const task=pdfEditor.pdfjs.getDocument({data:bytes.slice()});const doc=await task.promise;pdfEditor.srcBytes=bytes;pdfEditor.pdfDoc=doc;pdfEditor.fileName=file.name;pdfEditor.page=1;pdfEditor.total=doc.numPages;pdfEditor.edits=[];pdfEditor.textPoint=null;pdfHideTextPopover();pdfEl('[data-pdf-file-name]').textContent=file.name;pdfEl('[data-pdf-empty]').hidden=true;pdfEl('[data-pdf-stage-wrap]').hidden=false;pdfEl('[data-pdf-save]').disabled=false;const change=pdfEl('[data-pdf-change]'),openTop=pdfEl('[data-pdf-open-top]');if(change)change.hidden=false;if(openTop)openTop.hidden=true;await pdfRenderPage();pdfSetTool('text');pdfStatus('PDF aberto')}
  catch(e){console.error(e);alert('Não foi possível abrir o PDF: '+(e.message||e))}
}
function pdfPoint(e){const ov=pdfEl('[data-pdf-overlay]'),r=ov.getBoundingClientRect();return {px:(e.clientX-r.left)*(ov.offsetWidth/r.width),py:(e.clientY-r.top)*(ov.offsetHeight/r.height),w:ov.offsetWidth,h:ov.offsetHeight}}
function pdfHideTextPopover(){pdfEditor.textPoint=null}
function pdfFocusTextElement(el){
  if(!el)return;el.focus();
  const range=document.createRange();range.selectNodeContents(el);range.collapse(false);
  const sel=window.getSelection();sel.removeAllRanges();sel.addRange(range);
}
function pdfAddInlineText(e){
  if(!pdfEditor.pdfDoc)return;
  const p=pdfPoint(e);
  const edit={type:'text',page:pdfEditor.page,x:p.px/p.w,y:p.py/p.h,text:'',size:12,color:'#000000',font:'helvetica',bold:false};
  pdfEditor.edits.push(edit);pdfRenderOverlays();
  const idx=pdfEditor.edits.length-1;
  const el=pdfEl(`[data-pdf-text-index="${idx}"]`);
  pdfFocusTextElement(el);pdfStatus('Digite diretamente no PDF');
}

function pdfDeleteInlineText(index){
  if(index<0||index>=pdfEditor.edits.length)return;
  pdfEditor.edits.splice(index,1);pdfRenderOverlays();pdfStatus('Texto removido');
}
function pdfDragStart(e){if(pdfEditor.tool==='text'){if(e.target.closest('.lisa-pdf-text-item'))return;e.preventDefault();e.stopPropagation();pdfAddInlineText(e);return}const p=pdfPoint(e);pdfEditor.drag={x:p.px,y:p.py,w:p.w,h:p.h};const ov=pdfEl('[data-pdf-overlay]'),d=document.createElement('div');d.className='lisa-pdf-draw-preview';d.dataset.pdfPreview='1';d.style.left=p.px+'px';d.style.top=p.py+'px';if(pdfEditor.tool==='erase')d.style.background='rgba(255,255,255,.7)';else d.style.background=pdfEl('[data-pdf-marker-color]')?.value||'#fff176';ov.appendChild(d);e.preventDefault()}
function pdfDragMove(e){if(!pdfEditor.drag)return;const p=pdfPoint(e),x=Math.min(pdfEditor.drag.x,p.px),y=Math.min(pdfEditor.drag.y,p.py),w=Math.abs(p.px-pdfEditor.drag.x),h=Math.abs(p.py-pdfEditor.drag.y);const d=pdfEl('[data-pdf-preview]');if(d){d.style.left=x+'px';d.style.top=y+'px';d.style.width=w+'px';d.style.height=h+'px'}}
function pdfDragEnd(e){if(!pdfEditor.drag)return;const p=pdfPoint(e),x=Math.min(pdfEditor.drag.x,p.px),y=Math.min(pdfEditor.drag.y,p.py),w=Math.abs(p.px-pdfEditor.drag.x),h=Math.abs(p.py-pdfEditor.drag.y);pdfEl('[data-pdf-preview]')?.remove();if(w>3&&h>3){const edit={type:pdfEditor.tool,page:pdfEditor.page,x:x/pdfEditor.drag.w,y:y/pdfEditor.drag.h,w:w/pdfEditor.drag.w,h:h/pdfEditor.drag.h};if(pdfEditor.tool==='highlight'){edit.color=pdfEl('[data-pdf-marker-color]')?.value||'#fff176';edit.opacity=(Number(pdfEl('[data-pdf-marker-opacity]')?.value)||35)/100}pdfEditor.edits.push(edit);pdfRenderOverlays()}pdfEditor.drag=null}
async function pdfSave(){
  if(!pdfEditor.srcBytes||!pdfEditor.pdfLib)return;const btn=pdfEl('[data-pdf-save]'),old=btn.textContent;btn.disabled=true;btn.textContent='Salvando...';try{const {PDFDocument,StandardFonts,rgb}=pdfEditor.pdfLib;const doc=await PDFDocument.load(pdfEditor.srcBytes.slice());const fonts={};async function fontFor(e){const k=e.font+'_'+(e.bold?'b':'n');if(fonts[k])return fonts[k];let f=StandardFonts.Helvetica;if(e.font==='times')f=e.bold?StandardFonts.TimesRomanBold:StandardFonts.TimesRoman;else if(e.font==='courier')f=e.bold?StandardFonts.CourierBold:StandardFonts.Courier;else f=e.bold?StandardFonts.HelveticaBold:StandardFonts.Helvetica;return fonts[k]=await doc.embedFont(f)}
    for(const e of pdfEditor.edits){const page=doc.getPage(e.page-1),pw=page.getWidth(),ph=page.getHeight();if(e.type==='text'){const c=pdfHexRgb(e.color),font=await fontFor(e),lines=String(e.text||'').split(/\r?\n/),lh=e.size*1.15;lines.forEach((line,i)=>page.drawText(line,{x:e.x*pw,y:ph-(e.y*ph)-e.size-(i*lh),size:e.size,font,color:rgb(c.r,c.g,c.b)}))}else{const c=e.type==='erase'?{r:1,g:1,b:1}:pdfHexRgb(e.color);page.drawRectangle({x:e.x*pw,y:ph-((e.y+e.h)*ph),width:e.w*pw,height:e.h*ph,color:rgb(c.r,c.g,c.b),opacity:e.type==='erase'?1:e.opacity})}}
    const out=await doc.save();const blob=new Blob([out],{type:'application/pdf'}),a=document.createElement('a');a.href=URL.createObjectURL(blob);const base=(pdfEditor.fileName||'documento.pdf').replace(/\.pdf$/i,'');a.download=base+'_editado.pdf';document.body.appendChild(a);a.click();a.remove();setTimeout(()=>URL.revokeObjectURL(a.href),2000);pdfStatus('PDF salvo')
  }catch(e){console.error(e);alert('Não foi possível salvar o PDF: '+(e.message||e))}finally{btn.disabled=false;btn.textContent=old}}
function pdfResetDocument(){
  pdfHideTextPopover();
  pdfEditor.srcBytes=null;pdfEditor.pdfDoc=null;pdfEditor.fileName='';pdfEditor.page=1;pdfEditor.total=0;pdfEditor.edits=[];pdfEditor.drag=null;
  pdfEl('[data-pdf-preview]')?.remove();
  const empty=pdfEl('[data-pdf-empty]'),stage=pdfEl('[data-pdf-stage-wrap]'),save=pdfEl('[data-pdf-save]'),change=pdfEl('[data-pdf-change]'),openTop=pdfEl('[data-pdf-open-top]');
  if(empty)empty.hidden=false;if(stage)stage.hidden=true;if(save)save.disabled=true;if(change)change.hidden=true;if(openTop)openTop.hidden=false;
  const name=pdfEl('[data-pdf-file-name]');if(name)name.textContent='Nenhum arquivo selecionado';
  const canvas=pdfEl('[data-pdf-canvas]');if(canvas){canvas.width=0;canvas.height=0}
  pdfUpdateUndo();
}
function pdfClose(){const m=pdfEl('[data-pdf-editor-modal]');if(m)m.hidden=true;pdfEditor.drag=null;pdfHideTextPopover();pdfEl('[data-pdf-preview]')?.remove()}
const pdfModal=pdfEl('[data-pdf-editor-modal]');pdfEl('[data-pdf-editor-open]')?.addEventListener('click',()=>{closeFileMenu();pdfSetTool('text');if(pdfModal)pdfModal.hidden=false});pdfEl('[data-pdf-editor-close]')?.addEventListener('click',pdfClose);if(pdfModal)pdfModal.addEventListener('click',e=>{if(e.target===pdfModal)pdfClose()});
pdfEl('[data-pdf-change]')?.addEventListener('click',pdfResetDocument);
for(const input of [pdfEl('[data-pdf-file]'),pdfEl('[data-pdf-file-empty]')])if(input)input.addEventListener('change',e=>{const f=e.target.files?.[0];if(f)pdfOpenFile(f);e.target.value=''});
for(const b of root.querySelectorAll('[data-pdf-tool]'))b.addEventListener('click',()=>pdfSetTool(b.dataset.pdfTool));pdfEl('[data-pdf-prev]')?.addEventListener('click',async()=>{if(pdfEditor.page>1){pdfEditor.page--;await pdfRenderPage()}});pdfEl('[data-pdf-next]')?.addEventListener('click',async()=>{if(pdfEditor.page<pdfEditor.total){pdfEditor.page++;await pdfRenderPage()}});pdfEl('[data-pdf-zoom]')?.addEventListener('change',async e=>{pdfEditor.zoom=Number(e.target.value)||1.25;await pdfRenderPage()});pdfEl('[data-pdf-undo]')?.addEventListener('click',()=>{if(pdfEditor.edits.length){pdfEditor.edits.pop();pdfRenderOverlays();pdfStatus('Última edição desfeita')}});pdfEl('[data-pdf-save]')?.addEventListener('click',pdfSave);pdfEl('[data-pdf-marker-opacity]')?.addEventListener('input',e=>{const l=pdfEl('[data-pdf-marker-opacity-label]');if(l)l.textContent=e.target.value+'%'});
const pdfOverlay=pdfEl('[data-pdf-overlay]');if(pdfOverlay){pdfOverlay.addEventListener('pointerdown',pdfDragStart);pdfOverlay.addEventListener('pointermove',pdfDragMove);pdfOverlay.addEventListener('pointerup',pdfDragEnd);pdfOverlay.addEventListener('pointercancel',pdfDragEnd)}
})();


;

{
 const initLisaCorreiosModule=function(){
  'use strict';
  const panel=document.getElementById('lisa-app-vg-native');if(!panel||document.getElementById('lisacorreios-vg-native'))return;
  const searchBtn=panel.querySelector('[data-open-search]');if(!searchBtn)return;
  let openBtn=panel.querySelector('[data-lc-correios-open]');if(!openBtn){openBtn=document.createElement('button');openBtn.type='button';openBtn.className='ghost lisa-quick-btn';openBtn.setAttribute('data-lc-correios-open','');openBtn.textContent='CORREIOS';searchBtn.insertAdjacentElement('beforebegin',openBtn)}
  const wrap=document.createElement('div');wrap.id='lisacorreios-vg-native';wrap.innerHTML=`
   <div class="lc-submenu" data-lc-submenu hidden><button type="button" class="lc-submenu-btn" data-lc-open-track>RASTREIO</button><button type="button" class="lc-submenu-btn" data-lc-open-follow>ACOMPANHAMENTO</button></div>
   <div class="lc-modal" data-lc-modal hidden><div class="lc-card"><button type="button" class="lc-close" data-lc-close aria-label="Fechar">×</button><div class="lc-head"><div><h3>Rastreamento Correios</h3><span>Consulte códigos presentes no último Excel importado do VIPP. A API está desativada.</span></div><div class="lc-actions"><button type="button" class="lc-btn" data-lc-copy disabled>Copiar</button><button type="button" class="lc-btn" data-lc-download disabled>Baixar</button></div></div><div class="lc-query"><textarea data-lc-input placeholder="Ex.: AA123456789BR&#10;BB987654321BR"></textarea><button type="button" class="lc-search" data-lc-search>Consultar</button></div><div class="lc-status" data-lc-status></div><div class="lc-table-wrap"><table class="lc-table"><thead><tr><th>Código</th><th>Previsão</th><th>Status</th><th>Última atualização</th></tr></thead><tbody data-lc-body></tbody></table><div class="lc-empty" data-lc-empty>Nenhum rastreio consultado.</div></div></div></div>
   <div class="lc-modal" data-lc-follow-modal hidden><div class="lc-card lc-follow-card"><button type="button" class="lc-close" data-lc-follow-close aria-label="Fechar">×</button><div class="lc-follow-loading" data-lc-follow-loading><div class="lc-spinner"></div><strong>Carregando acompanhamento...</strong><span>Lendo códigos, notas, pedidos e status do arquivo de Rastreamento.</span></div><div data-lc-follow-content hidden style="display:flex;flex-direction:column;min-height:0;flex:1"><div class="lc-head"><div><h3>Acompanhamento Correios</h3><span data-lc-follow-meta>Objetos em aberto no arquivo de Rastreamento.</span></div></div><div class="lc-status" data-lc-follow-status></div><div class="lc-follow-kpis" data-lc-follow-kpis></div><div class="lc-table-wrap"><table class="lc-table lc-follow-table"><thead><tr>
    <th data-lc-sort="codigo"><button class="lc-sort" type="button">Código <span class="lc-arrows">↑↓</span></button><input class="lc-filter" data-lc-filter="codigo" placeholder="Filtrar"></th>
    <th data-lc-sort="nota"><button class="lc-sort" type="button">Nota fiscal <span class="lc-arrows">↑↓</span></button><input class="lc-filter" data-lc-filter="nota" placeholder="Filtrar"></th>
    <th data-lc-sort="pedido"><button class="lc-sort" type="button">Pedido <span class="lc-arrows">↑↓</span></button><input class="lc-filter" data-lc-filter="pedido" placeholder="Filtrar"></th>
    <th data-lc-sort="status"><button class="lc-sort" type="button">Status de rastreamento <span class="lc-arrows">↑↓</span></button><select class="lc-filter" data-lc-filter="status"><option value="">Todos</option></select></th>
   </tr></thead><tbody data-lc-follow-body></tbody></table><div class="lc-empty" data-lc-follow-empty hidden>Nenhum objeto em aberto encontrado.</div></div><div class="lc-follow-footer"><span data-lc-follow-count>0 objetos</span><span>Objetos com status de entrega concluída não são exibidos.</span></div></div></div></div>`;
  document.body.appendChild(wrap);
  const $=s=>wrap.querySelector(s),$$=s=>[...wrap.querySelectorAll(s)];
  const submenu=$('[data-lc-submenu]'),modal=$('[data-lc-modal]'),input=$('[data-lc-input]'),status=$('[data-lc-status]'),body=$('[data-lc-body]'),empty=$('[data-lc-empty]'),copyBtn=$('[data-lc-copy]'),downloadBtn=$('[data-lc-download]'),search=$('[data-lc-search]');
  const followModal=$('[data-lc-follow-modal]'),followLoading=$('[data-lc-follow-loading]'),followContent=$('[data-lc-follow-content]'),followBody=$('[data-lc-follow-body]'),followEmpty=$('[data-lc-follow-empty]'),followStatus=$('[data-lc-follow-status]');
  let rows=[],followRows=[],followFiltered=[],followSort={key:'codigo',dir:'asc'},followController=null,followSeq=0;
  const esc=s=>String(s??'').replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
  const norm=s=>String(s??'').normalize('NFD').replace(/[\u0300-\u036f]/g,'').toUpperCase().trim();
  function worker(){return String(panel.dataset.worker||'').replace(/\/$/,'')}function panelToken(){return sessionStorage.getItem('lisa_panel_token')||''}
  function setStatus(msg,type=''){status.textContent=msg||'';status.classList.toggle('is-error',type==='error');status.classList.toggle('is-ok',type==='ok')}
  function parseCodes(){return [...new Set(String(input.value||'').toUpperCase().split(/[\s,;]+/).map(x=>x.replace(/[^A-Z0-9]/g,'')).filter(Boolean))].slice(0,30)}
  function render(){body.innerHTML=rows.map(r=>`<tr><td>${esc(r.codigo||'-')}</td><td>${esc(r.previsao||'-')}</td><td class="${r.erro?'lc-row-error':''}">${esc(r.status||r.erro||'-')}</td><td>${esc(r.ultima_atualizacao||'-')}</td></tr>`).join('');empty.hidden=rows.length>0;copyBtn.disabled=!rows.length;downloadBtn.disabled=!rows.length}
  function reset(){rows=[];input.value='';setStatus('');render()}
  function positionSubmenu(){if(!submenu||submenu.hidden)return;const r=openBtn.getBoundingClientRect();submenu.style.left=Math.max(8,Math.min(window.innerWidth-170,r.left))+'px';submenu.style.top=(r.bottom+7)+'px'}
  function openChoice(){if(!submenu)return;const willOpen=submenu.hidden;if(willOpen){const appsMenu=document.querySelector('#lisaextra-vg-native [data-file-menu]');if(appsMenu)appsMenu.hidden=true;}submenu.hidden=!willOpen;if(!submenu.hidden){requestAnimationFrame(positionSubmenu)}}function closeChoice(){if(submenu)submenu.hidden=true}
  function openTrack(){closeChoice();reset();modal.hidden=false;setTimeout(()=>input.focus(),0)}function closeTrack(){modal.hidden=true;reset()}
  function copyText(){return rows.map(r=>[`Código: ${r.codigo||'-'}`,`Previsão: ${r.previsao||'-'}`,`Status: ${r.status||r.erro||'-'}`,`Última atualização: ${r.ultima_atualizacao||'-'}`].join('\n')).join('\n\n')}
  async function doCopy(){if(!rows.length)return;const value=copyText();try{await navigator.clipboard.writeText(value)}catch{const ta=document.createElement('textarea');ta.value=value;ta.style.position='fixed';ta.style.left='-9999px';document.body.appendChild(ta);ta.select();document.execCommand('copy');ta.remove()}const old=copyBtn.textContent;copyBtn.textContent='Copiado!';setTimeout(()=>copyBtn.textContent=old,1200)}
  async function ensureXlsx(){if(window.XLSX)return window.XLSX;await new Promise((resolve,reject)=>{const old=document.querySelector('script[data-lc-xlsx]');if(old){if(window.XLSX)return resolve();old.addEventListener('load',resolve,{once:true});old.addEventListener('error',reject,{once:true});return}const s=document.createElement('script');s.src='https://cdn.jsdelivr.net/npm/xlsx@0.18.5/dist/xlsx.full.min.js';s.async=true;s.dataset.lcXlsx='1';s.onload=resolve;s.onerror=reject;document.head.appendChild(s)});if(!window.XLSX)throw new Error('Leitor de Excel indisponível.');return window.XLSX}
  async function doDownload(){if(!rows.length)return;const old=downloadBtn.textContent;downloadBtn.disabled=true;downloadBtn.textContent='Gerando...';try{const XLSX=await ensureXlsx();const aoa=[['Código','Previsão','Status','Última atualização'],...rows.map(r=>[r.codigo||'',r.previsao||'',r.status||r.erro||'',r.ultima_atualizacao||''])];const ws=XLSX.utils.aoa_to_sheet(aoa);ws['!cols']=[{wch:18},{wch:18},{wch:48},{wch:24}];const wb=XLSX.utils.book_new();XLSX.utils.book_append_sheet(wb,ws,'Correios');XLSX.writeFile(wb,'rastreamento_correios_'+new Date().toLocaleDateString('pt-BR').replace(/\//g,'-')+'.xlsx',{compression:true})}catch(e){alert(e.message||'Não foi possível gerar o Excel.')}finally{downloadBtn.disabled=false;downloadBtn.textContent=old}}
  async function consultar(){const codes=parseCodes();if(!codes.length){setStatus('Informe pelo menos um código de rastreio.','error');return}if(!worker()){setStatus('URL do Worker não configurada.','error');return}search.disabled=true;search.textContent='Consultando...';copyBtn.disabled=true;downloadBtn.disabled=true;setStatus(`Consultando ${codes.length} ${codes.length===1?'objeto':'objetos'}...`);try{const r=await fetch(worker()+'/correios/rastrear',{method:'POST',headers:{'Content-Type':'application/json','Authorization':'Bearer '+panelToken()},body:JSON.stringify({codigos:codes})});const d=await r.json().catch(()=>({}));if(r.status===401)throw new Error(d.error||'Sessão expirada.');if(!r.ok)throw new Error(d.detail||d.error||'Não foi possível consultar os Correios.');rows=Array.isArray(d.resultados)?d.resultados:[];render();setStatus(`${rows.length} ${rows.length===1?'rastreio consultado':'rastreios consultados'}.`,'ok')}catch(e){rows=[];render();setStatus(e.message||'Erro ao consultar os Correios.','error')}finally{search.disabled=false;search.textContent='Consultar'}}
  function showFollowLoading(){followLoading.hidden=false;followContent.hidden=true}function showFollowContent(){followLoading.hidden=true;followContent.hidden=false}
  function followFilters(){const o={};for(const el of $$('.lc-filter'))o[el.dataset.lcFilter]=norm(el.value);return o}
  function fillFollowStatusOptions(){const sel=$('[data-lc-filter="status"]');if(!sel||sel.tagName!=='SELECT')return;const cur=sel.value;const values=[...new Set(followRows.map(r=>String(r?.status||'').trim()).filter(Boolean))].sort((a,b)=>a.localeCompare(b,'pt-BR',{numeric:true,sensitivity:'base'}));sel.innerHTML='<option value="">Todos</option>'+values.map(v=>`<option value="${esc(v)}">${esc(v)}</option>`).join('');if(values.includes(cur))sel.value=cur}
  function renderFollowKpis(){const host=$('[data-lc-follow-kpis]');if(!host)return;const sel=$('[data-lc-filter="status"]');const selected=String(sel?.value||'');const counts=new Map();for(const r of followRows){const s=String(r?.status||'-').trim()||'-';counts.set(s,(counts.get(s)||0)+1)}const statuses=[...counts.entries()].sort((a,b)=>a[0].localeCompare(b[0],'pt-BR',{numeric:true,sensitivity:'base'}));const btn=(value,label,count)=>`<button type="button" class="lc-follow-kpi${selected===value?' is-active':''}" data-lc-follow-value="${esc(value)}"><span>${esc(label)}</span><strong>${Number(count||0).toLocaleString('pt-BR')}</strong></button>`;host.innerHTML=btn('','TODOS',followRows.length)+statuses.map(([s,c])=>btn(s,s,c)).join('')}
  function renderFollow(){const f=followFilters();followFiltered=followRows.filter(r=>Object.entries(f).every(([k,v])=>!v||norm(r[k]).includes(v)));const mult=followSort.dir==='asc'?1:-1;followFiltered=followFiltered.map((x,i)=>({x,i})).sort((a,b)=>{const c=String(a.x[followSort.key]||'').localeCompare(String(b.x[followSort.key]||''),'pt-BR',{numeric:true,sensitivity:'base'});return c?c*mult:a.i-b.i}).map(z=>z.x);followBody.innerHTML=followFiltered.map(r=>`<tr><td>${esc(r.codigo||'-')}</td><td>${esc(r.nota||'-')}</td><td>${esc(r.pedido||'-')}</td><td>${esc(r.status||'-')}</td></tr>`).join('');followEmpty.hidden=followFiltered.length>0;$('[data-lc-follow-count]').textContent=`${followFiltered.length.toLocaleString('pt-BR')} ${followFiltered.length===1?'objeto':'objetos'}`;for(const th of $$('[data-lc-sort]'))th.classList.toggle('is-sort',th.dataset.lcSort===followSort.key);renderFollowKpis()}
  async function loadFollow(){const seq=++followSeq;if(followController)followController.abort();followController=new AbortController();showFollowLoading();followRows=[];renderFollow();try{const r=await fetch(worker()+'/correios/acompanhamento',{headers:{Authorization:'Bearer '+panelToken()},signal:followController.signal});const d=await r.json().catch(()=>({}));if(seq!==followSeq||followModal.hidden)return;if(r.status===401)throw new Error(d.error||'Sessão expirada.');if(!r.ok)throw new Error(d.detail||d.error||'Não foi possível carregar o acompanhamento.');followRows=Array.isArray(d.items)?d.items:[];fillFollowStatusOptions();renderFollowKpis();const meta=$('[data-lc-follow-meta]');if(meta)meta.textContent=`Rastreamento: ${d.source_name||'-'}${d.updated_at?' · atualizado '+new Date(d.updated_at).toLocaleString('pt-BR'):''}`;followStatus.textContent=`${Number(d.total_open||followRows.length).toLocaleString('pt-BR')} objetos dos Correios em aberto no arquivo de Rastreamento.`;followStatus.classList.remove('is-error');renderFollow();showFollowContent()}catch(e){if(e?.name==='AbortError'||seq!==followSeq)return;followRows=[];fillFollowStatusOptions();renderFollow();followStatus.textContent=e.message||'Erro ao carregar acompanhamento.';followStatus.classList.add('is-error');showFollowContent()}finally{if(seq===followSeq)followController=null}}
  function openFollow(){closeChoice();followModal.hidden=false;for(const el of $$('.lc-filter'))el.value='';followSort={key:'codigo',dir:'asc'};loadFollow()}function closeFollow(){followModal.hidden=true;followSeq++;if(followController){followController.abort();followController=null}followRows=[];followFiltered=[];const k=$('[data-lc-follow-kpis]');if(k)k.innerHTML='';showFollowLoading()}
  if(openBtn.__lisaCorreiosOpenHandler)openBtn.removeEventListener('click',openBtn.__lisaCorreiosOpenHandler);openBtn.__lisaCorreiosOpenHandler=openChoice;openBtn.addEventListener('click',e=>{e.stopPropagation();openChoice()});
  $('[data-lc-open-track]').addEventListener('click',openTrack);$('[data-lc-open-follow]').addEventListener('click',openFollow);document.addEventListener('click',e=>{if(!submenu.hidden&&!submenu.contains(e.target)&&e.target!==openBtn)closeChoice()});window.addEventListener('resize',()=>{if(!submenu.hidden)positionSubmenu()});window.addEventListener('scroll',()=>{if(!submenu.hidden)positionSubmenu()},{passive:true});
  $('[data-lc-close]').addEventListener('click',closeTrack);modal.addEventListener('click',e=>{if(e.target===modal)closeTrack()});search.addEventListener('click',consultar);input.addEventListener('keydown',e=>{if((e.ctrlKey||e.metaKey)&&e.key==='Enter'){e.preventDefault();consultar()}});copyBtn.addEventListener('click',doCopy);downloadBtn.addEventListener('click',doDownload);
  $('[data-lc-follow-close]').addEventListener('click',closeFollow);followModal.addEventListener('click',e=>{if(e.target===followModal)closeFollow()});for(const el of $$('.lc-filter'))el.addEventListener(el.tagName==='SELECT'?'change':'input',renderFollow);const followKpis=$('[data-lc-follow-kpis]');if(followKpis)followKpis.addEventListener('click',e=>{const b=e.target.closest('[data-lc-follow-value]');if(!b)return;const sel=$('[data-lc-filter="status"]');if(sel)sel.value=String(b.dataset.lcFollowValue||'');renderFollow()});for(const th of $$('[data-lc-sort]'))th.querySelector('.lc-sort')?.addEventListener('click',()=>{const k=th.dataset.lcSort;followSort=followSort.key===k?{key:k,dir:followSort.dir==='asc'?'desc':'asc'}:{key:k,dir:'asc'};renderFollow()});
 };
 initLisaCorreiosModule();
}

;

(() => {
  const root=document.getElementById('lisa-app-vg-native');
  if(!root)return;

  const carrierHost=root.querySelector('[data-carrier-status-table]');
  const setCarrierHeader=()=>{
    const th=carrierHost?.querySelector('.carrier-compact-table thead th:first-child');
    if(th&&th.textContent.trim()!=='NOME')th.textContent='NOME';
  };
  if(carrierHost){
    setCarrierHeader();
    const observer=new MutationObserver(setCarrierHeader);
    observer.observe(carrierHost,{childList:true,subtree:true});
  }

  const stack=root.querySelector('.right-column-stack');
  if(stack&&!stack.querySelector('[data-useful-phones]')){
    const card=document.createElement('section');
    card.className='useful-phones-card';
    card.setAttribute('data-useful-phones','');
    const phones=[
      ['Compras','(11) 97548-8337',false],
      ['Transporte','(11) 91273-8375',false],
      ['Financeiro','(11) 97201-7548',false],
      ['Atendimento','(11) 96403-8134',false],
      ['Fernanda LISA','(27) 99624-6444',true],
      ['AGF Limoeiro','(27) 99631-2715',false]
    ];
    card.innerHTML='<div class="useful-phones-list">'+phones.map(([name,phone,secondary],i)=>`<div class="useful-phone-row${secondary?' is-secondary':''}"><span class="useful-phone-name">${name}</span><span class="useful-phone-number">${phone}</span><button type="button" class="useful-phone-copy" data-phone-copy="${phone.replace(/[^0-9]/g,'')}" aria-label="Copiar ${name}" title="Copiar telefone">⧉</button></div>`).join('')+'</div>';
    stack.appendChild(card);
    card.addEventListener('click',async e=>{
      const btn=e.target.closest('[data-phone-copy]');
      if(!btn)return;
      const raw=btn.getAttribute('data-phone-copy')||'';
      const formatted=raw.length===11?`(${raw.slice(0,2)}) ${raw.slice(2,7)}-${raw.slice(7)}`:raw;
      try{
        if(navigator.clipboard?.writeText)await navigator.clipboard.writeText(formatted);
        else{
          const ta=document.createElement('textarea');ta.value=formatted;ta.style.position='fixed';ta.style.opacity='0';document.body.appendChild(ta);ta.select();document.execCommand('copy');ta.remove();
        }
        btn.classList.add('is-copied');btn.textContent='✓';
        setTimeout(()=>{btn.classList.remove('is-copied');btn.textContent='⧉'},1200);
      }catch(_e){}
    });
  }
})();

;

(() => {
  const root=document.getElementById('lisa-app-vg-native');
  if(!root)return;
  const norm=s=>String(s||'').normalize('NFD').replace(/[\u0300-\u036f]/g,'').toUpperCase().trim();
  const aliasFor=raw=>{
    const n=norm(raw);
    if(n.includes('MERCADO LIVRE')||n.includes('ML ES'))return 'MERCADO LIVRE';
    if(n.includes('MAGALU'))return 'MAGALU';
    if(n.includes('SHOPEE'))return 'SHOPEE';
    if(n.includes('SHOPIFY'))return 'SHOPIFY';
    if(n.includes('VENDAS INTERNAS')||n.includes('INTERNO'))return 'INTERNO';
    if(n.includes('EDI'))return 'EDI';
    return '';
  };
  const applyAliases=()=>{
    root.querySelectorAll('.campaign-summary-name[data-filter-campaign]').forEach(btn=>{
      const raw=btn.getAttribute('data-filter-campaign')||'';
      const alias=aliasFor(raw);
      if(alias&&btn.textContent!==alias){
        btn.dataset.campaignOriginalLabel=raw;
        btn.textContent=alias;
      }
    });
  };
  applyAliases();
  const host=root.querySelector('[data-campaign-grid]');
  if(host)new MutationObserver(applyAliases).observe(host,{childList:true,subtree:true});
})();

;

(() => {
  const root=document.getElementById('lisa-app-vg-native');
  if(!root)return;
  const grid=root.querySelector('[data-status-grid]');
  const overdue=root.querySelector('[data-overdue-card]');
  if(!grid||!overdue)return;

  let mobileCard=null;
  let syncing=false;

  const syncMobileCard=()=>{
    if(!mobileCard||!mobileCard.isConnected)return;
    const srcCount=overdue.querySelector('[data-overdue-count]');
    const srcDays=overdue.querySelector('[data-overdue-days-summary]');
    const dstCount=mobileCard.querySelector('.overdue-card-body strong');
    const dstDays=mobileCard.querySelector('.overdue-days-summary');
    if(srcCount&&dstCount)dstCount.textContent=srcCount.textContent;
    if(srcDays&&dstDays)dstDays.innerHTML=srcDays.innerHTML;
  };

  const createMobileCard=()=>{
    const card=overdue.cloneNode(true);
    card.removeAttribute('data-overdue-card');
    card.querySelectorAll('[data-overdue-count],[data-overdue-days-summary]').forEach(el=>{
      el.removeAttribute('data-overdue-count');
      el.removeAttribute('data-overdue-days-summary');
    });
    card.classList.add('mobile-overdue-card');
    card.setAttribute('aria-label','Ver pedidos em atraso');
    card.addEventListener('click',()=>overdue.click());
    card.addEventListener('keydown',e=>{
      if(e.key==='Enter'||e.key===' '){e.preventDefault();overdue.click()}
    });
    return card;
  };

  const ensureMobileCard=()=>{
    if(syncing)return;
    syncing=true;
    try{
      if(window.innerWidth<=700){
        mobileCard=grid.querySelector('.mobile-overdue-card');
        if(!mobileCard){
          mobileCard=createMobileCard();
          grid.appendChild(mobileCard);
        }
        syncMobileCard();
      }else{
        const existing=grid.querySelector('.mobile-overdue-card');
        if(existing)existing.remove();
        mobileCard=null;
      }
    }finally{
      syncing=false;
    }
  };

  const gridObserver=new MutationObserver(()=>ensureMobileCard());
  gridObserver.observe(grid,{childList:true});
  const overdueObserver=new MutationObserver(()=>syncMobileCard());
  overdueObserver.observe(overdue,{childList:true,subtree:true,characterData:true});

  ensureMobileCard();
  window.addEventListener('resize',ensureMobileCard,{passive:true});
})();

;

(() => {
  const root=document.getElementById('lisa-app-vg-native');
  if(!root)return;
  root.querySelectorAll('[data-useful-phones]').forEach(el=>el.remove());
})();

;

(function(){
  const panel=document.getElementById('lisa-app-vg-native')||document.querySelector('.lisa-app[data-worker]');
  if(!panel||panel.dataset.lisaAiAttached==='1')return;
  panel.dataset.lisaAiAttached='1';
  const bar=panel.querySelector('.lisa-actions');
  if(!bar)return;
  const button=document.createElement('button');button.type='button';button.className='ghost lisa-quick-btn lisa-ia-open';button.textContent='CHAT';button.setAttribute('data-lisa-ia-open','');
  const search=bar.querySelector('[data-open-search]');if(search)search.insertAdjacentElement('afterend',button);else bar.appendChild(button);
  const modal=document.createElement('div');modal.className='lisa-ia-modal';modal.hidden=true;modal.setAttribute('role','dialog');modal.setAttribute('aria-modal','true');modal.setAttribute('aria-label','CHAT de produtos');
  modal.innerHTML=`<div class="lisa-ia-panel">
   <header class="lisa-ia-head"><div><strong>CHAT</strong><small>Abastece Componentes · catálogo interno</small></div><div class="lisa-ia-head-actions"><button type="button" class="lisa-ia-button lisa-ia-reset" data-ia-reset title="Apagar o histórico desta conversa" aria-label="Reiniciar conversa">↻ Reiniciar conversa</button><button type="button" class="lisa-ia-button lisa-ia-close" data-ia-close aria-label="Fechar">×</button></div></header>
   <div class="lisa-ia-admin" data-ia-admin hidden>
    <div class="lisa-ia-health" data-ia-status role="status" aria-live="polite">Verificando catálogo e conexão com OpenAI...</div>
    <label>Arquivo do catálogo (.xlsx, .xls, .json.gz ou .json): <input type="file" data-ia-file accept=".xlsx,.xls,.xlsm,.json,.gz"></label><button class="lisa-ia-button" type="button" data-ia-upload>Importar</button><span data-ia-upload-status></span>
   </div>
   <div class="lisa-ia-main"><section class="lisa-ia-chat"><div class="lisa-ia-messages" data-ia-messages aria-live="polite"></div><div class="lisa-ia-controls" data-ia-drop-zone><div class="lisa-ia-image-tools"><input type="file" data-ia-image-file accept="image/png,image/jpeg,image/webp,.png,.jpg,.jpeg,.webp" hidden><button type="button" class="lisa-ia-attach" data-ia-attach title="Enviar foto, imagem ou print">📎 Anexar imagem</button><div class="lisa-ia-image-preview" data-ia-image-preview hidden><img class="lisa-ia-image-thumb" data-ia-image-thumb alt="Prévia da imagem"><span class="lisa-ia-image-name" data-ia-image-name></span><button type="button" class="lisa-ia-remove-photo" data-ia-remove-photo title="Remover imagem" aria-label="Remover imagem">×</button></div></div><textarea class="lisa-ia-input" data-ia-query rows="2" placeholder="Descreva o produto, cole um print (Ctrl+V) ou anexe uma foto..."></textarea><button type="button" class="lisa-ia-send" data-ia-send>Enviar</button></div></section><section class="lisa-ia-results"><div class="lisa-ia-tools"><h3>Produtos encontrados</h3><label class="lisa-ia-check"><input type="checkbox" data-ia-stock> Apenas em estoque</label></div><div data-ia-products class="lisa-ia-note">As recomendações serão exibidas aqui.</div></section></div>
   </div>`;
  document.body.appendChild(modal);
  const $=sel=>modal.querySelector(sel);
  const worker=String(panel.dataset.worker||'').replace(/\/$/,'');
  const tk=()=>sessionStorage.getItem('lisa_panel_token')||'';
  const history=[];
  let conversationVersion=0;
  let pendingRequest=null;
  let greetingShown=false;
  let selectedImage=null;
  let preparingImage=false;
  let imageVersion=0;
  const status=$('[data-ia-status]'),messages=$('[data-ia-messages]'),results=$('[data-ia-products]'),query=$('[data-ia-query]'),send=$('[data-ia-send]');
  function msg(s,cls=''){const b=document.createElement('div');b.className='lisa-ia-bubble '+cls;b.textContent=s;messages.appendChild(b);messages.scrollTop=messages.scrollHeight;return b;}
  function field(parent,text,tag='span',cls=''){const e=document.createElement(tag);e.className=cls;e.textContent=text;parent.appendChild(e);return e}
  function renderProducts(items){results.replaceChildren();if(!items.length){field(results,'Nenhum produto correspondente.','p');return}
   for(const p of items){const c=document.createElement('article');c.className='lisa-ia-product';field(c,p.nome||'Produto','b');field(c,`Código: ${p.codigo||p.id} · ${p.marca||'Marca não informada'} · ${p.familia||p.categoria||''}`,'small');if(p.atributos)field(c,p.atributos,'small');if(p.motivo)field(c,'Por que corresponde: '+p.motivo,'small');const stats=document.createElement('div');stats.className='lisa-ia-stats';field(stats,`Estoque na última importação: ${p.estoque??'n/d'}`,'small');field(stats,p.correspondencia==='descricao'?'Correspondência pela descrição':p.correspondencia==='similar'?'Alternativa semelhante':(p.tipo?.startsWith('KIT')?'Kit':'Produto'),'span','lisa-ia-pill'+(p.correspondencia==='similar'?' is-similar':''));c.appendChild(stats);results.appendChild(c)}
  }
  // Envio visual: somente imagens, nunca documentos ou URLs externos. A imagem nao e salva no R2.
  const imageInput=$('[data-ia-image-file]'),attachButton=$('[data-ia-attach]');
  const imagePreview=$('[data-ia-image-preview]'),imageThumb=$('[data-ia-image-thumb]'),imageName=$('[data-ia-image-name]');
  const imageTypes=new Set(['image/png','image/jpeg','image/webp']);
  function showSelectedImage(){
   imagePreview.hidden=!selectedImage;
   imageThumb.src=selectedImage?.dataURL||'';
   imageName.textContent=selectedImage?.name||'';
   imageName.title=selectedImage?.name||'';
  }
  function clearSelectedImage(){imageVersion++;selectedImage=null;preparingImage=false;imageInput.value='';attachButton.disabled=false;attachButton.textContent='📎 Anexar imagem';showSelectedImage();}
  async function imageData(file){
   if(!file||!imageTypes.has(file.type))throw Error('Use uma imagem PNG, JPG ou WEBP.');
   if(file.size>12*1024*1024)throw Error('Imagem muito grande. O limite inicial é 12 MB.');
   const raw=await new Promise((resolve,reject)=>{const r=new FileReader();r.onload=()=>resolve(String(r.result||''));r.onerror=()=>reject(Error('Falha ao ler a imagem.'));r.readAsDataURL(file)});
   const image=new Image();image.src=raw;
   try{await image.decode()}catch{throw Error('A imagem não pôde ser aberta. Envie outra foto ou print.')}
   if(!image.naturalWidth||!image.naturalHeight)throw Error('Imagem inválida.');
   // Mantemos a imagem original quando esta dentro dos limites, para preservar textos pequenos.
   if(raw.length<=4_400_000&&Math.max(image.naturalWidth,image.naturalHeight)<=2300)return raw;
   let edge=2000,quality=.86,converted='';
   for(let i=0;i<4;i++){
    const scale=Math.min(1,edge/Math.max(image.naturalWidth,image.naturalHeight));
    const canvas=document.createElement('canvas');canvas.width=Math.max(1,Math.round(image.naturalWidth*scale));canvas.height=Math.max(1,Math.round(image.naturalHeight*scale));
    const context=canvas.getContext('2d',{alpha:false});if(!context)throw Error('Não foi possível preparar a imagem.');
    context.fillStyle='#fff';context.fillRect(0,0,canvas.width,canvas.height);context.drawImage(image,0,0,canvas.width,canvas.height);
    converted=canvas.toDataURL(i===0&&file.type==='image/png'?'image/png':'image/jpeg',quality);
    if(converted.length<=4_400_000)break;
    edge=Math.max(1100,Math.round(edge*.8));quality=Math.max(.72,quality-.05);
   }
   if(converted.length>4_400_000)throw Error('Não foi possível reduzir a imagem. Recorte a área importante e tente novamente.');
   return converted;
  }
  async function addImage(file){
   if(!file||modal.hidden)return;
   const version=++imageVersion;preparingImage=true;attachButton.disabled=true;
   const current=attachButton.textContent;attachButton.textContent='Preparando imagem...';
   try{const dataURL=await imageData(file);if(version!==imageVersion)return;
    selectedImage={name:String(file.name||'Print da área de transferência').slice(0,120),dataURL};showSelectedImage();
   }catch(e){if(version===imageVersion){selectedImage=null;showSelectedImage();msg('Não consegui anexar a imagem: '+String(e.message||e))}}
   finally{if(version===imageVersion){preparingImage=false;attachButton.disabled=false;attachButton.textContent=current;send.disabled=!!pendingRequest}}
  }
  attachButton.addEventListener('click',()=>imageInput.click());
  imageInput.addEventListener('change',()=>{const f=imageInput.files?.[0];if(f)addImage(f);imageInput.value=''});
  $('[data-ia-remove-photo]').addEventListener('click',clearSelectedImage);
  // Colar print (Ctrl+V) enquanto o CHAT estiver aberto e focado.
  modal.addEventListener('paste',e=>{
   if(modal.hidden||adminPanel?.contains(e.target))return;
   const files=[...(e.clipboardData?.items||[])].filter(x=>x.kind==='file'&&x.type.startsWith('image/'));
   if(files.length){e.preventDefault();addImage(files[0].getAsFile())}
  });
  const dropZone=$('[data-ia-drop-zone]');
  modal.addEventListener('dragover',e=>{if(modal.hidden)return;
   if([...(e.dataTransfer?.types||[])].includes('Files')){e.preventDefault();dropZone.classList.add('lisa-ia-drop-active')}});
  modal.addEventListener('dragleave',e=>{if(!modal.contains(e.relatedTarget))dropZone.classList.remove('lisa-ia-drop-active')});
  modal.addEventListener('drop',e=>{if(modal.hidden)return;
   const files=[...(e.dataTransfer?.files||[])];if(files.length){e.preventDefault();dropZone.classList.remove('lisa-ia-drop-active');addImage(files[0])}});
  function adminNotice(text,level=''){status.className='lisa-ia-health'+(level?' is-'+level:'');status.textContent=text}
  async function meta(){if(!tk()){adminNotice('✗ Sessão encerrada. Entre novamente no LISA.','error');return}
   adminNotice('Verificando catálogo e configuração da OpenAI...');
   try{const r=await fetch(worker+'/assistente-ia/meta',{headers:{Authorization:'Bearer '+tk()},cache:'no-store'});const d=await r.json();if(!r.ok)throw Error(d.error||'Integração indisponível');
    const catalogOk=Boolean(d.available)&&Number(d.count||0)>0;
    const openaiOk=Boolean(d.ai_configured);
    const catText=catalogOk?`✓ Catálogo: ${Number(d.count).toLocaleString('pt-BR')} produtos importados`:'⚠ Catálogo: ainda não importado';
    const aiText=openaiOk?'✓ OpenAI: chave configurada':'⚠ OpenAI: configure OPENAI_API_KEY';
    let updated='';
    if(catalogOk&&d.updated_at){const dt=new Date(d.updated_at);if(!Number.isNaN(dt.getTime()))updated=' · Atualizado em '+dt.toLocaleString('pt-BR',{dateStyle:'short',timeStyle:'short'});}
    adminNotice(catText+' · '+aiText+updated,catalogOk&&openaiOk?'ok':'warning');
   }catch(e){adminNotice('✗ Não foi possível verificar o catálogo e a OpenAI: '+String(e.message||e),'error')}}
  function resetConversation(){
   conversationVersion++;
   if(pendingRequest){pendingRequest.abort();pendingRequest=null}
   history.length=0;messages.replaceChildren();results.replaceChildren();clearSelectedImage();
   field(results,'As recomendações da nova conversa serão exibidas aqui.','p');
   query.value='';send.disabled=false;
   $('[data-ia-stock]').checked=false;
   msg('Olá! O que você precisa encontrar no catálogo?');greetingShown=true;
   messages.scrollTop=0;const resultsPanel=results.closest('.lisa-ia-results');if(resultsPanel)resultsPanel.scrollTop=0;
   query.focus();
  }
  async function ask(){
   const typed=query.value.trim();if(send.disabled||preparingImage||(typed.length<2&&!selectedImage))return;
   if(!tk()){msg('Sessão encerrada. Entre novamente no LISA.');return}
   const photo=selectedImage;
   const q=typed||(photo?'Identifique o produto desta imagem e encontre opções equivalentes no catálogo da Abastece.':'');
   results.replaceChildren();field(results,'Analisando sua consulta e pesquisando o catálogo...','p');
   const resultsPanel=results.closest('.lisa-ia-results');if(resultsPanel)resultsPanel.scrollTop=0;
   const version=conversationVersion;
   const abort=new AbortController();pendingRequest=abort;
   const bubble=msg(typed||(photo?'Imagem enviada':'Consulta'),'user');
   if(photo){const img=document.createElement('img');img.className='lisa-ia-user-photo';img.alt='Imagem enviada para análise';img.src=photo.dataURL;bubble.appendChild(img)}
   query.value='';clearSelectedImage();send.disabled=true;
   try{
    const r=await fetch(worker+'/assistente-ia/buscar',{method:'POST',headers:{Authorization:'Bearer '+tk(),'Content-Type':'application/json'},signal:abort.signal,
     body:JSON.stringify({pergunta:q,imagem:photo?.dataURL||null,filtros:{somente_estoque:$('[data-ia-stock]').checked},limite:6,historico:history.slice(-8)})});
    const d=await r.json().catch(()=>({}));if(version!==conversationVersion)return;
    if(!r.ok)throw Error(d.error||'Falha na busca');
    if(photo&&d.imagem_interpretada)msg('Li no print: '+String(d.imagem_interpretada).slice(0,480));
    msg(d.resposta||'Veja os produtos sugeridos.');
    const imageSummary=photo&&d.imagem_interpretada?' [Imagem: '+String(d.imagem_interpretada).slice(0,650)+']':'';
    history.push({pergunta:q+imageSummary,resposta:d.resposta||''});
    renderProducts(d.produtos||[]);
   }catch(e){
    if(version!==conversationVersion||abort.signal.aborted)return;
    results.replaceChildren();field(results,'Não foi possível carregar os novos resultados. Tente novamente.','p');
    msg('Não foi possível consultar agora: '+String(e.message||e));
    if(photo&&!selectedImage){selectedImage=photo;showSelectedImage()}
    if(!query.value)query.value=typed;
   }finally{
    if(version===conversationVersion){send.disabled=false;pendingRequest=null;query.focus()}
   }
  }
  button.addEventListener('click',()=>{if(!tk()){alert('Faça login no painel.');return}modal.hidden=false;if(!greetingShown){msg('Olá! O que você precisa encontrar no catálogo?');greetingShown=true}query.focus()});
  $('[data-ia-reset]').addEventListener('click',resetConversation);
  // Atalho discreto: AltGr + clique no X alterna a area de importacao.
  // AltGr em Windows pode aparecer como AltGraph ou Ctrl+Alt no evento do mouse.
  const adminPanel=$('[data-ia-admin]');
  function closeChat(){adminPanel.hidden=true;modal.hidden=true;dropZone.classList.remove('lisa-ia-drop-active');}
  $('[data-ia-close]').addEventListener('click',e=>{
   const altGr=e.getModifierState?.('AltGraph')||(e.ctrlKey&&e.altKey&&!e.metaKey);
   if(altGr){e.preventDefault();adminPanel.hidden=!adminPanel.hidden;if(!adminPanel.hidden)meta();return}
   closeChat();
  });
  modal.addEventListener('click',e=>{if(e.target===modal)closeChat()});
  document.addEventListener('keydown',e=>{if(e.key==='Escape'&&!modal.hidden)closeChat()});
  // Excel e convertido no proprio navegador para o mesmo JSON compactado ja aceito pelo Worker.
  // Autorizacao de importacao conferida pela sessao autenticada no Worker.
  const iaNormalize=v=>String(v??'').normalize('NFD').replace(/[\u0300-\u036f]/g,'').replace(/\s+/g,' ').trim().toUpperCase();
  const iaValue=v=>{const x=String(v??'').trim();return /^(none|null|undefined)$/i.test(x)?'':x};
  function iaStock(v){const x=iaValue(v).replace(/\s/g,'').replace(/\.(?=\d{3}(?:\D|$))/g,'').replace(',','.');const n=Number(x);return Number.isFinite(n)?n:0}
  function iaFamily(nome,cat){const n=iaNormalize(nome),c=iaNormalize(cat);
   if(n.includes('LAMPADA')||c.includes('LAMPADA')){if(/DICROIC|MR16|GU10/.test(n))return 'Lâmpada dicróica / spot';if(/\bPAR\s?\d{2}\b/.test(n))return 'Lâmpada PAR / spot';if(/TUBULAR|TUBO LED/.test(n))return 'Lâmpada tubular';if(n.includes('BULBO'))return 'Lâmpada bulbo';return 'Lâmpada'}
   for(const [pattern,family] of [[/FITA (DE )?LED/,'Fita LED'],[/REFLETOR/,'Refletor'],[/LUMINARIA|PLAFON/,'Luminária'],[/DISJUNTOR/,'Disjuntor'],[/TORNEIRA/,'Torneira'],[/CHUVEIRO|DUCHA/,'Chuveiro / ducha'],[/INTERRUPTOR/,'Interruptor'],[/TOMADA/,'Tomada elétrica'],[/VEDA ROSCA/,'Fita veda-rosca'],[/FURADEIRA/,'Furadeira'],[/CADEADO/,'Cadeado'],[/SILICONE/,'Silicone / vedação'],[/PARAFUSO/,'Parafuso'],[/ALICATE/,'Alicate'],[/BROCA/,'Broca'],[/TRENA/,'Trena'],[/CABO ELETRICO|FIO ELETRICO/,'Cabo elétrico']])if(pattern.test(n)||pattern.test(c))return family;
   return ''
  }
  const iaSuggestions={
   'Lâmpada bulbo':['iluminação geral; substituição de lâmpada compatível','lâmpada de teto; bulbo; claridade; luz ambiente; lâmpada forte'],
   'Lâmpada dicróica / spot':['iluminação direcionada; iluminação de destaque','spot; dicróica; foco de luz; GU10'],
   'Lâmpada PAR / spot':['iluminação direcionada; iluminação de destaque','PAR20; PAR30; PAR38; spot; lâmpada focal'],
   'Lâmpada tubular':['iluminação linear; substituição de tubular compatível','tubo LED; tubular; luminária linear'],
   'Lâmpada':['iluminação com luminária compatível','lâmpada; luz; iluminar ambiente'],
   'Fita LED':['iluminação decorativa e indireta','fita de LED; iluminação indireta; decoração'],
   'Refletor':['iluminação de áreas; projeção de luz','holofote; refletor LED; iluminação de área'],
   'Luminária':['iluminação em instalação compatível','plafon; luminária de teto; luz ambiente'],
   'Disjuntor':['proteção de circuitos conforme dimensionamento técnico','quadro elétrico; proteção elétrica; circuito'],
   'Torneira':['ponto de água e instalação compatível','metais sanitários; torneira; cozinha; banheiro'],
   'Fita veda-rosca':['vedação de conexões roscadas compatíveis','fita teflon; vedação; rosca'],
   'Broca':['perfuração em material compatível','furar; broca; furo'],
   'Furadeira':['perfuração com acessórios compatíveis','furar; perfurar; ferramenta elétrica'],
   'Parafuso':['fixação em material compatível','fixar; prender; parafuso']
  };
  function iaAttributes(nome){const n=iaNormalize(nome),out=[];
   for(const [label,re] of [['Potência',/(?<![A-Z\d])\d+(?:[.,]\d+)?\s?W(?![A-Z\d])/g],['Temperatura de cor',/\b[2-7]\d{3}\s?K\b/g],['Fluxo luminoso',/\b\d{2,5}(?:[.,]\d+)?\s?(?:LM|LUMENS?)\b/g],['Soquete',/\b(?:E14|E27|E40|GU10|G9|G4|G13|G5)\b/g],['Tensão',/\b(?:12|24|110|127|220|230|240|380)\s?V\b/g],['Proteção IP',/\bIP\s?(?:4\d|5\d|6\d|7\d|8\d)\b/g]]){const hits=[...new Set(n.match(re)||[])].slice(0,3);if(hits.length)out.push(label+': '+hits.join(', '))}
   if(n.includes('BIVOLT'))out.push('Tensão: bivolt (no nome)');if(/\bRGBW?\b/.test(n))out.push('Cor de luz: RGB (no nome)');return out.join('; ')
  }
  async function iaGetXlsx(){
   if(window.XLSX?.read&&window.XLSX?.utils?.sheet_to_json)return window.XLSX;
   await new Promise((resolve,reject)=>{
    const existing=document.querySelector('script[data-lisa-chat-xlsx]')||document.querySelector('script[data-cte-xlsx]');
    const script=existing||document.createElement('script');
    if(!existing){script.src='https://cdn.jsdelivr.net/npm/xlsx@0.18.5/dist/xlsx.full.min.js';script.async=true;script.dataset.lisaChatXlsx='1'}
    if(window.XLSX?.read)return resolve();
    script.addEventListener('load',resolve,{once:true});script.addEventListener('error',()=>reject(new Error('Não foi possível carregar o leitor de Excel. Verifique a conexão com a internet.')),{once:true});
    if(!existing)document.head.appendChild(script);
   });
   if(!window.XLSX?.read)throw new Error('Leitor de Excel indisponível.');
   return window.XLSX;
  }
  async function iaConvertExcel(file,setProgress){
   setProgress('Abrindo Excel e identificando colunas...');
   const XLSX=await iaGetXlsx();
   const wb=XLSX.read(await file.arrayBuffer(),{type:'array',cellDates:false});
   let rows=[],fields={};
   for(const name of wb.SheetNames){
    const data=XLSX.utils.sheet_to_json(wb.Sheets[name],{header:1,raw:false,defval:'',blankrows:false});
    if(!data.length)continue;
    const headerIndex=data.findIndex(r=>Array.isArray(r)&&r.some(c=>iaNormalize(c)==='PRODUTO')&&r.some(c=>['CODIGO INTERNO','ID_PRODUTO','ID PRODUTO'].includes(iaNormalize(c))));
    if(headerIndex<0)continue;
    const head=data[headerIndex].map(iaNormalize);
    const ix=names=>{for(const n of names){const i=head.indexOf(iaNormalize(n));if(i>=0)return i}return -1};
    fields={id:ix(['Código Interno','ID_PRODUTO','ID Produto']),codigo:ix(['Código Auxiliar','CODIGO_AUXILIAR','SKU']),nome:ix(['Produto','Nome do Produto']),tipo:ix(['Tipo Produto']),fabricante_codigo:ix(['Código Fabricante']),ean:ix(['Código de Barras','EAN']),marca:ix(['Marca']),categoria:ix(['Categoria']),secao:ix(['Seção']),estoque:ix(['Estoque']),descricao:ix(['Descrição para IA','Descricao para IA','Descrição']),aplicacoes:ix(['Aplicações sugeridas (revisar)','Aplicações']),termos:ix(['Sinônimos e termos de busca IA','Sinônimos']),atributos:ix(['Características extraídas do nome','Características']),familia:ix(['Família IA']),status:ix(['Situação para busca IA'])};
    rows=data.slice(headerIndex+1);break;
   }
   if(!rows.length||fields.id<0||fields.nome<0)throw new Error('Planilha incompatível. As colunas "Código Interno" e "Produto" são obrigatórias.');
   if(rows.length>100000)throw new Error('Planilha acima do limite de 100 mil produtos.');
   setProgress('Organizando e enriquecendo os produtos...');
   const items=[];const seen=new Set();
   for(const r of rows){if(!Array.isArray(r))continue;const get=k=>fields[k]<0?'':iaValue(r[fields[k]]);
    const id=get('id'),nome=get('nome');if(!id||!nome||seen.has(id))continue;
    seen.add(id);
    const marca=get('marca'),categoria=get('categoria'),secao=get('secao'),tipo=get('tipo')||'MERCADORIA PARA REVENDA',familia=get('familia')||iaFamily(nome,categoria),s=iaSuggestions[familia]||['',''];
    const atributos=get('atributos')||iaAttributes(nome);
    const descricao=get('descricao')||[nome,marca?'Marca: '+marca:'',[categoria,secao].filter(Boolean).join(' / '),atributos].filter(Boolean).join(' | ');
    const aplicacoes=get('aplicacoes')||s[0];
    const termos=get('termos')||[s[1],categoria,secao,familia].filter(Boolean).join('; ');
    const estoque=iaStock(get('estoque'));
    const status=get('status')||(tipo==='PRODUTO PAI'?'PAI: não sugerir diretamente':estoque>0?'Cadastro apto a busca':estoque<0?'Estoque negativo':'Sem estoque na importação');
    items.push({id,codigo:get('codigo'),fabricante_codigo:get('fabricante_codigo'),ean:get('ean'),nome,marca,categoria,secao,tipo,estoque,descricao,aplicacoes,termos,atributos,familia,status});
   }
   if(!items.length)throw new Error('Nenhum produto válido encontrado na planilha.');
   setProgress(items.length.toLocaleString('pt-BR')+' produtos identificados. Compactando...');
   if(typeof CompressionStream!=='function')throw new Error('Seu navegador não oferece compactação gzip. Atualize o navegador ou importe o arquivo JSON já convertido.');
   const payload={schema:'lisa-catalogo-ia-v1',source_file:file.name,count:items.length,items};
   const blob=new Blob([JSON.stringify(payload)],{type:'application/json'});
   const zipped=await new Response(blob.stream().pipeThrough(new CompressionStream('gzip'))).blob();
   if(zipped.size>18500000)throw new Error('Catálogo compactado acima do limite de 18,5 MB.');
   return {body:zipped,type:'application/gzip',count:items.length};
  }
  $('[data-ia-upload]').addEventListener('click',async()=>{
   const file=$('[data-ia-file]').files[0],label=$('[data-ia-upload-status]');
   if(!file){label.textContent='Selecione um arquivo do catalogo.';return}
   if(!tk()){label.textContent='Sessao expirada. Entre novamente no painel.';return}
   const ext=(file.name.split('.').pop()||'').toLowerCase();
   if(!['xlsx','xls','xlsm','json','gz'].includes(ext)){label.textContent='Formato não aceito. Use Excel (.xlsx ou .xls), JSON ou JSON.GZ.';return}
   const up=$('[data-ia-upload]');up.disabled=true;
   try{
    let prepared;
    if(['xlsx','xls','xlsm'].includes(ext))prepared=await iaConvertExcel(file,s=>{label.textContent=s});
    else prepared={body:file,type:ext==='gz'?'application/gzip':'application/json'};
    label.textContent='Enviando catálogo ao servidor...';
    const r=await fetch(worker+'/assistente-ia/catalogo',{method:'POST',headers:{Authorization:'Bearer '+tk(),'Content-Type':prepared.type},body:prepared.body});
    const d=await r.json().catch(()=>({}));if(!r.ok||d.ok!==true)throw Error(d.error||'Erro na importação');
    label.textContent=`${Number(d.count||0).toLocaleString('pt-BR')} produtos importados com sucesso.`;
    $('[data-ia-file]').value='';await meta(); // Mantém aberta a área administrativa para confirmar o estado após importar.
   }catch(e){label.textContent='Falha: '+String(e.message||e)}finally{up.disabled=false}
  });
  send.addEventListener('click',ask);query.addEventListener('keydown',e=>{if(e.key==='Enter'&&!e.shiftKey){e.preventDefault();ask()}});
})();

(function(){
 const panel=document.getElementById('lisa-app-vg-native')||document.querySelector('.lisa-app[data-worker]');
 if(!panel||panel.dataset.salesMetricsAttached==='3')return;
 panel.dataset.salesMetricsAttached='3';
 const worker=String(panel.dataset.worker||'').replace(/\/$/,'');
 const modal=document.createElement('div');modal.className='lisa-sales-modal';modal.hidden=true;modal.setAttribute('role','dialog');modal.setAttribute('aria-modal','true');modal.setAttribute('aria-label','Métricas de vendas e atendimento');
 modal.innerHTML=`<section class="lisa-sales-box"><header class="lisa-sales-head"><div><h2>Métricas de vendas e atendimento</h2><small><span data-sales-updated>Última atualização: —</span></small></div><div class="lisa-sales-head-actions"><button type="button" data-sales-download disabled>BAIXAR</button><button type="button" data-sales-close aria-label="Fechar">×</button></div></header><div class="lisa-sales-tools"><label>Data inicial<input data-sales-from type="date"></label><label>Data final<input data-sales-to type="date"></label><button type="button" data-sales-month>Este mês</button><button type="button" data-sales-all>Todo histórico</button></div><div class="lisa-sales-status" data-sales-status role="status" aria-live="polite" hidden></div><div class="lisa-sales-body"><div class="lisa-sales-cards" data-sales-cards></div><p data-sales-return-note hidden>Quantidade de devoluções indisponível. A base precisa incluir a coluna CFOP.</p><div class="lisa-sales-table-wrap"><table class="lisa-sales-table lisa-sales-grouped"><colgroup span="1"></colgroup><colgroup span="2"></colgroup><colgroup span="5"></colgroup><colgroup span="3"></colgroup><thead><tr class="lisa-sales-group-head"><th rowspan="2" scope="col">Atendente</th><th colspan="2" scope="colgroup">Pedidos</th><th colspan="5" scope="colgroup">Faturamento</th><th colspan="3" scope="colgroup">Atendimento</th></tr><tr><th>Orçamentos</th><th>Valor orçamentos</th><th>Faturados</th><th>Valor faturado</th><th>Devoluções</th><th>Valor devoluções</th><th>Total líquido</th><th>Tratativa</th><th>Finalizados</th><th>Média 1ª resposta (h:m:s)</th></tr></thead><tbody data-sales-rows></tbody></table></div></div></section>`;
 document.body.appendChild(modal);
 const detail=document.createElement('div');detail.className='lisa-sales-detail';detail.hidden=true;detail.setAttribute('role','dialog');detail.setAttribute('aria-modal','true');detail.setAttribute('aria-label','Detalhamento de orçamentos e faturados');
 detail.innerHTML=`<div class="lisa-sales-detail-box"><header class="lisa-sales-detail-head"><h3 data-sdetail-title>Detalhamento</h3><button type="button" class="lisa-sales-detail-close" data-sdetail-close aria-label="Fechar detalhamento">×</button></header><div class="lisa-sales-detail-toolbar"><input type="search" placeholder="Buscar número ou cliente..." data-sdetail-search><span data-sdetail-count></span></div><div class="lisa-sales-detail-scroll"><table><thead><tr><th>Número</th><th>Nome</th><th>Frete</th><th>Valor do pedido</th><th title="Total registrado no ERP; frete separado somente quando informado">Valor total com frete</th></tr></thead><tbody data-sdetail-rows></tbody></table></div><footer class="lisa-sales-detail-foot"><span data-sdetail-page></span><button type="button" data-sdetail-prev>Anterior</button><button type="button" data-sdetail-next>Próximo</button></footer></div>`;
 document.body.appendChild(detail);
 const $=s=>modal.querySelector(s),ds=s=>detail.querySelector(s),fmt=v=>Number(v||0).toLocaleString('pt-BR');
 const formatMoney=c=>c===null||c===undefined?'—':(Number(c)/100).toLocaleString('pt-BR',{style:'currency',currency:'BRL'});
 const from=$('[data-sales-from]'),to=$('[data-sales-to]'),status=$('[data-sales-status]'),body=$('[data-sales-rows]'),cards=$('[data-sales-cards]');
 let snapshot=null,serial=0,detailSerial=0,detailRows=[],detailPage=0,detailState=null;
 function setMonth(){const d=new Date(),y=d.getFullYear(),m=String(d.getMonth()+1).padStart(2,'0');from.value=`${y}-${m}-01`;to.value=`${y}-${m}-${String(new Date(y,d.getMonth()+1,0).getDate()).padStart(2,'0')}`;}
 function duration(minutes){if(!Number.isFinite(Number(minutes))||Number(minutes)<0)return '—';const secs=Math.round(Number(minutes)*60),h=Math.floor(secs/3600),m=Math.floor(secs%3600/60),s=secs%60;return h+'h:'+String(m).padStart(2,'0')+'m:'+String(s).padStart(2,'0')+'s'}
 function cell(tr,v,click=null,title=''){const td=document.createElement('td');if(click){const b=document.createElement('button');b.type='button';b.className='lisa-sales-click';b.textContent=String(v);b.title=title||'Ver detalhes';b.addEventListener('click',click);td.appendChild(b)}else td.textContent=String(v);tr.appendChild(td)}
 function validRange(date){if(date==='__sem_data__'||!date)return !from.value&&!to.value;return !(from.value&&date<from.value||to.value&&date>to.value)}
 let exportRows=[];
 function render(){
  if(!snapshot)return;
  if(from.value&&to.value&&from.value>to.value){status.hidden=false;status.textContent='A data inicial não pode ser posterior à final.';status.className='lisa-sales-status error';return}
  const names=(snapshot.employees||[]).filter(n=>String(n).toLowerCase()!=='diego'&&n!=='Outros / sem de-para'),daily=snapshot.days||{},sum={created:0,invoiced:0,finalized:0,open:0,created_cents:0,invoiced_cents:0,created_value_known:0,invoiced_value_known:0};
  const rows=names.map(name=>({name,returned:0,created:0,invoiced:0,finalized:0,open:Number(snapshot.open_tickets?.[name]||0),response_sum:0,response_weight:0,created_cents:0,invoiced_cents:0,created_value_known:0,invoiced_value_known:0}));
  const byName=new Map(rows.map(x=>[x.name,x]));
  for(const [date,people] of Object.entries(daily)){
   if(!validRange(date))continue;
   for(const [name,nums] of Object.entries(people||{})){
    if(String(name).toLowerCase()==='diego')continue;
    const r=byName.get(name);if(!r)continue;
    for(const k of ['returned','created','invoiced','finalized','response_sum','response_weight','created_cents','invoiced_cents','created_value_known','invoiced_value_known'])r[k]+=Number(nums[k]||0);
   }
  }
  for(const row of rows){
   const records=(snapshot.return_details?.[row.name]||[]).filter(x=>validRange(x.date));
   row.returned_cents=records.reduce((sum,x)=>sum+Math.abs(Number(x.total_cents)||0),0);
   row.returned_value_known=records.filter(x=>x.total_cents!=null).length;
   row.returned_value_complete=Array.isArray(snapshot.return_details?.[row.name])?row.returned_value_known===row.returned:row.returned===0;
   row.net_cents=row.invoiced_cents-row.returned_cents;
  }
  sum.returned=0;sum.returned_cents=0;
  sum.returned_value_complete=rows.every(row=>row.returned_value_complete);
  for(const row of rows){sum.returned+=row.returned;sum.returned_cents+=row.returned_cents}
  exportRows=rows;
  $('[data-sales-download]').disabled=false;
  cards.replaceChildren();
  for(const r of rows)for(const k of ['created','invoiced','finalized','open','created_cents','invoiced_cents','created_value_known','invoiced_value_known'])sum[k]+=r[k];
  const invoiced=snapshot.sources?.sysemp_has_seller;
  const tiles=[['ORÇAMENTOS',fmt(sum.created),'orcamentos'],['VALOR ORÇAMENTOS',sum.created_value_known?formatMoney(sum.created_cents):'—','orcamentos'],['FATURADOS',invoiced?fmt(sum.invoiced):'—','faturados'],['VALOR FATURADO',sum.invoiced_value_known?formatMoney(sum.invoiced_cents):'—','faturados'],['DEVOLUÇÕES',fmt(sum.returned),'devolucoes'],['VALOR DEVOLUÇÕES',sum.returned_value_complete?formatMoney(sum.returned_cents):'—','devolucoes'],['TOTAL LÍQUIDO',sum.invoiced_value_known&&sum.returned_value_complete?formatMoney(sum.invoiced_cents-sum.returned_cents):'—',null]];
  for(const [label,value,tipo] of tiles){const x=document.createElement('div');x.className='lisa-sales-card'+(tipo?' is-clickable':'');x.title='Clique para visualizar os registros';x.tabIndex=0;x.setAttribute('role','button');const s=document.createElement('span');s.textContent=label;const b=document.createElement('b');b.textContent=value;x.append(s,b);if(tipo)x.addEventListener('click',()=>openDetails(tipo,'Todos'));x.addEventListener('keydown',e=>{if(tipo&&(e.key==='Enter'||e.key===' ')){e.preventDefault();openDetails(tipo,'Todos')}});cards.appendChild(x)}
  const formatStamp=value=>{
   const date=new Date(value||'');
   return Number.isFinite(date.getTime())?date.toLocaleString('pt-BR',{timeZone:'America/Sao_Paulo'}):'—';
  };
  const returnsAt=snapshot.sources?.returns_updated_at;
  $('[data-sales-updated]').textContent='Última atualização: '+formatStamp(snapshot.updated_at)+(returnsAt?' · Devoluções: '+formatStamp(returnsAt):'');
  const returnNote=$('[data-sales-return-note]');
  returnNote.hidden=!!snapshot.sources?.sysemp_has_cfop;
  returnNote.textContent=snapshot.sources?.returns_error||'Resumo de devoluções indisponível. Confira a versão do Worker e a importação do SYSEMP.';
  body.replaceChildren();
  for(const r of rows){
   const tr=document.createElement('tr');cell(tr,r.name);
   cell(tr,fmt(r.created),()=>openDetails('orcamentos',r.name),'Ver orçamentos de '+r.name);
   cell(tr,r.created_value_known?formatMoney(r.created_cents):'—',()=>openDetails('orcamentos',r.name),'Ver valores dos orçamentos');
   cell(tr,invoiced?fmt(r.invoiced):'—',()=>openDetails('faturados',r.name),'Ver faturados de '+r.name);
   cell(tr,r.invoiced_value_known?formatMoney(r.invoiced_cents):'—',()=>openDetails('faturados',r.name),'Ver valores faturados');
   cell(tr,snapshot.sources?.sysemp_has_cfop?fmt(r.returned):'—',r.returned>0&&Array.isArray(snapshot.return_details?.[r.name])?()=>openDetails('devolucoes',r.name):null,'Ver devoluções de '+r.name);
   cell(tr,r.returned_value_complete?formatMoney(r.returned_cents):'—',r.returned>0?()=>openDetails('devolucoes',r.name):null);
   cell(tr,r.invoiced_value_known&&r.returned_value_complete?formatMoney(r.net_cents):'—');
   cell(tr,fmt(r.open));cell(tr,fmt(r.finalized));cell(tr,r.response_weight?duration(r.response_sum/r.response_weight):'—');body.appendChild(tr);
  }
  status.hidden=true;status.textContent='';status.className='lisa-sales-status';
 }
 function renderDetails(){
  const term=ds('[data-sdetail-search]').value.trim().toLocaleLowerCase('pt-BR');
  const visible=detailRows.filter(x=>validRange(x.date)&&(!term||String(x.numero||'').toLowerCase().includes(term)||String(x.nome||'').toLocaleLowerCase('pt-BR').includes(term)||String(x.cfop||'').includes(term)));
  const pageSize=100,maxPage=Math.max(0,Math.ceil(visible.length/pageSize)-1);detailPage=Math.min(detailPage,maxPage);
  const list=ds('[data-sdetail-rows]');list.replaceChildren();
  for(const record of visible.slice(detailPage*pageSize,(detailPage+1)*pageSize)){
   const tr=document.createElement('tr');for(const v of (detailState.tipo==='devolucoes'?[record.numero||'—',record.nome||'—',record.date&&record.date!=='__sem_data__'?record.date.split('-').reverse().join('/'):'—',record.cfop||'—',record.total_cents!=null?formatMoney(Math.abs(record.total_cents)):'—']:[record.numero||'—',record.nome||'—',formatMoney(record.frete_cents),formatMoney(record.pedido_cents),formatMoney(record.total_cents)])){const td=document.createElement('td');td.textContent=v;tr.appendChild(td)}list.appendChild(tr);
  }
  ds('[data-sdetail-count]').textContent=fmt(visible.length)+' registros';
  ds('[data-sdetail-page]').textContent=fmt(detailPage+1)+' / '+fmt(maxPage+1);
  ds('[data-sdetail-prev]').disabled=detailPage===0;ds('[data-sdetail-next]').disabled=detailPage>=maxPage;
 }
 function namesForDetails(){return(snapshot.employees||[]).filter(name=>name!=='Outros / sem de-para'&&String(name).toLowerCase()!=='diego')}
 async function openDetails(tipo,owner){
  if(!snapshot)return;
  detail.hidden=false;detailState={tipo,owner};detailRows=[];detailPage=0;
  ds('[data-sdetail-title]').textContent=(tipo==='orcamentos'?'Orçamentos':tipo==='devolucoes'?'Devoluções':'Faturados')+' — '+(owner==='Todos'?'Todos os atendentes':owner);
  const headings=tipo==='devolucoes'?['Nota fiscal','Cliente','Emissão','CFOP','Valor da devolução']:['Número','Nome','Frete','Valor do pedido','Valor total com frete'];
  ds('thead tr').replaceChildren(...headings.map(text=>{const th=document.createElement('th');th.textContent=text;return th}));
  ds('[data-sdetail-search]').placeholder=tipo==='devolucoes'?'Buscar nota, cliente ou CFOP...':'Buscar número ou cliente...';
  ds('[data-sdetail-search]').value='';ds('[data-sdetail-count]').textContent=tipo==='devolucoes'?'':'Carregando...';ds('[data-sdetail-rows]').replaceChildren();
  const current=++detailSerial;
  if(tipo==='devolucoes'){
   detailRows=owner==='Todos'?Object.entries(snapshot.return_details||{}).filter(([name])=>namesForDetails().includes(name)).flatMap(([,rows])=>rows):snapshot.return_details?.[owner]||[];
   renderDetails();
   return;
  }
  try{
   const u=worker+'/realtime/vendas-atendentes/detalhes?tipo='+encodeURIComponent(tipo)+'&atendente='+encodeURIComponent(owner);
   const response=await fetch(u,{headers:{Authorization:'Bearer '+sessionStorage.getItem('lisa_panel_token')},cache:'no-store'});
   const data=await response.json().catch(()=>({}));if(current!==detailSerial||detail.hidden)return;
   if(!response.ok||!data.ok)throw Error(data.error||'Falha ao carregar detalhes');
   detailRows=Array.isArray(data.rows)?data.rows:[];renderDetails();
  }catch(e){if(current!==detailSerial||detail.hidden)return;ds('[data-sdetail-count]').textContent='Erro: '+String(e.message||e)}
 }
 async function open(){
  if(!sessionStorage.getItem('lisa_panel_token'))return;
  modal.hidden=false;from.value='';to.value='';
  if(snapshot)render();
  const current=++serial;status.hidden=false;status.className='lisa-sales-status';status.textContent='Carregando métricas...';
  try{
   const request=()=>fetch(worker+'/realtime/vendas-atendentes',{headers:{Authorization:'Bearer '+sessionStorage.getItem('lisa_panel_token')},cache:'no-store'});
   let r=await request();
   for(let attempt=0;attempt<6&&r.status===503;attempt++){
    await r.body?.cancel();
    status.textContent=snapshot?'Exibindo as métricas salvas enquanto o Cloudflare prepara a atualização.':'Preparando as métricas com os dados salvos no Cloudflare.';
    await new Promise(resolve=>setTimeout(resolve,5000));
    if(current!==serial||modal.hidden)return;
    r=await request();
   }
   const d=await r.json().catch(()=>({}));if(current!==serial||modal.hidden)return;
   if(!r.ok||!d.ok)throw Error(d.error||'Não foi possível ler as métricas pré-carregadas');
   if(d.version!==5)throw Error('Resumo antigo. Aguarde a atualização automática das métricas no Cloud.');
   snapshot=d;render();
  }catch(e){if(current!==serial||modal.hidden)return;status.hidden=false;status.className='lisa-sales-status error';status.textContent='Erro: '+String(e.message||e)}
 }
 function closeDetails(){detail.hidden=true;detailSerial++}
 function close(){modal.hidden=true;serial++;closeDetails()}
 async function downloadMetrics(){
  const button=$('[data-sales-download]');
  if(!snapshot)return;
  button.disabled=true;
  try{
   if(!window.XLSX?.writeFile)await new Promise((resolve,reject)=>{
    const script=document.createElement('script');
    script.src='https://cdn.jsdelivr.net/npm/xlsx@0.18.5/dist/xlsx.full.min.js';
    script.onload=resolve;script.onerror=()=>{script.remove();reject(Error('Não foi possível carregar o exportador Excel. Tente novamente.'))};
    document.head.appendChild(script);
   });
   const XLSX=window.XLSX;
   if(!XLSX?.writeFile)throw Error('Exportador Excel indisponível.');
   const header=['Atendente','Orçamentos','Valor orçamentos','Faturados','Valor faturado','Devoluções','Valor devoluções','Total líquido','Tratativa','Finalizados','Média 1ª resposta (h:m:s)'];
   const money=(cents,known)=>known?Number(cents)/100:null;
   const rows=exportRows.map(row=>[
    row.name,row.created,money(row.created_cents,row.created_value_known),row.invoiced,money(row.invoiced_cents,row.invoiced_value_known),
    snapshot.sources?.sysemp_has_cfop?row.returned:null,money(row.returned_cents,row.returned_value_complete),
    money(row.net_cents,row.invoiced_value_known&&row.returned_value_complete),row.open,row.finalized,
    row.response_weight?duration(row.response_sum/row.response_weight):''
   ]);
   const moneyColumns=[2,4,6,7];
   const sheet=XLSX.utils.aoa_to_sheet([header,...rows]);
   for(let index=1;index<=rows.length;index++)for(const column of moneyColumns){
    const cell=sheet[XLSX.utils.encode_cell({r:index,c:column})];
    if(cell?.t==='n')cell.z='"R$" #,##0.00';
   }
   sheet['!cols']=header.map((label,index)=>({wch:index===0?20:index===10?29:22}));
   sheet['!autofilter']={ref:XLSX.utils.encode_range({r:0,c:0},{r:rows.length,c:header.length-1})};
   const info=XLSX.utils.aoa_to_sheet([
    ['Métricas de vendas e atendimento'],
    ['Data inicial',from.value||'Todo histórico'],
    ['Data final',to.value||'Todo histórico'],
    ['Resumo atualizado em',snapshot.updated_at||''],
    ['Devoluções atualizadas em',snapshot.sources?.returns_updated_at||''],
    ['Total líquido','Valor faturado menos valor das devoluções'],
    ['Tratativa','Quantidade atual de tickets em tratativa']
   ]);
   info['!cols']=[{wch:32},{wch:58}];
   const workbook=XLSX.utils.book_new();
   XLSX.utils.book_append_sheet(workbook,sheet,'Métricas');
   XLSX.utils.book_append_sheet(workbook,info,'Período');
   XLSX.writeFile(workbook,'metricas_vendas_'+(from.value||'inicio')+'_'+(to.value||'todo_historico')+'.xlsx',{compression:true});
  }catch(error){
   status.hidden=false;status.className='lisa-sales-status error';
   status.textContent='Falha ao baixar Excel: '+String(error?.message||error);
  }finally{button.disabled=false}
 }
 $('[data-sales-download]').addEventListener('click',downloadMetrics);
 $('[data-sales-close]').addEventListener('click',close);modal.addEventListener('click',e=>{if(e.target===modal)close()});
 ds('[data-sdetail-close]').addEventListener('click',closeDetails);detail.addEventListener('click',e=>{if(e.target===detail)closeDetails()});
 ds('[data-sdetail-search]').addEventListener('input',()=>{detailPage=0;renderDetails()});
 ds('[data-sdetail-prev]').addEventListener('click',()=>{detailPage=Math.max(0,detailPage-1);renderDetails()});
 ds('[data-sdetail-next]').addEventListener('click',()=>{detailPage++;renderDetails()});
 $('[data-sales-month]').addEventListener('click',()=>{setMonth();render()});$('[data-sales-all]').addEventListener('click',()=>{from.value='';to.value='';render()});
 from.addEventListener('change',()=>{render();if(!detail.hidden)renderDetails()});to.addEventListener('change',()=>{render();if(!detail.hidden)renderDetails()});
 document.addEventListener('keydown',e=>{
  const altGr=e.getModifierState?.('AltGraph')||(e.altKey&&e.ctrlKey);
  if(altGr&&(e.code==='KeyB'||String(e.key||'').toLowerCase()==='b')){e.preventDefault();e.stopPropagation();modal.hidden?open():close()}
  else if(e.key==='Escape'&&!detail.hidden){e.preventDefault();closeDetails()}
  else if(e.key==='Escape'&&!modal.hidden){e.preventDefault();close()}
 },true);
})();


;
/* Presentation adapter. Original LISA queries, data rules and reports are retained. */
(()=>{
  'use strict';
  const root=document.getElementById('lisa-app-vg-native');
  if(!root||root.dataset.layoutReady)return;
  root.dataset.layoutReady='1';
  const q=s=>root.querySelector(s),shell=q('[data-shell]');
  const names={overview:'Visão Geral',service:'Atendimento',quote:'Orçamento',transport:'Transportes',site:'Site'};
  const oldMain=shell.querySelector('main'), topbar=shell.querySelector('.lisa-topbar');
  topbar.classList.add('vg-source-controls');
  const header=document.createElement('header');header.className='vg-page-heading';
  const title=document.createElement('h1');title.textContent=names.overview;header.append(title);
  header.append(topbar.querySelector('[data-go-latest]'));
  const nav=document.createElement('nav');nav.className='vg-side-nav';nav.setAttribute('aria-label','Menu principal');
  const actions=[['search','⌕','Consulta'],['chat','◇','Chat'],['apps','▦','Aplicativos'],['refresh','↻','Atualizar'],['upload','↑','Upload'],['overview','▦',names.overview],['service','☏',names.service],['quote','▤',names.quote],['transport','⇢',names.transport],['site','◎',names.site],['timeline','◷','Histórico'],['reversa','↩','Reversa'],['correios','✉','Correios'],['download','↓','Baixar arquivo'],['theme','◐','Tema'],['logout','⇥','Sair']];
  nav.innerHTML=actions.map(([key,icon,label])=>`<button type="button" data-sector-action="${key}"><span aria-hidden="true">${icon}</span>${label}</button>`).join('');
  const pages=document.createElement('main');pages.className='vg-pages';
  const sections={};
  for(const [id,label] of Object.entries(names)){const section=document.createElement('section');section.className='vg-page';section.dataset.sector=id;section.setAttribute('aria-label',label);section.hidden=id!=='overview';sections[id]=section;pages.append(section)}
  const overview=document.createElement('div');overview.className='vg-overview-content';
  const top=document.createElement('div');top.className='vg-overview-top';
  top.append(q('[data-status-grid]'),q('[data-overdue-card]'));
  const bottom=document.createElement('div');bottom.className='vg-overview-bottom';
  bottom.append(q('.movement-card'),q('[data-lisa-marketplaces-card]'));
  overview.append(top,q('.campaign-panel'),bottom);sections.overview.append(overview);
  sections.service.append(q('.attendance-section'));
  const quote=document.createElement('div');quote.className='vg-empty-sector';quote.setAttribute('aria-label','Área de orçamento');sections.quote.append(quote);
  const trackingSlot=document.createElement('div');trackingSlot.className='vg-tracking-slot';sections.transport.append(trackingSlot);
  const siteSlot=document.createElement('div');siteSlot.className='vg-site-slot';sections.site.append(siteSlot);
  // Keep the native mini-map host and its listeners intact.
  const mapHost=q('.carrier-section-side');mapHost.classList.add('vg-map-host');
  const boards={};
  for(const id of Object.keys(names)){
    const aside=document.createElement('aside');aside.className='vg-information';
    aside.innerHTML='<header><h2>Informações <small data-note-count>0</small></h2><button type="button" data-note-add aria-label="Adicionar anotação">+</button></header><div class="vg-note-tabs"><button type="button" data-note-filter="all">Todas</button><button type="button" data-note-filter="pending">Pendentes</button><button type="button" data-note-filter="verified">Conferidas</button></div><div class="vg-notes"></div><small>● Salvas neste navegador</small>';
    const side=document.createElement('div');side.className='vg-page-side';side.append(aside);if(id==='transport')side.append(mapHost);sections[id].append(side);boards[id]=aside;
  }
  // Keep unused native trigger elements available to their original modules.
  oldMain.hidden=true;oldMain.classList.add('vg-source-controls');
  shell.append(header,nav,pages);
  const tracking=document.getElementById('lisacte-vg-native');
  const trackingModal=tracking?.querySelector('[data-cte-modal]');
  if(tracking){trackingSlot.append(tracking);trackingModal.classList.add('vg-inline-tracking');trackingModal.querySelector('[data-cte-close]').hidden=true;trackingModal.querySelector('.cte-head h3').hidden=true;}
  const site=document.getElementById('lisasite-vg-native'),siteModal=site?.querySelector('[data-ls-modal]');
  if(site){siteSlot.append(site);siteModal.classList.add('vg-inline-site');siteModal.querySelector('[data-ls-close]').hidden=true;}
  let active='overview';
  function select(id){
    if(!sections[id])return;
    active=id;root.dataset.activeSector=id;
    Object.entries(sections).forEach(([key,el])=>el.hidden=key!==id);
    nav.querySelectorAll('[data-sector-action]').forEach(b=>{const selected=b.dataset.sectorAction===id;b.classList.toggle('is-active',selected);if(names[b.dataset.sectorAction])b.setAttribute('aria-current',selected?'page':'false')});
    title.textContent=names[id];document.title=names[id]+' · Abastece';
    if(trackingModal)trackingModal.hidden=id!=='transport';
    if(siteModal)siteModal.hidden=id!=='site';
    if(id==='transport')root.__lisaTrackingOpen?.('all');
    if(id==='site')q('[data-ls-site-open]')?.click();
  }
  const trigger={search:'[data-open-search]',chat:'[data-lisa-ia-open]',apps:'[data-file-menu-open]',refresh:'[data-refresh]',upload:'[data-tracking-upload-open]',timeline:'[data-timeline]',reversa:'[data-reversa-view]',correios:'[data-lc-correios-open]',download:'[data-download]',theme:'[data-theme-toggle]',logout:'[data-logout]'};
  // Move the real native controls so their menus can use the visible anchor.
  for(const [id,selector] of Object.entries(trigger)){
    const original=q(selector),proxy=nav.querySelector(`[data-sector-action="${id}"]`);
    if(!original||!proxy)continue;
    const label=proxy.textContent.slice(1).trim(),icon=proxy.querySelector('span').textContent;
    original.dataset.sectorAction=id;original.classList.add('vg-native-action');
    original.innerHTML=`<span aria-hidden="true">${icon}</span><span ${id==='download'?'data-download-label':''}>${label}</span>`;
    proxy.replaceWith(original);
  }
  nav.addEventListener('click',e=>{const b=e.target.closest('[data-sector-action]');if(b&&names[b.dataset.sectorAction])select(b.dataset.sectorAction)});
  // Expose page selection for keyboard navigation and regression checks.
  root.__selectSector=select;
  const notesDialog=document.createElement('dialog');notesDialog.className='vg-note-dialog';notesDialog.innerHTML='<form method="dialog"><header><h2>Anotação</h2><button value="cancel" formnovalidate aria-label="Fechar">×</button></header><label>Título<input name="title" required maxlength="160"></label><label>Descrição<textarea name="description" maxlength="2000"></textarea></label><button value="save">Salvar</button></form>';root.append(notesDialog);
  const esc=s=>String(s??'').replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
  let editing=null,noteSector='overview';
  function noteKey(id){return 'visa-geral:'+root.dataset.worker.replace(/\/$/,'')+':tasks:visao-geral'+(id==='overview'?'':':'+id)}
  function notes(id){try{let raw=localStorage.getItem(noteKey(id));if(raw===null&&id==='overview'){const prefix='visa-geral:'+root.dataset.worker.replace(/\/$/,'')+':tasks:';const legacy=Object.keys(localStorage).find(k=>k.startsWith(prefix)&&!k.slice(prefix.length).includes(':'));if(legacy)raw=localStorage.getItem(legacy)}const data=JSON.parse(raw);return Array.isArray(data)?data:[]}catch{return []}}
  function drawNotes(id){const board=boards[id],data=notes(id),filter=board.dataset.filter||'all';board.querySelector('[data-note-count]').textContent=data.length;board.querySelectorAll('[data-note-filter]').forEach(b=>b.setAttribute('aria-pressed',String(b.dataset.noteFilter===filter)));board.querySelector('.vg-notes').innerHTML=data.filter(n=>filter==='all'||(filter==='pending'?n.state!=='verified':n.state==='verified')).map(n=>`<article class="vg-note"><h3>${esc(n.title)}</h3><p>${esc(n.description)}</p><select data-note-state="${esc(n.id)}" aria-label="Estado da anotação">${[['pending','Pendente'],['done','Feito'],['verified','Conferido']].map(([k,v])=>`<option value="${k}" ${n.state===k?'selected':''}>${v}</option>`).join('')}</select><button data-note-edit="${esc(n.id)}" type="button">Editar</button><button data-note-delete="${esc(n.id)}" type="button">Excluir</button></article>`).join('')||'<p class="vg-no-notes">Tudo começa com um próximo passo.<br>Use o + para adicionar uma tarefa.</p>'}
  function saveNotes(id,items){localStorage.setItem(noteKey(id),JSON.stringify(items));drawNotes(id)}
  for(const [id,board] of Object.entries(boards)){
    drawNotes(id);
    board.addEventListener('click',e=>{const b=e.target.closest('button');if(!b)return;if(b.hasAttribute('data-note-filter')){board.dataset.filter=b.dataset.noteFilter;drawNotes(id)}if(b.hasAttribute('data-note-delete'))saveNotes(id,notes(id).filter(n=>n.id!==b.dataset.noteDelete));if(b.hasAttribute('data-note-add')||b.hasAttribute('data-note-edit')){noteSector=id;editing=b.dataset.noteEdit||null;const note=notes(id).find(n=>n.id===editing);notesDialog.querySelector('[name=title]').value=note?.title||'';notesDialog.querySelector('[name=description]').value=note?.description||'';notesDialog.showModal()}});
    board.addEventListener('change',e=>{if(e.target.matches('[data-note-state]'))saveNotes(id,notes(id).map(n=>n.id===e.target.dataset.noteState?{...n,state:e.target.value}:n))});
  }
  notesDialog.addEventListener('close',()=>{if(notesDialog.returnValue!=='save')return;const data=notes(noteSector),entry={id:editing||crypto.randomUUID(),title:notesDialog.querySelector('[name=title]').value.trim(),description:notesDialog.querySelector('[name=description]').value.trim(),state:data.find(n=>n.id===editing)?.state||'pending'};if(entry.title)saveNotes(noteSector,editing?data.map(n=>n.id===editing?entry:n):[entry,...data])});
  select('overview');
})();
