"use client"; import { useState, useEffect } from "react"; // ========================================== // 0. BASE DE DATOS SIMULADA (Para que todo funcione antes de Firebase) // ========================================== const clientesIniciales = [ { id: 1, nombre: "Sociedad de Mejoras Públicas", nit: "890.801.123-4", telefono: "3001234567", correo: "contacto@smp.com", direccion: "Cra 23 # 50-20", ltv: 4500000 }, { id: 2, nombre: "Café El Ruíz", nit: "900.222.333-1", telefono: "3109876543", correo: "gerencia@cafeelruiz.com", direccion: "Centro Histórico", ltv: 1200000 }, { id: 3, nombre: "Alcaldía de Manizales", nit: "890.801.111-1", telefono: "3112223344", correo: "compras@manizales.gov.co", direccion: "Plaza de Bolívar", ltv: 850000 } ]; const formatoPesos = (valor) => new Intl.NumberFormat('es-CO', { style: 'currency', currency: 'COP', maximumFractionDigits: 0 }).format(valor || 0); // ========================================== // 1. DASHBOARD Y MÉTRICAS // ========================================== function DashboardMetrics() { return (

Panel Principal (Resumen)

Ingresos del Mes

{formatoPesos(12500000)}

↑ 15% vs mes anterior

Cuentas por Cobrar (Cartera)

{formatoPesos(2150000)}

Dinero pendiente de cobro

En Producción

5

Trabajos activos en taller

); } // ========================================== // 2. DIRECTORIO DE CLIENTES Y CARTERA // ========================================== function ModuloClientes({ clientes }) { const deudas = [ { id: 1, cliente: "Sociedad de Mejoras Públicas", proyecto: "Rediseño Web", total: 4500000, abono: 2250000, saldo: 2250000 }, { id: 2, cliente: "Café El Ruíz", proyecto: "Empaques Litografía", total: 1200000, abono: 600000, saldo: 600000 } ]; return (

Gestión de Clientes y Cartera

Directorio de Clientes

{clientes.map(cli => ( ))}
Cliente / EmpresaNIT / CCTeléfonoVentas Totales
{cli.nombre}{cli.nit}{cli.telefono}{formatoPesos(cli.ltv)}

Cuentas por Cobrar

{deudas.map(deuda => (

{deuda.cliente}

{deuda.proyecto}

Valor: {formatoPesos(deuda.total)}Abono: {formatoPesos(deuda.abono)}
Debe: {formatoPesos(deuda.saldo)}
))}
); } // ========================================== // 3. CRM DE COTIZACIONES // ========================================== function ModuloCRM() { const [cotizaciones, setCotizaciones] = useState([ { id: 1, cliente: "Sociedad de Mejoras Públicas", proyecto: "Rediseño Web", total: 4500000, estado: "produccion" }, { id: 2, cliente: "Café El Ruíz", proyecto: "Empaques", total: 1200000, estado: "enviada" }, { id: 3, cliente: "Alcaldía de Manizales", proyecto: "Pendones", total: 850000, estado: "aceptada" }, { id: 4, cliente: "Constructora XYZ", proyecto: "Vallas", total: 3200000, estado: "rechazada" }, { id: 5, cliente: "Boutique María", proyecto: "Tarjetas", total: 350000, estado: "finalizada" }, ]); const estados = [ { id: "enviada", nombre: "Enviadas", color: "bg-blue-100 text-blue-800" }, { id: "aceptada", nombre: "Aceptadas", color: "bg-yellow-100 text-yellow-800" }, { id: "produccion", nombre: "En Producción", color: "bg-purple-100 text-purple-800" }, { id: "finalizada", nombre: "Finalizadas", color: "bg-green-100 text-green-800" }, { id: "rechazada", nombre: "Rechazadas", color: "bg-red-100 text-red-800" }, ]; return (

CRM de Seguimiento

