perf: creatividades warehouse-first + aviso OAuth Ads Production

Carga métricas desde ad_daily al instante (mode=fast) y enriquece previews en vivo; la UI de Agencia avisa si falta scope Ads o consent Testing.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
4Ventures
2026-09-29 09:35:45 +02:00
parent 939c4a3131
commit a75f3ae30c
5 changed files with 174 additions and 36 deletions

View File

@@ -552,39 +552,70 @@ export default function AdTrackerClient({
async function load() {
setCreativesLoading(true);
setCreativesWarning(null);
const platform = tab === 'meta' ? 'meta' : 'google';
const baseQs = {
since: range.since,
until: range.until,
platform,
};
let hasFast = false;
// 1) Warehouse first — instant metrics (source of truth from sync)
try {
const platform = tab === 'meta' ? 'meta' : 'google';
const qs = new URLSearchParams({
since: range.since,
until: range.until,
platform,
const qs = new URLSearchParams({ ...baseQs, mode: 'fast' });
const r = await fetch(`/api/ad-tracker/creatives?${qs}`, {
cache: 'no-store',
});
const data = await r.json().catch(() => ({}));
if (cancelled) return;
if (r.ok) {
hasFast = true;
setCreatives((data.ads as AdCreativeCard[]) || []);
if (
data.metaClickSplit &&
typeof data.metaClickSplit.clicks === 'number'
) {
setMetaClickSplit(data.metaClickSplit);
} else if (platform === 'google') {
setMetaClickSplit(null);
}
setCreativesLoading(false);
}
} catch {
/* continue to live */
}
// 2) Live enrich (previews) — never blocks empty UI
try {
const qs = new URLSearchParams({ ...baseQs, mode: 'live' });
const r = await fetch(`/api/ad-tracker/creatives?${qs}`, {
cache: 'no-store',
});
const data = await r.json().catch(() => ({}));
if (cancelled) return;
if (!r.ok) {
setCreatives([]);
setMetaClickSplit(null);
setCreativesWarning(data.hint || data.error || 'No se pudieron cargar creatividades');
if (!hasFast) {
setCreativesWarning(
data.hint || data.error || 'No se pudieron cargar creatividades'
);
}
return;
}
setCreatives((data.ads as AdCreativeCard[]) || []);
const liveAds = (data.ads as AdCreativeCard[]) || [];
if (liveAds.length) setCreatives(liveAds);
if (
data.metaClickSplit &&
typeof data.metaClickSplit.clicks === 'number'
) {
setMetaClickSplit(data.metaClickSplit);
} else if (platform === 'google') {
setMetaClickSplit(null);
}
const w = Array.isArray(data.warnings) ? data.warnings.filter(Boolean) : [];
const w = Array.isArray(data.warnings)
? data.warnings.filter(Boolean)
: [];
setCreativesWarning(w.length ? w.join(' · ') : null);
} catch {
if (!cancelled) {
setCreatives([]);
setMetaClickSplit(null);
if (!cancelled && !hasFast) {
setCreativesWarning('Error al cargar creatividades');
}
} finally {