/* ===================================================================== steps.jsx — los 7 pasos del wizard. Cada paso lee/escribe el store vía props. StepShell estandariza encabezado y ritmo vertical. ===================================================================== */ const C = window.Cotizador; /* Encabezado de paso */ function StepShell({ step, title, subtitle, children }) { return (
Paso {step} de 7

{title}

{subtitle &&

{subtitle}

}
{children}
); } /* Sub-pregunta dentro de un paso (label + tooltip opcional + control) */ function SubQuestion({ label, tip, tipLabel, children, hint }) { return (
{label} {tip && }
{children} {hint &&
{hint}
}
); } const grid2 = { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(240px, 1fr))", gap: 14 }; /* ---------------------------------------------------------------- PASO 1 */ function Step1({ state, setField, density }) { const icons = { landing: "panel-top", sales: "shopping-cart", funnel: "filter" }; const tips = { funnel: "Un embudo guía al visitante por una secuencia de páginas (captación → oferta → checkout → upsell) midiendo la conversión en cada paso.", }; function choose(type) { if (state.projectType === type) return; // cambiar tipo invalida escala y plataforma dependientes setField({ projectType: type, scale: null, platform: null, recommendChosen: false, platformConfirmed: false }); } return (
{Object.keys(C.PROJECT_TYPES).map(key => { const pt = C.PROJECT_TYPES[key]; return (
choose(key)} icon={icons[key]} title={pt.label} desc={pt.desc} density={density} /> {tips[key] && ( )}
); })}
); } /* ---------------------------------------------------------------- PASO 2 */ function Step2({ state, setField, density, openContact }) { const scales = C.SCALES[state.projectType] || []; const noun = state.projectType === "sales" ? "tu catálogo" : state.projectType === "funnel" ? "tus embudos" : "tu landing"; return (
{scales.map(s => ( { if (s.contact) { openContact(); } else { setField("scale", s.value); } }} title={s.label} desc={s.desc} badge={s.contact ? "Hablemos" : null} tone={s.contact ? "accent" : null} density={density} /> ))}
); } /* ---------------------------------------------------------------- PASO 3 */ function Step3({ state, setField, density }) { const order = ["recommend", "wordpress-elementor", "wordpress-woo", "shopify", "webflow", "framer", "nextjs-react", "php-laravel", "clickfunnels-systeme", "carrd"]; const rec = (state.recommendChosen || state.platform) ? C.recommendPlatform(state.projectType, state.scale) : null; function pickPlatform(key) { if (key === "recommend") { setField({ recommendChosen: true, platform: null, platformConfirmed: false }); } else { setField({ platform: key, recommendChosen: false, platformConfirmed: true }); } } function confirmRec() { const r = C.recommendPlatform(state.projectType, state.scale); setField({ platform: r.platform, platformConfirmed: true }); } const recPlatformLabel = rec ? C.PLATFORMS[rec.platform].label : ""; return (
{order.map(key => { const pl = C.PLATFORMS[key]; const isRecommendCard = key === "recommend"; const selected = isRecommendCard ? state.recommendChosen : (state.platform === key && !state.recommendChosen); return ( pickPlatform(key)} icon={isRecommendCard ? "sparkles" : null} title={pl.label} desc={pl.desc} tone={isRecommendCard ? "accent" : null} badge={isRecommendCard ? "Sugerido" : null} density={density} /> ); })}
{/* Panel de recomendación */} {state.recommendChosen && rec && (
Nuestra recomendación
{recPlatformLabel}

{rec.reason}

)} {/* Hosting / licencia */} {(state.platformConfirmed || (state.platform && !state.recommendChosen)) && (
setField("hostingOwnership", "client")} icon="user-round" title="A mi nombre" desc="Tú eres el dueño. Setup +$80 USD." density={density} /> setField("hostingOwnership", "sell-u")} icon="building-2" title="A nombre de Sell-U" desc="Nosotros lo administramos por ti." density={density} />
)}
); } /* ---------------------------------------------------------------- PASO 4 */ function Step4({ state, setNested }) { const cs = state.currentStatus; return ( setNested("currentStatus", "hasWebsite", v)} options={[ { value: "no", label: "No" }, { value: "migrate", label: "Sí, quiero migrar" }, { value: "redesign", label: "Sí, quiero rediseñar" }, ]} /> setNested("currentStatus", "hasDomain", v)} options={[ { value: "yes", label: "Sí, lo tengo" }, { value: "no", label: "No" }, { value: "unknown", label: "No sé dónde está" }, ]} /> setNested("currentStatus", "hasHosting", v === "yes")} options={[ { value: "yes", label: "Sí" }, { value: "no", label: "No" }, ]} /> setNested("currentStatus", "hasBranding", v)} options={[ { value: "no", label: "No" }, { value: "partial", label: "Parcial" }, { value: "complete", label: "Completo" }, ]} /> ); } /* ---------------------------------------------------------------- PASO 5 */ function Step5({ state, setNested }) { const co = state.content; return ( setNested("content", "productPhotos", v)} options={[ { value: "none", label: "Ninguna" }, { value: "some", label: "Algunas" }, { value: "all-pro", label: "Todas profesionales" }, ]} /> setNested("content", "copy", v)} options={[ { value: "client", label: "Yo los doy" }, { value: "sell-u", label: "Quiero que ustedes los escriban" }, ]} /> setNested("content", "videos", v)} options={[ { value: "none", label: "Ninguno" }, { value: "has", label: "Tengo" }, { value: "needs-production", label: "Necesito producción" }, ]} /> setNested("content", "languages", v)} options={[ { value: 1, label: "1" }, { value: 2, label: "2" }, { value: 3, label: "3+" }, ]} /> ); } /* ---------------------------------------------------------------- PASO 6 */ function Step6({ state, toggleInArray, density }) { const tips = { crm: "Un CRM organiza tus contactos y automatiza correos (ej. HubSpot, Mailchimp). Útil para dar seguimiento a clientes potenciales.", analytics: "El Pixel de Meta y GA4 miden quién visita tu sitio y qué hace, para optimizar tu publicidad. 'Server-side' lo hace más preciso.", }; return (
{C.INTEGRATIONS.map(ig => (
toggleInArray("integrations", ig.value)} title={ig.label} desc={ig.desc} density={density} /> {tips[ig.value] && ( )}
))}
); } /* ---------------------------------------------------------------- PASO 7 */ function Step7({ state, setField, density }) { return (
{C.SUPPORT_OPTIONS.map(opt => ( setField("support", opt.value)} title={opt.label} desc={opt.desc} badge={opt.price === 0 ? "Incluido" : "+" + C.fmtUSD(opt.price)} density={density} /> ))}
); } Object.assign(window, { StepShell, SubQuestion, Step1, Step2, Step3, Step4, Step5, Step6, Step7 });