feat(kpis): agregar seccion Windsor.ai de blueparrotschool.com debajo de Cruce por canal

This commit is contained in:
Marcos Fargas
2026-10-04 19:58:33 +02:00
parent e84310ec52
commit e07f21c674
4 changed files with 1034 additions and 2 deletions

View File

@@ -17,6 +17,7 @@ import {
sumWeekly,
} from '@/lib/metrics';
import { isAgencyRole, serviceHeaders, supabaseUrl, userRole } from '@/lib/agency';
import { OrganicDailyRow } from '@/lib/organic';
export const dynamic = 'force-dynamic';
@@ -256,6 +257,70 @@ async function enrichDealSources(
});
}
/** Paginate organic & web daily metrics (GSC + GA4) for active tenant / portfolio. */
async function fetchOrganicDaily(
tenantId: string | null,
useServiceRole: boolean
): Promise<OrganicDailyRow[]> {
const since = new Date();
since.setUTCDate(since.getUTCDate() - AD_DAILY_LOOKBACK_DAYS);
const sinceStr = since.toISOString().slice(0, 10);
if (useServiceRole) {
const headers = serviceHeaders();
const base = supabaseUrl();
if (!headers || !base) return [];
const tidFilter = tenantId
? `&tenant_id=eq.${encodeURIComponent(tenantId)}`
: '';
const r = await fetch(
`${base}/rest/v1/organic_daily?select=*&day=gte.${sinceStr}${tidFilter}&order=day.desc&limit=1000`,
{ headers, cache: 'no-store' }
);
if (!r.ok) return [];
return (await r.json()) as OrganicDailyRow[];
}
const supabase = await createClient();
let query = supabase
.from('organic_daily')
.select('*')
.gte('day', sinceStr)
.order('day', { ascending: false })
.limit(1000);
if (tenantId) {
query = query.eq('tenant_id', tenantId);
}
const { data, error } = await query;
if (error) return [];
return (data as OrganicDailyRow[]) || [];
}
/** Active integrations for connector status pills. */
async function fetchIntegrations(
tenantId: string | null,
useServiceRole: boolean
) {
if (!tenantId) return [];
if (useServiceRole) {
const headers = serviceHeaders();
const base = supabaseUrl();
if (!headers || !base) return [];
const r = await fetch(
`${base}/rest/v1/integrations?tenant_id=eq.${encodeURIComponent(tenantId)}&select=provider,status,external_account_id,last_sync_at,label`,
{ headers, cache: 'no-store' }
);
if (!r.ok) return [];
return await r.json();
}
const supabase = await createClient();
const { data } = await supabase
.from('integrations')
.select('provider,status,external_account_id,last_sync_at,label')
.eq('tenant_id', tenantId);
return data || [];
}
export default async function KpisPage() {
const supabase = await createClient();
const {
@@ -270,6 +335,8 @@ export default async function KpisPage() {
let daily: AdDailyRow[] = [];
let crmLeads: CrmLeadRow[] = [];
let wonDeals: DealWonRow[] = [];
let organic: OrganicDailyRow[] = [];
let integrations: any[] = [];
if (agency && (ctx.portfolioWide || ctx.viaView)) {
if (ctx.portfolioWide) {
@@ -280,6 +347,8 @@ export default async function KpisPage() {
daily = await fetchAdDaily(null, true);
crmLeads = [];
wonDeals = await fetchWonDeals(null, true);
organic = await fetchOrganicDaily(null, true);
integrations = [];
} else if (ctx.tenantId) {
const tid = encodeURIComponent(ctx.tenantId);
const w = await restGet<KpiWeeklyRow[]>(
@@ -289,6 +358,8 @@ export default async function KpisPage() {
daily = await fetchAdDaily(ctx.tenantId, true);
crmLeads = await fetchCrmLeads(ctx.tenantId, true);
wonDeals = await fetchWonDeals(ctx.tenantId, true);
organic = await fetchOrganicDaily(ctx.tenantId, true);
integrations = await fetchIntegrations(ctx.tenantId, true);
}
} else {
const { data: w } = await supabase
@@ -299,6 +370,8 @@ export default async function KpisPage() {
daily = await fetchAdDaily(ctx.tenantId, false);
crmLeads = await fetchCrmLeads(ctx.tenantId, false);
wonDeals = await fetchWonDeals(ctx.tenantId, false);
organic = await fetchOrganicDaily(ctx.tenantId, false);
integrations = await fetchIntegrations(ctx.tenantId, false);
}
const cohort = resolveWonCohort(wonDeals, crmLeads);
@@ -319,6 +392,7 @@ export default async function KpisPage() {
<MetricsDashboard
label={ctx.label}
portfolioWide={ctx.portfolioWide}
tenantId={ctx.tenantId}
month={month}
week={comparisons.currentWeek}
deltas={comparisons.deltas}
@@ -327,8 +401,10 @@ export default async function KpisPage() {
weeklyRows={weekly}
channelPoints={channelSeries.points}
dailyRows={daily}
organicRows={organic}
crmLeads={crmLeads}
wonDeals={wonDeals}
integrations={integrations}
cohortSource={cohort.source}
/>
);