// 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=>)}{ventas.map(v=>)}
{h}
{v.fecha} {v.producto||'—'} {v.clienta||'—'} {v.canal||'—'} {fmtMoney(v.monto)} {v.tipo_clienta||'—'} {v.tipo_campana||'—'}
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,tipo_clienta:e.target.value}))} options={['nueva','recurrente','reactivada'].map(v=>({value:v,label:v}))}/> setForm(f=>({...f,notas:e.target.value}))}/>
setModal(false)}>CancelarGuardar
); }; // ── 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,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=>)}{productos.map(p=>)}
{h}
{p.nombre} {p.tipo} {fmtMoney(p.precio)}
)}
📅 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=>
{x.icon}
{x.t}
{x.desc}
)}
setModal(false)} title="📦 Agregar producto">
setProd(p=>({...p,nombre:e.target.value}))}/>
setProd(p=>({...p,precio:e.target.value}))}/> setProd(p=>({...p,margen:e.target.value}))}/>
setModal(false)}>CancelarGuardar
); }; // ── 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')}
)}
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=>
{p.q}