feat(kpis): agregar seccion Windsor.ai de blueparrotschool.com debajo de Cruce por canal
This commit is contained in:
@@ -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}
|
||||
/>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user