/* PANEL SAAS · Orgullo Millonario — React + Tailwind · Liquid Glass
Conectado a OM-DB: lo que se edita aquí se refleja EN VIVO en la página principal (site.html). */
const {useState,useEffect,useRef,useMemo,useSyncExternalStore,createContext,useContext}=React;
const useDB=()=>useSyncExternalStore(OMDB.subscribe,OMDB.get);
/* ============ ICONS ============ */
const I={
dash: ,
card:<> >,
squad:<> >,
spark: ,
gift:<> >,
heart: ,
play:<> >,
chat: ,
grid:<> >,
bell: ,
search:<> >,
x: ,
down: ,
eye:<> >,
};
const Ic=({n,s=18,c=""})=>{I[n]} ;
/* ============ TOAST ============ */
const ToastCtx=createContext(()=>{});
function ToastHost({children}){
const [msg,setMsg]=useState(null);const t=useRef();
const toast=m=>{setMsg(m);clearTimeout(t.current);t.current=setTimeout(()=>setMsg(null),2400);};
return {children}
;
}
/* ============ SHARED ============ */
const Sec=({label,children,right})=>(
{label} {right}
);
const Bar=({pct,cls})=>(
);
/* ============ MODULES ============ */
function KpiModule(){
const db=useDB();const k=db.kpis;
const kpis=[[k.viajes,"VIAJES PUBLICADOS","#2f7df6","🚌"],[k.reservas,"RESERVAS","#22c55e","📅"],[k.socios,"SOCIOS","#ffcf3e","👤"],[k.visitas,"VISITAS TOTALES","#ff2e3e","👁"]];
return
{kpis.map(([n,l,c,e],i)=>(
;
}
/* ============ SITIO & HERO — controla la página principal EN VIVO ============ */
function SiteModule(){
const db=useDB();const toast=useContext(ToastCtx);const c=db.config;
const F=({label,val,onChange,mono})=>(
{label}
onChange(e.target.value)} className={`w-full bg-white/[.06] border border-white/10 rounded-xl px-3.5 py-2.5 text-[12px] outline-none focus:border-river/60 transition-colors ${mono?'font-mono':''}`}/>);
return <>
ABRIR SITIO ↗}/>
OMDB.set('config.hero_titulo',v)}/>
OMDB.set('config.hero_subtitulo',v)}/>
OMDB.set('config.whatsapp',v)} mono/>
OMDB.set('config.mp_link',v)} mono/>
TEMA DEL SITIO
{[['dark','Oscuro'],['light','Claro']].map(([t,l])=>(
{OMDB.set('config.tema',t);toast('Tema: '+l);}} className={`press rounded-xl py-2.5 text-[12px] font-bold border transition-all ${c.tema===t?'text-white border-river':'text-mut border-white/10'}`} style={c.tema===t?{background:'linear-gradient(160deg,#ff2e3e,#8d0917)'}:{}}>{l} ))}
{OMDB.reset();toast('Datos restablecidos a los valores de fábrica');}} className="press glass rounded-xl py-2.5 text-[11px] font-bold text-mut hover:text-ink mt-1">↺ Restablecer datos del sitio
>;
}
function RevenueModule(){
return <>
$24.900/mes}/>
{[["$24.900","MES ACTUAL","text-gold2"],["$1.22M","META ANUAL","text-ink"],["20,41%","PROGRESO","text-green-400"],["21","OPERACIONES","text-ink"]].map(([b,s,c],i)=>(
{b} {s}
))}
>;
}
function WorldModule(){
const dots=useMemo(()=>{
let seed=77;const rnd=()=>((seed=(seed*1103515245+12345)&0x7fffffff)/0x7fffffff);
const zones=[[18,18,55,32,.5],[55,52,28,48,.6],[112,12,40,26,.5],[118,40,32,38,.55],[150,14,60,40,.5],[196,60,26,20,.45]];
const a=[];zones.forEach(z=>{for(let i=0;i
EN VIVO}/>
{dots.map((d,i)=> )}
{hot.map((p,i)=> )}
>;
}
function FiguritasModule(){
const toast=useContext(ToastCtx);
const RZ=[["COMÚN","#9a9aa6"],["PLATA","#c0c0cb"],["ORO","#ffcf3e"],["LEYENDA","#ff2e3e"],["HOLOGRÁFICA","#8b5cf6"],["ULTRA PREMIUM","#22d3ee"]];
const CLUBS=[["River","assets/river.png"],["Boca","assets/boca.png"],["Barracas","assets/barracas.png"],["Racing","assets/racing.png"],["Talleres","assets/talleres.png"],["Argentinos","assets/argentinos.png"]];
const FLAGS=["🇦🇷","🇧🇷","🇺🇾","🇨🇴","🇨🇱","🇵🇾","🇻🇪"];
const [rz,setRz]=useState(4);
const [name,setName]=useState("Franco Armani");
const [num,setNum]=useState("23");
const [pos,setPos]=useState("ARQ");
const [club,setClub]=useState(0);
const [flag,setFlag]=useState(0);
const [photo,setPhoto]=useState("assets/armani.png");
const [imgH,setImgH]=useState(108);
const [imgY,setImgY]=useState(0);
const fileRef=useRef();
const c=RZ[rz][1];
const onFile=e=>{const f=e.target.files&&e.target.files[0];if(!f)return;if(f.size>4.5e6){toast('Imagen muy grande (máx 4.5MB)');return;}const r=new FileReader();r.onload=()=>{setPhoto(r.result);toast('Imagen del jugador cargada');};r.readAsDataURL(f);};
const crear=()=>{
if(!name.trim()){toast('Poné un nombre');return;}
OMDB.update(s=>{s.figuritas=[{clave:'c'+Date.now(),nombre:name.trim(),tier:RZ[rz][0],img:photo,tenida:true,cant:1,num,pos,flag:FLAGS[flag],club:CLUBS[club][1]},...s.figuritas].slice(0,20);});
toast(`✔ ${name} (${RZ[rz][0]}) publicada en el álbum del sitio`);
};
const exportPNG=async()=>{
const W=520,H=728,cv=document.createElement('canvas');cv.width=W;cv.height=H;const x=cv.getContext('2d');
const rr=(a,b,w,h,r)=>{x.beginPath();x.moveTo(a+r,b);x.arcTo(a+w,b,a+w,b+h,r);x.arcTo(a+w,b+h,a,b+h,r);x.arcTo(a,b+h,a,b,r);x.arcTo(a,b,a+w,b,r);x.closePath();};
rr(0,0,W,H,40);x.clip();
const g=x.createLinearGradient(0,0,W,H);g.addColorStop(0,c+'55');g.addColorStop(.5,'#1a1226');g.addColorStop(1,'#0b0714');x.fillStyle=g;x.fillRect(0,0,W,H);
const load=src=>new Promise(res=>{const im=new Image();im.crossOrigin='anonymous';im.onload=()=>res(im);im.onerror=()=>res(null);im.src=src;});
const pl=await load(photo);
if(pl){const ph=H*(imgH/100),pw=ph*(pl.width/pl.height);x.drawImage(pl,(W-pw)/2,H-ph+imgY*3,pw,ph);}
const ov=x.createLinearGradient(0,H*0.45,0,H);ov.addColorStop(0,'rgba(0,0,0,0)');ov.addColorStop(1,'rgba(0,0,0,.95)');x.fillStyle=ov;x.fillRect(0,H*0.45,W,H*0.55);
// holo streak
const hs=x.createLinearGradient(0,0,W,H);hs.addColorStop(.3,'rgba(255,255,255,0)');hs.addColorStop(.47,'rgba(255,255,255,.28)');hs.addColorStop(.52,c+'88');hs.addColorStop(.68,'rgba(255,255,255,0)');x.fillStyle=hs;x.fillRect(0,0,W,H);
// flag + number
x.font='40px serif';x.fillText(FLAGS[flag],24,58);
x.fillStyle='rgba(255,255,255,.14)';x.font='900 120px Oswald, sans-serif';x.textAlign='right';x.fillText(num,W-28,150);
// name + tier
x.textAlign='center';x.fillStyle='#fff';x.font='700 46px Oswald, sans-serif';x.fillText(name.toUpperCase().slice(0,16),W/2,H-92);
x.fillStyle=c;x.font='700 22px "JetBrains Mono", monospace';x.fillText(RZ[rz][0]+' · #'+num,W/2,H-54);
// border
x.strokeStyle=c;x.lineWidth=8;rr(4,4,W-8,H-8,36);x.stroke();
const club_im=await load(CLUBS[club][1]);if(club_im){x.drawImage(club_im,W-84,24,56,56);}
const a=document.createElement('a');a.download=`figurita-${name.toLowerCase().replace(/\s+/g,'-')||'cromo'}.png`;a.href=cv.toDataURL('image/png');a.click();
toast('PNG descargado ✔');
};
return <>
{RZ.map(([r,cc],i)=>(
{setRz(i);}}
className={`press rounded-full px-4 py-2 text-[10px] font-mono tracking-[1px] border transition-all ${i===rz?'text-white':'text-mut border-white/10 hover:text-ink'}`}
style={i===rz?{borderColor:cc,background:`color-mix(in srgb, ${cc} 22%, transparent)`,boxShadow:`0 0 20px ${cc}44`}:{}}>{r} ))}
{CLUBS.map(([n,src],i)=>
setClub(i)} className={`press w-9 h-9 rounded-lg grid place-items-center bg-white/[.05] border ${i===club?'border-river':'border-white/10'}`}> )}
{FLAGS.map((f,i)=>setFlag(i)} className={`press w-9 h-9 rounded-lg grid place-items-center text-[16px] bg-white/[.05] border ${i===flag?'border-river':'border-white/10'}`}>{f} )}
+ Crear y publicar
⬇ Descargar PNG
VISTA PREVIA EN VIVO
{FLAGS[flag]}
{num}
{(rz===4||rz===5)&&
}
{name.toUpperCase()||'—'}
{RZ[rz][0]} · #{num}
>;
}
function PlantillaModule(){
const toast=useContext(ToastCtx);
const G=[["① ARQUEROS",[["armani","Armani","#1"]]],
["② DEFENSORES",[["montiel","Montiel","#4"],["pezzella","Pezzella","#6"],["diaz","Paulo Díaz","#17"]]],
["③ MEDIOCAMPISTAS",[["perez","E. Pérez","#24"],["castano","Castaño","#20"],["quintero","Quintero","#10"]]],
["④ FORWARDS",[["borja","Borja","#9"],["colidio","Colidio","#11"],["salas","M. Salas","#7"]]]];
return <>
40 EN PLANTEL }/>
{G.map(([lbl,ps])=>
{lbl}
{ps.map(([img,n,num])=>(
{n} {num}
))}
)}
toast('Abriendo gestión de plantilla…')} className="press glass rounded-2xl py-2.5 mt-3 text-[11px] font-bold text-mut hover:text-ink w-full">Ver plantilla completa
>;
}
function SponsorsModule(){
const toast=useContext(ToastCtx);
const tiers=[["ORO","text-gold2","rgba(212,164,55,.35)",[ ,"CODERE","GATORADE"]],
["PLATA","text-[#c0c0cb]","rgba(192,192,203,.25)",["STANLEY","KONAMI","MOSTAZA"]],
["BRONCE","text-[#b0763a]","rgba(176,118,58,.3)",["TIENDA RIVER","QUILMES","ASSIST CARD"]]];
return <>
{tiers.map(([t,tc,bc,logos])=>(
{t}
{logos.map((l,i)=>(
{l} ))}
))}
toast('Abriendo gestor de sponsors…')} className="press glass rounded-2xl py-2.5 mt-3 text-[11px] font-bold text-mut hover:text-ink w-full">Gestionar sponsors
>;
}
function HistoriasModule(){
const toast=useContext(ToastCtx);
const viajes=[["Boca Juniors vs River Plate","La Bombonera · 25/05",["BUS","ENTRADA"]],["River Plate vs Talleres","Más Monumental · 01/06",["BUS","COMIDA"]],["River Plate vs Independiente","Más Monumental · 08/06",["BUS"]]];
return <>
⚡ IA}/>
{viajes.map(([t,s,tags])=>(
{t} {s}
{tags.map(g=>{g} )}
))}
{`VAMOS A LA BOMBONERA\nBOCA JRS VS RIVER PLATE\nVIERNES 25/05 · BUS 10:00 HS + ENTRADA\nDESDE EL OBELISCO · RESERVA AL 11 2234-5678\n¡CUPOS LIMITADOS!`}
toast('🤖 Mensaje analizado: viaje detectado y publicado')} className="press rounded-2xl py-3 mt-3 text-[12px] font-bold text-white w-full" style={{background:'linear-gradient(160deg,#ff2e3e,#8d0917)',boxShadow:'0 10px 30px -8px rgba(224,30,47,.6)'}}>Analizar mensaje
>;
}
function SorteosModule(){
const toast=useContext(ToastCtx);
const db=useDB();const s=db.sorteo;const sold=useMemo(()=>new Set(s.sold),[s.sold]);
const toggle=n=>OMDB.update(st=>{const set=new Set(st.sorteo.sold);set.has(n)?set.delete(n):set.add(n);st.sorteo.sold=[...set].sort((a,b)=>a-b);});
return <>
${s.precio} C/U}/>
{Array.from({length:s.total},(_,n)=>(
toggle(n)} className={`press aspect-square rounded-[4px] grid place-items-center font-mono text-[6px] ${sold.has(n)?'bg-crimson text-white':'bg-white/[.06] text-dim border border-white/[.06]'}`}>{String(n).padStart(2,'0')} ))}
■ VENDIDOS {sold.size} □ DISPONIBLES {s.total-sold.size}
>;
}
function ReelsModule(){
const db=useDB();const toast=useContext(ToastCtx);const [txt,setTxt]=useState('');
const add=()=>{if(!txt.trim())return;OMDB.update(s=>{s.reels=[{titulo:txt.trim(),vistas:'0',plataforma:'TIKTOK'},...s.reels];});setTxt('');toast('Reel publicado en el sitio');};
const del=i=>OMDB.update(s=>{s.reels.splice(i,1);});
return <>
setTxt(e.target.value)} onKeyDown={e=>e.key==='Enter'&&add()} placeholder="Título del reel…" className="flex-1 bg-white/[.06] border border-white/10 rounded-xl px-3 py-2 text-[11px] outline-none focus:border-river/60 min-w-0"/>
+
{db.reels.map((r,i)=>(
▶
{r.titulo} {r.vistas} VISTAS · {r.plataforma}
del(i)} className="press w-7 h-7 rounded-lg grid place-items-center text-dim hover:text-river2">
))}
>;
}
/* ============ MODULE REGISTRY (hiper modular) ============ */
const MODULES=[
{id:'kpi', title:'Dashboard & Métricas', icon:'dash', span:'lg:col-span-6', bare:true, C:KpiModule},
{id:'site', title:'Sitio & Hero', icon:'eye', span:'lg:col-span-3', C:SiteModule},
{id:'rev', title:'Facturación', icon:'down', span:'lg:col-span-3', C:RevenueModule},
{id:'world', title:'Visitas', icon:'eye', span:'lg:col-span-3', C:WorldModule},
{id:'figus', title:'Figuritas Builder', icon:'card', span:'lg:col-span-4', C:FiguritasModule},
{id:'squad', title:'Plantilla', icon:'squad', span:'lg:col-span-2 lg:row-span-2', C:PlantillaModule},
{id:'hist', title:'Historias · Telegram', icon:'chat', span:'lg:col-span-2', C:HistoriasModule},
{id:'sorteo',title:'Sorteos', icon:'gift', span:'lg:col-span-2', C:SorteosModule},
{id:'spons', title:'Sponsors', icon:'heart', span:'lg:col-span-2', C:SponsorsModule},
{id:'reels', title:'Reels TikTok', icon:'play', span:'lg:col-span-2', C:ReelsModule},
];
const LS_KEY='om-panel-modules-v1';
/* ============ MODULE MANAGER SHEET ============ */
function ModuleSheet({open,onClose,enabled,setEnabled}){
if(!open)return null;
return
e.stopPropagation()} className="glass-deep relative rounded-t-glass md:rounded-glass w-full md:w-[420px] p-6 safe-b" style={{animation:'sheetUp .35s cubic-bezier(.22,1,.36,1) both'}}>
MÓDULOS DEL PANEL
{MODULES.map(m=>{
const on=enabled.includes(m.id);
return setEnabled(on?enabled.filter(x=>x!==m.id):[...enabled,m.id])}
className="press flex items-center gap-3 bg-white/[.05] border border-white/[.08] rounded-2xl px-4 py-3 text-left">
{m.title}
;})}
TU CONFIGURACIÓN SE GUARDA AUTOMÁTICAMENTE
;
}
/* ============ APP ============ */
function App(){
const toast=useContext(ToastCtx);
const [enabled,setEnabledRaw]=useState(()=>{const all=MODULES.map(m=>m.id);try{const v=JSON.parse(localStorage.getItem(LS_KEY));if(!Array.isArray(v))return all;return[...v.filter(id=>all.includes(id)),...all.filter(id=>!v.includes(id))];}catch(e){return all;}});
const setEnabled=v=>{setEnabledRaw(v);try{localStorage.setItem(LS_KEY,JSON.stringify(v));}catch(e){}};
const [sheet,setSheet]=useState(false);
const [clock,setClock]=useState('');
useEffect(()=>{const f=()=>{const d=new Date(),p=n=>String(n).padStart(2,'0');setClock(`${p(d.getDate())}/${p(d.getMonth()+1)}/${d.getFullYear()} · ${p(d.getHours())}:${p(d.getMinutes())} HS`);};f();const t=setInterval(f,30000);return()=>clearInterval(t);},[]);
const mods=MODULES.filter(m=>enabled.includes(m.id));
const navGo=id=>{const el=document.getElementById('mod-'+id);if(el){const y=el.getBoundingClientRect().top+window.scrollY-84;window.scrollTo({top:y,behavior:'smooth'});}else toast('Activá ese módulo desde “Módulos”');};
return <>
{/* fondo liquid */}
{/* topbar glass */}
{/* grid de módulos */}
{mods.map((m,i)=>m.bare
?
:)}
{mods.length===0&&No hay módulos activos. Tocá Módulos para activarlos.
}
{/* footer */}
ORGULLO MILLONARIO ® 2026 PANEL SAAS v3.0 · LIQUID
SERVIDORES: ONLINE ● ACTUALIZADO: {clock}
{/* tab bar móvil */}
{[['kpi','dash','Inicio'],['figus','card','Figus'],['squad','squad','Plantel'],['sorteo','gift','Sorteos']].map(([id,ic,l])=>(
navGo(id)} className="press flex flex-col items-center gap-1 px-4 py-1.5 rounded-2xl text-mut hover:text-ink">
{l}
))}
setSheet(true)} className="press flex flex-col items-center gap-1 px-4 py-1.5 rounded-2xl text-white">
Módulos
setSheet(false)} enabled={enabled} setEnabled={setEnabled}/>
>;
}
ReactDOM.createRoot(document.getElementById('root')).render( );