'use client'; import React, { useEffect } from 'react'; import type { AdCreativeCard, CreativeAspect, CreativeAsset, } from '@/lib/ad-creatives-meta'; import { formatEur, formatEurCents, formatNum } from '@/lib/metrics'; import { ImageIcon, Play, Maximize2, X, ExternalLink } from 'lucide-react'; export function proxiedMediaUrl(raw: string): string { if (!raw) return ''; if (raw.startsWith('/api/')) return raw; return `/api/ad-tracker/media?url=${encodeURIComponent(raw)}`; } function adHeadlines(ad: AdCreativeCard): string[] { if (ad.headlines?.length) return ad.headlines.slice(0, 5); return (ad.headline || '') .split('\n') .map((s) => s.trim()) .filter(Boolean) .slice(0, 5); } function adBodies(ad: AdCreativeCard): string[] { if (ad.bodies?.length) return ad.bodies.slice(0, 5); // Legacy: primaryText used to hold bodies return (ad.primaryText || '') .split('\n') .map((s) => s.trim()) .filter(Boolean) .slice(0, 5); } function adLinkDescriptions(ad: AdCreativeCard): string[] { if (ad.descriptions?.length) return ad.descriptions.slice(0, 5); return []; } function adAssets(ad: AdCreativeCard): CreativeAsset[] { if (ad.assets?.length) return ad.assets; const out: CreativeAsset[] = []; const u = ad.imageUrl || ad.thumbnailUrl; if (u) out.push({ kind: 'image', url: u, label: 'preview', aspect: '9:16' }); if (ad.youtubeId) { out.push({ kind: 'youtube', url: `https://i.ytimg.com/vi/${ad.youtubeId}/hqdefault.jpg`, youtubeId: ad.youtubeId, label: 'YOUTUBE', aspect: '16:9', }); } return out; } function resolveAspect(asset: CreativeAsset): CreativeAspect { if (asset.aspect && asset.aspect !== 'unknown') return asset.aspect; const L = (asset.label || '').toLowerCase(); if (/9\s*:?\s*16|story|reels|vertical|tall/.test(L)) return '9:16'; if (/4\s*:?\s*5|portrait/.test(L)) return '4:5'; if (/16\s*:?\s*9|1\.91|landscape|horizontal|youtube|video/.test(L)) { return '16:9'; } if (/1\s*:?\s*1|square|logo/.test(L)) return '1:1'; return 'unknown'; } function aspectClass(aspect: CreativeAspect): string { switch (aspect) { case '9:16': return 'aspect-[9/16]'; case '4:5': return 'aspect-[4/5]'; case '16:9': return 'aspect-video'; case '1:1': return 'aspect-square'; default: return 'aspect-[4/5]'; } } function previewUrlFor(ad: AdCreativeCard): string { const assets = adAssets(ad); const nine = assets.find( (a) => a.kind === 'image' && resolveAspect(a) === '9:16' ); const fourFive = assets.find( (a) => a.kind === 'image' && resolveAspect(a) === '4:5' ); const raw = nine?.url || fourFive?.url || ad.imageUrl || ad.thumbnailUrl || assets.find((a) => a.kind === 'image')?.url || (ad.youtubeId ? `https://i.ytimg.com/vi/${ad.youtubeId}/hqdefault.jpg` : '') || assets.find((a) => a.youtubeId)?.url || ''; return raw ? proxiedMediaUrl(raw) : ''; } function MiniKpi({ label, value }: { label: string; value: string }) { return (

{label}

{value}

); } export function CreativesGallery({ platform, ads, loading, warning, onZoom, }: { platform: 'meta' | 'google'; ads: AdCreativeCard[]; loading: boolean; warning?: string | null; onZoom: (ad: AdCreativeCard) => void; }) { const filtered = ads.filter((a) => a.platform === platform); const platformWarning = warning ?.split(' · ') .find((w) => { // Never surface soft Graph/insights failures when we still render cards if (/insights ad-level|fetch failed|métricas repartidas|sin imagen/i.test(w)) { return false; } return platform === 'meta' ? /meta|token meta|sin ads meta|sin actividad meta|sin cuenta meta/i.test(w) : /google|pmax|rsa|sin ads google|sin assets|oauth|warehouse|reconecta/i.test( w ); }) || (platform === 'google' && warning && /google|oauth|token/i.test(warning) ? warning : null); return (

Creatividades · preview por anuncio

{platform === 'meta' ? 'Meta Ads' : 'Google Ads'} · creatividades

Preview 9:16. Pulsa Zoom para ver el resto de piezas en su formato.

{platformWarning ? (

{platformWarning}

) : null} {loading ? (

Cargando creatividades…

) : filtered.length === 0 ? (

Sin creatividades para este periodo.

) : (
{filtered.map((ad) => ( onZoom(ad)} /> ))}
)}
); } function CreativeCard({ ad, onZoom, }: { ad: AdCreativeCard; onZoom: () => void; }) { const preview = previewUrlFor(ad); const isVideo = ad.mediaType === 'video' || Boolean(ad.youtubeId); const headlines = adHeadlines(ad); const bodies = adBodies(ad); const linkDescs = adLinkDescriptions(ad); const assets = adAssets(ad); const extraCount = Math.max(0, assets.length - 1); return (

{ad.adName || 'Sin nombre'}

{ad.status || ad.platform}

{ad.campaignName || '—'}

{ad.adsetName ? (

Conjunto: {ad.adsetName}

) : null}
{(headlines.length > 0 || bodies.length > 0 || linkDescs.length > 0) && (
{headlines.length > 0 ? (

Titulares ({headlines.length})

    {headlines.map((h) => (
  • {h}
  • ))}
) : null} {bodies.length > 0 ? (

Textos ({bodies.length})

    {bodies.map((b) => (
  • {b}
  • ))}
) : null} {linkDescs.length > 0 ? (

Descripción

    {linkDescs.map((d) => (
  • {d}
  • ))}
) : null} {ad.cta ? (

CTA · {ad.cta}

) : null}
)}
); } export function CreativeZoomModal({ ad, onClose, }: { ad: AdCreativeCard; onClose: () => void; }) { const headlines = adHeadlines(ad); const bodies = adBodies(ad); const linkDescs = adLinkDescriptions(ad); const longH = ad.longHeadlines || []; const assets = adAssets(ad); const images = assets.filter((a) => a.kind === 'image' || a.kind === 'logo'); const videos = assets.filter((a) => a.kind === 'youtube' || a.kind === 'video'); const main = previewUrlFor(ad); const previewAsset = images.find((a) => resolveAspect(a) === '9:16') || images[0]; const otherImages = images.filter((a) => a.url !== previewAsset?.url); useEffect(() => { const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose(); }; window.addEventListener('keydown', onKey); const prev = document.body.style.overflow; document.body.style.overflow = 'hidden'; return () => { window.removeEventListener('keydown', onKey); document.body.style.overflow = prev; }; }, [onClose]); return (
e.stopPropagation()} >

Zoom creatividades ·{' '} {ad.platform === 'meta' ? 'Meta Ads' : 'Google Ads'}

{ad.adName || 'Anuncio'}

{ad.campaignName} {ad.adsetName ? ` · ${ad.adsetName}` : ''}

{main ? ( // eslint-disable-next-line @next/next/no-img-element {ad.adName} ) : (
Sin preview
)}

Preview 9:16

Impresiones:{' '} {formatNum(ad.impressions)}

Formato:{' '} {ad.mediaType || '—'}

{ad.cta ? (

CTA: {ad.cta}

) : null} {ad.linkUrl ? ( {ad.linkUrl} ) : null}
{otherImages.length > 0 ? (

Otras piezas ({otherImages.length})

{otherImages.map((asset, i) => { const aspect = resolveAspect(asset); return (
{/* eslint-disable-next-line @next/next/no-img-element */} {asset.label
{aspect !== 'unknown' ? aspect : asset.label || 'imagen'} {asset.kind === 'logo' ? ' · logo' : ''}
); })}
) : null} {videos.length > 0 ? (

Vídeos ({videos.length})

{videos.map((v, i) => (
{v.youtubeId ? (