// Convertidor Sell-U — App principal (Hero · Analizando · Resultados · Captura lead) // Llama al backend Laravel para datos reales de PageSpeed + HTML scraping. const { useState, useEffect, useRef } = React; const ANALYZE_STEPS = window.ConvierteEngine.CATEGORIES.map((c) => c.label); function scrollToId(id) { const el = document.getElementById(id); if (!el) return; const reduce = window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches; const y = el.getBoundingClientRect().top + window.scrollY - 24; window.scrollTo({ top: y, behavior: reduce ? "auto" : "smooth" }); } function normalizeUrl(raw) { let u = (raw || "").trim(); if (!u) return ""; if (!/^https?:\/\//i.test(u)) u = "https://" + u; return u; } function isValidUrl(raw) { const u = normalizeUrl(raw); return /^https?:\/\/([\w-]+\.)+[a-z]{2,}(\/\S*)?$/i.test(u); } function prettyHost(url) { try { return new URL(normalizeUrl(url)).host.replace(/^www\./, ""); } catch { return url; } } /* Nav y Footer del convertidor se eliminaron — ahora la vista Blade envuelve el #root con y de Sell-U para que el convertidor se integre visualmente con el resto del sitio. */ /* ===================== HERO ===================== */ function Hero({ url, setUrl, device, setDevice, onAnalyze, error }) { const inputRef = useRef(null); return (
); } /* ===================== ANALIZANDO ===================== */ function Analyzing({ url, device, done, error, onRetry }) { const reduce = useRef(window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches).current; const total = ANALYZE_STEPS.length; // 'done' viene del padre cuando termina el fetch — pero animamos visualmente // un progreso suave mientras esperamos la respuesta del backend. const [step, setStep] = useState(0); useEffect(() => { if (done) { setStep(total); return; } const perStep = reduce ? 280 : 1100; // analisis backend tarda ~10–20s const id = setInterval(() => { setStep((s) => Math.min(total - 1, s + 1)); }, perStep); return () => clearInterval(id); }, [done]); const pct = Math.round((step / total) * 100); if (error) { return (

No pudimos completar el análisis

{error}

); } return (

Analizando {prettyHost(url)}…

Ejecutando auditoría en {device === "mobile" ? "móvil" : "escritorio"} con Google PageSpeed. Esto puede tomar 10–20 segundos.

{pct}%
    {ANALYZE_STEPS.map((label, i) => { const state = i < step ? "done" : i === step ? "active" : "wait"; return (
  • ); })}
); } /* ===================== RESULTADOS ===================== */ function PotentialCard({ pct }) { return (
Mejora potencial de conversión estimada
+{pct}%

Resolviendo los problemas detectados, basado en tu puntaje actual.

); } /* ===================== CORE WEB VITALS (nuevo: datos reales PSI) ===================== */ function CWVCard({ label, value, unit, threshold, lowerBetter = true }) { if (value === null || value === undefined) { return (
{label}

Sin datos suficientes.

); } // Determinar tono según threshold ([bueno, necesita-mejora]) let tone = "success"; if (lowerBetter) { if (value > threshold[1]) tone = "danger"; else if (value > threshold[0]) tone = "warning"; } else { if (value < threshold[0]) tone = "danger"; else if (value < threshold[1]) tone = "warning"; } const display = unit === "ms" && value > 1000 ? (value / 1000).toFixed(1) + "s" : unit === "ms" ? Math.round(value) + "ms" : unit === "cls" ? value.toFixed(2) : value; return (
{display}
{label}

{tone === "success" ? "Bueno" : tone === "warning" ? "Necesita mejora" : "Pobre"} {" "}— meta: {lowerBetter ? "≤" : "≥"} {unit === "ms" && threshold[0] > 1000 ? (threshold[0]/1000) + "s" : threshold[0] + (unit === "cls" ? "" : unit)}

); } function CoreWebVitalsSection({ cwv }) { if (!cwv) return null; return (
Core Web Vitals · datos reales (Google Lighthouse)

Métricas de rendimiento

Estas son las métricas oficiales de Google que afectan tu posicionamiento en búsqueda y la experiencia del usuario. Medidas en vivo en tu URL.

); } /* ===================== AUDITORÍA TÉCNICA (datos REALES) ===================== */ function AuditoriaTecnicaSection({ a }) { if (!a || !a.ok) return null; const { fmtNum, fmtCompact } = window.ConvierteEngine; const w = a.whois || {}; const c = a.crawl || {}; const s = a.seo || {}; const idx = a.indexed || {}; const t = a.tech || {}; const ssl = a.ssl || {}; const sec = a.security || {}; const ct = a.content || {}; // 12 KPIs reales (sin estimaciones) const kpis = []; // 1. Páginas indexadas if (idx.indexed_pages !== null && idx.indexed_pages !== undefined) { kpis.push({ icon:"globe", label:"Páginas indexadas en Google", value:fmtCompact(idx.indexed_pages), hint: idx.method === 'bing-scrape' ? "Medido vía site: query en Bing (proxy a Google)." : "Estimado del sitemap.xml.", tone: idx.indexed_pages > 10 ? "success" : "warning" }); } // 2. Edad del dominio if (w.domain_age_years) { kpis.push({ icon:"network", label:"Edad del dominio", value:w.domain_age_years, suffix:" años", hint: w.registrar ? `Registrado en ${w.registrar}.` : "Antigüedad real (WHOIS/RDAP).", tone: w.domain_age_years >= 3 ? "success" : (w.domain_age_years >= 1 ? "info" : "warning") }); } // 3. Páginas crawleadas con OK kpis.push({ icon:"check", label:"Páginas auditadas", value:c.pages_crawled || 0, hint:`${c.pages_ok || 0} OK · ${c.broken_count || 0} con problemas. Profundidad máx ${c.max_depth || 0}.`, tone: (c.broken_count || 0) === 0 ? "success" : "warning" }); // 4. Links rotos if ((c.broken_count || 0) > 0) { kpis.push({ icon:"alert", label:"Links rotos detectados", value:c.broken_count, hint:"Páginas internas que devuelven 4xx/5xx. Reducen UX y SEO.", tone: c.broken_count > 5 ? "danger" : "warning" }); } // 5. SSL grade if (ssl.grade) { kpis.push({ icon:"shield", label:"Grado SSL", value:ssl.grade, suffix:"/A+", hint: ssl.cert_expiry ? `Certificado válido hasta ${ssl.cert_expiry}.` : "Evaluación SSL Labs.", tone: ssl.grade.startsWith('A') ? "success" : (ssl.grade === 'B' ? "info" : "warning") }); } else if (ssl.has_https === false) { kpis.push({ icon:"alert", label:"HTTPS", value:"NO", hint:"Tu sitio no usa HTTPS — crítico.", tone:"danger" }); } // 6. Security headers kpis.push({ icon:"shield", label:"Security headers", value:sec.grade || "F", suffix:` · ${sec.present_count || 0}/${(sec.present_count || 0) + (sec.missing_count || 0)}`, hint: (sec.missing_count || 0) > 0 ? `Faltan: ${(sec.missing || []).slice(0,2).map(m=>m.header).join(', ')}.` : "Todos presentes.", tone: (sec.grade || "F") === "A" ? "success" : ((sec.grade || "F") === "B" ? "info" : "warning") }); // 7. CMS detectado if (t.cms) { kpis.push({ icon:"layers", label:"CMS detectado", value:t.cms, hint:"Plataforma identificada por fingerprinting.", tone:"info" }); } else if (t.frameworks?.length) { kpis.push({ icon:"layers", label:"Framework principal", value:t.frameworks[0], hint:"Detectado por análisis del HTML.", tone:"info" }); } // 8. E-commerce if (t.ecommerce) { kpis.push({ icon:"cursor", label:"Plataforma e-commerce", value:t.ecommerce, hint:"Tienda online detectada.", tone:"info" }); } // 9. Schema.org if ((s.meta?.schema_count || 0) > 0) { kpis.push({ icon:"check", label:"Schema.org markup", value:s.meta.schema_count, suffix:" bloques", hint: s.meta.schema_types?.length ? `Tipos: ${s.meta.schema_types.slice(0,3).join(', ')}.` : "Datos estructurados detectados.", tone:"success" }); } else { kpis.push({ icon:"alert", label:"Schema.org markup", value:"0", hint:"Sin datos estructurados — pierdes rich snippets en Google.", tone:"warning" }); } // 10. Open Graph const ogOk = s.meta?.has_og_image && s.meta?.has_og_title && s.meta?.has_og_desc; kpis.push({ icon:"globe", label:"Open Graph completo", value: ogOk ? "Sí" : "No", suffix: " · " + (s.meta?.og_count || 0) + " tags", hint: ogOk ? "Bien previsualizado en redes sociales." : "Faltan tags og:image / og:title / og:description.", tone: ogOk ? "success" : "warning" }); // 11. Sitemap kpis.push({ icon:"layers", label:"Sitemap.xml", value: s.sitemap?.exists ? `${s.sitemap.urls_count} URLs` : "No", hint: s.sitemap?.exists ? "Encontrado y procesado." : "Sin sitemap — Google rastrea peor.", tone: s.sitemap?.exists ? "success" : "warning" }); // 12. Contenido kpis.push({ icon:"message", label:"Palabras en la home", value: fmtCompact(ct.word_count || 0), suffix: ` · ${ct.text_html_ratio || 0}%`, hint: ct.has_thin_content ? "Contenido muy corto (<250 palabras). Google penaliza thin content." : "Densidad de contenido aceptable.", tone: ct.has_thin_content ? "warning" : "success" }); return (
Auditoría técnica · datos reales del sitio REAL

Auditoría técnica profunda

Crawleamos hasta {c.pages_crawled || 0} páginas internas, analizamos tecnologías, verificamos SSL, security headers, Schema.org, sitemap y más. Todos los datos son reales, medidos directamente sobre tu sitio.

{kpis.map((m, i) => (
{m.value}{m.suffix ? {m.suffix} : null}
{m.label}

{m.hint}

))}
{/* Tecnologías y analytics si hay */} {(t.analytics?.length > 0 || t.frameworks?.length > 0) && (
{t.frameworks?.length > 0 && (
Frameworks / librerías
{t.frameworks.map((f) => ( {f} ))}
)} {t.analytics?.length > 0 && (
Analytics / pixels
{t.analytics.map((a) => ( {a} ))}
)} {t.hosting && (
Hosting / CDN
{t.hosting}
)}
)}

100% datos reales — sin estimaciones. Auditamos tu sitio en vivo (crawler propio + WHOIS + SSL Labs + Bing index + análisis HTML). Las métricas de backlinks externos NO se incluyen aquí (requieren Ahrefs/Semrush).

); } function CategoryGrid({ categories }) { return (
{categories.map((c, i) => (
{c.score}
{c.label}
))}
); } function ProblemCard({ p, index, locked, onUnlock }) { return (
{p.catLabel} {!locked && Quick win}

{p.problema}

Impacto en ventas: {p.impacto}

{locked ? "Solución bloqueada" : "Cómo solucionarlo"}
{locked ? (
) : (

{p.solucion}

)}
); } /* Banner de verificación: muestra cuándo se hizo el análisis y permite comparar con pagespeed.web.dev en una pestaña nueva. */ function VerificacionBanner({ dx, onReanalizar }) { const [reanalizando, setReanalizando] = useState(false); const fetched = dx.meta?.fetched_at ? new Date(dx.meta.fetched_at) : null; const ageMin = fetched ? Math.round((Date.now() - fetched.getTime()) / 60000) : null; const psWebUrl = `https://pagespeed.web.dev/analysis?url=${encodeURIComponent(dx.url)}&form_factor=${dx.device}`; const handleRe = async () => { setReanalizando(true); await onReanalizar(); setReanalizando(false); }; return (
Datos reales medidos por Google PageSpeed Insights. {fetched && <> Analizado {ageMin < 1 ? "ahora mismo" : `hace ${ageMin} min`} · {fetched.toLocaleString('es-LA')}}
Lighthouse tiene ±5-10 puntos de variabilidad normal entre runs.
Verificar en pagespeed.web.dev
); } function Results({ dx, onReset, onReanalizar }) { const free = dx.problems.slice(0, dx.freeCount); const locked = dx.problems.slice(dx.freeCount); const lockedCount = locked.length; return (
Diagnóstico para {prettyHost(dx.url)} {dx.device === "mobile" ? "Móvil" : "Escritorio"}

Tu diagnóstico de conversión

Detectamos {dx.problems.length} problemas que están frenando tus ventas. Aquí están tus quick wins gratis y la ruta para arreglar el resto.

{/* Core Web Vitals reales */} {dx.meta?.psi_ok && } {/* Auditoría técnica REAL (sustituye a la sección Tráfico antigua) */} {dx.auditoria?.ok && } {/* Categorías */}
Optimización de conversión por área

Puntaje por categoría

{/* Problemas detectados */}
Problemas de tu página web

Problemas detectados y cómo resolverlos

Las 3 primeras son quick wins con la solución completa. Las {lockedCount} restantes se desbloquean con el reporte completo.

{free.map((p, i) => ( ))} {locked.map((p, i) => ( scrollToId("captura")} /> ))}
); } function Methodology({ categories }) { return (
Cómo se calcula este puntaje

¿En qué se basa tu diagnóstico?

Combinamos Google PageSpeed Insights (Performance, Accessibility, Best Practices, SEO, Core Web Vitals reales) con análisis del HTML de tu página (titular, CTAs, formularios, jerarquía). Cada categoría pondera por su impacto en conversión.

{categories.map((c) => (

{c.label}

Impacto {c.weight >= 1.3 ? "alto" : c.weight >= 1.1 ? "medio" : "base"}
    {c.signals.map((s, i) => (
  • {s}
  • ))}
))}

Sobre los datos: Performance, Accessibility, Best Practices, SEO y Core Web Vitals son reales de Google PageSpeed Insights API. Tráfico orgánico y backlinks son estimaciones derivadas — la versión completa los mide con Ahrefs.

); } /* ===================== CAPTURA ===================== */ function Capture({ url, dx }) { const [data, setData] = useState({ nombre: "", correo: "", sitio: url || "", objetivo: "", mensaje: "" }); const [touched, setTouched] = useState({}); const [sent, setSent] = useState(false); const [submitting, setSubmitting] = useState(false); const [serverErr, setServerErr] = useState(""); useEffect(() => { setData((d) => ({ ...d, sitio: d.sitio || url || "" })); }, [url]); const errors = { nombre: !data.nombre.trim() ? "Ingresa tu nombre." : "", correo: !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(data.correo) ? "Ingresa un correo válido." : "", objetivo: !data.objetivo ? "Elige una opción." : "", }; const valid = !errors.nombre && !errors.correo && !errors.objetivo; const set = (k) => (e) => setData((d) => ({ ...d, [k]: e.target.value })); const blur = (k) => () => setTouched((t) => ({ ...t, [k]: true })); const submit = async () => { setTouched({ nombre: true, correo: true, objetivo: true }); if (!valid) return; setSubmitting(true); setServerErr(""); try { // Snapshot del diagnóstico actual para guardarlo con el lead const payload = { nombre: data.nombre.trim(), correo: data.correo.trim().toLowerCase(), sitio: data.sitio.trim() || null, objetivo: data.objetivo, mensaje: data.mensaje.trim() || null, }; if (dx) { payload.url_analizada = dx.url; payload.dispositivo = dx.device; payload.score_general = dx.overall; payload.scores_categorias = Object.fromEntries(dx.categories.map(c => [c.key, c.score])); payload.core_web_vitals = dx.core_web_vitals || dx.meta?.core_web_vitals || null; payload.problemas_count = dx.problems.length; payload.snapshot_diagnostico = { overall: dx.overall, potentialPct: dx.potentialPct, problems: dx.problems, categories: dx.categories.map(c => ({ key: c.key, label: c.label, score: c.score })), }; } await window.ConvierteEngine.submitLead(payload); setSent(true); } catch (e) { setServerErr(e.message || "No pudimos enviar tu solicitud. Intenta de nuevo."); } finally { setSubmitting(false); } }; return (
Implementamos las mejoras por ti

¿Quieres que solucionemos
estos problemas por ti?

En Sell-U LATAM implementamos las mejoras y aumentamos tu tasa de conversión. Déjanos tus datos y te enviamos el reporte completo + una propuesta a tu medida.

  • Reporte completo con todas las soluciones
  • Plan de mejoras priorizado por impacto
  • Propuesta sin compromiso en menos de 24h
{sent ? (

¡Listo! Te contactaremos en menos de 24h

Recibimos tu solicitud para {prettyHost(data.sitio)}. Nuestro equipo te enviará el reporte completo y propuesta a {data.correo}.

) : (
Recibe tu reporte completo
{/* Honeypot anti-bot (oculto) */} {}} />