// Ventas, Planificación y Revisión — con backend real
// ── VENTAS ─────────────────────────────────────────────────────
const VentasScreen = () => {
const mes=mesActual();
const[ventas,setVentas]=React.useState([]);
const[kpis,setKpis]=React.useState({});
const[modal,setModal]=React.useState(false);
const[form,setForm]=React.useState({fecha:new Date().toISOString().slice(0,10),monto:'',producto:'',clienta:'',canal:'',tipo_clienta:'nueva',tipo_campana:'',notas:''});
const load=()=>Promise.all([API.get(`/ventas?mes=${mes}`).then(r=>r&&setVentas(r)),API.get(`/kpis?mes=${mes}`).then(r=>r&&setKpis(r))]);
React.useEffect(()=>{load();},[]);
const save=async()=>{if(!form.fecha||!form.monto){showToast('Fecha y monto requeridos','error');return;}await API.post('/ventas',form);setModal(false);showToast('Venta registrada ✓');load();setForm({fecha:new Date().toISOString().slice(0,10),monto:'',producto:'',clienta:'',canal:'',tipo_clienta:'nueva',tipo_campana:'',notas:''});};
const del=async id=>{if(!confirm('¿Eliminar esta venta?'))return;await API.del(`/ventas/${id}`);showToast('Eliminada');load();};
const si=c=>({verde:'🟢',amarillo:'🟡',rojo:'🔴'}[c]||'🟡');
const sv=(v,m)=>!m?'amarillo':v/m>=.9?'verde':v/m>=.6?'amarillo':'rojo';
return (
setModal(true)}>+ Nueva venta}/>
{[
{label:'Facturación',value:fmtMoney(kpis.total||0),sem:sv(kpis.total,kpis.meta)},
{label:'Ventas cerradas',value:kpis.count||0,sem:(kpis.count||0)>5?'verde':'amarillo'},
{label:'Ticket promedio',value:fmtMoney(kpis.ticket||0),sem:'amarillo'},
{label:'Nuevas',value:kpis.nuevas||0,sem:(kpis.nuevas||0)>0?'verde':'rojo'},
{label:'Recurrentes',value:kpis.recurrentes||0,sem:(kpis.recurrentes||0)>0?'verde':'amarillo'},
{label:'Canal top',value:kpis.canal_top||'—',sem:'cyan'},
].map((k,i)=>
{k.label}
{k.value}
{si(k.sem)}
)}
📝 Registro de ventas
{ventas.length===0?💰
Sin ventas este mes. ¡Registra la primera!
:(
{['Fecha','Producto','Clienta','Canal','Monto','Tipo','Campaña',''].map(h=>{h} )}
{ventas.map(v=>
{v.fecha}
{v.producto||'—'}
{v.clienta||'—'}
{v.canal||'—'}
{fmtMoney(v.monto)}
{v.tipo_clienta||'—'}
{v.tipo_campana||'—'}
del(v.id)} style={{background:'none',border:'none',cursor:'pointer',color:EJ.pink,fontSize:16}}>✕
)}
Total del mes
{fmtMoney(kpis.total||0)}
)}
setModal(false)} title="💰 Registrar venta" width={480}>
setForm(f=>({...f,fecha:e.target.value}))}/>
setForm(f=>({...f,monto:e.target.value}))}/>
setForm(f=>({...f,producto:e.target.value}))}/>
setForm(f=>({...f,clienta:e.target.value}))}/>
setForm(f=>({...f,canal:e.target.value}))} options={['','WhatsApp','Instagram','Facebook','Referido','Email','TikTok','Presencial'].map(v=>({value:v,label:v||'— Canal —'}))}/>
setForm(f=>({...f,tipo_clienta:e.target.value}))} options={['nueva','recurrente','reactivada'].map(v=>({value:v,label:v}))}/>
setForm(f=>({...f,tipo_campana:e.target.value}))} options={['','Adquisición','Monetización','Activación'].map(v=>({value:v,label:v||'— Sin campaña —'}))}/>
setForm(f=>({...f,notas:e.target.value}))}/>
setModal(false)}>Cancelar Guardar
);
};
// ── PLANIFICACIÓN ──────────────────────────────────────────────
const PlanificacionScreen = () => {
const mes=mesActual();
const[config,setConfig]=React.useState({});
const[productos,setProductos]=React.useState([]);
const[modal,setModal]=React.useState(false);
const[prod,setProd]=React.useState({nombre:'',tipo:'A - Lead',precio:'',canal:'',margen:''});
const[saved,setSaved]=React.useState(false);
React.useEffect(()=>{API.get(`/config?mes=${mes}`).then(r=>r&&setConfig(r));API.get('/productos').then(r=>r&&setProductos(r));},[]);
const saveConf=async()=>{await API.post('/config',{mes,...config});setSaved(true);setTimeout(()=>setSaved(false),2000);showToast('Configuración guardada ✓');};
const saveProd=async()=>{if(!prod.nombre){showToast('Nombre requerido','error');return;}await API.post('/productos',prod);setModal(false);setProd({nombre:'',tipo:'A - Lead',precio:'',canal:'',margen:''});API.get('/productos').then(r=>r&&setProductos(r));showToast('Producto agregado ✓');};
const delProd=async id=>{if(!confirm('¿Eliminar?'))return;await API.del(`/productos/${id}`);API.get('/productos').then(r=>r&&setProductos(r));};
return (
⚙️ Configuración de {mesNombre(mes)}
setConfig(c=>({...c,meta_facturacion:e.target.value}))}/>
setConfig(c=>({...c,meta_clientes_nuevas:e.target.value}))}/>
setConfig(c=>({...c,sector:e.target.value}))}/>
setConfig(c=>({...c,tipo_campana:e.target.value}))} options={['Adquisición','Monetización','Activación'].map(v=>({value:v,label:v}))}/>
setConfig(c=>({...c,producto_protagonista:e.target.value}))}/>
{saved?'✓ Guardado':'Guardar configuración'}
📦 Inventario de productos
setModal(true)}>+ Agregar
{productos.length===0?📦
Sin productos. ¡Agrega tu catálogo!
:(
{['Nombre','Tipo','Precio',''].map(h=>{h} )}
{productos.map(p=>
{p.nombre}
{p.tipo}
{fmtMoney(p.precio)}
delProd(p.id)} style={{background:'none',border:'none',cursor:'pointer',color:EJ.pink}}>✕
)}
)}
📅 Los 3 tipos de campaña — ¿Cuál aplica este mes?
{[{t:'Adquisición',icon:'🎯',desc:'Atraer clientas nuevas.',c:EJ.cyan},{t:'Monetización',icon:'💰',desc:'Vender más a quien ya confía.',c:EJ.violet},{t:'Activación',icon:'🔄',desc:'Reactivar clientas inactivas.',c:EJ.pink}].map(x=>
)}
setModal(false)} title="📦 Agregar producto">
setProd(p=>({...p,nombre:e.target.value}))}/>
setProd(p=>({...p,tipo:e.target.value}))} options={['A - Lead','B - Cliente','C - Caja','D - Lucro','Maximizador'].map(v=>({value:v,label:v}))}/>
setProd(p=>({...p,precio:e.target.value}))}/>
setProd(p=>({...p,canal:e.target.value}))} options={['','WhatsApp','Instagram','Facebook','Presencial','Online'].map(v=>({value:v,label:v||'—'}))}/>
setProd(p=>({...p,margen:e.target.value}))}/>
setModal(false)}>Cancelar Guardar
);
};
// ── REVISIÓN MENSUAL ───────────────────────────────────────────
const RevisionScreen = () => {
const mes=mesActual();
const[rev,setRev]=React.useState({});
const[kpis,setKpis]=React.useState({});
React.useEffect(()=>{API.get(`/revision?mes=${mes}`).then(r=>r&&setRev(r));API.get(`/kpis?mes=${mes}`).then(r=>r&&setKpis(r));},[]);
const save=async()=>{await API.post('/revision',{mes,...rev});showToast('Revisión guardada ✓');};
const areas=['ventas','marketing','producto','operaciones'];
const si=c=>({verde:'🟢',amarillo:'🟡',rojo:'🔴'}[c]||'🟡');
return (
Guardar revisión}/>
{areas.map(a=>
{a.charAt(0).toUpperCase()+a.slice(1)}
{si(rev[`semaforo_${a}`]||'amarillo')}
setRev(r=>({...r,[`semaforo_${a}`]:e.target.value}))} style={{fontSize:12,border:`1px solid ${EJ.border}`,borderRadius:8,padding:'3px 8px',background:'#fff'}}>
{['verde','amarillo','rojo'].map(s=>{s} )}
)}
Facturación real del mes
{fmtMoney(kpis.total||0)}
{kpis.count||0} ventas · Ticket {fmtMoney(kpis.ticket||0)}
Facturación real (ajustable)
setRev(r=>({...r,facturacion_real:e.target.value}))}/>
🔍 Las 6 preguntas del cierre mensual
{[{id:'p1',q:'P1 · ¿Cuánto gané de verdad — ganancia neta después de gastos?'},{id:'p2',q:'P2 · ¿Cuál área tuvo el semáforo más rojo? ¿Qué acción la mejoraría?'},{id:'p3',q:'P3 · ¿Qué funcionó especialmente bien? ¿Cómo lo repito?'},{id:'p4',q:'P4 · ¿Ejecuté la rutina más del 80% de los días?'},{id:'p5',q:'P5 · ¿Hay dinero que dejé sobre la mesa?'},{id:'p6',q:'P6 · Si mi negocio siguiera igual 6 meses más, ¿estaría satisfecha?'}].map(p=>)}
🎯 Decisiones para el mes que viene
setRev(r=>({...r,meta_siguiente:e.target.value}))}/>
setRev(r=>({...r,tipo_campana_siguiente:e.target.value}))} options={['Adquisición','Monetización','Activación'].map(v=>({value:v,label:v}))}/>
);
};
Object.assign(window, { VentasScreen, PlanificacionScreen, RevisionScreen });