Mark GEO as coming soon and slim the SEO menu to live data.

Hide duplicate GSC/GA4 views and the manual day-load CTA so Orgánico matches Search Console, Analytics, and Métricas.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
4Ventures
2026-10-06 12:40:53 +02:00
parent 81b424bd2f
commit 8a9d92cb6b
3 changed files with 60 additions and 73 deletions

View File

@@ -171,7 +171,9 @@ export default function OrganicoDashboard({
}, [load]);
useEffect(() => {
const first = ORGANICO_SECTIONS.find((s) => s.pillar === pillar);
const first = ORGANICO_SECTIONS.find(
(s) => s.pillar === pillar && !s.soon
);
if (first) setSectionId(first.id);
}, [pillar]);
@@ -214,7 +216,9 @@ export default function OrganicoDashboard({
const maxSessions = Math.max(1, ...seoDaily.map((d) => d.sessions));
const section = useMemo(
() => ORGANICO_SECTIONS.find((s) => s.id === sectionId)!,
() =>
ORGANICO_SECTIONS.find((s) => s.id === sectionId) ||
ORGANICO_SECTIONS.find((s) => s.pillar === 'seo' && !s.soon)!,
[sectionId]
);
const navGroups = useMemo(() => groupsForPillar(pillar), [pillar]);
@@ -406,11 +410,11 @@ export default function OrganicoDashboard({
<PageHeader
eyebrow="Crecimiento"
title="Orgánico"
description="SEO + GEO (visibilidad en IA). Elige un cliente en Contexto."
description="SEO con Search Console y GA4. Elige un cliente en Contexto."
actions={<SyncNowButton forceModule="organico" />}
/>
<p className="text-sm text-gray-600 border border-gray-200 rounded-xl px-4 py-8 text-center bg-gray-50">
Selecciona un cliente para abrir su suite SEO / GEO.
Selecciona un cliente para ver su SEO.
</p>
</div>
);
@@ -429,7 +433,7 @@ export default function OrganicoDashboard({
<PageHeader
eyebrow="Crecimiento · Suite"
title="Orgánico"
description={`${label} · Observa posicionamiento en búsqueda e IA, competencia y oportunidades de contenido.`}
description={`${label} · Search Console y Analytics. GEO (visibilidad en IA) llega después.`}
actions={
<div className="flex flex-wrap items-center gap-2">
<SyncNowButton forceModule="organico" />
@@ -438,20 +442,10 @@ export default function OrganicoDashboard({
onChange={(p) => setPillar(p)}
size="sm"
options={[
{ id: 'geo', label: 'GEO · IA' },
{ id: 'seo', label: 'SEO' },
{ id: 'geo', label: 'GEO · IA' },
]}
/>
{isAgency ? (
<button
type="button"
onClick={() => setShowCapture((v) => !v)}
className="inline-flex items-center gap-1.5 text-xs font-semibold px-3 py-1.5 rounded-lg bg-brand-500 text-white hover:bg-brand-600"
>
<Plus className="w-3.5 h-3.5" />
{pillar === 'geo' ? 'Medición GEO' : 'Cargar día SEO'}
</button>
) : null}
</div>
}
/>
@@ -473,15 +467,6 @@ export default function OrganicoDashboard({
{[
{ title: 'Search Console', row: gsc },
{ title: 'GA4', row: ga4 },
{
title: 'GEO / IA',
row: latestGeo
? {
status: 'connected',
external_account_id: latestGeo.label || 'Medición activa',
}
: { status: 'disconnected', external_account_id: null },
},
].map((c) => (
<div
key={c.title}
@@ -498,7 +483,7 @@ export default function OrganicoDashboard({
<p className="text-sm font-medium text-gray-900 mt-1 truncate">
{c.row?.external_account_id || 'Sin vincular'}
</p>
{isAgency && tenantId && c.title !== 'GEO / IA' ? (
{isAgency && tenantId ? (
<Link
href={`/dashboard/agencia/cliente/${tenantId}`}
className="inline-flex items-center gap-1 text-[11px] font-semibold text-brand-700 mt-1.5 hover:underline"
@@ -508,10 +493,35 @@ export default function OrganicoDashboard({
) : null}
</div>
))}
<div className="rounded-xl border border-dashed border-gray-300 bg-gray-50 px-4 py-3">
<div className="flex items-center justify-between">
<p className="text-xs font-semibold text-gray-700">GEO / IA</p>
<span className="text-[10px] font-bold uppercase px-2 py-0.5 rounded-full border border-amber-200 bg-amber-50 text-amber-800">
Próximamente
</span>
</div>
<p className="text-sm text-gray-600 mt-1">
Visibilidad de marca en ChatGPT, Gemini y AI Overviews.
</p>
</div>
</div>
{/* Capture forms */}
{isAgency && showCapture && pillar === 'seo' && (
{pillar === 'geo' ? (
<div className="rounded-2xl border border-gray-200 bg-white px-6 py-14 text-center shadow-sm">
<p className="text-[11px] font-bold uppercase tracking-wider text-amber-700">
Próximamente disponible
</p>
<h2 className="text-xl font-bold text-gray-900 mt-2">GEO · visibilidad en IA</h2>
<p className="text-sm text-gray-600 mt-2 max-w-lg mx-auto">
Aquí mediremos cómo aparece la marca en respuestas de ChatGPT,
Gemini y AI Overviews (Share of Model, citas, competidores). SEO
(Google) sigue en la pestaña SEO, con datos de Search Console y
Analytics.
</p>
</div>
) : (
<>
{isAgency && showCapture && (
<CaptureSeo
form={seoForm}
setForm={setSeoForm}
@@ -519,28 +529,12 @@ export default function OrganicoDashboard({
onSave={() => void saveOrganicDay()}
/>
)}
{isAgency && showCapture && pillar === 'geo' && (
<CaptureGeo
form={geoForm}
setForm={setGeoForm}
saving={saving}
onSave={() => void saveGeo()}
/>
)}
{/* Shell: nav + content */}
<div className="grid grid-cols-1 lg:grid-cols-[240px_minmax(0,1fr)] gap-4 items-start">
<aside className="rounded-2xl border border-gray-200 bg-white p-3 shadow-sm lg:sticky lg:top-20 max-h-[calc(100vh-7rem)] overflow-y-auto">
<p className="px-2 pb-2 text-[10px] font-bold uppercase tracking-wider text-gray-500 flex items-center gap-1.5">
{pillar === 'geo' ? (
<>
<Bot className="w-3.5 h-3.5 text-brand-600" /> Análisis de IA
</>
) : (
<>
<Leaf className="w-3.5 h-3.5 text-lime-600" /> Suite SEO
</>
)}
</p>
{navGroups.map((g) => (
<div key={g.group} className="mb-3">
@@ -948,7 +942,7 @@ export default function OrganicoDashboard({
<section className="rounded-xl border border-gray-200 bg-white p-4 shadow-sm space-y-2">
<h3 className="text-sm font-semibold text-gray-900 flex items-center gap-2">
<Sparkles className="w-4 h-4 text-brand-600" />
Prioridades claras · {pillar === 'geo' ? 'GEO' : 'SEO'}
Prioridades claras · SEO
</h3>
<p className="text-[11px] text-gray-600">
Una por línea. Lo que hay que mejorar primero.
@@ -989,10 +983,7 @@ export default function OrganicoDashboard({
type="button"
disabled={saving}
onClick={() => {
const def =
pillar === 'geo'
? DEFAULT_EXPERT_PRIORITIES.geo
: DEFAULT_EXPERT_PRIORITIES.seo;
const def = DEFAULT_EXPERT_PRIORITIES.seo;
setPrioritiesText(def.join('\n'));
}}
className="ml-2 inline-flex items-center gap-1.5 text-xs font-semibold px-3 py-1.5 rounded-lg border border-gray-200 text-gray-700 hover:bg-gray-50"
@@ -1059,9 +1050,9 @@ export default function OrganicoDashboard({
</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.
Search Console ni GA4. Cuando conectemos Ahrefs o Semrush, esta
vista se activará. El SEO con datos reales está en Visión
general, Keywords y Páginas.
</p>
<ul className="space-y-1.5 pt-1">
{section.actions.map((a) => (
@@ -1100,6 +1091,8 @@ export default function OrganicoDashboard({
)}
</div>
</div>
</>
)}
</div>
);
}

View File

@@ -439,13 +439,10 @@ export const ORGANICO_SECTIONS: OrganicoSection[] = [
group: 'Rendimiento del sitio',
label: 'Rastreo de posición',
blurb:
'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: [
'Mirar tendencia de posición media',
'Atacar queries en pos. 4–15 (quick wins)',
'Ampliar cartera vía contenido',
],
'Cubierto en Palabras clave (posición GSC) y Visión general (tendencia diaria).',
actions: [],
live: 'seo-rankings',
soon: true,
},
{
id: 'seo-organic',
@@ -453,13 +450,10 @@ export const ORGANICO_SECTIONS: OrganicoSection[] = [
group: 'Tráfico y páginas',
label: 'Posicionamiento orgánico',
blurb:
'Evolución de clics e impresiones (visibilidad). Distinto de «Páginas principales».',
actions: [
'Leer tendencia MoM de clics',
'Cruzar con campañas de contenido',
'Alertar caídas bruscas',
],
'Misma serie de clics/impresiones que Visión general. Se unifica ahí.',
actions: [],
live: 'seo-trend',
soon: true,
},
{
id: 'seo-pages',
@@ -495,13 +489,10 @@ export const ORGANICO_SECTIONS: OrganicoSection[] = [
group: 'Keywords',
label: 'Organic Traffic Insights',
blurb:
'Cruce GSC (páginas) + GA4 (sesiones orgánicas / landings) para ver qué rankea y qué convierte.',
actions: [
'Comparar top pages GSC vs landings GA4',
'Detectar páginas con clics y poca conversión',
'Informar ROI orgánico',
],
'El cruce GSC + GA4 ya está en Visión general (top queries, páginas y landings).',
actions: [],
live: 'seo-cross',
soon: true,
},
{
id: 'seo-competition',
@@ -540,7 +531,10 @@ export function sectionsForPillar(pillar: OrganicoPillar): OrganicoSection[] {
export function groupsForPillar(
pillar: OrganicoPillar
): { group: string; items: OrganicoSection[] }[] {
const list = sectionsForPillar(pillar);
if (pillar === 'geo') return [];
const list = sectionsForPillar('seo').filter(
(s) => !s.soon || s.id === 'seo-competition'
);
const order: string[] = [];
const map = new Map<string, OrganicoSection[]>();
for (const s of list) {

View File

@@ -77,7 +77,7 @@ export const DASHBOARD_MODULES: DashboardModuleDef[] = [
href: '/dashboard/organico',
label: 'Orgánico',
providers: ['ga4', 'search_console'],
hint: 'SEO (GSC/GA4) + GEO (visibilidad IA)',
hint: 'SEO (Search Console y GA4)',
},
{
id: 'activos',