feat: vista Orgánico con SEO (GA4/GSC) y GEO (visibilidad IA)

Nueva sección con métricas Search Console/GA4, cimientos SEO y
Share of Model por motor (ChatGPT/Gemini/AI Overviews).

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
4Ventures
2026-09-24 11:18:26 +02:00
parent dd0e867108
commit 5c90bcf940
8 changed files with 1529 additions and 1 deletions

167
src/lib/organic.ts Normal file
View File

@@ -0,0 +1,167 @@
/** Types + helpers for SEO (GA4/GSC) and GEO (AI visibility). */
export type OrganicDailyRow = {
day?: string;
date?: string;
gsc_clicks?: number;
gsc_impressions?: number;
gsc_ctr?: number | null;
gsc_avg_position?: number | null;
ga4_sessions?: number;
ga4_users?: number;
ga4_organic_sessions?: number;
ga4_conversions?: number;
source?: string;
};
export type GeoEngineScores = {
chatgpt?: number;
gemini?: number;
ai_overviews?: number;
};
export type GeoCompetitor = {
brand: string;
share: number;
rank?: number;
};
export type GeoCategory = {
category: string;
visibility: number;
rank?: number;
};
export type GeoSource = {
name: string;
share: number;
kind?: 'official' | 'pharmacy' | 'media' | 'other';
};
export type GeoSnapshot = {
id?: string;
tenant_id?: string;
label?: string;
period_start?: string | null;
period_end?: string | null;
share_of_model?: number | null;
engines?: GeoEngineScores;
official_citation_pct?: number | null;
sentiment_score?: number | null;
competitors?: GeoCompetitor[];
categories?: GeoCategory[];
top_sources?: GeoSource[];
prompts_analyzed?: number | null;
responses_analyzed?: number | null;
study_url?: string | null;
notes?: string | null;
created_at?: string;
};
export type IntegrationLite = {
provider: string;
status?: string;
enabled?: boolean;
external_account_id?: string | null;
last_sync_at?: string | null;
error_message?: string | null;
};
function n(v: unknown): number {
const x = Number(v);
return Number.isFinite(x) ? x : 0;
}
export type OrganicTotals = {
gscClicks: number;
gscImpressions: number;
gscCtr: number;
gscAvgPosition: number;
ga4Sessions: number;
ga4Users: number;
ga4Organic: number;
ga4Conversions: number;
days: number;
};
export function sumOrganic(rows: OrganicDailyRow[]): OrganicTotals {
let clicks = 0;
let impr = 0;
let posW = 0;
let posN = 0;
let sessions = 0;
let users = 0;
let organic = 0;
let conv = 0;
for (const r of rows) {
clicks += n(r.gsc_clicks);
impr += n(r.gsc_impressions);
const pos = r.gsc_avg_position;
if (pos != null && Number.isFinite(Number(pos))) {
posW += Number(pos) * Math.max(1, n(r.gsc_impressions));
posN += Math.max(1, n(r.gsc_impressions));
}
sessions += n(r.ga4_sessions);
users += n(r.ga4_users);
organic += n(r.ga4_organic_sessions);
conv += n(r.ga4_conversions);
}
return {
gscClicks: clicks,
gscImpressions: impr,
gscCtr: impr > 0 ? (100 * clicks) / impr : 0,
gscAvgPosition: posN > 0 ? posW / posN : 0,
ga4Sessions: sessions,
ga4Users: users,
ga4Organic: organic,
ga4Conversions: conv,
days: rows.length,
};
}
export function formatNum(v: number, digits = 0): string {
return v.toLocaleString('es-ES', {
maximumFractionDigits: digits,
minimumFractionDigits: digits,
});
}
export function formatPct(v: number, digits = 1): string {
return `${formatNum(v, digits)}%`;
}
/** SEO technical health checklist (Vivegreen-style foundations). */
export const SEO_FOUNDATIONS = [
{
id: 'indexacion',
title: 'Indexación y rastreo',
hint: 'Sitemap, robots, casuísticas de URLs dinámicas',
},
{
id: 'arquitectura',
title: 'Arquitectura y enlazado interno',
hint: 'Distribuir autoridad hacia landings clave',
},
{
id: 'render',
title: 'Renderizado y Core Web Vitals',
hint: 'Velocidad, JS crítico, UX móvil',
},
{
id: 'contenido',
title: 'Contenido indexable',
hint: 'Guías, FAQs, fichas que la IA y Google puedan citar',
},
{
id: 'medicion',
title: 'Medición GA4 + GSC',
hint: 'Property + propiedad Search Console conectadas',
},
] as const;
/** GEO engines tracked (Pixelclip-style AI visibility). */
export const GEO_ENGINES = [
{ id: 'chatgpt', label: 'ChatGPT' },
{ id: 'gemini', label: 'Gemini' },
{ id: 'ai_overviews', label: 'Google AI Overviews' },
] as const;

View File

@@ -7,6 +7,7 @@ export type DashboardModuleId =
| 'estrategia'
| 'calendario'
| 'ad-tracker'
| 'organico'
| 'activos'
| 'creatives'
| 'asistente';
@@ -25,7 +26,7 @@ export const DASHBOARD_MODULES: DashboardModuleDef[] = [
id: 'kpis',
href: '/dashboard/kpis',
label: 'Métricas',
providers: ['meta_ads', 'google_ads', 'ga4'],
providers: ['meta_ads', 'google_ads'],
hint: 'KPIs de inversión y leads',
},
{
@@ -63,6 +64,13 @@ export const DASHBOARD_MODULES: DashboardModuleDef[] = [
providers: ['meta_ads', 'google_ads'],
hint: 'Creatividades Meta / Google',
},
{
id: 'organico',
href: '/dashboard/organico',
label: 'Orgánico',
providers: ['ga4', 'search_console'],
hint: 'SEO (GSC/GA4) + GEO (visibilidad IA)',
},
{
id: 'activos',
href: '/dashboard/activos',