feat: sync Meta metrics, objetivos ppto/consecución y simulador por tenant

Rellena ad_daily desde Meta, KPIs con fallback a daily, objetivos de presupuesto vs consecución, y simulador sin TikTok anclado a métricas del cliente.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
4Ventures
2026-09-23 10:59:15 +02:00
parent 0d286dc66c
commit a1a00840d5
13 changed files with 1392 additions and 154 deletions

View File

@@ -10,6 +10,7 @@ import {
buildComparisons,
sumDaily,
sumWeekly,
weeklyFromDaily,
} from '@/lib/metrics';
import { isAgencyRole, userRole } from '@/lib/agency';
@@ -35,7 +36,7 @@ export default async function KpisPage() {
'/rest/v1/kpi_weekly?select=*&order=week.desc'
);
const d = await restGet<AdDailyRow[]>(
'/rest/v1/ad_daily?select=*&order=date.desc&limit=500'
'/rest/v1/ad_daily?select=*&order=date.desc&limit=5000'
);
weekly = w.data || [];
daily = d.data || [];
@@ -45,7 +46,7 @@ export default async function KpisPage() {
`/rest/v1/kpi_weekly?tenant_id=eq.${tid}&select=*&order=week.desc`
);
const d = await restGet<AdDailyRow[]>(
`/rest/v1/ad_daily?tenant_id=eq.${tid}&select=*&order=date.desc&limit=500`
`/rest/v1/ad_daily?tenant_id=eq.${tid}&select=*&order=date.desc&limit=5000`
);
weekly = w.data || [];
daily = d.data || [];
@@ -59,11 +60,19 @@ export default async function KpisPage() {
.from('ad_daily')
.select('*')
.order('date', { ascending: false })
.limit(500);
.limit(5000);
weekly = (w as KpiWeeklyRow[]) || [];
daily = (d as AdDailyRow[]) || [];
}
// kpi_weekly is often a stale MV — prefer ad_daily when it has more signal
const fromDaily = weeklyFromDaily(daily);
const weeklySpend = weekly.reduce((a, r) => a + Number(r.total_spend || 0), 0);
const dailySpend = daily.reduce((a, r) => a + Number(r.spend || 0), 0);
if (fromDaily.length && dailySpend >= weeklySpend) {
weekly = fromDaily;
}
const comparisons = buildComparisons(weekly);
const month =
comparisons.currentMonth.spend > 0 || comparisons.currentMonth.leads > 0