import React, { useState } from 'react'; import { Plus, Trash2, Calculator, ChefHat, Package, FileDown, RefreshCw, DollarSign, Percent, Box, Users, Wrench, Gift, Sparkles, ArrowRight, Check, Cake, Cookie, CakeSlice } from 'lucide-react'; // Importar fuente Nunito const FontLoader = () => ( ); // Navegación const NavButton = ({ active, onClick, icon: Icon, label }) => ( ); // Input con estilo const StyledInput = ({ label, type = "text", value, onChange, placeholder, icon: Icon, suffix, min, step }) => (
{label && }
{Icon && (
)} {suffix && ( {suffix} )}
); // Select con estilo const StyledSelect = ({ label, value, onChange, options, icon: Icon }) => (
{label && }
{Icon && (
)}
); // Botón principal const PrimaryButton = ({ onClick, children, variant = "primary", className = "", disabled }) => { const variants = { primary: "bg-gradient-to-r from-pink-400 to-rose-400 text-white hover:from-pink-500 hover:to-rose-500 shadow-lg shadow-pink-200/50", secondary: "bg-white text-rose-400 border-2 border-pink-200 hover:border-pink-400 hover:bg-pink-50", danger: "bg-gradient-to-r from-red-300 to-rose-300 text-white hover:from-red-400 hover:to-rose-400 shadow-lg shadow-red-200/50", success: "bg-gradient-to-r from-emerald-300 to-teal-300 text-white hover:from-emerald-400 hover:to-teal-400 shadow-lg shadow-emerald-200/50" }; return ( ); }; // Tarjeta de sección const SectionCard = ({ title, icon: Icon, children, color = "pink" }) => { const colors = { pink: "from-pink-300 to-rose-300", mint: "from-emerald-300 to-teal-300", lavender: "from-violet-300 to-purple-300", peach: "from-orange-200 to-amber-200", sky: "from-sky-300 to-cyan-300" }; return (

{title}

