diff --git a/src/components/MetricsDashboard.tsx b/src/components/MetricsDashboard.tsx index beaf8a2..8b325b8 100644 --- a/src/components/MetricsDashboard.tsx +++ b/src/components/MetricsDashboard.tsx @@ -11,6 +11,8 @@ import { TrendingUp, Users, MousePointerClick, + Globe, + Search, } from 'lucide-react'; import { AdDailyRow, @@ -300,6 +302,29 @@ export default function MetricsDashboard({ hasCohort && periodTotals.revenue <= 0 && cohortFromLeads; const showWow = rangeDaySpan(since, until) <= 7; + const organicPeriod = useMemo(() => { + const rows = organicRows.filter((r) => { + const d = (r.day || r.date || '').slice(0, 10); + return d >= since && d <= until; + }); + const gscClicks = rows.reduce((a, r) => a + (Number(r.gsc_clicks) || 0), 0); + const ga4Sessions = rows.reduce( + (a, r) => a + (Number(r.ga4_sessions) || 0), + 0 + ); + const ga4Conversions = rows.reduce( + (a, r) => a + (Number(r.ga4_conversions) || 0), + 0 + ); + const denom = periodTotals.leads + ga4Conversions; + return { + gscClicks, + ga4Sessions, + ga4Conversions, + organicShare: denom > 0 ? (ga4Conversions / denom) * 100 : 0, + }; + }, [organicRows, since, until, periodTotals.leads]); + const cards: Card[] = [ { key: 'spend', @@ -311,7 +336,7 @@ export default function MetricsDashboard({ { key: 'leads', label: 'Leads plataforma', - hint: 'Conversiones reportadas por ads', + hint: 'Conversiones reportadas por ads (no CRM)', format: formatNum, icon: , }, @@ -424,41 +449,6 @@ export default function MetricsDashboard({

)} -
- {[ - { label: 'Inversión periodo', value: formatEur(periodTotals.spend) }, - { - label: 'Leads plataforma', - value: formatNum(periodTotals.leads), - }, - { - label: 'Leads CRM', - value: hasCrm ? formatNum(crmPeriodTotal) : '—', - }, - { - label: 'CPL CRM (paid)', - value: - hasCrm && crmPeriodTotal > 0 - ? `${formatNum(periodTotals.spend / crmPeriodTotal)} €` - : periodTotals.cpl - ? `${formatNum(periodTotals.cpl)} €` - : '—', - }, - ].map((s) => ( -
-

- {s.label} -

-

- {s.value} -

-
- ))} -
-
{cards.map((c) => { const emptyCohort = Boolean(c.cohort) && !hasCohort; @@ -493,6 +483,81 @@ export default function MetricsDashboard({ })}
+
+
+

+ Leads CRM +

+

+ {hasCrm ? formatNum(crmPeriodTotal) : '—'} +

+

+ Altas Clientify en el periodo (no es el mismo número que ads) +

+
+
+

+ CPL CRM +

+

+ {hasCrm && crmPeriodTotal > 0 + ? `${formatNum(periodTotals.spend / crmPeriodTotal)} €` + : '—'} +

+

+ Inversión ads ÷ leads CRM (distinto del CPL ads) +

+
+
+

+ Gap plat vs CRM +

+

+ {hasCrm + ? `${crmPeriodTotal - periodTotals.leads > 0 ? '+' : ''}${formatNum(crmPeriodTotal - periodTotals.leads)}` + : '—'} +

+

+ CRM − leads plataforma · detalle por canal abajo +

+
+
+

+ Clics SEO +

+

+ {formatNum(organicPeriod.gscClicks)} +

+

Search Console · mismo periodo

+
+
+

+ Sesiones web +

+

+ {formatNum(organicPeriod.ga4Sessions)} +

+

+ GA4 · {formatNum(organicPeriod.ga4Conversions)} conv. web +

+
+
+

+ Cuota orgánica +

+

+ {formatPct(organicPeriod.organicShare)} +

+

+ Conv. GA4 / (leads ads + conv. web) +

+
+
+
@@ -739,8 +804,8 @@ export default function MetricsDashboard({

- {/* Windsor.ai Data — follows Contexto tenant */} ('period'); const [showAllCampaigns, setShowAllCampaigns] = useState(false); - const [activeTab, setActiveTab] = useState<'all' | 'google' | 'organic' | 'matrix'>('all'); + const [activeTab, setActiveTab] = useState<'google' | 'organic' | 'matrix'>( + 'google' + ); const contextLabel = (label && label.trim()) || @@ -409,9 +414,13 @@ export default function WindsorAiSection({ return (
- {/* Top Banner & Header */} + {!embedded ? (
@@ -426,11 +435,11 @@ export default function WindsorAiSection({

- Atribución omnicanal · {contextLabel} + Desglose por fuente · {contextLabel}

- Atribución omnicanal y métricas agregadas multi-red (Google Ads, Meta Ads, GA4, Search - Console y CRM) consolidadas en el warehouse de 4Ventures. + Lo de arriba es el scorecard ads+CRM. Aquí solo el detalle que no + cabe ahí: campañas Google, SEO/GA4 y mix paid vs orgánico.

@@ -460,8 +469,45 @@ export default function WindsorAiSection({
+ ) : ( +
+
+

+ Detalle por fuente +

+

+ Campañas Google, SEO/GA4 y mix paid vs orgánico · mismo periodo + del header. +

+
+
+ + +
+
+ )} - {/* Connectors Status Pills */} + {!embedded ? (

@@ -485,86 +531,55 @@ export default function WindsorAiSection({

- {/* Top Blended KPI Cards */} -
-
-

- - Inversión Medios Blended + {/* Solo métricas que NO están en el scorecard ads+CRM de arriba */} +

+
+

+ Clics SEO (GSC)

- {formatEur(blendedTotals.totalMediaSpend)} + {formatNum(organicData.gscClicks)}

-

- Meta ({googleData.totalSpend > 0 ? Math.round((metaData.spend / blendedTotals.totalMediaSpend) * 100) : 100}%) + Google Ads ({googleData.totalSpend > 0 ? Math.round((googleData.totalSpend / blendedTotals.totalMediaSpend) * 100) : 0}%) +

+ {gscSite || 'Search Console'}

- -
-

- - Tráfico Multi-Red +

+

+ Sesiones web (GA4)

- {formatNum(blendedTotals.totalWebInteractions)} + {formatNum(organicData.ga4Sessions)}

-

- Ads ({formatNum(blendedTotals.totalPaidClicks)}) + SEO ({formatNum(organicData.gscClicks)}) + Web ({formatNum(organicData.ga4Sessions)}) +

+ {ga4Id || 'Sin GA4 enlazado'}

- -
-

- - Captación Omnicanal +

+

+ Conv. web (GA4)

- {formatNum(Math.round(blendedTotals.totalConversions))} -

-

- {formatNum(Math.round(blendedTotals.totalPaidLeads))} leads ads + {formatNum(Math.round(organicData.ga4Conversions))} web + {formatNum(organicData.ga4Conversions)}

+

No son leads de ads

- -
-

- - CPL / CPA Blended -

-

- {blendedTotals.blendedCpl > 0 ? `${formatNum(blendedTotals.blendedCpl)} €` : '—'} -

-

- Inversión media por conversión multi-canal -

-
- -
-

- - Cuota Orgánica (Free) +

+

+ Cuota orgánica

{formatPct(blendedTotals.organicAcquisitionShare)}

-

- Conversiones generadas sin coste de pauta +

+ Conv. GA4 / (leads ads + conv. web)

+ ) : null} - {/* Tabs navigation for in-depth Windsor data views */} -
- + {/* Tabs: detalle, no scorecard duplicado */} +
{/* VIEW: GOOGLE ADS CAMPAIGNS (Windsor.ai Data missing from initial Cruce) */} - {(activeTab === 'all' || activeTab === 'google') && ( + {activeTab === 'google' && (
@@ -744,7 +759,7 @@ export default function WindsorAiSection({ )} {/* VIEW: ORGANIC & WEB (GA4 + GSC from Windsor.ai pipeline) */} - {(activeTab === 'all' || activeTab === 'organic') && ( + {activeTab === 'organic' && (
@@ -857,7 +872,7 @@ export default function WindsorAiSection({ )} {/* VIEW: OMNICHANNEL ATTRIBUTION MATRIX (Side-by-Side) */} - {(activeTab === 'all' || activeTab === 'matrix') && ( + {activeTab === 'matrix' && (
@@ -925,7 +940,7 @@ export default function WindsorAiSection({ - Total Omnicanal Blended (Windsor.ai) + Total paid + orgánico {formatEur(blendedTotals.totalMediaSpend)} @@ -950,7 +965,7 @@ export default function WindsorAiSection({
)} - {/* Windsor ETL Architecture Explanatory Note */} + {!embedded ? (
@@ -959,6 +974,7 @@ export default function WindsorAiSection({

+ ) : null}
); }