{"id":2784,"date":"2026-03-14T23:00:21","date_gmt":"2026-03-15T04:00:21","guid":{"rendered":"https:\/\/fast-net.com.co\/?page_id=2784"},"modified":"2026-03-14T23:17:32","modified_gmt":"2026-03-15T04:17:32","slug":"panel-interno","status":"publish","type":"page","link":"https:\/\/fast-net.com.co\/index.php\/panel-interno\/","title":{"rendered":"Panel Interno"},"content":{"rendered":"<p><!DOCTYPE html><br \/>\n<html lang=\"es\"><br \/>\n<head><br \/>\n<meta charset=\"UTF-8\"><br \/>\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><br \/>\n<title>FastNet \u2014 Panel Administrativo<\/title><\/p>\n<style>\n@import url('https:\/\/fonts.googleapis.com\/css2?family=Syne:wght@400;500;700&family=IBM+Plex+Mono:wght@400;500&display=swap');\n*{box-sizing:border-box;margin:0;padding:0}\n:root{\n  --bg:#07101f;--surf:#0d1a2d;--card:#112236;\n  --acc:#00d4ff;--acc2:#0055ff;--green:#00e676;--amber:#ffb300;--red:#ff5252;\n  --text:#dff0ff;--muted:#4a7099;--border:#1a3456;\n}\nbody{font-family:'Syne',sans-serif;background:var(--bg);color:var(--text);min-height:100vh}\n#screen-login{min-height:100vh;display:flex;align-items:center;justify-content:center;position:relative;overflow:hidden}\n#screen-login::before{content:'';position:absolute;inset:0;background:radial-gradient(ellipse at 65% 35%,rgba(0,212,255,.08) 0%,transparent 60%),radial-gradient(ellipse at 20% 75%,rgba(0,85,255,.06) 0%,transparent 50%)}\n.grid-bg{position:absolute;inset:0;background-image:linear-gradient(rgba(0,212,255,.03) 1px,transparent 1px),linear-gradient(90deg,rgba(0,212,255,.03) 1px,transparent 1px);background-size:36px 36px;pointer-events:none}\n.lcard{background:var(--surf);border:1px solid var(--border);border-radius:16px;padding:2.5rem;width:100%;max-width:390px;position:relative;z-index:1}\n.llogo{text-align:center;margin-bottom:2rem}\n.licon{width:56px;height:56px;background:linear-gradient(135deg,var(--acc2),var(--acc));border-radius:14px;display:inline-flex;align-items:center;justify-content:center;font-size:28px;margin-bottom:10px}\n.ltitle{font-size:26px;font-weight:700}.ltitle span{color:var(--acc)}\n.lsub{font-size:11px;color:var(--muted);font-family:'IBM Plex Mono',monospace;margin-top:3px}\n.lf{margin-bottom:1.1rem}\n.lf label{display:block;font-size:10px;color:var(--muted);margin-bottom:5px;font-family:'IBM Plex Mono',monospace;letter-spacing:1px}\n.lf input{width:100%;background:var(--card);border:1px solid var(--border);color:var(--text);padding:.7rem 1rem;border-radius:8px;font-family:'Syne',sans-serif;font-size:14px;outline:none;transition:border-color .2s}\n.lf input:focus{border-color:var(--acc)}\n.lbtn{width:100%;background:linear-gradient(135deg,var(--acc2),var(--acc));border:none;color:#040d1a;padding:.85rem;border-radius:8px;font-family:'Syne',sans-serif;font-size:15px;font-weight:700;cursor:pointer;transition:opacity .2s;margin-top:.5rem}\n.lbtn:hover{opacity:.88}.lbtn:disabled{opacity:.5;cursor:not-allowed}\n.lerr{color:#ff6b6b;font-size:11px;text-align:center;margin-top:.7rem;display:none;font-family:'IBM Plex Mono',monospace}\n.lspinner{display:none;text-align:center;margin-top:.7rem;font-size:11px;color:var(--acc);font-family:'IBM Plex Mono',monospace}\n#screen-dash{display:none;padding:1.25rem 1.5rem;background:var(--bg);min-height:100vh}\n.topbar{display:flex;justify-content:space-between;align-items:center;margin-bottom:1.25rem;padding-bottom:.9rem;border-bottom:1px solid var(--border)}\n.tlogo{font-size:20px;font-weight:700}.tlogo span{color:var(--acc)}\n.tsub{font-size:11px;color:var(--muted);font-family:'IBM Plex Mono',monospace;margin-left:8px}\n.tright{display:flex;align-items:center;gap:8px}\n.ubadge{background:var(--card);border:1px solid var(--border);border-radius:7px;padding:3px 11px;font-size:11px;color:var(--muted);font-family:'IBM Plex Mono',monospace}\n.logoutbtn,.refresh-btn{background:none;border:1px solid var(--border);color:var(--muted);padding:3px 11px;border-radius:7px;cursor:pointer;font-size:11px;font-family:'IBM Plex Mono',monospace;transition:all .2s}\n.logoutbtn:hover,.refresh-btn:hover{border-color:var(--acc);color:var(--acc)}\n.last-update{font-size:10px;color:var(--muted);font-family:'IBM Plex Mono',monospace}\n.loading-overlay{display:none;position:fixed;inset:0;background:rgba(7,16,31,.85);z-index:200;align-items:center;justify-content:center;flex-direction:column;gap:12px}\n.spinner{width:36px;height:36px;border:3px solid var(--border);border-top-color:var(--acc);border-radius:50%;animation:spin .7s linear infinite}\n@keyframes spin{to{transform:rotate(360deg)}}\n.loading-msg{font-size:12px;color:var(--acc);font-family:'IBM Plex Mono',monospace}\n.sec{font-size:10px;color:var(--muted);font-family:'IBM Plex Mono',monospace;letter-spacing:1.2px;text-transform:uppercase;margin:1.25rem 0 .7rem}\n.kpi-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:8px}\n.kpi{background:var(--card);border:1px solid var(--border);border-radius:11px;padding:.85rem 1rem}\n.kpi.hi{border-color:rgba(0,212,255,.35);background:rgba(0,212,255,.04)}\n.kpi.ok{border-color:rgba(0,230,118,.25);background:rgba(0,230,118,.03)}\n.kpi.warn{border-color:rgba(255,179,0,.25);background:rgba(255,179,0,.03)}\n.kpi.danger{border-color:rgba(255,82,82,.25);background:rgba(255,82,82,.03)}\n.kpi-lbl{font-size:10px;color:var(--muted);font-family:'IBM Plex Mono',monospace;margin-bottom:5px}\n.kpi-val{font-size:24px;font-weight:700;line-height:1}\n.kpi-sub{font-size:10px;margin-top:4px;font-family:'IBM Plex Mono',monospace;color:var(--muted)}\n.g2{display:grid;grid-template-columns:1fr 1fr;gap:10px}\n@media(max-width:620px){.g2{grid-template-columns:1fr}}\n.cc{background:var(--card);border:1px solid var(--border);border-radius:11px;padding:1rem}\n.cc-title{font-size:10px;color:var(--muted);font-family:'IBM Plex Mono',monospace;letter-spacing:.8px;margin-bottom:.85rem}\n.bar-area{display:flex;align-items:flex-end;gap:5px;height:90px;margin-bottom:20px}\n.bcol{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:flex-end;height:100%}\n.bbar{width:80%;border-radius:3px 3px 0 0;min-height:3px;cursor:pointer;transition:opacity .15s}\n.bbar:hover{opacity:.7}\n.bval{font-size:9px;color:var(--text);font-family:'IBM Plex Mono',monospace;margin-bottom:2px}\n.blbl{font-size:9px;color:var(--muted);font-family:'IBM Plex Mono',monospace;margin-top:4px;white-space:nowrap}\n.dual-bar{width:42%;border-radius:3px 3px 0 0;min-height:3px;cursor:pointer;transition:opacity .15s}\n.dual-bar:hover{opacity:.7}\n.prog{margin-bottom:.55rem}\n.prog-head{display:flex;justify-content:space-between;font-size:11px;margin-bottom:3px}\n.prog-bg{background:var(--surf);border-radius:4px;height:7px;overflow:hidden}\n.prog-fill{height:100%;border-radius:4px;transition:width .5s}\n.donut-row{display:flex;align-items:center;justify-content:center;gap:1.25rem}\n.dleg{display:flex;flex-direction:column;gap:6px}\n.dli{display:flex;align-items:center;gap:6px;font-size:11px}\n.ddot{width:8px;height:8px;border-radius:50%;flex-shrink:0}\n.tk-row{display:flex;gap:8px;margin-bottom:1rem}\n.tk-box{flex:1;background:var(--surf);border-radius:9px;padding:.75rem;text-align:center}\n.tk-num{font-size:28px;font-weight:700;line-height:1}\n.tk-lbl{font-size:10px;color:var(--muted);font-family:'IBM Plex Mono',monospace;margin-top:4px}\n.bill-row{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:1rem}\n.bill-box{background:var(--surf);border-radius:9px;padding:.85rem}\n.bill-lbl{font-size:10px;color:var(--muted);font-family:'IBM Plex Mono',monospace;margin-bottom:5px}\n.bill-val{font-size:19px;font-weight:700}\n.bill-bar-bg{background:var(--border);border-radius:4px;height:5px;margin-top:7px;overflow:hidden}\n.bill-bar-fill{height:100%;border-radius:4px}\n.leg-row{display:flex;gap:12px;flex-wrap:wrap;margin-bottom:.5rem}\n.leg-item{display:flex;align-items:center;gap:5px;font-size:10px;color:var(--muted);font-family:'IBM Plex Mono',monospace}\n.leg-dot{width:8px;height:8px;border-radius:2px;flex-shrink:0}\n.tbl-wrap{overflow-x:auto}\ntable.dtbl{width:100%;border-collapse:collapse;font-size:12px}\n.dtbl th{color:var(--muted);font-family:'IBM Plex Mono',monospace;font-size:10px;letter-spacing:.5px;padding:6px 8px;text-align:left;border-bottom:1px solid var(--border)}\n.dtbl td{padding:7px 8px;border-bottom:1px solid rgba(26,52,86,.4)}\n.dtbl tr:last-child td{border-bottom:none}\n.badge{display:inline-block;padding:2px 8px;border-radius:20px;font-size:10px;font-family:'IBM Plex Mono',monospace}\n.b-active{background:rgba(0,230,118,.12);color:var(--green)}\n.b-suspend{background:rgba(255,179,0,.12);color:var(--amber)}\n.b-cancel{background:rgba(255,82,82,.12);color:var(--red)}\n.b-open{background:rgba(255,179,0,.12);color:var(--amber)}\n.b-closed{background:rgba(0,230,118,.12);color:var(--green)}\n.sheets-note{background:rgba(0,212,255,.05);border:1px solid rgba(0,212,255,.2);border-radius:9px;padding:.85rem 1rem;margin-bottom:.75rem;font-size:11px;color:var(--muted);font-family:'IBM Plex Mono',monospace}\n.sheets-input{display:flex;gap:8px;margin-bottom:.75rem;flex-wrap:wrap}\n.sheets-input input{flex:1;min-width:200px;background:var(--card);border:1px solid var(--border);color:var(--text);padding:.5rem .75rem;border-radius:7px;font-family:'IBM Plex Mono',monospace;font-size:11px;outline:none;transition:border-color .2s}\n.sheets-input input:focus{border-color:var(--acc)}\n.sheets-input button{background:var(--acc2);border:none;color:#fff;padding:.5rem 1rem;border-radius:7px;cursor:pointer;font-family:'IBM Plex Mono',monospace;font-size:11px;white-space:nowrap}\n<\/style>\n<p><\/head><br \/>\n<body><\/p>\n<div class=\"loading-overlay\" id=\"overlay\">\n<div class=\"spinner\"><\/div>\n<div class=\"loading-msg\" id=\"overlay-msg\">Cargando datos&#8230;<\/div>\n<\/div>\n<div id=\"screen-login\">\n<div class=\"grid-bg\"><\/div>\n<div class=\"lcard\">\n<div class=\"llogo\">\n<div class=\"licon\">\u26a1<\/div>\n<div class=\"ltitle\">Fast<span>Net<\/span><\/div>\n<div class=\"lsub\">\/\/ Panel Administrativo Interno<\/div>\n<\/p><\/div>\n<div class=\"lf\"><label>USUARIO<\/label><input type=\"text\" id=\"l-user\" placeholder=\"usuario\" autocomplete=\"username\"\/><\/div>\n<div class=\"lf\"><label>CONTRASE\u00d1A<\/label><input type=\"password\" id=\"l-pass\" placeholder=\"\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\" autocomplete=\"current-password\"\/><\/div>\n<p>    <button class=\"lbtn\" id=\"l-btn\" onclick=\"doLogin()\">INGRESAR AL PANEL<\/button><\/p>\n<div class=\"lerr\" id=\"l-err\">Usuario o contrase\u00f1a incorrectos<\/div>\n<div class=\"lspinner\" id=\"l-spin\">Verificando&#8230;<\/div>\n<\/p><\/div>\n<\/div>\n<div id=\"screen-dash\">\n<div class=\"topbar\">\n<div><span class=\"tlogo\">Fast<span>Net<\/span><\/span><span class=\"tsub\">\/\/ Panel Interno<\/span><\/div>\n<div class=\"tright\">\n      <span class=\"last-update\" id=\"last-update\"><\/span><br \/>\n      <button class=\"refresh-btn\" onclick=\"loadAll()\">\u21bb actualizar<\/button><br \/>\n      <span class=\"ubadge\" id=\"u-label\">admin<\/span><br \/>\n      <button class=\"logoutbtn\" onclick=\"doLogout()\">\u21a9 salir<\/button>\n    <\/div>\n<\/p><\/div>\n<div class=\"sec\">Resumen en tiempo real<\/div>\n<div class=\"kpi-row\">\n<div class=\"kpi hi\">\n<div class=\"kpi-lbl\">CLIENTES ACTIVOS<\/div>\n<div class=\"kpi-val\" style=\"color:var(--acc)\" id=\"k-activos\">\u2014<\/div>\n<div class=\"kpi-sub\" id=\"k-activos-sub\">cargando&#8230;<\/div>\n<\/div>\n<div class=\"kpi warn\">\n<div class=\"kpi-lbl\">SUSPENDIDOS<\/div>\n<div class=\"kpi-val\" style=\"color:var(--amber)\" id=\"k-suspend\">\u2014<\/div>\n<div class=\"kpi-sub ne\">este mes<\/div>\n<\/div>\n<div class=\"kpi danger\">\n<div class=\"kpi-lbl\">CANCELADOS<\/div>\n<div class=\"kpi-val\" style=\"color:var(--red)\" id=\"k-cancel\">\u2014<\/div>\n<div class=\"kpi-sub ne\">acumulado<\/div>\n<\/div>\n<div class=\"kpi ok\">\n<div class=\"kpi-lbl\">FACTURADO MES<\/div>\n<div class=\"kpi-val\" style=\"color:var(--green)\" id=\"k-fact\">\u2014<\/div>\n<div class=\"kpi-sub\" id=\"k-fact-sub\">cargando&#8230;<\/div>\n<\/div>\n<div class=\"kpi ok\">\n<div class=\"kpi-lbl\">RECAUDADO MES<\/div>\n<div class=\"kpi-val\" style=\"color:var(--green)\" id=\"k-rec\">\u2014<\/div>\n<div class=\"kpi-sub\" id=\"k-rec-pct\">\u2014<\/div>\n<\/div>\n<div class=\"kpi warn\">\n<div class=\"kpi-lbl\">TICKETS ABIERTOS<\/div>\n<div class=\"kpi-val\" style=\"color:var(--amber)\" id=\"k-tks-open\">\u2014<\/div>\n<div class=\"kpi-sub\" id=\"k-tks-closed-sub\">cerrados: \u2014<\/div>\n<\/div><\/div>\n<div class=\"sec\">Clientes instalados \u2014 2026<\/div>\n<div class=\"g2\">\n<div class=\"cc\">\n<div class=\"cc-title\">CLIENTES ACTIVOS POR MES<\/div>\n<div class=\"bar-area\" id=\"bar-clientes\"><\/div>\n<\/p><\/div>\n<div class=\"cc\">\n<div class=\"cc-title\">ESTADO ACTUAL DE CLIENTES<\/div>\n<div class=\"donut-row\">\n        <svg width=\"90\" height=\"90\" viewBox=\"0 0 90 90\" id=\"svg-clientes\"><circle cx=\"45\" cy=\"45\" r=\"33\" fill=\"none\" stroke=\"#1a2235\" stroke-width=\"13\"\/><\/svg><\/p>\n<div class=\"dleg\" id=\"leg-clientes\"><\/div>\n<\/p><\/div>\n<\/p><\/div>\n<\/p><\/div>\n<div class=\"sec\">Facturaci\u00f3n y recaudo \u2014 2026<\/div>\n<div class=\"g2\">\n<div class=\"cc\">\n<div class=\"cc-title\">FACTURADO VS RECAUDADO POR MES<\/div>\n<div class=\"leg-row\">\n<div class=\"leg-item\">\n<div class=\"leg-dot\" style=\"background:#0055ff\"><\/div>\n<p>Facturado<\/p><\/div>\n<div class=\"leg-item\">\n<div class=\"leg-dot\" style=\"background:#00e676\"><\/div>\n<p>Recaudado<\/p><\/div>\n<\/p><\/div>\n<div class=\"bar-area\" id=\"bar-billing\"><\/div>\n<\/p><\/div>\n<div class=\"cc\">\n<div class=\"cc-title\">DETALLE MES ACTUAL<\/div>\n<div class=\"bill-row\">\n<div class=\"bill-box\">\n<div class=\"bill-lbl\">FACTURADO<\/div>\n<div class=\"bill-val\" style=\"color:var(--acc)\" id=\"b-fact\">\u2014<\/div>\n<div class=\"bill-bar-bg\">\n<div class=\"bill-bar-fill\" style=\"width:100%;background:var(--acc)\"><\/div>\n<\/div>\n<\/div>\n<div class=\"bill-box\">\n<div class=\"bill-lbl\">RECAUDADO<\/div>\n<div class=\"bill-val\" style=\"color:var(--green)\" id=\"b-rec\">\u2014<\/div>\n<div class=\"bill-bar-bg\">\n<div class=\"bill-bar-fill\" id=\"b-rec-bar\" style=\"width:0%;background:var(--green)\"><\/div>\n<\/div>\n<\/div><\/div>\n<div class=\"prog\">\n<div class=\"prog-head\"><span style=\"font-size:11px\">% Recaudo<\/span><span style=\"font-family:'IBM Plex Mono',monospace;font-size:10px;color:var(--muted)\" id=\"b-pct-lbl\">0%<\/span><\/div>\n<div class=\"prog-bg\" style=\"height:9px\">\n<div class=\"prog-fill\" id=\"b-pct-bar\" style=\"width:0%;background:linear-gradient(90deg,var(--green),#00bcd4)\"><\/div>\n<\/div>\n<\/div>\n<div class=\"prog\">\n<div class=\"prog-head\"><span style=\"font-size:11px;color:var(--red)\">Cartera pendiente<\/span><span style=\"font-family:'IBM Plex Mono',monospace;font-size:10px;color:var(--red)\" id=\"b-pend-lbl\">\u2014<\/span><\/div>\n<div class=\"prog-bg\" style=\"height:9px\">\n<div class=\"prog-fill\" id=\"b-pend-bar\" style=\"width:0%;background:var(--red)\"><\/div>\n<\/div>\n<\/div><\/div>\n<\/p><\/div>\n<div class=\"sec\">Tickets de soporte \u2014 2026<\/div>\n<div class=\"g2\">\n<div class=\"cc\">\n<div class=\"cc-title\">TICKETS CREADOS VS CERRADOS POR MES<\/div>\n<div class=\"leg-row\">\n<div class=\"leg-item\">\n<div class=\"leg-dot\" style=\"background:var(--amber)\"><\/div>\n<p>Creados<\/p><\/div>\n<div class=\"leg-item\">\n<div class=\"leg-dot\" style=\"background:var(--green)\"><\/div>\n<p>Cerrados<\/p><\/div>\n<\/p><\/div>\n<div class=\"bar-area\" id=\"bar-tickets\"><\/div>\n<\/p><\/div>\n<div class=\"cc\">\n<div class=\"cc-title\">ESTADO MES ACTUAL<\/div>\n<div class=\"tk-row\">\n<div class=\"tk-box\">\n<div class=\"tk-num\" style=\"color:var(--amber)\" id=\"tk-open\">\u2014<\/div>\n<div class=\"tk-lbl\">ABIERTOS<\/div>\n<\/div>\n<div class=\"tk-box\">\n<div class=\"tk-num\" style=\"color:var(--green)\" id=\"tk-closed\">\u2014<\/div>\n<div class=\"tk-lbl\">CERRADOS<\/div>\n<\/div><\/div>\n<div class=\"prog\">\n<div class=\"prog-head\"><span style=\"font-size:11px\">Tasa de cierre<\/span><span style=\"font-family:'IBM Plex Mono',monospace;font-size:10px;color:var(--muted)\" id=\"tk-rate\">0%<\/span><\/div>\n<div class=\"prog-bg\" style=\"height:8px\">\n<div class=\"prog-fill\" id=\"tk-rate-bar\" style=\"width:0%;background:var(--green)\"><\/div>\n<\/div>\n<\/div><\/div>\n<\/p><\/div>\n<div class=\"sec\">\u00daltimos tickets<\/div>\n<div class=\"cc\" style=\"margin-bottom:1rem\">\n<div class=\"tbl-wrap\">\n<table class=\"dtbl\">\n<thead>\n<tr>\n<th>#<\/th>\n<th>CLIENTE<\/th>\n<th>ASUNTO<\/th>\n<th>FECHA<\/th>\n<th>ESTADO<\/th>\n<\/tr>\n<\/thead>\n<tbody id=\"tabla-tickets\">\n<tr>\n<td colspan=\"5\" style=\"text-align:center;color:var(--muted);padding:1rem\">Cargando&#8230;<\/td>\n<\/tr>\n<\/tbody>\n<\/table><\/div>\n<\/p><\/div>\n<div class=\"sec\">Equipos recogidos (Google Sheets)<\/div>\n<div class=\"sheets-note\">\n    \ud83d\udccb Publica tu Google Sheets como CSV: Archivo \u2192 Compartir \u2192 Publicar en la web \u2192 CSV \u2192 Copiar enlace y p\u00e9galo aqu\u00ed.\n  <\/div>\n<div class=\"sheets-input\">\n    <input type=\"text\" id=\"sheets-url\" placeholder=\"https:\/\/docs.google.com\/spreadsheets\/d\/...\"\/><br \/>\n    <button onclick=\"loadSheets()\">Cargar<\/button>\n  <\/div>\n<div class=\"g2\" id=\"equipos-section\" style=\"display:none\">\n<div class=\"cc\">\n<div class=\"cc-title\">EQUIPOS RECOGIDOS POR MES<\/div>\n<div class=\"bar-area\" id=\"bar-equipos\"><\/div>\n<\/p><\/div>\n<div class=\"cc\">\n<div class=\"cc-title\">POR TECNOLOG\u00cdA \u2014 MES ACTUAL<\/div>\n<div class=\"donut-row\">\n        <svg width=\"90\" height=\"90\" viewBox=\"0 0 90 90\" id=\"svg-equipos\"><circle cx=\"45\" cy=\"45\" r=\"33\" fill=\"none\" stroke=\"#1a2235\" stroke-width=\"13\"\/><\/svg><\/p>\n<div class=\"dleg\" id=\"leg-equipos\"><\/div>\n<\/p><\/div>\n<\/p><\/div>\n<\/p><\/div>\n<\/div>\n<p><script>\nconst WP_BASE = 'https:\/\/fast-net.com.co\/wp-json\/fastnet\/v1';\nconst MESES = ['Ene','Feb','Mar','Abr','May','Jun','Jul','Ago','Sep','Oct','Nov','Dic'];\nlet SESSION_TOKEN = localStorage.getItem('fn_token') || '';\nlet SESSION_USER  = localStorage.getItem('fn_user')  || '';<\/p>\n<p>function showOverlay(msg='Cargando...'){\n  document.getElementById('overlay').style.display='flex';\n  document.getElementById('overlay-msg').textContent=msg;\n}\nfunction hideOverlay(){ document.getElementById('overlay').style.display='none' }\nfunction fmtK(n){ return n>=1e6?'$'+(n\/1e6).toFixed(1)+'M':n>=1000?'$'+(n\/1000).toFixed(0)+'K':'$'+n }<\/p>\n<p>async function doLogin(){\n  const u=document.getElementById('l-user').value.trim();\n  const p=document.getElementById('l-pass').value;\n  const btn=document.getElementById('l-btn');\n  const err=document.getElementById('l-err');\n  err.style.display='none'; btn.disabled=true;\n  document.getElementById('l-spin').style.display='block';\n  try{\n    const r=await fetch(WP_BASE+'\/login',{method:'POST',headers:{'Content-Type':'application\/json'},body:JSON.stringify({usuario:u,password:p})});\n    const d=await r.json();\n    if(r.ok && d.token){\n      SESSION_TOKEN=d.token; SESSION_USER=d.usuario;\n      localStorage.setItem('fn_token',d.token);\n      localStorage.setItem('fn_user',d.usuario);\n      document.getElementById('screen-login').style.display='none';\n      document.getElementById('screen-dash').style.display='block';\n      document.getElementById('u-label').textContent=d.usuario;\n      await loadAll();\n    }else{ err.textContent=d.message||'Error de autenticaci\u00f3n'; err.style.display='block'; }\n  }catch(e){ err.textContent='No se pudo conectar con el servidor'; err.style.display='block'; }\n  finally{ btn.disabled=false; document.getElementById('l-spin').style.display='none'; }\n}<\/p>\n<p>function doLogout(){\n  SESSION_TOKEN=''; SESSION_USER='';\n  localStorage.removeItem('fn_token'); localStorage.removeItem('fn_user');\n  document.getElementById('screen-dash').style.display='none';\n  document.getElementById('screen-login').style.display='flex';\n  document.getElementById('l-user').value=''; document.getElementById('l-pass').value='';\n}<\/p>\n<p>document.addEventListener('keydown',e=>{ if(e.key==='Enter') doLogin() });<\/p>\n<p>async function apiCall(endpoint,body={}){\n  const r=await fetch(WP_BASE+'\/mikro\/'+endpoint,{\n    method:'POST',\n    headers:{'Content-Type':'application\/json','X-Fastnet-Token':SESSION_TOKEN},\n    body:JSON.stringify(body)\n  });\n  if(r.status===401){ doLogout(); return null; }\n  return r.json();\n}<\/p>\n<p>async function loadAll(){\n  showOverlay('Conectando con Mikrosystem...');\n  try{\n    const now=new Date();\n    const [clientes,facturas,pagos,tickets]=await Promise.all([\n      apiCall('clientes'),\n      apiCall('facturas',{mes:now.getMonth()+1,anio:now.getFullYear()}),\n      apiCall('pagos',{mes:now.getMonth()+1,anio:now.getFullYear()}),\n      apiCall('tickets'),\n    ]);\n    if(clientes) renderClientes(clientes);\n    if(facturas&&pagos) renderBilling(facturas,pagos);\n    if(tickets) renderTickets(tickets);\n    document.getElementById('last-update').textContent='Actualizado: '+now.toLocaleTimeString('es-CO',{hour:'2-digit',minute:'2-digit'});\n  }catch(e){ console.error(e); alert('Error cargando datos. Revisa la consola (F12).'); }\n  finally{ hideOverlay(); }\n}<\/p>\n<p>function renderClientes(data){\n  const activos=data.activos??data.total_activos??0;\n  const suspendidos=data.suspendidos??data.total_suspendidos??0;\n  const cancelados=data.cancelados??data.total_cancelados??0;\n  const total=activos+suspendidos+cancelados||1;\n  document.getElementById('k-activos').textContent=activos.toLocaleString('es-CO');\n  document.getElementById('k-activos-sub').textContent='de '+total.toLocaleString('es-CO')+' totales';\n  document.getElementById('k-suspend').textContent=suspendidos.toLocaleString('es-CO');\n  document.getElementById('k-cancel').textContent=cancelados.toLocaleString('es-CO');\n  const porMes=data.por_mes||new Array(12).fill(0);\n  renderBarSimple('bar-clientes',porMes,'linear-gradient(to top,#0055ff,#00d4ff)');\n  renderDonut('svg-clientes','leg-clientes',[\n    {label:'Activos',val:activos,color:'#00e676'},\n    {label:'Suspendidos',val:suspendidos,color:'#ffb300'},\n    {label:'Cancelados',val:cancelados,color:'#ff5252'},\n  ],total);\n}<\/p>\n<p>function renderBilling(facturas,pagos){\n  const fact=facturas.total??facturas.monto_total??0;\n  const rec=pagos.total??pagos.monto_total??0;\n  const pct=fact>0?Math.round((rec\/fact)*100):0;\n  const pend=fact-rec;\n  document.getElementById('k-fact').textContent=fmtK(fact);\n  document.getElementById('k-fact-sub').textContent='mes '+MESES[new Date().getMonth()];\n  document.getElementById('k-rec').textContent=fmtK(rec);\n  document.getElementById('k-rec-pct').textContent='recaudo: '+pct+'%';\n  document.getElementById('b-fact').textContent=fmtK(fact);\n  document.getElementById('b-rec').textContent=fmtK(rec);\n  document.getElementById('b-rec-bar').style.width=pct+'%';\n  document.getElementById('b-pct-lbl').textContent=pct+'%';\n  document.getElementById('b-pct-bar').style.width=pct+'%';\n  document.getElementById('b-pend-lbl').textContent=fmtK(pend);\n  document.getElementById('b-pend-bar').style.width=(100-pct)+'%';\n  renderBarDual('bar-billing',facturas.por_mes||new Array(12).fill(0),pagos.por_mes||new Array(12).fill(0),'#0055ff','#00e676');\n}<\/p>\n<p>function renderTickets(data){\n  const abiertos=data.abiertos??0;\n  const cerrados=data.cerrados??0;\n  const total=abiertos+cerrados||1;\n  const rate=Math.round((cerrados\/total)*100);\n  document.getElementById('k-tks-open').textContent=abiertos;\n  document.getElementById('k-tks-closed-sub').textContent='cerrados: '+cerrados;\n  document.getElementById('tk-open').textContent=abiertos;\n  document.getElementById('tk-closed').textContent=cerrados;\n  document.getElementById('tk-rate').textContent=rate+'%';\n  document.getElementById('tk-rate-bar').style.width=rate+'%';\n  renderBarDual('bar-tickets',data.creados_mes||new Array(12).fill(0),data.cerrados_mes||new Array(12).fill(0),'#ffb300','#00e676');\n  const lista=data.tickets||data.lista||[];\n  const tbody=document.getElementById('tabla-tickets');\n  if(!lista.length){ tbody.innerHTML='<\/p>\n<tr>\n<td colspan=\"5\" style=\"text-align:center;color:var(--muted);padding:1rem\">Sin tickets<\/td>\n<\/tr>\n<p>'; return; }\n  tbody.innerHTML=lista.slice(0,10).map(t=>`<\/p>\n<tr>\n<td style=\"color:var(--muted);font-family:'IBM Plex Mono',monospace\">#${t.id||t.codigo||'\u2014'}<\/td>\n<td>${t.cliente||t.nombre||'\u2014'}<\/td>\n<td>${t.asunto||t.titulo||'\u2014'}<\/td>\n<td style=\"color:var(--muted);font-family:'IBM Plex Mono',monospace;white-space:nowrap\">${t.fecha||'\u2014'}<\/td>\n<td><span class=\"badge ${t.estado==='Abierto'||t.estado==='abierto'?'b-open':'b-closed'}\">${t.estado||'\u2014'}<\/span><\/td>\n<\/tr>\n<p>`).join('');\n}<\/p>\n<p>async function loadSheets(){\n  const url=document.getElementById('sheets-url').value.trim();\n  if(!url){ alert('Ingresa la URL del Google Sheets'); return; }\n  showOverlay('Cargando Google Sheets...');\n  try{\n    const r=await fetch(url);\n    const text=await r.text();\n    const rows=text.trim().split('\\n').map(l=>l.split(','));\n    const headers=rows[0];\n    const mesesData=rows.slice(1).filter(r=>r.length>1);\n    const techs=headers.slice(1,headers.length-1);\n    const colors=['#00d4ff','#0055ff','#00e676','#ffb300','#7c3aed','#ff5252'];\n    renderBarSimple('bar-equipos',mesesData.map(r=>parseInt(r[r.length-1])||0),'linear-gradient(to top,#7c3aed,#00d4ff)');\n    const lastRow=mesesData[mesesData.length-1];\n    const lastTotal=parseInt(lastRow[lastRow.length-1])||1;\n    renderDonut('svg-equipos','leg-equipos',techs.map((t,i)=>({label:t,val:parseInt(lastRow[i+1])||0,color:colors[i]||'#888'})),lastTotal);\n    document.getElementById('equipos-section').style.display='grid';\n  }catch(e){ alert('Error cargando Sheets. Verifica que la URL sea CSV p\u00fablica.'); }\n  finally{ hideOverlay(); }\n}<\/p>\n<p>function renderBarSimple(id,values,color){\n  const el=document.getElementById(id); if(!el)return;\n  const active=values.map((v,i)=>({v,i})).filter(x=>x.v>0);\n  const max=Math.max(...active.map(x=>x.v),1);\n  el.innerHTML='';\n  active.forEach(({v,i})=>{\n    const col=document.createElement('div'); col.className='bcol';\n    col.innerHTML=`<\/p>\n<div class=\"bval\">${v}<\/div>\n<div class=\"bbar\" style=\"height:${Math.round(v\/max*100)}%;background:${color}\" title=\"${MESES[i]}: ${v}\"><\/div>\n<div class=\"blbl\">${MESES[i]}<\/div>\n<p>`;\n    el.appendChild(col);\n  });\n}<\/p>\n<p>function renderBarDual(id,vals1,vals2,c1,c2){\n  const el=document.getElementById(id); if(!el)return;\n  const max=Math.max(...[...vals1,...vals2].filter(v=>v>0),1);\n  el.innerHTML='';\n  vals1.forEach((v1,i)=>{\n    if(!v1&&!vals2[i])return;\n    const col=document.createElement('div');\n    col.style.cssText='flex:1;display:flex;flex-direction:column;align-items:center;justify-content:flex-end;height:100%';\n    col.innerHTML=`<\/p>\n<div style=\"display:flex;gap:2px;align-items:flex-end;width:100%;justify-content:center;flex:1\">\n<div class=\"dual-bar\" style=\"height:${Math.round(v1\/max*100)}%;background:${c1}\"><\/div>\n<div class=\"dual-bar\" style=\"height:${Math.round((vals2[i]||0)\/max*100)}%;background:${c2}\"><\/div>\n<\/p><\/div>\n<div class=\"blbl\">${MESES[i]}<\/div>\n<p>`;\n    el.appendChild(col);\n  });\n}<\/p>\n<p>function renderDonut(svgId,legId,items,total){\n  const svg=document.getElementById(svgId); if(!svg)return;\n  const C=2*Math.PI*33; let off=0;\n  const circles=items.filter(x=>x.val>0).map(item=>{\n    const d=(item.val\/total)*C; const g=C-d;\n    const c=`<circle cx=\"45\" cy=\"45\" r=\"33\" fill=\"none\" stroke=\"${item.color}\" stroke-width=\"13\" stroke-dasharray=\"${d.toFixed(1)} ${g.toFixed(1)}\" stroke-dashoffset=\"${(-off).toFixed(1)}\"\/>`;\n    off+=d; return c;\n  }).join('');\n  svg.innerHTML=`<circle cx=\"45\" cy=\"45\" r=\"33\" fill=\"none\" stroke=\"#1a2235\" stroke-width=\"13\"\/>${circles}<text x=\"45\" y=\"49\" text-anchor=\"middle\" font-size=\"13\" font-weight=\"700\" fill=\"#dff0ff\" font-family=\"Syne,sans-serif\">${total}<\/text>`;\n  document.getElementById(legId).innerHTML=items.map(x=>`<\/p>\n<div class=\"dli\">\n<div class=\"ddot\" style=\"background:${x.color}\"><\/div>\n<p>${x.label} <span style=\"color:var(--muted);font-family:'IBM Plex Mono',monospace;font-size:10px;margin-left:3px\">${x.val}<\/span><\/div>\n<p>`).join('');\n}<\/p>\n<p>if(SESSION_TOKEN){\n  document.getElementById('screen-login').style.display='none';\n  document.getElementById('screen-dash').style.display='block';\n  document.getElementById('u-label').textContent=SESSION_USER;\n  loadAll();\n}\n<\/script><br \/>\n<\/body><br \/>\n<\/html><\/p>\n","protected":false},"excerpt":{"rendered":"<p>FastNet \u2014 Panel Administrativo Cargando datos&#8230; \u26a1 FastNet \/\/ Panel Administrativo Interno USUARIO CONTRASE\u00d1A INGRESAR AL PANEL Usuario o contrase\u00f1a incorrectos Verificando&#8230; FastNet\/\/ Panel Interno \u21bb actualizar admin \u21a9 salir Resumen en tiempo real CLIENTES ACTIVOS \u2014 cargando&#8230; SUSPENDIDOS \u2014 este mes CANCELADOS \u2014 acumulado FACTURADO MES \u2014 cargando&#8230; RECAUDADO MES \u2014 \u2014 TICKETS ABIERTOS [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"elementor_canvas","meta":{"footnotes":""},"class_list":["post-2784","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/fast-net.com.co\/index.php\/wp-json\/wp\/v2\/pages\/2784"}],"collection":[{"href":"https:\/\/fast-net.com.co\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/fast-net.com.co\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/fast-net.com.co\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/fast-net.com.co\/index.php\/wp-json\/wp\/v2\/comments?post=2784"}],"version-history":[{"count":1,"href":"https:\/\/fast-net.com.co\/index.php\/wp-json\/wp\/v2\/pages\/2784\/revisions"}],"predecessor-version":[{"id":2785,"href":"https:\/\/fast-net.com.co\/index.php\/wp-json\/wp\/v2\/pages\/2784\/revisions\/2785"}],"wp:attachment":[{"href":"https:\/\/fast-net.com.co\/index.php\/wp-json\/wp\/v2\/media?parent=2784"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}