{children}
); }; // Fila de item editable const ItemRow = ({ item, onUpdate, onDelete, fields }) => (
{fields.map((field, i) => (
{field.type === 'select' ? ( ) : ( onUpdate(field.key, e.target.value)} placeholder={field.placeholder} min={field.min} step={field.step} className="w-full px-3 py-2 bg-white border-2 border-pink-100 rounded-xl text-sm focus:border-pink-400 outline-none font-medium text-gray-600 placeholder:text-pink-200" /> )}
))}
); // ==================== PÁGINA 1: CALCULADORA DE PRECIOS ==================== const CalculadoraPrecios = () => { const [config, setConfig] = useState({ nombre: '', cantidad: 1, unidad: 'unidad', margen: 30 }); const [insumos, setInsumos] = useState([]); const [manoObra, setManoObra] = useState([]); const [costosIndirectos, setCostosIndirectos] = useState([]); const [empaque, setEmpaque] = useState([]); const [extras, setExtras] = useState([]); const unidades = [ { value: 'unidad', label: 'Unidad' }, { value: 'kg', label: 'Kilogramo' }, { value: 'litro', label: 'Litro' }, { value: 'docena', label: 'Docena' }, { value: 'caja', label: 'Caja' }, { value: 'paquete', label: 'Paquete' } ]; const addItem = (setter, template) => { setter(prev => [...prev, { id: Date.now(), ...template }]); }; const updateItem = (setter, id, key, value) => { setter(prev => prev.map(item => item.id === id ? { ...item, [key]: value } : item)); }; const deleteItem = (setter, id) => { setter(prev => prev.filter(item => item.id !== id)); }; const calcularTotal = (items) => items.reduce((sum, item) => sum + (parseFloat(item.precio) || 0) * (parseFloat(item.cantidad) || 1), 0); const totalInsumos = calcularTotal(insumos); const totalManoObra = manoObra.reduce((sum, item) => sum + (parseFloat(item.costo) || 0), 0); const totalIndirectos = costosIndirectos.reduce((sum, item) => sum + (parseFloat(item.costo) || 0), 0); const totalEmpaque = calcularTotal(empaque); const totalExtras = extras.reduce((sum, item) => sum + (parseFloat(item.costo) || 0), 0); const costoTotal = totalInsumos + totalManoObra + totalIndirectos + totalEmpaque + totalExtras; const costoPorUnidad = config.cantidad > 0 ? costoTotal / config.cantidad : 0; const precioSugerido = costoPorUnidad * (1 + config.margen / 100); const precioEspecial = precioSugerido * 0.9; const gananciaUnidad = precioSugerido - costoPorUnidad; const rentabilidad = costoPorUnidad > 0 ? (gananciaUnidad / precioSugerido) * 100 : 0; return (
{/* Configuración del Producto */}
setConfig({...config, nombre: e.target.value})} placeholder="Ej: Torta de Chocolate" /> setConfig({...config, cantidad: e.target.value})} min="1" /> setConfig({...config, unidad: e.target.value})} options={unidades} /> setConfig({...config, margen: e.target.value})} suffix="%" min="0" />
{/* Insumos y Materia Prima */}
{insumos.map(item => ( updateItem(setInsumos, item.id, key, value)} onDelete={() => deleteItem(setInsumos, item.id)} fields={[ { key: 'nombre', placeholder: 'Ingrediente', className: 'flex-[2] min-w-[120px]' }, { key: 'cantidad', type: 'number', placeholder: 'Cant.', min: '0', step: '0.01', className: 'w-16 sm:w-20' }, { key: 'unidad', type: 'select', options: unidades, className: 'w-24 sm:w-28' }, { key: 'precio', type: 'number', placeholder: '$ Precio', min: '0', step: '0.01', className: 'w-20 sm:w-24' } ]} /> ))} addItem(setInsumos, { nombre: '', cantidad: 1, unidad: 'kg', precio: '' })} className="w-full sm:w-auto"> Agregar Insumo
Subtotal: ${totalInsumos.toFixed(2)}
{/* Mano de Obra */}
{manoObra.map(item => ( updateItem(setManoObra, item.id, key, value)} onDelete={() => deleteItem(setManoObra, item.id)} fields={[ { key: 'descripcion', placeholder: 'Descripción', className: 'flex-[2] min-w-[120px]' }, { key: 'horas', type: 'number', placeholder: 'Horas', min: '0', step: '0.5', className: 'w-16 sm:w-20' }, { key: 'costo', type: 'number', placeholder: '$ Costo', min: '0', step: '0.01', className: 'w-20 sm:w-24' } ]} /> ))} addItem(setManoObra, { descripcion: '', horas: 1, costo: '' })} className="w-full sm:w-auto"> Agregar Mano de Obra
Subtotal: ${totalManoObra.toFixed(2)}
{/* Costos Indirectos */}
{costosIndirectos.map(item => ( updateItem(setCostosIndirectos, item.id, key, value)} onDelete={() => deleteItem(setCostosIndirectos, item.id)} fields={[ { key: 'concepto', placeholder: 'Concepto (luz, gas, etc.)', className: 'flex-[2] min-w-[120px]' }, { key: 'costo', type: 'number', placeholder: '$ Costo', min: '0', step: '0.01', className: 'w-24 sm:w-28' } ]} /> ))} addItem(setCostosIndirectos, { concepto: '', costo: '' })} className="w-full sm:w-auto"> Agregar Costo Indirecto
Subtotal: ${totalIndirectos.toFixed(2)}
{/* Empaque y Presentación */}
{empaque.map(item => ( updateItem(setEmpaque, item.id, key, value)} onDelete={() => deleteItem(setEmpaque, item.id)} fields={[ { key: 'item', placeholder: 'Caja, etiqueta, etc.', className: 'flex-[2] min-w-[120px]' }, { key: 'cantidad', type: 'number', placeholder: 'Cant.', min: '0', className: 'w-16 sm:w-20' }, { key: 'precio', type: 'number', placeholder: '$ Precio', min: '0', step: '0.01', className: 'w-20 sm:w-24' } ]} /> ))} addItem(setEmpaque, { item: '', cantidad: 1, precio: '' })} className="w-full sm:w-auto"> Agregar Empaque
Subtotal: ${totalEmpaque.toFixed(2)}
{/* Extras y Servicios */}
{extras.map(item => ( updateItem(setExtras, item.id, key, value)} onDelete={() => deleteItem(setExtras, item.id)} fields={[ { key: 'servicio', placeholder: 'Delivery, decoración, etc.', className: 'flex-[2] min-w-[120px]' }, { key: 'costo', type: 'number', placeholder: '$ Costo', min: '0', step: '0.01', className: 'w-24 sm:w-28' } ]} /> ))} addItem(setExtras, { servicio: '', costo: '' })} className="w-full sm:w-auto"> Agregar Extra
Subtotal: ${totalExtras.toFixed(2)}
{/* Resultados */}