{estados.map(est => (

{est.nombre} ({cotizaciones.filter(c => c.estado === est.id).length})

{cotizaciones.filter(c => c.estado === est.id).map(cot => (

{cot.cliente}

{cot.proyecto}

{formatoPesos(cot.total)}

))}
))}
); } // ========================================== // 4. COTIZADOR, OP Y PROPUESTA COMERCIAL // ========================================== function CotizadorMulti({ costoPapelInicial, clientesBD }) { const [tipoServicio, setTipoServicio] = useState("litografia"); const [clienteIdSeleccionado, setClienteIdSeleccionado] = useState("nuevo"); const [cliente, setCliente] = useState(""); const [nit, setNit] = useState(""); const [telefono, setTelefono] = useState(""); const [direccion, setDireccion] = useState(""); const [correo, setCorreo] = useState(""); const [descripcion, setDescripcion] = useState(""); useEffect(() => { if (clienteIdSeleccionado !== "nuevo") { const cli = clientesBD.find(c => c.id === Number(clienteIdSeleccionado)); if (cli) { setCliente(cli.nombre); setNit(cli.nit); setTelefono(cli.telefono); setCorreo(cli.correo); setDireccion(cli.direccion); } } else { setCliente(""); setNit(""); setTelefono(""); setCorreo(""); setDireccion(""); } }, [clienteIdSeleccionado, clientesBD]); const [margenGanancia, setMargenGanancia] = useState(33.3); const [costoPapel, setCostoPapel] = useState(costoPapelInicial || ""); const [costoPlanchas, setCostoPlanchas] = useState(""); const [costoTiraje, setCostoTiraje] = useState(""); const [ancho, setAncho] = useState(""); const [alto, setAlto] = useState(""); const [costoMetro, setCostoMetro] = useState(""); const [costoEstructura, setCostoEstructura] = useState(""); const [horas, setHoras] = useState(""); const [valorHora, setValorHora] = useState(""); const [costoDiseno, setCostoDiseno] = useState(""); const [costoAcabados, setCostoAcabados] = useState(""); const [costoTroquelado, setCostoTroquelado] = useState(""); const [costoCorte, setCostoCorte] = useState(""); const [costoEnvio, setCostoEnvio] = useState(""); const [costoInstalacion, setCostoInstalacion] = useState(""); const [abono, setAbono] = useState(""); const [costoTotal, setCostoTotal] = useState(0); const [precioVenta, setPrecioVenta] = useState(0); const [mostrarPropuesta, setMostrarPropuesta] = useState(false); const [mostrarOP, setMostrarOP] = useState(false); useEffect(() => { if (costoPapelInicial) setCostoPapel(costoPapelInicial); }, [costoPapelInicial]); useEffect(() => { let costoServicio = 0; if (tipoServicio === "litografia") costoServicio = Number(costoPapel) + Number(costoPlanchas) + Number(costoTiraje); else if (tipoServicio === "gran_formato") costoServicio = (Number(ancho) * Number(alto) * Number(costoMetro)) + Number(costoEstructura); else if (tipoServicio === "agencia") costoServicio = Number(horas) * Number(valorHora); const costoBaseTotal = costoServicio + Number(costoAcabados) + Number(costoTroquelado) + Number(costoCorte) + Number(costoDiseno) + Number(costoEnvio) + Number(costoInstalacion); setCostoTotal(costoBaseTotal); const margenDecimal = Number(margenGanancia) / 100; setPrecioVenta(margenDecimal >= 1 ? 0 : costoBaseTotal / (1 - margenDecimal)); }, [tipoServicio, costoPapel, costoPlanchas, costoTiraje, ancho, alto, costoMetro, costoEstructura, horas, valorHora, costoAcabados, costoTroquelado, costoCorte, costoDiseno, costoEnvio, costoInstalacion, margenGanancia]); const saldoPendiente = precioVenta - (Number(abono) || 0); const inputStyle = "w-full border border-gray-300 p-2 rounded text-gray-900 bg-white font-medium focus:ring-2 focus:ring-blue-500 outline-none"; if (mostrarOP) { return (

ORDEN DE PRODUCCIÓN

Uso Interno

OP - {Math.floor(Math.random() * 9000) + 1000}

{new Date().toLocaleDateString('es-CO')}

Datos del Cliente

Cliente: {cliente}

Tel: {telefono}

Tipo de Trabajo

{tipoServicio}

Especificaciones

