// ── PANEL DE ADMIN ──────────────────────────────────────────── const AdminScreen = () => { const[tab,setTab]=React.useState('usuarios'); const[stats,setStats]=React.useState({}); const[users,setUsers]=React.useState([]); const[invites,setInvites]=React.useState([]); const[events,setEvents]=React.useState([]); const[modal,setModal]=React.useState(false); const[invForm,setInvForm]=React.useState({email:'',plan:'v1',name:''}); const[copied,setCopied]=React.useState(''); const[sending,setSending]=React.useState(false); const load=()=>{ API.get('/admin/stats').then(r=>r&&setStats(r)); API.get('/admin/users').then(r=>r&&setUsers(r)); API.get('/admin/invitations').then(r=>r&&setInvites(r)); API.get('/admin/hotmart-events').then(r=>r&&setEvents(r)); }; React.useEffect(()=>{load();},[]); const sendInvite=async()=>{ if(!invForm.email){showToast('Email requerido','error');return;} setSending(true); const res=await API.post('/admin/invite',invForm); setSending(false); if(res.error){showToast(res.error,'error');return;} setModal(false); setInvForm({email:'',plan:'v1',name:''}); showToast(res.email_sent?'Invitación enviada por email ✓':'Invitación creada (email no configurado)'); load(); // Copiar link al portapapeles if(res.invite_url) { try{navigator.clipboard.writeText(res.invite_url);}catch{} } }; const copyLink=async(url)=>{ try{await navigator.clipboard.writeText(url); setCopied(url); setTimeout(()=>setCopied(''),2000);}catch{} }; const updateUser=async(id,field,val)=>{ const u=users.find(x=>x.id===id); await API.put(`/admin/users/${id}`,{...u,[field]:val}); showToast('Actualizado ✓'); load(); }; const deleteInvite=async(id)=>{ if(!confirm('¿Cancelar esta invitación?'))return; await API.del(`/admin/invitations/${id}`); showToast('Invitación cancelada'); load(); }; const statusColor={active:EJ.green,suspended:EJ.pink,pending:'#F59E0B'}; const statusLabel={active:'Activa',suspended:'Suspendida',pending:'Pendiente'}; const planLabel={v1:'Esencial ($3)',v2:'Pro IA ($6)'}; const hotmartColor={'active':EJ.green,'canceled':'#F59E0B','payment_failed':EJ.pink,'suspended':EJ.pink}; return (

Panel de Admin

Gestión de usuarias, invitaciones y Hotmart

setModal(true)}>+ Enviar invitación
{/* Stats */}
{[ {label:'Usuarias totales',value:stats.total_users||0,color:EJ.navy}, {label:'Activas',value:stats.active_users||0,color:EJ.green}, {label:'Suspendidas',value:stats.suspended_users||0,color:EJ.pink}, {label:'Invitaciones pendientes',value:stats.pending_invites||0,color:EJ.violet}, {label:'Eventos Hotmart hoy',value:stats.events_today||0,color:EJ.cyan}, ].map((s,i)=>
{s.label}
{s.value}
)}
{/* Tabs */}
{[{id:'usuarios',label:'👥 Usuarias'},{id:'invitaciones',label:'✉️ Invitaciones'},{id:'hotmart',label:'🔗 Hotmart'}].map(t=>( ))}
{/* USUARIOS */} {tab==='usuarios'&&
{['Email','Nombre','Plan','Estado Cuenta','Estado Hotmart','Registro','Último acceso','Acciones'].map(h=>)} {users.length===0?: users.map(u=>)}
{h}
Sin usuarias registradas
{u.email} {u.name||'—'} {u.hotmart_subscription_status||'—'} {u.created_at?.slice(0,10)||'—'} {u.last_login?.slice(0,10)||'Nunca'}
} {/* INVITACIONES */} {tab==='invitaciones'&&
💡 Cómo funciona: Cuando envías una invitación, la usuaria recibe un email con un link único. Al hacer clic, puede registrarse y el link se marca como usado. Si no tienes email configurado, copia el link manualmente y envíalo por WhatsApp.
{['Email','Plan','Estado','Expira','Link de invitación','Transacción Hotmart',''].map(h=>)} {invites.length===0?: invites.map(inv=>{ const expired=new Date(inv.expires_at) ; })}
{h}
Sin invitaciones
{inv.email} {inv.plan==='v2'?'Pro':'Esencial'} {inv.status==='used'?'✓ Usada':expired?'Expirada':'Pendiente'} {inv.expires_at?.slice(0,10)} {inv.status==='pending'&&!expired&&
{invUrl}
}
{inv.hotmart_transaction||'Manual'} {inv.status==='pending'&&}
} {/* HOTMART EVENTOS */} {tab==='hotmart'&&
🔗 URL del Webhook para Hotmart
{window.location.origin}/api/webhook/hotmart
Configura en Hotmart: Panel → Herramientas → Webhook → + Registrar → Pega esta URL.
Eventos recomendados: PURCHASE_APPROVED · SUBSCRIPTION_CANCELLATION · PURCHASE_REFUNDED · SUBSCRIPTION_PAYMENT_FAILED
Variable de entorno: Agrega HOTMART_HOTTOK con el token de seguridad de Hotmart para mayor seguridad.
📋 Log de eventos recientes
{events.length===0?
🔗

Sin eventos. Cuando Hotmart envíe notificaciones, aparecerán aquí.

:(
{['Evento','Email','Transacción','Plan','Estado','Fecha'].map(h=>)} {events.map(e=>)}
{h}
{e.event||'—'} {e.email||'—'} {e.transaction?.slice(0,16)||'—'} {e.plan} {e.processed?'✓ Procesado':'Pendiente'} {e.created_at?.slice(0,16)}
)}
} {/* MODAL INVITACIÓN */} setModal(false)} title="✉️ Enviar invitación" width={440}>
La usuaria recibirá un email con link de activación. Si SMTP no está configurado, se generará un link que puedes copiar y enviar manualmente.
setInvForm(f=>({...f,email:e.target.value}))}/> setInvForm(f=>({...f,name:e.target.value}))}/>