Resumen de Costos

Insumos y Materia Prima ${totalInsumos.toFixed(2)}
Mano de Obra ${totalManoObra.toFixed(2)}
Costos Indirectos ${totalIndirectos.toFixed(2)}
Empaque y Presentación ${totalEmpaque.toFixed(2)}
Extras y Servicios ${totalExtras.toFixed(2)}
COSTO TOTAL ${costoTotal.toFixed(2)}
Costo por {config.unidad} ${costoPorUnidad.toFixed(2)}

💰 Precio Sugerido (con {config.margen}% margen)

${precioSugerido.toFixed(2)}

por {config.unidad}

🏷️ Precio Especial (-10%)

${precioEspecial.toFixed(2)}

por {config.unidad}

Análisis de Rentabilidad

Ganancia por unidad ${gananciaUnidad.toFixed(2)}
Margen de rentabilidad {rentabilidad.toFixed(1)}%
); }; // ==================== PÁGINA 2: MULTIPLICADOR DE RECETAS ==================== const MultiplicadorRecetas = () => { const [receta, setReceta] = useState({ nombre: '', moneda: 'USD' }); const [ingredientes, setIngredientes] = useState([]); const [factor, setFactor] = useState(1); const monedas = [ { value: 'USD', label: '$ USD' }, { value: 'EUR', label: '€ EUR' }, { value: 'MXN', label: '$ MXN' }, { value: 'ARS', label: '$ ARS' }, { value: 'COP', label: '$ COP' }, { value: 'PEN', label: 'S/ PEN' } ]; const unidades = [ { value: 'g', label: 'Gramos' }, { value: 'kg', label: 'Kilogramos' }, { value: 'ml', label: 'Mililitros' }, { value: 'l', label: 'Litros' }, { value: 'unidad', label: 'Unidad' }, { value: 'taza', label: 'Taza' }, { value: 'cda', label: 'Cucharada' }, { value: 'cdta', label: 'Cucharadita' } ]; const simboloMoneda = monedas.find(m => m.value === receta.moneda)?.label.split(' ')[0] || '$'; const addIngrediente = () => { setIngredientes([...ingredientes, { id: Date.now(), nombre: '', cantidad: '', unidad: 'g', precio: '' }]); }; const updateIngrediente = (id, key, value) => { setIngredientes(ingredientes.map(ing => ing.id === id ? { ...ing, [key]: value } : ing)); }; const deleteIngrediente = (id) => { setIngredientes(ingredientes.filter(ing => ing.id !== id)); }; const calcularNuevaCantidad = (cantidad) => { const num = parseFloat(cantidad) || 0; return (num * factor).toFixed(2); }; const calcularNuevoPrecio = (precio) => { const num = parseFloat(precio) || 0; return (num * factor).toFixed(2); }; const costoOriginal = ingredientes.reduce((sum, ing) => sum + (parseFloat(ing.precio) || 0), 0); const costoNuevo = costoOriginal * factor; return (
{/* Configuración de Receta */}
setReceta({...receta, nombre: e.target.value})} placeholder="Ej: Pastel de Tres Leches" icon={ChefHat} /> setReceta({...receta, moneda: e.target.value})} options={monedas} icon={DollarSign} />
{/* Ingredientes */}
{ingredientes.map(ing => (
updateIngrediente(ing.id, 'nombre', e.target.value)} placeholder="Ingrediente" className="flex-[2] min-w-[100px] px-3 py-2 bg-white border-2 border-emerald-100 rounded-xl text-sm focus:border-emerald-400 outline-none font-medium text-gray-600 placeholder:text-emerald-200" /> updateIngrediente(ing.id, 'cantidad', e.target.value)} placeholder="Cantidad" min="0" step="0.01" className="w-20 sm:w-24 px-3 py-2 bg-white border-2 border-emerald-100 rounded-xl text-sm focus:border-emerald-400 outline-none font-medium text-gray-600 placeholder:text-emerald-200" />
{simboloMoneda} updateIngrediente(ing.id, 'precio', e.target.value)} placeholder="Precio" min="0" step="0.01" className="w-full pl-8 pr-3 py-2 bg-white border-2 border-emerald-100 rounded-xl text-sm focus:border-emerald-400 outline-none font-medium text-gray-600 placeholder:text-emerald-200" />
))} Añadir Ingrediente
{/* Factor de Multiplicación */}