{descripcion || "Sin descripción."}

); } if (mostrarPropuesta) { return (
e.target.style.display='none'} />

TOPRINTT.

Manizales

{new Date().toLocaleDateString('es-CO')}

{cliente || "Cliente"}

{nit&&

NIT: {nit}

}{telefono&&

Tel: {telefono}

}{correo&&

Correo: {correo}

}

Proyecto

{descripcion}

Valor Total:{formatoPesos(precioVenta)}
{Number(abono)>0 && (
Anticipo:- {formatoPesos(abono)}
)}
Saldo Pendiente:{formatoPesos(saldoPendiente)}
); } return (

Cotizador Comercial

setCliente(e.target.value)} />
setNit(e.target.value)} />
setTelefono(e.target.value)} />
{tipoServicio === "litografia" && (<>
setCostoPapel(e.target.value)} />
setCostoPlanchas(e.target.value)} />
setCostoTiraje(e.target.value)} />
)} {tipoServicio === "gran_formato" && (<>
setAncho(e.target.value)} />
setAlto(e.target.value)} />
setCostoMetro(e.target.value)} />
setCostoEstructura(e.target.value)} />
)} {tipoServicio === "agencia" && (<>
setHoras(e.target.value)} />
setValorHora(e.target.value)} />
)}
setCostoDiseno(e.target.value)} />
setCostoAcabados(e.target.value)} />
setCostoTroquelado(e.target.value)} />
setCostoInstalacion(e.target.value)} />

Costo

{formatoPesos(costoTotal)}

Margen (%)

setMargenGanancia(e.target.value)}/>

Abono Inicial

setAbono(e.target.value)}/>

Total Venta

{formatoPesos(precioVenta)}

); } // ========================================== // 5. CALCULADORA DE MONTAJE Y PAPEL // ========================================== function CalculadoraCortes({ irACotizador }) { const [tipoPapel, setTipoPapel] = useState("Propalcote 300g"); const [papelOtro, setPapelOtro] = useState(""); const [cantidadPliegos, setCantidadPliegos] = useState(""); const [precioPliego, setPrecioPliego] = useState(""); const [pliegoAncho, setPliegoAncho] = useState(70); const [pliegoAlto, setPliegoAlto] = useState(100); const [corteAncho, setCorteAncho] = useState(9); const [corteAlto, setCorteAlto] = useState(5); const [resultado, setResultado] = useState(null); const formatoPesos = (valor) => new Intl.NumberFormat('es-CO', { style: 'currency', currency: 'COP', maximumFractionDigits: 0 }).format(valor || 0); const inputStyle = "w-full border border-gray-300 p-2 rounded text-gray-900 bg-white font-medium focus:ring-2 focus:ring-blue-500 outline-none"; const precioTotalPapel = (Number(cantidadPliegos) || 0) * (Number(precioPliego) || 0); useEffect(() => { const pA = parseFloat(pliegoAncho) || 0, pAl = parseFloat(pliegoAlto) || 0, cA = parseFloat(corteAncho) || 0, cAl = parseFloat(corteAlto) || 0; if (pA <= 0 || pAl <= 0 || cA <= 0 || cAl <= 0) return; const total1 = Math.floor(pA / cA) * Math.floor(pAl / cAl); const total2 = Math.floor(pA / cAl) * Math.floor(pAl / cA); const areaPliego = pA * pAl; if (total1 >= total2) setResultado({ piezas: total1, columnas: Math.floor(pA / cA), filas: Math.floor(pAl / cAl), anchoUsado: cA, altoUsado: cAl, porcentaje: (((areaPliego - (total1 * (cA * cAl))) / areaPliego) * 100).toFixed(1) }); else setResultado({ piezas: total2, columnas: Math.floor(pA / cAl), filas: Math.floor(pAl / cA), anchoUsado: cAl, altoUsado: cA, porcentaje: (((areaPliego - (total2 * (cA * cAl))) / areaPliego) * 100).toFixed(1) }); }, [pliegoAncho, pliegoAlto, corteAncho, corteAlto]); return (

Calculadora de Montajes y Papel

1. Información del Papel

{tipoPapel === "otro" &&
setPapelOtro(e.target.value)} className={inputStyle} />
}
setCantidadPliegos(e.target.value)} className={inputStyle} />
setPrecioPliego(e.target.value)} className={inputStyle} />
{formatoPesos(precioTotalPapel)}

2. Medidas de Corte

Pliego Base (cm)

setPliegoAncho(e.target.value)} className={inputStyle} placeholder="Ancho"/>setPliegoAlto(e.target.value)} className={inputStyle} placeholder="Alto"/>

Corte Final (cm)

setCorteAncho(e.target.value)} className={inputStyle} placeholder="Ancho"/>setCorteAlto(e.target.value)} className={inputStyle} placeholder="Alto"/>
{resultado && (

Piezas x Pliego

{resultado.piezas}

Desperdicio

{resultado.porcentaje}%

)}

