From 78bd80079dd88b2cdb3dd86df0d52046e158699d Mon Sep 17 00:00:00 2001
From: 4Ventures <4ventures@MacBook-Air-de-4Ventures.local>
Date: Mon, 5 Oct 2026 18:10:17 +0200
Subject: [PATCH] fix: bind Windsor KPIs block to Contexto tenant
Stop hardcoding Blue Parrot branding/connectors so switching clients
in the portfolio selector updates labels and integration chips.
Co-authored-by: Cursor
---
src/components/MetricsDashboard.tsx | 2 +-
src/components/WindsorAiSection.tsx | 109 ++++++++++++++++++----------
2 files changed, 70 insertions(+), 41 deletions(-)
diff --git a/src/components/MetricsDashboard.tsx b/src/components/MetricsDashboard.tsx
index 060054b..beaf8a2 100644
--- a/src/components/MetricsDashboard.tsx
+++ b/src/components/MetricsDashboard.tsx
@@ -739,7 +739,7 @@ export default function MetricsDashboard({
- {/* Windsor.ai Data for blueparrotschool.com (Directly below Cruce por canal) */}
+ {/* Windsor.ai Data — follows Contexto tenant */}
('all');
- // Determine if context is Blue Parrot School or Portfolio
- const isBps =
- tenantId === BPS_TENANT_ID ||
- label?.toLowerCase().includes('blue parrot') ||
- label?.toLowerCase().includes('bps') ||
- (!tenantId && !portfolioWide);
+ const contextLabel =
+ (label && label.trim()) ||
+ (portfolioWide ? 'Portafolio' : 'Cliente');
- // Filter dailyRows for target tenant (prefer BPS rows if in portfolio view)
+ const findIntegration = (provider: string) => {
+ const rows = (integrations || []).filter((i: any) => i?.provider === provider);
+ return (
+ rows.find((i: any) => i?.status === 'connected') ||
+ rows[0] ||
+ null
+ );
+ };
+
+ const gads = findIntegration('google_ads');
+ const meta = findIntegration('meta_ads');
+ const ga4 = findIntegration('ga4');
+ const gsc = findIntegration('search_console');
+ const clientify = findIntegration('clientify');
+
+ const gscSite = String(
+ gsc?.external_account_id ||
+ gsc?.meta?.site_url ||
+ gsc?.label ||
+ ''
+ )
+ .replace(/^sc-domain:/i, '')
+ .replace(/^https?:\/\//i, '')
+ .replace(/\/$/, '');
+
+ const ga4Id = String(
+ ga4?.meta?.measurement_id ||
+ ga4?.external_account_id ||
+ ga4?.label ||
+ ''
+ );
+
+ const gadsId = String(gads?.external_account_id || gads?.label || '')
+ .replace(/\D/g, '')
+ .replace(/(\d{3})(\d{3})(\d{4})/, '$1-$2-$3');
+
+ // Filter dailyRows for the active context (never pin to BPS when another tenant is selected)
const targetDailyRows = useMemo(() => {
if (!dailyRows.length) return [];
if (tenantId) {
return dailyRows.filter((r) => !r.tenant_id || r.tenant_id === tenantId);
}
- // In portfolio wide view, focus on BPS rows for this Windsor component
- const bpsRows = dailyRows.filter((r) => r.tenant_id === BPS_TENANT_ID);
- return bpsRows.length > 0 ? bpsRows : dailyRows;
+ return dailyRows;
}, [dailyRows, tenantId]);
- // Filter organicRows for target tenant
+ // Filter organicRows for the active context
const targetOrganicRows = useMemo(() => {
if (!organicRows.length) return [];
if (tenantId) {
return organicRows.filter((r) => !r.tenant_id || r.tenant_id === tenantId);
}
- const bpsRows = organicRows.filter((r) => r.tenant_id === BPS_TENANT_ID);
- return bpsRows.length > 0 ? bpsRows : organicRows;
+ return organicRows;
}, [organicRows, tenantId]);
// Apply Date Filtering based on viewMode ('period' vs 'all')
@@ -344,42 +372,37 @@ export default function WindsorAiSection({
];
}, [metaData, googleData, organicData, crmLeads, blendedTotals]);
- // Connectors Status List for blueparrotschool.com
+ // Connectors for the active tenant (from integrations), not a hardcoded BPS list
const connectors = [
{
name: 'Google Ads',
- id: '106-444-1284',
- status: 'connected',
+ id: gadsId || gads?.label || '—',
+ status: gads?.status === 'connected' ? 'connected' : 'disconnected',
type: 'Search & PMax',
- lastSync: 'Normalizado',
},
{
name: 'Meta Ads',
- id: 'act_414283212550847',
- status: 'connected',
+ id: meta?.external_account_id || meta?.label || '—',
+ status: meta?.status === 'connected' ? 'connected' : 'disconnected',
type: 'Instagram & Facebook Ads',
- lastSync: 'Normalizado',
},
{
name: 'Google Analytics 4',
- id: 'G-8CMQFCK82C (469229613)',
- status: 'connected',
+ id: ga4Id || '—',
+ status: ga4?.status === 'connected' ? 'connected' : 'disconnected',
type: 'Sesiones & Eventos Web',
- lastSync: 'Sincronizado',
},
{
name: 'Search Console',
- id: 'blueparrotschool.com',
- status: 'connected',
+ id: gscSite || '—',
+ status: gsc?.status === 'connected' ? 'connected' : 'disconnected',
type: 'Tráfico & Clics Orgánicos',
- lastSync: 'Sincronizado',
},
{
name: 'Clientify CRM',
- id: 'bps (Webhook & API)',
- status: 'connected',
+ id: clientify?.label || clientify?.external_account_id || '—',
+ status: clientify?.status === 'connected' ? 'connected' : 'disconnected',
type: 'Leads & Closed-Loop Won',
- lastSync: 'En tiempo real',
},
];
@@ -403,7 +426,7 @@ export default function WindsorAiSection({
- Datos Windsor.ai · blueparrotschool.com
+ Atribución omnicanal · {contextLabel}
Atribución omnicanal y métricas agregadas multi-red (Google Ads, Meta Ads, GA4, Search
@@ -442,7 +465,7 @@ export default function WindsorAiSection({
- Conectores Windsor.ai activos para blueparrotschool.com:
+ Conectores activos para {contextLabel}:
{connectors.map((c) => (
@@ -450,7 +473,11 @@ export default function WindsorAiSection({
key={c.name}
className="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-lg bg-white border border-slate-200 shadow-xs text-xs"
>
-
+
{c.name}
({c.id})
@@ -586,7 +613,8 @@ export default function WindsorAiSection({
Campañas Google Ads · Búsqueda & Performance Max (Windsor)
- Datos de Google Ads (`ID: 106-444-1284`) no desglosados en el cruce agregado. Normalizados con micro-unidades y atribución Windsor.
+ Datos de Google Ads
+ {gadsId ? ` (ID: ${gadsId})` : ''} normalizados en el warehouse.
{viewMode === 'period' && googleData.campaigns.length === 0 && (
@@ -602,7 +630,7 @@ export default function WindsorAiSection({
No hay actividad de Google Ads registrada dentro del periodo seleccionado ({rangeLabel}).
- En el warehouse de 4Ventures constan 334 registros históricos de Google Ads para blueparrotschool.com (abril–septiembre 2026).
+ No hay filas de Google Ads para {contextLabel} en el warehouse con el filtro actual.
setViewMode('all')}
@@ -725,7 +753,8 @@ export default function WindsorAiSection({
Tráfico Web & Rendimiento Orgánico (GA4 & Google Search Console)
- Posicionamiento orgánico y analítica de sesiones web registradas para blueparrotschool.com.
+ Posicionamiento orgánico y analítica de sesiones web registradas para{' '}
+ {gscSite || contextLabel}.
@@ -739,7 +768,7 @@ export default function WindsorAiSection({
Google Search Console (SEO)
- blueparrotschool.com
+ {gscSite || 'Sin GSC'}
@@ -783,7 +812,7 @@ export default function WindsorAiSection({
Google Analytics 4 (Web Analytics)
- G-8CMQFCK82C
+ {ga4Id || 'Sin GA4'}