fix: menú SEO realista — paneles distintos y sin stubs Semrush vacíos

Recorta la suite a secciones con GSC/GA4 (overview, auditoría checklist, rankings con histórico, tendencia, páginas, keywords, insights, contenido). Competencia/backlinks quedan como “herramienta externa”. Ya no se reutiliza el mismo dashboard en ítems distintos.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
4Ventures
2026-09-24 16:47:24 +02:00
parent 4b6f7f1896
commit d896041bed
2 changed files with 306 additions and 296 deletions

View File

@@ -547,15 +547,12 @@ export default function OrganicoDashboard({
<div className="space-y-4 min-w-0"> <div className="space-y-4 min-w-0">
<SectionHero section={section} /> <SectionHero section={section} />
{/* Paneles por sección (no el mismo dashboard en todos) */} {/* Paneles SEO por sección */}
{section.id === 'seo-overview' && ( {section.id === 'seo-overview' && (
<> <>
<div className="grid grid-cols-2 lg:grid-cols-4 gap-3"> <div className="grid grid-cols-2 lg:grid-cols-4 gap-3">
<Kpi label="Clics GSC" value={formatNum(totals.gscClicks)} /> <Kpi label="Clics GSC" value={formatNum(totals.gscClicks)} />
<Kpi <Kpi label="Impresiones" value={formatNum(totals.gscImpressions)} />
label="Impresiones"
value={formatNum(totals.gscImpressions)}
/>
<Kpi label="CTR" value={formatPct(totals.gscCtr)} /> <Kpi label="CTR" value={formatPct(totals.gscCtr)} />
<Kpi <Kpi
label="Posición media" label="Posición media"
@@ -576,9 +573,9 @@ export default function OrganicoDashboard({
value={formatNum(totals.ga4Conversions)} value={formatNum(totals.ga4Conversions)}
/> />
<Kpi <Kpi
label="Días con datos" label="Días con histórico"
value={formatNum(totals.days)} value={formatNum(totals.days)}
hint="Serie cargada" hint="organic_daily"
/> />
</div> </div>
{seoDaily.length > 0 && ( {seoDaily.length > 0 && (
@@ -589,16 +586,10 @@ export default function OrganicoDashboard({
</p> </p>
<div className="flex items-end gap-0.5 h-16"> <div className="flex items-end gap-0.5 h-16">
{seoDaily.map((d) => ( {seoDaily.map((d) => (
<div <div key={`c-${d.day}`} className="flex-1" title={`${d.day}: ${d.clicks}`}>
key={`c-${d.day}`}
className="flex-1"
title={`${d.day}: ${d.clicks} clics`}
>
<div <div
className="w-full rounded-t bg-emerald-400/80" className="w-full rounded-t bg-emerald-400/80"
style={{ style={{ height: `${Math.max(3, (d.clicks / maxClicks) * 100)}%` }}
height: `${Math.max(3, (d.clicks / maxClicks) * 100)}%`,
}}
/> />
</div> </div>
))} ))}
@@ -610,16 +601,10 @@ export default function OrganicoDashboard({
</p> </p>
<div className="flex items-end gap-0.5 h-16"> <div className="flex items-end gap-0.5 h-16">
{seoDaily.map((d) => ( {seoDaily.map((d) => (
<div <div key={`s-${d.day}`} className="flex-1" title={`${d.day}: ${d.sessions}`}>
key={`s-${d.day}`}
className="flex-1"
title={`${d.day}: ${d.sessions} sesiones`}
>
<div <div
className="w-full rounded-t bg-sky-400/80" className="w-full rounded-t bg-sky-400/80"
style={{ style={{ height: `${Math.max(3, (d.sessions / maxSessions) * 100)}%` }}
height: `${Math.max(3, (d.sessions / maxSessions) * 100)}%`,
}}
/> />
</div> </div>
))} ))}
@@ -628,7 +613,7 @@ export default function OrganicoDashboard({
</div> </div>
)} )}
{totals.days === 0 && ( {totals.days === 0 && (
<EmptyHint text="GSC/GA4 conectados pero sin filas en organic_daily. Lanza sync en Agencia o revisa property/URL-prefix." /> <EmptyHint text="Sin filas en organic_daily. Sync GA4/GSC en Agencia." />
)} )}
<SeoTables <SeoTables
queries={gscQueries.slice(0, 8)} queries={gscQueries.slice(0, 8)}
@@ -638,22 +623,169 @@ export default function OrganicoDashboard({
</> </>
)} )}
{(section.id === 'seo-keywords' || {section.live === 'seo-foundations' && (
section.id === 'seo-rankings' || <div className="space-y-3">
section.id === 'seo-keyword-magic' || <p className="text-xs text-gray-600">
section.id === 'seo-keyword-gap') && ( Checklist de auditoría operativa. El estado «OK» de medición
<SeoTables refleja si GA4 + GSC están conectados; el resto lo cierra el equipo.
queries={gscQueries} </p>
pages={[]} <ul className="grid grid-cols-1 md:grid-cols-2 gap-2">
landings={[]} {SEO_FOUNDATIONS.map((f) => {
focus="queries" const ok =
/> f.id === 'medicion' &&
ga4?.status === 'connected' &&
gsc?.status === 'connected';
return (
<li
key={f.id}
className={`rounded-lg border px-3 py-2.5 ${
ok
? 'border-emerald-200 bg-emerald-50'
: 'border-gray-200 bg-white'
}`}
>
<p className="text-sm font-semibold text-gray-900">
{f.title}
{ok ? (
<span className="ml-2 text-[10px] uppercase text-emerald-700">
OK
</span>
) : (
<span className="ml-2 text-[10px] uppercase text-amber-700">
Revisar
</span>
)}
</p>
<p className="text-[11px] text-gray-600 mt-0.5">{f.hint}</p>
</li>
);
})}
</ul>
<div className="rounded-xl border border-gray-200 bg-white p-3 text-xs text-gray-700 space-y-1">
<p className="font-semibold text-gray-900">Estado conexiones</p>
<p>GSC: {gsc?.status === 'connected' ? 'conectado' : 'sin conectar'}{gsc?.external_account_id ? ` · ${gsc.external_account_id}` : ''}</p>
<p>GA4: {ga4?.status === 'connected' ? 'conectado' : 'sin conectar'}{ga4?.external_account_id ? ` · ${ga4.external_account_id}` : ''}</p>
</div>
</div>
)} )}
{(section.id === 'seo-pages' || {section.live === 'seo-rankings' && (
section.id === 'seo-organic' || <div className="space-y-4">
section.id === 'seo-traffic-insights' || <div className="rounded-xl border border-sky-200 bg-sky-50 px-3 py-2 text-xs text-sky-900">
section.id === 'seo-compare') && ( <strong>Sí hay histórico:</strong> posición media diaria en{' '}
<code className="text-[10px]">organic_daily</code> (acumulamos
nosotros). Las queries actuales vienen live de GSC (ventana ~28d).
No es un rank tracker de 10k KW tipo Semrush.
</div>
<div className="grid grid-cols-2 lg:grid-cols-3 gap-3">
<Kpi
label="Posición media (periodo)"
value={
totals.gscAvgPosition
? formatNum(totals.gscAvgPosition, 1)
: '—'
}
/>
<Kpi label="Queries con datos" value={formatNum(gscQueries.length)} />
<Kpi
label="Días históricos"
value={formatNum(totals.days)}
hint="organic_daily"
/>
</div>
{seoDaily.length > 0 && (
<div className="rounded-xl border border-gray-200 bg-white p-3">
<p className="text-[10px] uppercase tracking-wider text-gray-500 mb-2">
Posición media GSC · histórico diario
</p>
<div className="flex items-end gap-0.5 h-20">
{organic
.filter((r) => r.day)
.sort((a, b) => String(a.day).localeCompare(String(b.day)))
.slice(-28)
.map((r) => {
const pos = Number(r.gsc_avg_position) || 0;
const h =
pos > 0 ? Math.max(8, Math.min(100, (1 / pos) * 100)) : 4;
return (
<div
key={`pos-${r.day}`}
className="flex-1"
title={`${String(r.day).slice(0, 10)}: pos ${pos || '—'}`}
>
<div
className="w-full rounded-t bg-violet-400/80"
style={{ height: `${h}%` }}
/>
</div>
);
})}
</div>
<p className="text-[10px] text-gray-500 mt-1">
Barras más altas ≈ mejor posición (inverso)
</p>
</div>
)}
<SeoTables
queries={[...gscQueries].sort((a, b) => a.position - b.position)}
pages={[]}
landings={[]}
focus="queries"
/>
</div>
)}
{section.live === 'seo-trend' && (
<div className="space-y-4">
<div className="grid grid-cols-2 lg:grid-cols-4 gap-3">
<Kpi label="Clics" value={formatNum(totals.gscClicks)} />
<Kpi label="Impresiones" value={formatNum(totals.gscImpressions)} />
<Kpi label="CTR" value={formatPct(totals.gscCtr)} />
<Kpi
label="Sesiones orgánicas"
value={formatNum(totals.ga4Organic)}
/>
</div>
{seoDaily.length > 0 ? (
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
<div className="rounded-xl border border-gray-200 bg-white p-3">
<p className="text-[10px] uppercase tracking-wider text-gray-500 mb-2">
Clics · evolución
</p>
<div className="flex items-end gap-0.5 h-24">
{seoDaily.map((d) => (
<div key={`t-${d.day}`} className="flex-1" title={`${d.day}: ${d.clicks}`}>
<div
className="w-full rounded-t bg-emerald-500/80"
style={{ height: `${Math.max(3, (d.clicks / maxClicks) * 100)}%` }}
/>
</div>
))}
</div>
</div>
<div className="rounded-xl border border-gray-200 bg-white p-3">
<p className="text-[10px] uppercase tracking-wider text-gray-500 mb-2">
Sesiones orgánicas · evolución
</p>
<div className="flex items-end gap-0.5 h-24">
{seoDaily.map((d) => (
<div key={`u-${d.day}`} className="flex-1" title={`${d.day}: ${d.sessions}`}>
<div
className="w-full rounded-t bg-sky-500/80"
style={{ height: `${Math.max(3, (d.sessions / maxSessions) * 100)}%` }}
/>
</div>
))}
</div>
</div>
</div>
) : (
<EmptyHint text="Sin serie diaria todavía." />
)}
</div>
)}
{section.live === 'seo-pages' && (
<SeoTables <SeoTables
queries={[]} queries={[]}
pages={gscPages} pages={gscPages}
@@ -662,27 +794,45 @@ export default function OrganicoDashboard({
/> />
)} )}
{section.id === 'seo-organic' && seoDaily.length > 0 && ( {section.live === 'seo-queries' && (
<div className="rounded-xl border border-gray-200 bg-white p-3"> <SeoTables
<p className="text-[10px] uppercase tracking-wider text-gray-500 mb-2"> queries={gscQueries}
Evolución clics orgánicos pages={[]}
landings={[]}
focus="queries"
/>
)}
{section.live === 'seo-cross' && (
<div className="space-y-4">
<p className="text-xs text-gray-600">
Cruce: qué URLs rankean (GSC) vs qué landings reciben sesión
orgánica (GA4).
</p> </p>
<div className="flex items-end gap-0.5 h-20"> <SeoTables
{seoDaily.map((d) => ( queries={[]}
<div pages={gscPages}
key={`o-${d.day}`} landings={ga4Landings}
className="flex-1" focus="pages"
title={`${d.day}: ${d.clicks}`} />
> </div>
<div )}
className="w-full rounded-t bg-emerald-500/80"
style={{ {section.live === 'seo-content' && (
height: `${Math.max(3, (d.clicks / maxClicks) * 100)}%`, <div className="space-y-3">
}} <p className="text-xs text-gray-600">
/> Ideas a partir de queries con impresiones altas y CTR bajo
</div> (oportunidad de mejorar snippet / contenido).
))} </p>
</div> <SeoTables
queries={[...gscQueries]
.filter((q) => q.impressions >= 50)
.sort((a, b) => a.ctr - b.ctr)
.slice(0, 25)}
pages={[]}
landings={[]}
focus="queries"
/>
</div> </div>
)} )}
@@ -793,32 +943,6 @@ export default function OrganicoDashboard({
<CategoryPanel latest={latestGeo} /> <CategoryPanel latest={latestGeo} />
)} )}
{section.live === 'seo-foundations' && (
<ul className="grid grid-cols-1 md:grid-cols-2 gap-2">
{SEO_FOUNDATIONS.map((f) => {
const ok =
f.id === 'medicion' &&
ga4?.status === 'connected' &&
gsc?.status === 'connected';
return (
<li
key={f.id}
className={`rounded-lg border px-3 py-2.5 ${
ok
? 'border-emerald-200 bg-emerald-50'
: 'border-gray-200 bg-white'
}`}
>
<p className="text-sm font-semibold text-gray-900">
{f.title}
</p>
<p className="text-[11px] text-gray-600 mt-0.5">{f.hint}</p>
</li>
);
})}
</ul>
)}
{/* Prioridades + notas solo en visión general SEO/GEO */} {/* Prioridades + notas solo en visión general SEO/GEO */}
{(section.id === 'seo-overview' || section.id === 'geo-overview') && ( {(section.id === 'seo-overview' || section.id === 'geo-overview') && (
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4"> <div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
@@ -930,10 +1054,28 @@ export default function OrganicoDashboard({
)} )}
{section.soon && ( {section.soon && (
<p className="text-xs text-amber-900 bg-amber-50 border border-amber-200 rounded-lg px-3 py-2"> <div className="rounded-xl border border-amber-200 bg-amber-50 p-4 space-y-2">
Disponible en breve: informes de ROI IA + tráfico/conversiones GA4 <p className="text-sm font-semibold text-amber-900">
en un clic. Requiere herramienta externa
</p> </p>
<p className="text-xs text-amber-800">
Competencia de dominio, keyword gap y backlinks no salen de
GSC/GA4. Hay que conectar Ahrefs/Semrush (o importar CSV) para
alimentar este panel. Mientras tanto usa las secciones con datos
reales: visión general, rankings, keywords y páginas.
</p>
<ul className="space-y-1.5 pt-1">
{section.actions.map((a) => (
<li
key={a}
className="flex items-start gap-2 text-xs text-amber-900"
>
<ArrowRight className="w-3.5 h-3.5 shrink-0 mt-0.5" />
{a}
</li>
))}
</ul>
</div>
)} )}
{!section.soon && {!section.soon &&

View File

@@ -213,7 +213,18 @@ export type OrganicoSection = {
blurb: string; blurb: string;
/** What the user should do next when empty. */ /** What the user should do next when empty. */
actions: string[]; actions: string[];
live?: 'seo-metrics' | 'geo-metrics' | 'geo-competitors' | 'geo-categories' | 'seo-foundations'; live?:
| 'seo-metrics'
| 'seo-foundations'
| 'seo-rankings'
| 'seo-trend'
| 'seo-pages'
| 'seo-queries'
| 'seo-cross'
| 'seo-content'
| 'geo-metrics'
| 'geo-competitors'
| 'geo-categories';
soon?: boolean; soon?: boolean;
}; };
@@ -370,18 +381,18 @@ export const ORGANICO_SECTIONS: OrganicoSection[] = [
soon: true, soon: true,
}, },
// ——— SEO ——— // ——— SEO (solo secciones con datos reales GSC/GA4 o checklist operable) ———
{ {
id: 'seo-overview', id: 'seo-overview',
pillar: 'seo', pillar: 'seo',
group: 'Rendimiento del sitio', group: 'Rendimiento del sitio',
label: 'Visión general', label: 'Visión general',
blurb: blurb:
'Hazte visible donde busca tu audiencia. Serie diaria desde GA4/GSC — el portal guarda histórico propio (GSC solo ofrece ~16 meses en API).', 'KPIs diarios GA4 + GSC. El histórico vive en organic_daily (nosotros lo acumulamos; la API de GSC sola no basta).',
actions: [ actions: [
'Conectar Search Console y GA4', 'Conectar Search Console y GA4',
'Sincronizar (histórico acumulado en organic_daily)', 'Revisar serie 28d',
'Definir 3 prioridades del mes', 'Definir prioridades del mes',
], ],
live: 'seo-metrics', live: 'seo-metrics',
}, },
@@ -391,11 +402,11 @@ export const ORGANICO_SECTIONS: OrganicoSection[] = [
group: 'Rendimiento del sitio', group: 'Rendimiento del sitio',
label: 'Auditoría del sitio', label: 'Auditoría del sitio',
blurb: blurb:
'Consulta problemas de SEO que limitan posicionamiento: salud, contenido y UX.', 'Checklist operativo de salud SEO (indexación, arquitectura, CWV, contenido, medición). No es un crawl Semrush: es la base que hay que cerrar con los datos conectados.',
actions: [ actions: [
'Auditar indexación y CWV', 'Marcar OK/KO por bloque',
'Corregir errores críticos', 'Priorizar indexación + medición',
'Plan de remediación por impacto', 'CWV en landings de conversión',
], ],
live: 'seo-foundations', live: 'seo-foundations',
}, },
@@ -405,240 +416,97 @@ export const ORGANICO_SECTIONS: OrganicoSection[] = [
group: 'Rendimiento del sitio', group: 'Rendimiento del sitio',
label: 'Rastreo de posición', label: 'Rastreo de posición',
blurb: blurb:
'Rastrea posicionamiento en Google (y más adelante Bing / Modo IA) para keywords de alto valor.', 'Sí: posición media diaria en organic_daily (histórico nuestro) + ranking actual de queries GSC. No es un rank tracker de 10k KW tipo Semrush; es lo que GSC reporta para tus impresiones reales.',
actions: [ actions: [
'Definir cartera de keywords', 'Mirar tendencia de posición media',
'Registrar posición media GSC', 'Atacar queries en pos. 4–15 (quick wins)',
'Alertar caídas de top 10', 'Ampliar cartera vía contenido',
], ],
live: 'seo-metrics', live: 'seo-rankings',
},
{
id: 'seo-competition',
pillar: 'seo',
group: 'Análisis de la competencia',
label: 'Visión general del dominio',
blurb:
'Supera a tu competencia con análisis SEO: tráfico, keywords y estrategias.',
actions: [
'Fijar 3 dominios rivales',
'Comparar autoridad y temáticas',
'Extraer oportunidades de contenido',
],
soon: true,
}, },
{ {
id: 'seo-organic', id: 'seo-organic',
pillar: 'seo', pillar: 'seo',
group: 'Análisis de la competencia', group: 'Tráfico y páginas',
label: 'Posicionamiento orgánico', label: 'Posicionamiento orgánico',
blurb: blurb:
'Mide visibilidad orgánica y evolución de clics/impresiones frente al mercado.', 'Evolución de clics e impresiones (visibilidad). Distinto de «Páginas principales».',
actions: [ actions: [
'Revisar serie GSC MoM', 'Leer tendencia MoM de clics',
'Identificar queries en ascenso/descenso', 'Cruzar con campañas de contenido',
'Alinear con objetivos de negocio', 'Alertar caídas bruscas',
], ],
live: 'seo-metrics', live: 'seo-trend',
}, },
{ {
id: 'seo-pages', id: 'seo-pages',
pillar: 'seo', pillar: 'seo',
group: 'Análisis de la competencia', group: 'Tráfico y páginas',
label: 'Páginas principales', label: 'Páginas principales',
blurb: 'Landings que concentran tráfico y conversión orgánica.',
actions: [
'Listar top páginas por clics',
'Optimizar títulos/meta y UX',
'Reforzar enlazado interno',
],
},
{
id: 'seo-compare',
pillar: 'seo',
group: 'Análisis de la competencia',
label: 'Comparación de dominios',
blurb: 'Compara tu dominio con rivales en una sola vista.',
actions: [
'Elegir métricas comunes (tráfico, KW, backlinks)',
'Marcar gaps críticos',
'Asignar owners internos',
],
},
{
id: 'seo-keyword-gap',
pillar: 'seo',
group: 'Análisis de la competencia',
label: 'Brecha de palabras clave',
blurb: blurb:
'Keywords donde rivales rankean y tú no — hoja de ruta clara.', 'URLs que concentran clics GSC + landings orgánicas GA4. No es el mismo panel que posicionamiento.',
actions: [ actions: [
'Exportar gap vs top rivales', 'Optimizar top 10 URLs',
'Priorizar por intención de compra', 'Alinear title/H1/CTA',
'Crear o potenciar páginas', 'Enlazar desde hubs',
],
},
{
id: 'seo-backlink-gap',
pillar: 'seo',
group: 'Análisis de la competencia',
label: 'Brecha de backlinks',
blurb:
'Dominios que enlazan a rivales y no a ti: autoridad y confianza.',
actions: [
'Listar dominios de referencia únicos de rivales',
'Pitch / digital PR',
'Monitorizar nuevos enlaces',
], ],
live: 'seo-pages',
}, },
{ {
id: 'seo-keywords', id: 'seo-keywords',
pillar: 'seo', pillar: 'seo',
group: 'Investigación de palabras clave', group: 'Keywords',
label: 'Visión general de keywords', label: 'Visión general de keywords',
blurb: blurb:
'Palabras clave y prompts con intención de compra para Google y ChatGPT.', 'Queries reales con impresiones/clics/CTR/posición desde Search Console (28d).',
actions: [ actions: [
'Mapear KW → intención → URL', 'Mapear KW → URL',
'Detectar huecos de cobertura', 'Separar informativas vs compra',
'Alinear con calendario editorial', 'Priorizar quick wins',
],
},
{
id: 'seo-keyword-magic',
pillar: 'seo',
group: 'Investigación de palabras clave',
label: 'Keyword Magic / ideas',
blurb: 'Explora ideas de keywords y variantes semánticas.',
actions: [
'Sembrar seed keywords del negocio',
'Agrupar por clusters',
'Seleccionar quick wins',
],
},
{
id: 'seo-strategy',
pillar: 'seo',
group: 'Investigación de palabras clave',
label: 'Keyword Strategy Builder',
blurb: 'Construye la estrategia de keywords del trimestre.',
actions: [
'Definir pilares y clusters',
'Asignar prioridad P1/P2/P3',
'Vincular a OKRs',
],
},
{
id: 'seo-content-ideas',
pillar: 'seo',
group: 'Ideas de contenido',
label: 'Ideas de contenido',
blurb:
'Temas en tendencia, planes y briefings optimizados para posicionar.',
actions: [
'Topic research del mes',
'Briefs con outline + KW + FAQs',
'Publicar y medir en GSC',
],
},
{
id: 'seo-writing',
pillar: 'seo',
group: 'Ideas de contenido',
label: 'SEO Writing Assistant',
blurb: 'Asistencia para redactar y optimizar on-page.',
actions: [
'Revisar densidad y entidades',
'Mejorar claridad y E-E-A-T',
'Checklist pre-publicación',
],
},
{
id: 'seo-topics',
pillar: 'seo',
group: 'Ideas de contenido',
label: 'Topic Research',
blurb: 'Tendencias de temas que alimentan SEO y GEO.',
actions: [
'Cruzar topics con prompts IA',
'Detectar ángulos diferenciales',
'Planificar pieza pilar + satélites',
],
},
{
id: 'seo-template',
pillar: 'seo',
group: 'Ideas de contenido',
label: 'SEO Content Template',
blurb: 'Plantillas de contenido basadas en lo que rankea.',
actions: [
'Analizar SERP / respuestas IA',
'Definir estructura ganadora',
'Reutilizar en el equipo',
],
},
{
id: 'seo-backlinks',
pillar: 'seo',
group: 'Generación de enlaces',
label: 'Backlinks',
blurb:
'Gana autoridad con backlinks de calidad que impulsan confianza y SEO.',
actions: [
'Auditar perfil actual',
'Campañas de link earning',
'Desautorizar tóxicos',
],
},
{
id: 'seo-refdomains',
pillar: 'seo',
group: 'Generación de enlaces',
label: 'Dominios de referencia',
blurb: 'Diversidad y calidad de dominios que te enlazan.',
actions: [
'Medir referidos únicos',
'Buscar nichos afines',
'Relaciones con medios / partners',
],
},
{
id: 'seo-backlink-audit',
pillar: 'seo',
group: 'Generación de enlaces',
label: 'Backlink Audit',
blurb: 'Salud del perfil de enlaces y riesgo reputacional.',
actions: [
'Clasificar enlaces',
'Disavow si procede',
'Informe trimestral',
],
},
{
id: 'seo-onpage',
pillar: 'seo',
group: 'Extras',
label: 'On Page SEO Checker',
blurb: 'Comprueba on-page en URLs prioritarias.',
actions: [
'Auditar top landings',
'Corregir title/H1/internal links',
'Re-crawl',
], ],
live: 'seo-queries',
}, },
{ {
id: 'seo-traffic-insights', id: 'seo-traffic-insights',
pillar: 'seo', pillar: 'seo',
group: 'Extras', group: 'Keywords',
label: 'Organic Traffic Insights', label: 'Organic Traffic Insights',
blurb: blurb:
'Cruza GSC + GA4: cómo el orgánico mueve tráfico y conversiones reales.', 'Cruce GSC (páginas) + GA4 (sesiones orgánicas / landings) para ver qué rankea y qué convierte.',
actions: [ actions: [
'Conectar GA4', 'Comparar top pages GSC vs landings GA4',
'Atribuir landings a leads/ventas', 'Detectar páginas con clics y poca conversión',
'Informar ROI orgánico', 'Informar ROI orgánico',
], ],
live: 'seo-metrics', live: 'seo-cross',
},
{
id: 'seo-competition',
pillar: 'seo',
group: 'Competencia y enlaces',
label: 'Competencia / backlinks',
blurb:
'Requiere Ahrefs/Semrush u otra fuente de backlinks y dominio rival. No está en GSC/GA4. Aquí solo el marco de trabajo hasta integrar una herramienta.',
actions: [
'Fijar 3 dominios rivales',
'Importar gap KW / backlinks desde herramienta externa',
'Priorizar 5 acciones/mes',
],
soon: true,
},
{
id: 'seo-content-ideas',
pillar: 'seo',
group: 'Contenido',
label: 'Ideas de contenido',
blurb:
'Briefs a partir de queries GSC con tracción (clics o impresiones altas y CTR bajo).',
actions: [
'Partir de queries con CTR bajo',
'Crear/ampliar pieza',
'Medir de nuevo en 28d',
],
live: 'seo-content',
}, },
]; ];