Vista Previa del Montaje

{resultado && pliegoAncho > 0 && pliegoAlto > 0 ? ( {Array.from({ length: resultado.filas }).map((_, filaIndex) => Array.from({ length: resultado.columnas }).map((_, colIndex) => ( )))} ) :

Ingresa medidas

}
); } // ========================================== // 6. MÓDULO DE CAJA (CON HISTORIAL Y FILTROS) // ========================================== function ModuloCaja() { const [baseInicial, setBaseInicial] = useState(100000); const [movimientos, setMovimientos] = useState([]); const [tipo, setTipo] = useState("ingreso"); const [descripcion, setDescripcion] = useState(""); const [monto, setMonto] = useState(""); const [metodo, setMetodo] = useState("efectivo"); const [busqueda, setBusqueda] = useState(""); const [tipoFiltro, setTipoFiltro] = useState("todo"); const [fechaExacta, setFechaExacta] = useState(new Date().toISOString().split('T')[0]); const [mesExacto, setMesExacto] = useState(new Date().toISOString().slice(0, 7)); const formatoPesos = (valor) => new Intl.NumberFormat('es-CO', { style: 'currency', currency: 'COP', maximumFractionDigits: 0 }).format(valor || 0); const inputStyle = "w-full border border-gray-300 p-2 rounded text-gray-900 bg-white font-medium focus:ring-2 focus:ring-blue-500 outline-none"; const registrarMovimiento = (e) => { e.preventDefault(); if (!descripcion || !monto) return; const ahora = new Date(); setMovimientos([{ id: Date.now(), tipo, descripcion, monto: Number(monto), metodo, fechaISO: ahora.toISOString().split('T')[0], hora: ahora.toLocaleTimeString('es-CO', { hour: '2-digit', minute: '2-digit' }) }, ...movimientos]); setDescripcion(""); setMonto(""); }; const movimientosFiltrados = movimientos.filter(mov => { const coincideTexto = mov.descripcion.toLowerCase().includes(busqueda.toLowerCase()); let coincideFecha = true; if (tipoFiltro === "dia") coincideFecha = mov.fechaISO === fechaExacta; else if (tipoFiltro === "mes") coincideFecha = mov.fechaISO.startsWith(mesExacto); return coincideTexto && coincideFecha; }); const saldoActual = Number(baseInicial) + movimientos.filter(m=>m.tipo==="ingreso").reduce((a,c)=>a+c.monto,0) - movimientos.filter(m=>m.tipo==="egreso").reduce((a,c)=>a+c.monto,0); return (

Flujo de Caja y Reportes

Base Inicial

setBaseInicial(e.target.value)} className="w-full font-bold text-xl outline-none" />

Ingresos (Filtro)

{formatoPesos(movimientosFiltrados.filter(m=>m.tipo==="ingreso").reduce((a,c)=>a+c.monto,0))}

Egresos (Filtro)

{formatoPesos(movimientosFiltrados.filter(m=>m.tipo==="egreso").reduce((a,c)=>a+c.monto,0))}

Saldo Real Global

{formatoPesos(saldoActual)}

Nuevo Movimiento

setDescripcion(e.target.value)} required />
setMonto(e.target.value)} required />
setBusqueda(e.target.value)} /> {tipoFiltro === "dia" && setFechaExacta(e.target.value)} />} {tipoFiltro === "mes" && setMesExacto(e.target.value)} />}
{movimientosFiltrados.map(mov => (

{mov.descripcion}

{mov.fechaISO} • {mov.hora}

{mov.tipo==="ingreso"?"+":"-"}{formatoPesos(mov.monto)}
))}
); } // ========================================== // 7. NAVEGACIÓN Y "MEMORIA" CENTRAL // ========================================== export default function Home() { const [vistaActiva, setVistaActiva] = useState("cotizador"); const [costoPapelCompartido, setCostoPapelCompartido] = useState(""); const enviarACotizador = (valorTotalCalculado) => { setCostoPapelCompartido(valorTotalCalculado); setVistaActiva("cotizador"); }; return (
{vistaActiva === "dashboard" && } {vistaActiva === "clientes" && } {vistaActiva === "crm" && } {vistaActiva === "cotizador" && } {vistaActiva === "calculadora" && } {vistaActiva === "caja" && }
); }