Factor de Multiplicación

{[0.5, 1, 1.5, 2, 3, 4, 5].map(f => ( ))}
Personalizado: setFactor(parseFloat(e.target.value) || 1)} min="0.1" step="0.1" className="w-20 sm:w-24 px-3 py-2 bg-white/80 border-2 border-violet-300 rounded-xl text-violet-600 font-bold text-center focus:border-violet-500 outline-none" />
{/* Receta Final */} {ingredientes.length > 0 && (
{ingredientes.map(ing => ( ))}
Ingrediente Original Nueva Cantidad Nuevo Precio
{ing.nombre || '—'} {ing.cantidad} {unidades.find(u => u.value === ing.unidad)?.label} {calcularNuevaCantidad(ing.cantidad)} {unidades.find(u => u.value === ing.unidad)?.label} {simboloMoneda}{calcularNuevoPrecio(ing.precio)}
COSTO TOTAL {simboloMoneda}{costoOriginal.toFixed(2)} {simboloMoneda}{costoNuevo.toFixed(2)}
)}
); }; // ==================== PÁGINA 3: PACKAGING Y MERMAS ==================== const PackagingMermas = () => { const [envases, setEnvases] = useState([]); const [resultados, setResultados] = useState(null); const tiposEnvase = [ { value: 'caja_carton', label: '📦 Caja de Cartón' }, { value: 'bolsa_papel', label: '🛍️ Bolsa de Papel' }, { value: 'envase_plastico', label: '🥤 Envase Plástico' }, { value: 'frasco_vidrio', label: '🫙 Frasco de Vidrio' }, { value: 'bandeja_aluminio', label: '🍱 Bandeja de Aluminio' }, { value: 'film_transparente', label: '🎞️ Film Transparente' }, { value: 'etiqueta', label: '🏷️ Etiqueta' }, { value: 'cinta_decorativa', label: '🎀 Cinta Decorativa' }, { value: 'otro', label: '📋 Otro' } ]; const addEnvase = () => { setEnvases([...envases, { id: Date.now(), tipo: 'caja_carton', precioUnidad: '', cantidad: 1, merma: 5 }]); }; const updateEnvase = (id, key, value) => { setEnvases(envases.map(env => env.id === id ? { ...env, [key]: value } : env)); }; const deleteEnvase = (id) => { setEnvases(envases.filter(env => env.id !== id)); }; const calcularCostos = () => { const detalle = envases.map(env => { const precioBase = (parseFloat(env.precioUnidad) || 0) * (parseInt(env.cantidad) || 1); const costoMerma = precioBase * ((parseFloat(env.merma) || 0) / 100); const costoReal = precioBase + costoMerma; return { ...env, tipoLabel: tiposEnvase.find(t => t.value === env.tipo)?.label || env.tipo, precioBase, costoMerma, costoReal }; }); const totalBase = detalle.reduce((sum, d) => sum + d.precioBase, 0); const totalMerma = detalle.reduce((sum, d) => sum + d.costoMerma, 0); const totalReal = detalle.reduce((sum, d) => sum + d.costoReal, 0); setResultados({ detalle, totalBase, totalMerma, totalReal }); }; const reiniciar = () => { setEnvases([]); setResultados(null); }; const exportarPDF = () => { const content = ` Reporte de Packaging y Mermas

