fix(ui): paneles Zona de marca tipo Overview/Prohibido
Cards con eyebrow, tipografía sm legible, más aire y agrupación por bloques en Identidad, Voz, Visual, Ads, Canales y Compliance. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -79,27 +79,93 @@ function Field({
|
||||
hint,
|
||||
children,
|
||||
className = '',
|
||||
wide = false,
|
||||
}: {
|
||||
label: string;
|
||||
hint?: string;
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
/** Ocupa las 2 columnas del grid del PanelCard */
|
||||
wide?: boolean;
|
||||
}) {
|
||||
return (
|
||||
<div className={`block ${className}`}>
|
||||
<label className="block text-xs font-bold text-gray-700 mb-1">{label}</label>
|
||||
<div className={`block min-w-0 ${wide ? 'sm:col-span-2' : ''} ${className}`}>
|
||||
<label className="block text-[13px] font-semibold text-gray-900 mb-1.5 leading-snug">
|
||||
{label}
|
||||
</label>
|
||||
{hint ? (
|
||||
<p className="text-[11px] text-gray-500 leading-snug mb-1.5">{hint}</p>
|
||||
<p className="text-xs text-gray-500 leading-relaxed mb-2">{hint}</p>
|
||||
) : null}
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/** Bloque tipo Overview / Prohibido: aire, eyebrow, título y grid legible */
|
||||
function PanelCard({
|
||||
eyebrow,
|
||||
title,
|
||||
description,
|
||||
children,
|
||||
accent = 'neutral',
|
||||
cols = 2,
|
||||
}: {
|
||||
eyebrow?: string;
|
||||
title: string;
|
||||
description?: string;
|
||||
children: React.ReactNode;
|
||||
accent?: 'neutral' | 'brand' | 'amber' | 'sky' | 'rose';
|
||||
cols?: 1 | 2;
|
||||
}) {
|
||||
const tone =
|
||||
accent === 'brand'
|
||||
? 'border-brand-200/80 bg-gradient-to-br from-brand-50/70 via-white to-white'
|
||||
: accent === 'amber'
|
||||
? 'border-amber-200/80 bg-gradient-to-br from-amber-50/60 via-white to-white'
|
||||
: accent === 'sky'
|
||||
? 'border-sky-200/80 bg-gradient-to-br from-sky-50/60 via-white to-white'
|
||||
: accent === 'rose'
|
||||
? 'border-rose-200/80 bg-gradient-to-br from-rose-50/50 via-white to-white'
|
||||
: 'border-gray-200 bg-gradient-to-br from-gray-50/80 via-white to-white';
|
||||
return (
|
||||
<section
|
||||
className={`rounded-2xl border ${tone} px-5 py-5 sm:px-6 sm:py-6 shadow-xs`}
|
||||
>
|
||||
<div className="mb-4 pb-3 border-b border-black/[0.04]">
|
||||
{eyebrow ? (
|
||||
<p className="text-[10px] font-bold uppercase tracking-[0.16em] text-gray-500">
|
||||
{eyebrow}
|
||||
</p>
|
||||
) : null}
|
||||
<h4
|
||||
className={`text-[15px] font-bold text-gray-900 tracking-tight ${
|
||||
eyebrow ? 'mt-1' : ''
|
||||
}`}
|
||||
>
|
||||
{title}
|
||||
</h4>
|
||||
{description ? (
|
||||
<p className="text-xs text-gray-500 mt-1.5 leading-relaxed max-w-2xl">
|
||||
{description}
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
<div
|
||||
className={
|
||||
cols === 1 ? 'space-y-4' : 'grid gap-4 sm:grid-cols-2 sm:gap-x-5 sm:gap-y-5'
|
||||
}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
const inputCls =
|
||||
'w-full rounded-xl border border-gray-300 bg-white px-3 py-2 text-xs text-gray-900 placeholder:text-gray-400 focus:outline-none focus:ring-2 focus:ring-brand-200';
|
||||
const areaCls = `${inputCls} min-h-[88px] resize-y`;
|
||||
'w-full rounded-xl border border-gray-200 bg-white px-3.5 py-2.5 text-sm text-gray-900 placeholder:text-gray-400 shadow-xs focus:outline-none focus:border-brand-400 focus:ring-2 focus:ring-brand-100 transition-colors';
|
||||
const areaCls = `${inputCls} min-h-[104px] resize-y leading-relaxed`;
|
||||
const selectCls = inputCls;
|
||||
const sectionStack = 'space-y-5';
|
||||
|
||||
function KitThumb({ assetFileId, alt }: { assetFileId: string; alt: string }) {
|
||||
const [url, setUrl] = useState<string | null>(null);
|
||||
@@ -555,7 +621,7 @@ export default function BrandZone({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className={sectionStack}>
|
||||
<div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between rounded-2xl border border-brand-200 bg-brand-50/50 p-4">
|
||||
<div className="flex items-start gap-3 min-w-0">
|
||||
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-brand-600 text-white shadow-xs">
|
||||
@@ -662,11 +728,18 @@ export default function BrandZone({
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div className="lg:col-span-9 rounded-2xl border border-gray-200 bg-white p-6 shadow-xs space-y-6">
|
||||
<div className="lg:col-span-9 rounded-2xl border border-gray-200 bg-white p-5 sm:p-8 shadow-xs space-y-6 min-h-[520px]">
|
||||
{section !== 'overview' ? (
|
||||
<div>
|
||||
<h3 className="text-base font-bold text-gray-900">{secMeta?.label}</h3>
|
||||
<p className="text-xs text-gray-500 mt-0.5">{secMeta?.subtitle}</p>
|
||||
<div className="pb-1">
|
||||
<p className="text-[10px] font-bold uppercase tracking-[0.16em] text-brand-600">
|
||||
Zona de marca
|
||||
</p>
|
||||
<h3 className="text-lg font-bold text-gray-900 tracking-tight mt-1">
|
||||
{secMeta?.label}
|
||||
</h3>
|
||||
<p className="text-sm text-gray-500 mt-1.5 leading-relaxed max-w-2xl">
|
||||
{secMeta?.subtitle}
|
||||
</p>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
@@ -683,147 +756,179 @@ export default function BrandZone({
|
||||
)}
|
||||
|
||||
{section === 'identidad' && (
|
||||
<div className="grid gap-3 sm:grid-cols-2">
|
||||
<Field label="Nombre de marca">
|
||||
<input
|
||||
className={inputCls}
|
||||
value={book.brand_name}
|
||||
onChange={(e) => patch({ brand_name: e.target.value })}
|
||||
/>
|
||||
</Field>
|
||||
<Field label="Tagline">
|
||||
<input
|
||||
className={inputCls}
|
||||
value={book.tagline}
|
||||
onChange={(e) => patch({ tagline: e.target.value })}
|
||||
/>
|
||||
</Field>
|
||||
<Field
|
||||
label="Elevator pitch (30s)"
|
||||
hint="Una frase que un desconocido entiende"
|
||||
className="sm:col-span-2"
|
||||
<div className={sectionStack}>
|
||||
<PanelCard
|
||||
eyebrow="Esencia"
|
||||
title="Quién es la marca"
|
||||
description="Nombre, tagline y pitch: lo primero que debe leerse con claridad."
|
||||
accent="brand"
|
||||
>
|
||||
<textarea
|
||||
className={areaCls}
|
||||
value={book.elevator_pitch}
|
||||
onChange={(e) => patch({ elevator_pitch: e.target.value })}
|
||||
/>
|
||||
</Field>
|
||||
<Field label="Promesa de marca">
|
||||
<textarea
|
||||
className={areaCls}
|
||||
value={book.brand_promise}
|
||||
onChange={(e) => patch({ brand_promise: e.target.value })}
|
||||
/>
|
||||
</Field>
|
||||
<Field label="Posicionamiento">
|
||||
<textarea
|
||||
className={areaCls}
|
||||
value={book.positioning}
|
||||
onChange={(e) => patch({ positioning: e.target.value })}
|
||||
/>
|
||||
</Field>
|
||||
<Field label="Arquetipo" hint="Jung / marca (Sabio, Héroe…)">
|
||||
<input
|
||||
className={inputCls}
|
||||
value={book.archetype}
|
||||
onChange={(e) => patch({ archetype: e.target.value })}
|
||||
/>
|
||||
</Field>
|
||||
<Field label="Vertical / sector">
|
||||
<input
|
||||
className={inputCls}
|
||||
value={book.vertical}
|
||||
onChange={(e) => patch({ vertical: e.target.value })}
|
||||
/>
|
||||
</Field>
|
||||
<Field label="Misión">
|
||||
<textarea
|
||||
className={areaCls}
|
||||
value={book.mission}
|
||||
onChange={(e) => patch({ mission: e.target.value })}
|
||||
/>
|
||||
</Field>
|
||||
<Field label="Visión">
|
||||
<textarea
|
||||
className={areaCls}
|
||||
value={book.vision}
|
||||
onChange={(e) => patch({ vision: e.target.value })}
|
||||
/>
|
||||
</Field>
|
||||
<Field label="Valores (uno por línea)">
|
||||
<textarea
|
||||
className={areaCls}
|
||||
value={listToLines(book.values)}
|
||||
onChange={(e) =>
|
||||
patch({ values: linesToList(e.target.value) })
|
||||
}
|
||||
/>
|
||||
</Field>
|
||||
<Field label="Historia / origen">
|
||||
<textarea
|
||||
className={areaCls}
|
||||
value={book.origin_story}
|
||||
onChange={(e) => patch({ origin_story: e.target.value })}
|
||||
/>
|
||||
</Field>
|
||||
<Field label="Sitio Web">
|
||||
<input
|
||||
className={inputCls}
|
||||
value={book.website || ''}
|
||||
placeholder="ej. blueparrotschool.com"
|
||||
onChange={(e) => patch({ website: e.target.value })}
|
||||
/>
|
||||
</Field>
|
||||
<Field
|
||||
label="Oferta y programas B2B (empresas)"
|
||||
hint="Programas in-company, formación equipos, acreditaciones oficiales"
|
||||
className="sm:col-span-2"
|
||||
<Field label="Nombre de marca">
|
||||
<input
|
||||
className={inputCls}
|
||||
value={book.brand_name}
|
||||
onChange={(e) => patch({ brand_name: e.target.value })}
|
||||
/>
|
||||
</Field>
|
||||
<Field label="Tagline">
|
||||
<input
|
||||
className={inputCls}
|
||||
value={book.tagline}
|
||||
onChange={(e) => patch({ tagline: e.target.value })}
|
||||
/>
|
||||
</Field>
|
||||
<Field
|
||||
label="Elevator pitch (30s)"
|
||||
hint="Una frase que un desconocido entiende"
|
||||
wide
|
||||
>
|
||||
<textarea
|
||||
className={areaCls}
|
||||
value={book.elevator_pitch}
|
||||
onChange={(e) => patch({ elevator_pitch: e.target.value })}
|
||||
/>
|
||||
</Field>
|
||||
<Field label="Promesa de marca">
|
||||
<textarea
|
||||
className={areaCls}
|
||||
value={book.brand_promise}
|
||||
onChange={(e) => patch({ brand_promise: e.target.value })}
|
||||
/>
|
||||
</Field>
|
||||
<Field label="Posicionamiento">
|
||||
<textarea
|
||||
className={areaCls}
|
||||
value={book.positioning}
|
||||
onChange={(e) => patch({ positioning: e.target.value })}
|
||||
/>
|
||||
</Field>
|
||||
</PanelCard>
|
||||
|
||||
<PanelCard
|
||||
eyebrow="Marco"
|
||||
title="Misión, visión y valores"
|
||||
description="Marco estratégico que sostiene el tono y las campañas."
|
||||
>
|
||||
<textarea
|
||||
className={areaCls}
|
||||
value={book.b2b_offering || ''}
|
||||
onChange={(e) => patch({ b2b_offering: e.target.value })}
|
||||
/>
|
||||
</Field>
|
||||
<Field
|
||||
label="Oferta y cursos B2C (particulares)"
|
||||
hint="Cursos adultos, intensivos, certificaciones Cambridge/Aptis, 1to1"
|
||||
className="sm:col-span-2"
|
||||
<Field label="Arquetipo" hint="Jung / marca (Sabio, Héroe…)">
|
||||
<input
|
||||
className={inputCls}
|
||||
value={book.archetype}
|
||||
onChange={(e) => patch({ archetype: e.target.value })}
|
||||
/>
|
||||
</Field>
|
||||
<Field label="Vertical / sector">
|
||||
<input
|
||||
className={inputCls}
|
||||
value={book.vertical}
|
||||
onChange={(e) => patch({ vertical: e.target.value })}
|
||||
/>
|
||||
</Field>
|
||||
<Field label="Misión">
|
||||
<textarea
|
||||
className={areaCls}
|
||||
value={book.mission}
|
||||
onChange={(e) => patch({ mission: e.target.value })}
|
||||
/>
|
||||
</Field>
|
||||
<Field label="Visión">
|
||||
<textarea
|
||||
className={areaCls}
|
||||
value={book.vision}
|
||||
onChange={(e) => patch({ vision: e.target.value })}
|
||||
/>
|
||||
</Field>
|
||||
<Field label="Valores (uno por línea)">
|
||||
<textarea
|
||||
className={areaCls}
|
||||
value={listToLines(book.values)}
|
||||
onChange={(e) =>
|
||||
patch({ values: linesToList(e.target.value) })
|
||||
}
|
||||
/>
|
||||
</Field>
|
||||
<Field label="Historia / origen">
|
||||
<textarea
|
||||
className={areaCls}
|
||||
value={book.origin_story}
|
||||
onChange={(e) => patch({ origin_story: e.target.value })}
|
||||
/>
|
||||
</Field>
|
||||
<Field label="Sitio web" wide>
|
||||
<input
|
||||
className={inputCls}
|
||||
value={book.website || ''}
|
||||
placeholder="ej. blueparrotschool.com"
|
||||
onChange={(e) => patch({ website: e.target.value })}
|
||||
/>
|
||||
</Field>
|
||||
</PanelCard>
|
||||
|
||||
<PanelCard
|
||||
eyebrow="Negocio"
|
||||
title="Oferta B2B / B2C y foco comercial"
|
||||
description="Qué vende cada línea y qué priorizamos ahora."
|
||||
accent="sky"
|
||||
>
|
||||
<textarea
|
||||
className={areaCls}
|
||||
value={book.b2c_offering || ''}
|
||||
onChange={(e) => patch({ b2c_offering: e.target.value })}
|
||||
/>
|
||||
</Field>
|
||||
<Field
|
||||
label="Diferenciación clave (USPs frente a competencia)"
|
||||
hint="Profesor fijo, sesiones completas, acreditación directa FUNDAE..."
|
||||
className="sm:col-span-2"
|
||||
>
|
||||
<textarea
|
||||
className={areaCls}
|
||||
value={book.usp_differentiators || ''}
|
||||
onChange={(e) => patch({ usp_differentiators: e.target.value })}
|
||||
/>
|
||||
</Field>
|
||||
<Field
|
||||
label="Prioridades actuales y foco comercial"
|
||||
hint="Foco de captación prioritario para este periodo"
|
||||
className="sm:col-span-2"
|
||||
>
|
||||
<textarea
|
||||
className={areaCls}
|
||||
value={book.current_priorities || ''}
|
||||
onChange={(e) => patch({ current_priorities: e.target.value })}
|
||||
/>
|
||||
</Field>
|
||||
<Field
|
||||
label="Oferta y programas B2B (empresas)"
|
||||
hint="In-company, equipos, acreditaciones"
|
||||
wide
|
||||
>
|
||||
<textarea
|
||||
className={areaCls}
|
||||
value={book.b2b_offering || ''}
|
||||
onChange={(e) => patch({ b2b_offering: e.target.value })}
|
||||
/>
|
||||
</Field>
|
||||
<Field
|
||||
label="Oferta y cursos B2C (particulares)"
|
||||
hint="Adultos, intensivos, certificaciones"
|
||||
wide
|
||||
>
|
||||
<textarea
|
||||
className={areaCls}
|
||||
value={book.b2c_offering || ''}
|
||||
onChange={(e) => patch({ b2c_offering: e.target.value })}
|
||||
/>
|
||||
</Field>
|
||||
<Field
|
||||
label="Diferenciación clave (USPs)"
|
||||
hint="Por qué ganar frente a alternativas"
|
||||
wide
|
||||
>
|
||||
<textarea
|
||||
className={areaCls}
|
||||
value={book.usp_differentiators || ''}
|
||||
onChange={(e) =>
|
||||
patch({ usp_differentiators: e.target.value })
|
||||
}
|
||||
/>
|
||||
</Field>
|
||||
<Field
|
||||
label="Prioridades actuales y foco comercial"
|
||||
hint="Captación prioritaria este periodo"
|
||||
wide
|
||||
>
|
||||
<textarea
|
||||
className={areaCls}
|
||||
value={book.current_priorities || ''}
|
||||
onChange={(e) =>
|
||||
patch({ current_priorities: e.target.value })
|
||||
}
|
||||
/>
|
||||
</Field>
|
||||
</PanelCard>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{section === 'voz' && (
|
||||
<div className="grid gap-3 sm:grid-cols-2">
|
||||
<div className={sectionStack}>
|
||||
<PanelCard
|
||||
eyebrow="Voz"
|
||||
title="Cómo suena la marca"
|
||||
description="Tono, do/don’t y ejemplos reales"
|
||||
accent="brand"
|
||||
>
|
||||
<Field label="Tono" className="sm:col-span-2">
|
||||
<textarea
|
||||
className={areaCls}
|
||||
@@ -954,28 +1059,107 @@ export default function BrandZone({
|
||||
}
|
||||
/>
|
||||
</Field>
|
||||
</PanelCard>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{section === 'visual' && (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<p className="text-sm font-semibold text-gray-900 inline-flex items-center gap-1.5">
|
||||
<Palette className="w-4 h-4 text-rose-500" />
|
||||
Colores
|
||||
<div className={sectionStack}>
|
||||
<PanelCard
|
||||
eyebrow="Upload"
|
||||
title="Añadir al kit"
|
||||
description="Logos, tipografías, patrones e imágenes oficiales."
|
||||
accent="neutral"
|
||||
cols={1}
|
||||
>
|
||||
<div className="flex flex-col md:flex-row md:items-end gap-3">
|
||||
<Field label="Tipo" className="md:w-40">
|
||||
<select
|
||||
className={selectCls}
|
||||
value={kitKind}
|
||||
onChange={(e) => {
|
||||
const k = e.target.value as BrandAssetKind;
|
||||
setKitKind(k);
|
||||
setKitRole(
|
||||
k === 'logo'
|
||||
? 'primary'
|
||||
: k === 'font'
|
||||
? 'body'
|
||||
: k === 'image'
|
||||
? 'moodboard'
|
||||
: 'asset'
|
||||
);
|
||||
}}
|
||||
>
|
||||
{Object.entries(ASSET_KIND_LABELS).map(([k, v]) => (
|
||||
<option key={k} value={k}>
|
||||
{v}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</Field>
|
||||
<Field label="Rol" className="md:w-44" hint="ej. primary, dark">
|
||||
<input
|
||||
className={inputCls}
|
||||
value={kitRole}
|
||||
onChange={(e) => setKitRole(e.target.value)}
|
||||
/>
|
||||
</Field>
|
||||
<label className="inline-flex items-center justify-center gap-1.5 rounded-xl bg-gray-900 px-4 py-2.5 text-xs font-bold text-white cursor-pointer hover:bg-gray-800">
|
||||
{kitUploading ? (
|
||||
<Loader2 className="w-3.5 h-3.5 animate-spin" />
|
||||
) : (
|
||||
<Plus className="w-3.5 h-3.5" />
|
||||
)}
|
||||
Subir archivo
|
||||
<input
|
||||
type="file"
|
||||
className="hidden"
|
||||
accept={
|
||||
kitKind === 'logo' ||
|
||||
kitKind === 'image' ||
|
||||
kitKind === 'icon'
|
||||
? 'image/*,.svg,.pdf'
|
||||
: kitKind === 'font'
|
||||
? '.ttf,.otf,.woff,.woff2,.zip'
|
||||
: kitKind === 'video'
|
||||
? 'video/*'
|
||||
: '*/*'
|
||||
}
|
||||
disabled={kitUploading}
|
||||
onChange={(e) => {
|
||||
const f = e.target.files?.[0];
|
||||
if (f) void uploadKit(f);
|
||||
e.target.value = '';
|
||||
}}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</PanelCard>
|
||||
|
||||
<PanelCard
|
||||
eyebrow="Paleta"
|
||||
title="Colores de marca"
|
||||
description="Tokens HEX con rol y uso. Misma claridad que el overview."
|
||||
accent="brand"
|
||||
cols={1}
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<p className="text-sm font-semibold text-gray-800 inline-flex items-center gap-1.5">
|
||||
<Palette className="w-4 h-4 text-brand-600" />
|
||||
Tokens de color
|
||||
</p>
|
||||
<button
|
||||
type="button"
|
||||
onClick={addColor}
|
||||
className="text-xs font-semibold text-brand-700 inline-flex items-center gap-1"
|
||||
className="inline-flex items-center gap-1 rounded-lg bg-brand-50 px-2.5 py-1.5 text-xs font-semibold text-brand-800 hover:bg-brand-100"
|
||||
>
|
||||
<Plus className="w-3.5 h-3.5" /> Añadir color
|
||||
</button>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div className="space-y-2.5">
|
||||
{book.colors.length === 0 ? (
|
||||
<p className="text-xs text-gray-500">
|
||||
<p className="text-sm text-gray-500 italic">
|
||||
Añade tokens de color con picker o pega un #hex.
|
||||
</p>
|
||||
) : null}
|
||||
@@ -1061,9 +1245,14 @@ export default function BrandZone({
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</PanelCard>
|
||||
|
||||
<div className="space-y-3">
|
||||
<PanelCard
|
||||
eyebrow="Tipografía"
|
||||
title="Fuentes del kit"
|
||||
description="Headline y body con la misma jerarquía visual que el overview."
|
||||
cols={1}
|
||||
>
|
||||
<GoogleFontsGallery
|
||||
selectedFamilies={book.fonts_kit.map((f) => f.family)}
|
||||
onPick={(font, pickRole) => {
|
||||
@@ -1093,15 +1282,15 @@ export default function BrandZone({
|
||||
setOkMsg(`${font.family} añadida al kit (${pickRole})`);
|
||||
}}
|
||||
/>
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<p className="text-sm font-semibold text-gray-900 inline-flex items-center gap-1.5">
|
||||
<Type className="w-4 h-4 text-rose-500" />
|
||||
<div className="flex items-center justify-between">
|
||||
<p className="text-sm font-semibold text-gray-800 inline-flex items-center gap-1.5">
|
||||
<Type className="w-4 h-4 text-brand-600" />
|
||||
Tipografías del kit
|
||||
</p>
|
||||
<button
|
||||
type="button"
|
||||
onClick={addFont}
|
||||
className="text-xs font-semibold text-brand-700 inline-flex items-center gap-1"
|
||||
className="inline-flex items-center gap-1 rounded-lg bg-brand-50 px-2.5 py-1.5 text-xs font-semibold text-brand-800 hover:bg-brand-100"
|
||||
>
|
||||
<Plus className="w-3.5 h-3.5" /> Manual
|
||||
</button>
|
||||
@@ -1199,13 +1388,17 @@ export default function BrandZone({
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<p className="text-[11px] text-gray-500 mt-2">
|
||||
<p className="text-xs text-gray-500">
|
||||
Para archivos .ttf/.otf/.woff usa la barra «Añadir al kit» con
|
||||
tipo Tipografía.
|
||||
</p>
|
||||
</div>
|
||||
</PanelCard>
|
||||
|
||||
<div className="grid gap-3 sm:grid-cols-2">
|
||||
<PanelCard
|
||||
eyebrow="Estilo"
|
||||
title="Logo, foto y reglas de composición"
|
||||
description="Cómo se ve la marca en creatividades y layouts."
|
||||
>
|
||||
<Field label="Uso de logo">
|
||||
<textarea
|
||||
className={areaCls}
|
||||
@@ -1284,7 +1477,7 @@ export default function BrandZone({
|
||||
onChange={(e) => patch({ typography_body: e.target.value })}
|
||||
/>
|
||||
</Field>
|
||||
<Field label="Reglas de texto en imagen" className="sm:col-span-2">
|
||||
<Field label="Reglas de texto en imagen" wide>
|
||||
<textarea
|
||||
className={areaCls}
|
||||
value={book.image_text_rules || ''}
|
||||
@@ -1292,12 +1485,14 @@ export default function BrandZone({
|
||||
onChange={(e) => patch({ image_text_rules: e.target.value })}
|
||||
/>
|
||||
</Field>
|
||||
</div>
|
||||
</PanelCard>
|
||||
|
||||
<div>
|
||||
<p className="text-sm font-semibold text-gray-900 mb-2">
|
||||
Archivos del kit ({book.kit_assets.length})
|
||||
</p>
|
||||
<PanelCard
|
||||
eyebrow="Kit"
|
||||
title={`Archivos del kit (${book.kit_assets.length})`}
|
||||
description="Logos, tipografías e imágenes subidos al brand kit."
|
||||
cols={1}
|
||||
>
|
||||
{book.kit_assets.length === 0 ? (
|
||||
<p className="text-xs text-gray-500">
|
||||
Todavía no hay logos ni archivos. Usa «Añadir al kit»
|
||||
@@ -1344,13 +1539,19 @@ export default function BrandZone({
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
</PanelCard>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{section === 'audiencia' && (
|
||||
<div className="grid gap-3 sm:grid-cols-2">
|
||||
<Field label="ICP" className="sm:col-span-2">
|
||||
<div className={sectionStack}>
|
||||
<PanelCard
|
||||
eyebrow="ICP"
|
||||
title="A quién hablamos"
|
||||
description="Perfil ideal, geografía e idiomas — misma claridad que Overview."
|
||||
accent="sky"
|
||||
>
|
||||
<Field label="ICP" wide>
|
||||
<textarea
|
||||
className={areaCls}
|
||||
value={book.icp}
|
||||
@@ -1501,7 +1702,7 @@ export default function BrandZone({
|
||||
{(!book.b2b_personas || book.b2b_personas.length === 0) ? (
|
||||
<p className="text-xs text-gray-400 italic">No hay buyer personas B2B definidas.</p>
|
||||
) : (
|
||||
<div className="grid gap-3 sm:grid-cols-2">
|
||||
<div className="grid gap-4 sm:grid-cols-2 sm:gap-x-5 sm:gap-y-5">
|
||||
{book.b2b_personas.map((p, idx) => (
|
||||
<div key={idx} className="rounded-xl border border-gray-200 bg-gray-50/50 p-3 space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
@@ -1622,7 +1823,7 @@ export default function BrandZone({
|
||||
{(!book.b2c_personas || book.b2c_personas.length === 0) ? (
|
||||
<p className="text-xs text-gray-400 italic">No hay buyer personas B2C definidas.</p>
|
||||
) : (
|
||||
<div className="grid gap-3 sm:grid-cols-2">
|
||||
<div className="grid gap-4 sm:grid-cols-2 sm:gap-x-5 sm:gap-y-5">
|
||||
{book.b2c_personas.map((p, idx) => (
|
||||
<div key={idx} className="rounded-xl border border-gray-200 bg-gray-50/50 p-3 space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
@@ -1710,11 +1911,18 @@ export default function BrandZone({
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</PanelCard>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{section === 'oferta' && (
|
||||
<div className="grid gap-3 sm:grid-cols-2">
|
||||
<div className={sectionStack}>
|
||||
<PanelCard
|
||||
eyebrow="Oferta"
|
||||
title="Propuesta de valor"
|
||||
description="Qué vendes, prueba y alternativas"
|
||||
accent="sky"
|
||||
>
|
||||
<Field label="Propuesta de valor" className="sm:col-span-2">
|
||||
<textarea
|
||||
className={areaCls}
|
||||
@@ -1797,11 +2005,18 @@ export default function BrandZone({
|
||||
onChange={(e) => patch({ price_and_offer_framing: e.target.value })}
|
||||
/>
|
||||
</Field>
|
||||
</PanelCard>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{section === 'pilares' && (
|
||||
<div className="grid gap-3 sm:grid-cols-2">
|
||||
<div className={sectionStack}>
|
||||
<PanelCard
|
||||
eyebrow="Contenido"
|
||||
title="Pilares y series"
|
||||
description="Temas recurrentes y matriz editorial"
|
||||
accent="neutral"
|
||||
>
|
||||
<Field label="Pilares RRSS">
|
||||
<textarea
|
||||
className={areaCls}
|
||||
@@ -1855,12 +2070,18 @@ export default function BrandZone({
|
||||
placeholder="Pilar × formato × KPI…"
|
||||
/>
|
||||
</Field>
|
||||
</PanelCard>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{section === 'canales' && (
|
||||
<div className="space-y-4">
|
||||
<div className="grid gap-3 sm:grid-cols-2">
|
||||
<div className={sectionStack}>
|
||||
<PanelCard
|
||||
eyebrow="Presencia"
|
||||
title="Rol por canal"
|
||||
description="Para qué usamos cada red y qué no debe mezclarse."
|
||||
accent="sky"
|
||||
>
|
||||
{(
|
||||
[
|
||||
['instagram', 'Instagram'],
|
||||
@@ -1885,8 +2106,12 @@ export default function BrandZone({
|
||||
/>
|
||||
</Field>
|
||||
))}
|
||||
</div>
|
||||
<div className="grid gap-3 sm:grid-cols-2">
|
||||
</PanelCard>
|
||||
<PanelCard
|
||||
eyebrow="Handles"
|
||||
title="URLs y cadencia"
|
||||
description="Enlaces oficiales y ritmo de publicación."
|
||||
>
|
||||
{(
|
||||
[
|
||||
['instagram', 'Handle IG'],
|
||||
@@ -1929,12 +2154,18 @@ export default function BrandZone({
|
||||
}
|
||||
/>
|
||||
</Field>
|
||||
</div>
|
||||
</PanelCard>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{section === 'ads' && (
|
||||
<div className="grid gap-3 sm:grid-cols-2">
|
||||
<div className={sectionStack}>
|
||||
<PanelCard
|
||||
eyebrow="Paid"
|
||||
title="Anuncios que convierten"
|
||||
description="USPs, hooks, CTAs y ángulos observados"
|
||||
accent="amber"
|
||||
>
|
||||
<Field label="USPs">
|
||||
<textarea
|
||||
className={areaCls}
|
||||
@@ -2124,6 +2355,7 @@ export default function BrandZone({
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</PanelCard>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -2381,7 +2613,7 @@ export default function BrandZone({
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="grid gap-3 sm:grid-cols-2">
|
||||
<div className="grid gap-4 sm:grid-cols-2 sm:gap-x-5 sm:gap-y-5">
|
||||
<Field label="Temas LinkedIn">
|
||||
<textarea
|
||||
className={areaCls}
|
||||
@@ -2742,7 +2974,13 @@ export default function BrandZone({
|
||||
)}
|
||||
|
||||
{section === 'compliance' && (
|
||||
<div className="grid gap-3 sm:grid-cols-2">
|
||||
<div className={sectionStack}>
|
||||
<PanelCard
|
||||
eyebrow="Líneas rojas"
|
||||
title="Prohibido y compliance"
|
||||
description="Claims, competencia, emojis y testimonios"
|
||||
accent="rose"
|
||||
>
|
||||
<Field label="Claims prohibidos">
|
||||
<textarea
|
||||
className={areaCls}
|
||||
@@ -2820,6 +3058,7 @@ export default function BrandZone({
|
||||
onChange={(e) => patch({ testimonials_policy: e.target.value })}
|
||||
/>
|
||||
</Field>
|
||||
</PanelCard>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user