// Cotizador interactivo — STFLOW const { useState, useEffect, useMemo } = React; const PRODUCTS = [ { id: 'remera', name: 'Remera', ico: '👕', base: 180, desc: 'Algodón peinado 24/1' }, { id: 'gorra', name: 'Gorra', ico: '🧢', base: 260, desc: 'Trucker, snapback o cerrada' }, { id: 'buzo', name: 'Buzo', ico: '🧥', base: 480, desc: 'Canguro o redondo' }, { id: 'polo', name: 'Polo', ico: '👔', base: 320, desc: 'Piqué algodón corporativo' }, { id: 'tote', name: 'Tote bag', ico: '👜', base: 140, desc: 'Crudo 100% algodón' }, { id: 'taza', name: 'Taza', ico: '☕', base: 110, desc: 'Cerámica sublimable' }, ]; const TECHNIQUES = { remera: [ { id: 'dtf', name: 'DTF', note: 'Full color, todo tipo de tela', cost: 150 }, { id: 'sub', name: 'Sublimación', note: 'Poliéster claro', cost: 110 }, { id: 'seri', name: 'Serigrafía', note: '30+ unidades', cost: 70 }, { id: 'vin', name: 'Vinilo', note: 'Texto / números', cost: 60 }, ], gorra: [ { id: 'bord', name: 'Bordado', note: 'Premium · recomendado', cost: 240 }, { id: 'dtf', name: 'DTF', note: 'Full color', cost: 140 }, { id: 'sub', name: 'Sublimación', note: 'Gorras claras', cost: 120 }, ], buzo: [ { id: 'dtf', name: 'DTF', note: 'Full color', cost: 220 }, { id: 'bord', name: 'Bordado', note: 'Logo pequeño', cost: 280 }, { id: 'seri', name: 'Serigrafía', note: '30+ unidades', cost: 110 }, ], polo: [ { id: 'bord', name: 'Bordado', note: 'Recomendado corporativo', cost: 260 }, { id: 'dtf', name: 'DTF', note: 'Full color', cost: 160 }, ], tote: [ { id: 'dtf', name: 'DTF', note: 'Full color', cost: 130 }, { id: 'seri', name: 'Serigrafía', note: '30+ unidades', cost: 70 }, { id: 'vin', name: 'Vinilo', note: 'Texto plano', cost: 60 }, ], taza: [ { id: 'sub', name: 'Sublimación', note: 'Full color', cost: 90 }, ], }; const ADDONS = [ { id: 'packaging', name: 'Packaging kraft + tarjeta', desc: 'Bolsita kraft, sticker y tarjeta de agradecimiento', price: 40 }, { id: 'etiqueta', name: 'Etiqueta con tu marca', desc: 'Etiqueta cosida interna con tu logo', price: 60 }, { id: 'back', name: 'Estampa en la espalda', desc: 'Segunda posición de estampado', price: 120 }, { id: 'urgente', name: 'Producción express', desc: 'Entrega en 5 días hábiles (vs 10-15)', price: 150 }, ]; function volumeDiscount(qty) { if (qty >= 100) return 0.20; if (qty >= 50) return 0.15; if (qty >= 20) return 0.10; return 0; } function fmt(n) { return '$' + Math.round(n).toLocaleString('es-MX'); } function Quoter() { const [step, setStep] = useState(0); const [productId, setProductId] = useState(null); const [techId, setTechId] = useState(null); const [qty, setQty] = useState(10); const [addons, setAddons] = useState({}); const product = PRODUCTS.find(p => p.id === productId); const techniques = productId ? TECHNIQUES[productId] : []; const technique = techniques.find(t => t.id === techId); // Reset technique when product changes useEffect(() => { if (productId && techId && !TECHNIQUES[productId].some(t => t.id === techId)) { setTechId(null); } }, [productId]); const calc = useMemo(() => { if (!product || !technique) return null; const addonPrice = Object.entries(addons) .filter(([, on]) => on) .reduce((s, [id]) => s + (ADDONS.find(a => a.id === id)?.price || 0), 0); const unitCost = product.base + technique.cost + addonPrice + 50; // 50 = packaging base + control const margin = 2.4; const rawUnitPrice = unitCost * margin; const discount = volumeDiscount(qty); const unitPrice = rawUnitPrice * (1 - discount); const subtotal = unitPrice * qty; const profit = (unitPrice - unitCost) * qty; return { unitCost, rawUnitPrice, unitPrice, subtotal, profit, discount, addonPrice, }; }, [product, technique, qty, addons]); const canNext = (step === 0 && productId) || (step === 1 && techId) || step === 2 || step === 3; const goNext = () => setStep(s => Math.min(s + 1, 3)); const goBack = () => setStep(s => Math.max(s - 1, 0)); const sendWhatsApp = () => { if (!product || !technique || !calc) return; const activeAddons = Object.entries(addons) .filter(([, on]) => on) .map(([id]) => ADDONS.find(a => a.id === id).name) .join(', ') || 'ninguno'; const msg = `Hola STFLOW! Quiero cotizar: • Producto: ${product.name} • Técnica: ${technique.name} • Cantidad: ${qty} • Adicionales: ${activeAddons} • Estimado: ${fmt(calc.subtotal)} MX ¿Me pasan detalles?`; window.open(`https://wa.me/525572888676?text=${encodeURIComponent(msg)}`, '_blank'); }; const steps = [ { label: 'Producto' }, { label: 'Técnica' }, { label: 'Cantidad' }, { label: 'Extras' }, ]; return (
{/* Steps indicator */}
{steps.map((s, i) => ( ))}
{step === 0 && } {step === 1 && } {step === 2 && } {step === 3 && }
{step < 3 ? ( ) : ( )}
{/* Summary panel */}
); } function Step0({ productId, setProductId }) { return ( <>

¿Qué producto querés cotizar?

Elegí la prenda o artículo base. Después elegimos la técnica de estampado.

{PRODUCTS.map(p => ( ))}
); } function Step1({ product, techniques, techId, setTechId }) { return ( <>

Elegí la técnica de estampado

Para {product.name} estas son las opciones disponibles.

{techniques.map(t => ( ))}
); } function Step2({ qty, setQty, product }) { const presets = [1, 10, 20, 50, 100]; const discount = volumeDiscount(qty); return ( <>

¿Cuántas unidades?

A partir de 20 unidades aplicamos descuentos automáticos por volumen.

{qty} {discount > 0 ? ( −{Math.round(discount*100)}% VOL ) : 'sin descuento'} {product.name.toLowerCase()}
setQty(parseInt(e.target.value))} className="slider" />
{presets.map(p => ( ))}
); } function Step3({ addons, setAddons }) { const toggle = id => setAddons(a => ({ ...a, [id]: !a[id] })); return ( <>

Extras

Adicionales opcionales. Podés saltarlos si no te hacen falta.

{ADDONS.map(a => (
toggle(a.id)} >
{a.name}
{a.desc}
+{fmt(a.price)}/u
))}
); } function SummaryPanel({ product, technique, qty, addons, calc, sendWhatsApp }) { if (!product || !technique || !calc) { return (
Armá tu pedido
Completá los pasos y vas a ver acá tu cotización en vivo, actualizada al instante.
); } const activeAddons = ADDONS.filter(a => addons[a.id]); return ( <>
Total estimado
{fmt(calc.subtotal)}
Pesos mexicanos · IVA incluido
{fmt(calc.unitPrice)} por unidad × {qty}
Producto{product.name}
Técnica{technique.name}
Cantidad{qty} uds
{activeAddons.length > 0 && (
Extras {activeAddons.map(a => a.name).join(' · ')}
)} {calc.discount > 0 && (
Descuento volumen −{Math.round(calc.discount*100)}%
)}
estimado orientativo · cotización final 1-3 días hábiles
); } window.Quoter = Quoter;