🧁 Reporte de Packaging y Mermas

Fecha: ${new Date().toLocaleDateString()}

📦 Detalle de Envases

${resultados?.detalle.map(d => ` `).join('') || ''}
Tipo de Envase Cantidad Precio Unit. % Merma Costo Base Costo Merma Costo Real
${d.tipoLabel} ${d.cantidad} $${parseFloat(d.precioUnidad).toFixed(2)} ${d.merma}% $${d.precioBase.toFixed(2)} $${d.costoMerma.toFixed(2)} $${d.costoReal.toFixed(2)}
TOTALES $${resultados?.totalBase.toFixed(2) || '0.00'} $${resultados?.totalMerma.toFixed(2) || '0.00'} $${resultados?.totalReal.toFixed(2) || '0.00'}

📊 Resumen

Costo Base Total: $${resultados?.totalBase.toFixed(2) || '0.00'}

Costo por Mermas: $${resultados?.totalMerma.toFixed(2) || '0.00'}

Costo Real Total: $${resultados?.totalReal.toFixed(2) || '0.00'}

`; const blob = new Blob([content], { type: 'text/html' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'reporte-packaging-mermas.html'; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); }; return (
{/* Configuración de Envases */}
{envases.map(env => (
updateEnvase(env.id, 'cantidad', e.target.value)} min="1" className="w-full px-3 py-2.5 bg-white border-2 border-sky-200 rounded-xl text-center font-bold focus:border-sky-400 outline-none text-gray-600 text-sm" />
$ updateEnvase(env.id, 'precioUnidad', e.target.value)} placeholder="0.00" min="0" step="0.01" className="w-full pl-7 pr-3 py-2.5 bg-white border-2 border-sky-200 rounded-xl font-bold focus:border-sky-400 outline-none text-gray-600 text-sm" />
updateEnvase(env.id, 'merma', e.target.value)} min="0" max="100" className="w-full px-3 py-2.5 bg-white border-2 border-sky-200 rounded-xl text-center font-bold focus:border-sky-400 outline-none text-gray-600 text-sm" /> %
))} Agregar Envase
{/* Botones de Acción */}
Calcular Costo Real Reiniciar
{/* Resultados */} {resultados && ( <>
{resultados.detalle.map((d, i) => ( ))}
Envase Cant. Costo Base + Merma Costo Real
{d.tipoLabel} {d.cantidad} ${d.precioBase.toFixed(2)} +${d.costoMerma.toFixed(2)} ${d.costoReal.toFixed(2)}
{/* Indicadores */}
💵 Costo Base
${resultados.totalBase.toFixed(2)}
📉 Costo Mermas
+${resultados.totalMerma.toFixed(2)}
✨ Costo Real Total
${resultados.totalReal.toFixed(2)}
{/* Exportar PDF */}
Exportar Resultados (HTML)
)}
); }; // ==================== APP PRINCIPAL ==================== export default function App() { const [pagina, setPagina] = useState('calculadora'); return (
{/* Patrón decorativo */}
{/* Decoraciones de repostería */}
🧁
🍰
🍪
🎂
{/* Header */}
🧁

Calculadora de Repostería

🍰

✨ Gestiona costos, recetas y packaging de manera dulce y profesional ✨

{/* Navegación */} {/* Contenido */}
{pagina === 'calculadora' && } {pagina === 'recetas' && } {pagina === 'packaging' && }
{/* Footer */}
); }
error: Content is protected !!