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:
@@ -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
|
||||
</>
|
||||
)}
|
||||
<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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user