Split Google Ads copy into titles, long titles and descriptions.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -179,6 +179,42 @@ function formatThumbs(ad: AdCreativeCard): { url: string; aspect: CreativeAspect
|
||||
return out;
|
||||
}
|
||||
|
||||
function CopyList({
|
||||
label,
|
||||
items,
|
||||
strong,
|
||||
muted,
|
||||
}: {
|
||||
label: string;
|
||||
items: string[];
|
||||
strong?: boolean;
|
||||
muted?: boolean;
|
||||
}) {
|
||||
return (
|
||||
<div>
|
||||
<p className="text-[9px] uppercase tracking-wider text-gray-500 mb-1">
|
||||
{label}
|
||||
</p>
|
||||
<ul className="space-y-1">
|
||||
{items.map((h) => (
|
||||
<li
|
||||
key={h}
|
||||
className={`leading-snug ${
|
||||
strong
|
||||
? 'text-gray-900 font-medium'
|
||||
: muted
|
||||
? 'text-gray-500'
|
||||
: 'text-gray-600'
|
||||
}`}
|
||||
>
|
||||
{h}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function MiniKpi({ label, value }: { label: string; value: string }) {
|
||||
return (
|
||||
<div className="rounded-lg border border-gray-200 bg-gray-50 px-2.5 py-2">
|
||||
@@ -363,6 +399,13 @@ function CreativeCard({
|
||||
const headlines = adHeadlines(ad);
|
||||
const bodies = adBodies(ad);
|
||||
const linkDescs = adLinkDescriptions(ad);
|
||||
const longTitles = ad.longHeadlines || [];
|
||||
const googleDescs =
|
||||
ad.platform === 'google'
|
||||
? ad.descriptions?.length
|
||||
? ad.descriptions
|
||||
: bodies
|
||||
: [];
|
||||
const assets = adAssets(ad);
|
||||
const extraCount = Math.max(0, assets.length - 1);
|
||||
const pmaxFormats = formatThumbs(ad);
|
||||
@@ -409,9 +452,9 @@ function CreativeCard({
|
||||
{h}
|
||||
</p>
|
||||
))}
|
||||
{bodies[0] ? (
|
||||
{googleDescs[0] ? (
|
||||
<p className="text-[11px] text-gray-600 leading-snug line-clamp-3">
|
||||
{bodies[0]}
|
||||
{googleDescs[0]}
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
@@ -510,53 +553,52 @@ function CreativeCard({
|
||||
<p className="text-xs text-gray-400">Conjunto: {ad.adsetName}</p>
|
||||
) : null}
|
||||
</div>
|
||||
{(headlines.length > 0 || bodies.length > 0 || linkDescs.length > 0) && (
|
||||
{(headlines.length > 0 ||
|
||||
longTitles.length > 0 ||
|
||||
bodies.length > 0 ||
|
||||
linkDescs.length > 0 ||
|
||||
googleDescs.length > 0) && (
|
||||
<div className="space-y-2.5 text-xs border border-gray-200 rounded-xl p-3 bg-gray-50">
|
||||
{headlines.length > 0 ? (
|
||||
<div>
|
||||
<p className="text-[9px] uppercase tracking-wider text-gray-500 mb-1">
|
||||
Titulares ({headlines.length})
|
||||
</p>
|
||||
<ul className="space-y-1">
|
||||
{headlines.map((h) => (
|
||||
<li
|
||||
key={h}
|
||||
className="text-gray-900 font-medium leading-snug"
|
||||
>
|
||||
{h}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
) : null}
|
||||
{bodies.length > 0 ? (
|
||||
<div>
|
||||
<p className="text-[9px] uppercase tracking-wider text-gray-500 mb-1">
|
||||
Textos ({bodies.length})
|
||||
</p>
|
||||
<ul className="space-y-1.5">
|
||||
{bodies.map((b) => (
|
||||
<li key={b} className="text-gray-600 leading-relaxed">
|
||||
{b}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
) : null}
|
||||
{linkDescs.length > 0 ? (
|
||||
<div>
|
||||
<p className="text-[9px] uppercase tracking-wider text-gray-500 mb-1">
|
||||
Descripción
|
||||
</p>
|
||||
<ul className="space-y-1">
|
||||
{linkDescs.map((d) => (
|
||||
<li key={d} className="text-gray-500 leading-relaxed">
|
||||
{d}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
) : null}
|
||||
{ad.platform === 'google' ? (
|
||||
<>
|
||||
{headlines.length > 0 ? (
|
||||
<CopyList
|
||||
label={`Títulos (${headlines.length})`}
|
||||
items={headlines}
|
||||
strong
|
||||
/>
|
||||
) : null}
|
||||
{longTitles.length > 0 ? (
|
||||
<CopyList
|
||||
label={`Títulos largos (${longTitles.length})`}
|
||||
items={longTitles}
|
||||
strong
|
||||
/>
|
||||
) : null}
|
||||
{googleDescs.length > 0 ? (
|
||||
<CopyList
|
||||
label={`Descripciones (${googleDescs.length})`}
|
||||
items={googleDescs}
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{headlines.length > 0 ? (
|
||||
<CopyList
|
||||
label={`Titulares (${headlines.length})`}
|
||||
items={headlines}
|
||||
strong
|
||||
/>
|
||||
) : null}
|
||||
{bodies.length > 0 ? (
|
||||
<CopyList label={`Textos (${bodies.length})`} items={bodies} />
|
||||
) : null}
|
||||
{linkDescs.length > 0 ? (
|
||||
<CopyList label="Descripción" items={linkDescs} muted />
|
||||
) : null}
|
||||
</>
|
||||
)}
|
||||
{ad.cta ? (
|
||||
<p className="text-[10px] text-brand-700 uppercase tracking-wide pt-0.5">
|
||||
CTA · {ad.cta}
|
||||
@@ -589,6 +631,13 @@ export function CreativeZoomModal({
|
||||
const bodies = adBodies(ad);
|
||||
const linkDescs = adLinkDescriptions(ad);
|
||||
const longH = ad.longHeadlines || [];
|
||||
const googleDescs =
|
||||
ad.platform === 'google'
|
||||
? ad.descriptions?.length
|
||||
? ad.descriptions
|
||||
: bodies
|
||||
: [];
|
||||
const isGoogle = ad.platform === 'google';
|
||||
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');
|
||||
@@ -802,7 +851,7 @@ export function CreativeZoomModal({
|
||||
{headlines.length > 0 ? (
|
||||
<section>
|
||||
<h3 className="text-sm font-semibold text-gray-900 mb-2">
|
||||
Titulares ({headlines.length})
|
||||
{isGoogle ? 'Títulos' : 'Titulares'} ({headlines.length})
|
||||
</h3>
|
||||
<ul className="space-y-1.5 text-sm text-gray-800">
|
||||
{headlines.map((h) => (
|
||||
@@ -820,7 +869,7 @@ export function CreativeZoomModal({
|
||||
{longH.length > 0 ? (
|
||||
<section>
|
||||
<h3 className="text-sm font-semibold text-gray-900 mb-2">
|
||||
Titulares largos ({longH.length})
|
||||
Títulos largos ({longH.length})
|
||||
</h3>
|
||||
<ul className="space-y-1.5 text-sm text-gray-800">
|
||||
{longH.map((h) => (
|
||||
@@ -835,7 +884,25 @@ export function CreativeZoomModal({
|
||||
</section>
|
||||
) : null}
|
||||
|
||||
{bodies.length > 0 ? (
|
||||
{isGoogle && googleDescs.length > 0 ? (
|
||||
<section>
|
||||
<h3 className="text-sm font-semibold text-gray-900 mb-2">
|
||||
Descripciones ({googleDescs.length})
|
||||
</h3>
|
||||
<ul className="space-y-2 text-sm text-gray-600">
|
||||
{googleDescs.map((b) => (
|
||||
<li
|
||||
key={b}
|
||||
className="rounded-lg border border-gray-200 bg-gray-50 px-3 py-2 leading-relaxed whitespace-pre-wrap"
|
||||
>
|
||||
{b}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
) : null}
|
||||
|
||||
{!isGoogle && bodies.length > 0 ? (
|
||||
<section>
|
||||
<h3 className="text-sm font-semibold text-gray-900 mb-2">
|
||||
Textos ({bodies.length})
|
||||
@@ -853,7 +920,7 @@ export function CreativeZoomModal({
|
||||
</section>
|
||||
) : null}
|
||||
|
||||
{linkDescs.length > 0 ? (
|
||||
{!isGoogle && linkDescs.length > 0 ? (
|
||||
<section>
|
||||
<h3 className="text-sm font-semibold text-gray-900 mb-2">
|
||||
Descripción
|
||||
|
||||
Reference in New Issue
Block a user