feat: restaurar todas las secciones en Creatividades Ads con sincronización bidireccional en tiempo real con Zona de Marca
This commit is contained in:
@@ -1,10 +1,12 @@
|
||||
/**
|
||||
* GET / PUT brandbook for active tenant (documents type=brandbook).
|
||||
* Sincronización bidireccional permanente con tenants.creative_style.
|
||||
*/
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { createClient } from '@/lib/supabase/server';
|
||||
import { resolveActiveTenant, restGet } from '@/lib/tenant-context';
|
||||
import { isAgencyRole, serviceHeaders, supabaseUrl, userRole } from '@/lib/agency';
|
||||
import { createClient as createAdminClient } from '@supabase/supabase-js';
|
||||
import {
|
||||
BRANDBOOK_DOC_TITLE,
|
||||
BRANDBOOK_DOC_TYPE,
|
||||
@@ -17,6 +19,12 @@ import {
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
function getAdmin() {
|
||||
const url = supabaseUrl();
|
||||
const key = process.env.SUPABASE_SERVICE_ROLE_KEY!;
|
||||
return createAdminClient(url, key);
|
||||
}
|
||||
|
||||
async function loadDoc(tenantId: string): Promise<{
|
||||
id: string | null;
|
||||
book: Brandbook | null;
|
||||
@@ -31,7 +39,7 @@ async function loadDoc(tenantId: string): Promise<{
|
||||
return { id: row.id, book: parseBrandbook(row.content) };
|
||||
}
|
||||
|
||||
export async function GET() {
|
||||
export async function GET(req: NextRequest) {
|
||||
const supabase = await createClient();
|
||||
const {
|
||||
data: { user },
|
||||
@@ -39,22 +47,48 @@ export async function GET() {
|
||||
if (!user) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
const ctx = await resolveActiveTenant(user);
|
||||
if (!ctx.tenantId || ctx.portfolioWide) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Selecciona un cliente en Contexto.' },
|
||||
{ status: 400 }
|
||||
);
|
||||
|
||||
const isAgency = isAgencyRole(userRole(user));
|
||||
const queryTenantId = req.nextUrl.searchParams.get('tenant_id') || req.nextUrl.searchParams.get('tenantId');
|
||||
|
||||
let tenantId: string | null = null;
|
||||
let label = 'Cliente';
|
||||
let slug = 'cliente';
|
||||
|
||||
if (isAgency && queryTenantId) {
|
||||
const admin = getAdmin();
|
||||
const { data: tenant } = await admin
|
||||
.from('tenants')
|
||||
.select('id, name, slug')
|
||||
.eq('id', queryTenantId)
|
||||
.maybeSingle();
|
||||
|
||||
if (tenant) {
|
||||
tenantId = tenant.id;
|
||||
label = tenant.name || 'Cliente';
|
||||
slug = tenant.slug || 'cliente';
|
||||
}
|
||||
}
|
||||
|
||||
let slug = 'cliente';
|
||||
const t = await restGet<{ slug?: string }[]>(
|
||||
`/rest/v1/tenants?id=eq.${encodeURIComponent(ctx.tenantId)}&select=slug`
|
||||
);
|
||||
slug = t.data?.[0]?.slug || slug;
|
||||
if (!tenantId) {
|
||||
const ctx = await resolveActiveTenant(user);
|
||||
if (!ctx.tenantId || ctx.portfolioWide) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Selecciona un cliente en Contexto.' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
tenantId = ctx.tenantId;
|
||||
label = ctx.label;
|
||||
|
||||
const { id, book } = await loadDoc(ctx.tenantId);
|
||||
const current = book || emptyBrandbook(ctx.label);
|
||||
const t = await restGet<{ slug?: string }[]>(
|
||||
`/rest/v1/tenants?id=eq.${encodeURIComponent(tenantId)}&select=slug`
|
||||
);
|
||||
slug = t.data?.[0]?.slug || slug;
|
||||
}
|
||||
|
||||
const { id, book } = await loadDoc(tenantId);
|
||||
const current = book || emptyBrandbook(label);
|
||||
const completeness = brandbookCompleteness(current);
|
||||
|
||||
return NextResponse.json({
|
||||
@@ -62,7 +96,7 @@ export async function GET() {
|
||||
id,
|
||||
brandbook: current,
|
||||
slug,
|
||||
label: ctx.label,
|
||||
label,
|
||||
completeness,
|
||||
exists: !!book,
|
||||
});
|
||||
@@ -79,28 +113,59 @@ export async function PUT(req: NextRequest) {
|
||||
if (!isAgencyRole(userRole(user))) {
|
||||
return NextResponse.json({ error: 'Solo agencia' }, { status: 403 });
|
||||
}
|
||||
const ctx = await resolveActiveTenant(user);
|
||||
if (!ctx.tenantId || ctx.portfolioWide) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Selecciona un cliente en Contexto.' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
const body = (await req.json().catch(() => ({}))) as {
|
||||
brandbook?: unknown;
|
||||
seed?: boolean;
|
||||
tenant_id?: string;
|
||||
tenantId?: string;
|
||||
};
|
||||
|
||||
const queryTenantId =
|
||||
req.nextUrl.searchParams.get('tenant_id') ||
|
||||
req.nextUrl.searchParams.get('tenantId') ||
|
||||
body.tenant_id ||
|
||||
body.tenantId;
|
||||
|
||||
let tenantId: string | null = null;
|
||||
let label = 'Cliente';
|
||||
let slug = 'cliente';
|
||||
const t = await restGet<{ slug?: string }[]>(
|
||||
`/rest/v1/tenants?id=eq.${encodeURIComponent(ctx.tenantId)}&select=slug`
|
||||
);
|
||||
slug = t.data?.[0]?.slug || slug;
|
||||
|
||||
if (queryTenantId) {
|
||||
const admin = getAdmin();
|
||||
const { data: tenant } = await admin
|
||||
.from('tenants')
|
||||
.select('id, name, slug')
|
||||
.eq('id', queryTenantId)
|
||||
.maybeSingle();
|
||||
|
||||
if (tenant) {
|
||||
tenantId = tenant.id;
|
||||
label = tenant.name || 'Cliente';
|
||||
slug = tenant.slug || 'cliente';
|
||||
}
|
||||
}
|
||||
|
||||
if (!tenantId) {
|
||||
const ctx = await resolveActiveTenant(user);
|
||||
if (!ctx.tenantId || ctx.portfolioWide) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Selecciona un cliente en Contexto.' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
tenantId = ctx.tenantId;
|
||||
label = ctx.label;
|
||||
|
||||
const t = await restGet<{ slug?: string }[]>(
|
||||
`/rest/v1/tenants?id=eq.${encodeURIComponent(tenantId)}&select=slug`
|
||||
);
|
||||
slug = t.data?.[0]?.slug || slug;
|
||||
}
|
||||
|
||||
let book: Brandbook | null = null;
|
||||
if (body.seed) {
|
||||
book = seedBrandbook(slug, ctx.label);
|
||||
book = seedBrandbook(slug, label);
|
||||
} else {
|
||||
book = parseBrandbook(body.brandbook);
|
||||
}
|
||||
@@ -108,68 +173,118 @@ export async function PUT(req: NextRequest) {
|
||||
return NextResponse.json({ error: 'brandbook inválido' }, { status: 400 });
|
||||
}
|
||||
book.updated_at = new Date().toISOString();
|
||||
book.brand_name = book.brand_name || ctx.label;
|
||||
book.brand_name = book.brand_name || label;
|
||||
|
||||
const headers = serviceHeaders();
|
||||
const base = supabaseUrl();
|
||||
if (!headers || !base) {
|
||||
return NextResponse.json({ error: 'No service role' }, { status: 500 });
|
||||
}
|
||||
const admin = getAdmin();
|
||||
const { id } = await loadDoc(tenantId);
|
||||
|
||||
const { id } = await loadDoc(ctx.tenantId);
|
||||
let docId = id;
|
||||
if (id) {
|
||||
const patch = await fetch(`${base}/rest/v1/documents?id=eq.${id}`, {
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
...headers,
|
||||
'Content-Type': 'application/json',
|
||||
Prefer: 'return=representation',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
const { error: patchErr } = await admin
|
||||
.from('documents')
|
||||
.update({
|
||||
content: book,
|
||||
title: BRANDBOOK_DOC_TITLE,
|
||||
updated_at: book.updated_at,
|
||||
}),
|
||||
});
|
||||
if (!patch.ok) {
|
||||
return NextResponse.json(
|
||||
{ error: await patch.text() },
|
||||
{ status: 500 }
|
||||
);
|
||||
})
|
||||
.eq('id', id);
|
||||
|
||||
if (patchErr) {
|
||||
return NextResponse.json({ error: patchErr.message }, { status: 500 });
|
||||
}
|
||||
const rows = (await patch.json()) as { id: string }[];
|
||||
return NextResponse.json({
|
||||
ok: true,
|
||||
id: rows[0]?.id || id,
|
||||
brandbook: book,
|
||||
completeness: brandbookCompleteness(book),
|
||||
});
|
||||
} else {
|
||||
const { data: insRow, error: insErr } = await admin
|
||||
.from('documents')
|
||||
.insert({
|
||||
tenant_id: tenantId,
|
||||
type: BRANDBOOK_DOC_TYPE,
|
||||
title: BRANDBOOK_DOC_TITLE,
|
||||
status: 'approved',
|
||||
content: book,
|
||||
updated_at: book.updated_at,
|
||||
})
|
||||
.select('id')
|
||||
.single();
|
||||
|
||||
if (insErr) {
|
||||
return NextResponse.json({ error: insErr.message }, { status: 500 });
|
||||
}
|
||||
docId = insRow.id;
|
||||
}
|
||||
|
||||
const ins = await fetch(`${base}/rest/v1/documents`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
...headers,
|
||||
'Content-Type': 'application/json',
|
||||
Prefer: 'return=representation',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
tenant_id: ctx.tenantId,
|
||||
type: BRANDBOOK_DOC_TYPE,
|
||||
title: BRANDBOOK_DOC_TITLE,
|
||||
status: 'approved',
|
||||
content: book,
|
||||
}),
|
||||
});
|
||||
if (!ins.ok) {
|
||||
return NextResponse.json({ error: await ins.text() }, { status: 500 });
|
||||
// Sincronizar simultáneamente con tenants.creative_style para consistencia 100%
|
||||
const { data: tenantRow } = await admin
|
||||
.from('tenants')
|
||||
.select('creative_style, settings')
|
||||
.eq('id', tenantId)
|
||||
.maybeSingle();
|
||||
|
||||
if (tenantRow) {
|
||||
const existingStyle = (tenantRow.creative_style && typeof tenantRow.creative_style === 'object') ? tenantRow.creative_style : {};
|
||||
const updatedStyle = {
|
||||
...existingStyle,
|
||||
identity: {
|
||||
...(existingStyle.identity || {}),
|
||||
summary: book.elevator_pitch || existingStyle.identity?.summary || '',
|
||||
website: book.website || existingStyle.identity?.website || '',
|
||||
b2b_offering: book.b2b_offering || existingStyle.identity?.b2b_offering || '',
|
||||
b2c_offering: book.b2c_offering || existingStyle.identity?.b2c_offering || '',
|
||||
usp_differentiators: book.usp_differentiators || existingStyle.identity?.usp_differentiators || '',
|
||||
current_priorities: book.current_priorities || existingStyle.identity?.current_priorities || '',
|
||||
price_and_offer_framing: book.price_and_offer_framing || existingStyle.identity?.price_and_offer_framing || '',
|
||||
},
|
||||
voice: {
|
||||
...(existingStyle.voice || {}),
|
||||
tone: book.tone || existingStyle.voice?.tone || '',
|
||||
copy_structure_rules: book.copy_structure_rules || existingStyle.voice?.copy_structure_rules || '',
|
||||
english_terms_policy: book.english_terms_policy || existingStyle.voice?.english_terms_policy || '',
|
||||
power_words: book.power_words?.length ? book.power_words : existingStyle.voice?.power_words || [],
|
||||
allowed_ctas: book.allowed_ctas?.length ? book.allowed_ctas : existingStyle.voice?.allowed_ctas || [],
|
||||
},
|
||||
visual: {
|
||||
...(existingStyle.visual || {}),
|
||||
surface_rules: book.surface_rules || existingStyle.visual?.surface_rules || '',
|
||||
image_text_rules: book.image_text_rules || existingStyle.visual?.image_text_rules || '',
|
||||
typography_headline: book.typography_headline || existingStyle.visual?.typography_headline || '',
|
||||
typography_body: book.typography_body || existingStyle.visual?.typography_body || '',
|
||||
},
|
||||
audiences: {
|
||||
...(existingStyle.audiences || {}),
|
||||
b2b_personas: book.b2b_personas?.length ? book.b2b_personas : existingStyle.audiences?.b2b_personas || [],
|
||||
b2c_personas: book.b2c_personas?.length ? book.b2c_personas : existingStyle.audiences?.b2c_personas || [],
|
||||
customer_frustrations: book.customer_frustrations || existingStyle.audiences?.customer_frustrations || '',
|
||||
desired_transformation: book.desired_transformation || existingStyle.audiences?.desired_transformation || '',
|
||||
},
|
||||
arguments: {
|
||||
...(existingStyle.arguments || {}),
|
||||
objections: book.objections?.length ? book.objections : existingStyle.arguments?.objections || [],
|
||||
winning_hooks: book.winning_hooks?.length ? book.winning_hooks : existingStyle.arguments?.winning_hooks || [],
|
||||
preferred_angles: book.preferred_angles?.length ? book.preferred_angles : existingStyle.arguments?.preferred_angles || [],
|
||||
},
|
||||
prohibitions: {
|
||||
...(existingStyle.prohibitions || {}),
|
||||
emoji_policy: book.emoji_policy || existingStyle.prohibitions?.emoji_policy || '',
|
||||
testimonials_policy: book.testimonials_policy || existingStyle.prohibitions?.testimonials_policy || '',
|
||||
competitors_never_mention: book.competitors_never_mention?.length ? book.competitors_never_mention : existingStyle.prohibitions?.competitors_never_mention || [],
|
||||
},
|
||||
};
|
||||
|
||||
await admin
|
||||
.from('tenants')
|
||||
.update({
|
||||
creative_style: updatedStyle,
|
||||
settings: {
|
||||
...(tenantRow.settings || {}),
|
||||
creative_style: updatedStyle,
|
||||
},
|
||||
})
|
||||
.eq('id', tenantId);
|
||||
}
|
||||
const rows = (await ins.json()) as { id: string }[];
|
||||
|
||||
return NextResponse.json({
|
||||
ok: true,
|
||||
id: rows[0]?.id,
|
||||
id: docId,
|
||||
brandbook: book,
|
||||
completeness: brandbookCompleteness(book),
|
||||
created: true,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -241,10 +241,10 @@ export async function PUT(req: NextRequest) {
|
||||
return NextResponse.json({ error: updErr.message }, { status: 500 });
|
||||
}
|
||||
|
||||
// Cargar brandbook de documents para compilar skill actualizado
|
||||
// Cargar brandbook de documents para compilar skill actualizado y mantener sincronización
|
||||
const { data: brandDoc } = await admin
|
||||
.from('documents')
|
||||
.select('content')
|
||||
.select('id, content')
|
||||
.eq('tenant_id', tenantId)
|
||||
.eq('type', 'brandbook')
|
||||
.order('updated_at', { ascending: false })
|
||||
@@ -267,6 +267,60 @@ export async function PUT(req: NextRequest) {
|
||||
brandbook = seedBrandbook(existing.slug || 'cliente', existing.name || 'Cliente');
|
||||
}
|
||||
|
||||
// Sincronizar campos de normalizedStyle hacia brandbook
|
||||
if (normalizedStyle.identity) {
|
||||
if (normalizedStyle.identity.summary) brandbook.elevator_pitch = normalizedStyle.identity.summary;
|
||||
if (normalizedStyle.identity.website) brandbook.website = normalizedStyle.identity.website;
|
||||
if (normalizedStyle.identity.b2b_offering) brandbook.b2b_offering = normalizedStyle.identity.b2b_offering;
|
||||
if (normalizedStyle.identity.b2c_offering) brandbook.b2c_offering = normalizedStyle.identity.b2c_offering;
|
||||
if (normalizedStyle.identity.usp_differentiators) brandbook.usp_differentiators = normalizedStyle.identity.usp_differentiators;
|
||||
if (normalizedStyle.identity.current_priorities) brandbook.current_priorities = normalizedStyle.identity.current_priorities;
|
||||
if (normalizedStyle.identity.price_and_offer_framing) brandbook.price_and_offer_framing = normalizedStyle.identity.price_and_offer_framing;
|
||||
}
|
||||
if (normalizedStyle.voice) {
|
||||
if (normalizedStyle.voice.tone) brandbook.tone = normalizedStyle.voice.tone;
|
||||
if (normalizedStyle.voice.copy_structure_rules) brandbook.copy_structure_rules = normalizedStyle.voice.copy_structure_rules;
|
||||
if (normalizedStyle.voice.english_terms_policy) brandbook.english_terms_policy = normalizedStyle.voice.english_terms_policy;
|
||||
if (normalizedStyle.voice.power_words?.length) brandbook.power_words = normalizedStyle.voice.power_words;
|
||||
if (normalizedStyle.voice.allowed_ctas?.length) brandbook.allowed_ctas = normalizedStyle.voice.allowed_ctas;
|
||||
}
|
||||
if (normalizedStyle.visual) {
|
||||
if (normalizedStyle.visual.surface_rules) brandbook.surface_rules = normalizedStyle.visual.surface_rules;
|
||||
if (normalizedStyle.visual.image_text_rules) brandbook.image_text_rules = normalizedStyle.visual.image_text_rules;
|
||||
if (normalizedStyle.visual.typography_headline) brandbook.typography_headline = normalizedStyle.visual.typography_headline;
|
||||
if (normalizedStyle.visual.typography_body) brandbook.typography_body = normalizedStyle.visual.typography_body;
|
||||
}
|
||||
if (normalizedStyle.audiences) {
|
||||
if (normalizedStyle.audiences.b2b_personas?.length) brandbook.b2b_personas = normalizedStyle.audiences.b2b_personas;
|
||||
if (normalizedStyle.audiences.b2c_personas?.length) brandbook.b2c_personas = normalizedStyle.audiences.b2c_personas;
|
||||
if (normalizedStyle.audiences.customer_frustrations) brandbook.customer_frustrations = normalizedStyle.audiences.customer_frustrations;
|
||||
if (normalizedStyle.audiences.desired_transformation) brandbook.desired_transformation = normalizedStyle.audiences.desired_transformation;
|
||||
}
|
||||
if (normalizedStyle.arguments) {
|
||||
if (normalizedStyle.arguments.objections?.length) brandbook.objections = normalizedStyle.arguments.objections;
|
||||
if (normalizedStyle.arguments.winning_hooks?.length) brandbook.winning_hooks = normalizedStyle.arguments.winning_hooks;
|
||||
if (normalizedStyle.arguments.preferred_angles?.length) brandbook.preferred_angles = normalizedStyle.arguments.preferred_angles;
|
||||
}
|
||||
if (normalizedStyle.prohibitions) {
|
||||
if (normalizedStyle.prohibitions.emoji_policy) brandbook.emoji_policy = normalizedStyle.prohibitions.emoji_policy;
|
||||
if (normalizedStyle.prohibitions.testimonials_policy) brandbook.testimonials_policy = normalizedStyle.prohibitions.testimonials_policy;
|
||||
if (normalizedStyle.prohibitions.competitors_never_mention?.length) brandbook.competitors_never_mention = normalizedStyle.prohibitions.competitors_never_mention;
|
||||
}
|
||||
if (normalizedStyle.image_ai?.photo_style) {
|
||||
brandbook.photography_style = normalizedStyle.image_ai.photo_style;
|
||||
}
|
||||
brandbook.updated_at = new Date().toISOString();
|
||||
|
||||
if (brandDoc?.id) {
|
||||
await admin
|
||||
.from('documents')
|
||||
.update({
|
||||
content: brandbook,
|
||||
updated_at: brandbook.updated_at,
|
||||
})
|
||||
.eq('id', brandDoc.id);
|
||||
}
|
||||
|
||||
const compiledMarkdown = compileClientSkillFromBrandbook(
|
||||
brandbook,
|
||||
{
|
||||
|
||||
@@ -32,5 +32,5 @@ export default async function MarcaPage() {
|
||||
);
|
||||
}
|
||||
|
||||
return <BrandZone tenantLabel={ctx.label} />;
|
||||
return <BrandZone tenantId={ctx.tenantId || ''} tenantLabel={ctx.label} />;
|
||||
}
|
||||
|
||||
@@ -97,7 +97,15 @@ function KitThumb({ assetFileId, alt }: { assetFileId: string; alt: string }) {
|
||||
);
|
||||
}
|
||||
|
||||
export default function BrandZone({ tenantLabel }: { tenantLabel: string }) {
|
||||
export default function BrandZone({
|
||||
tenantId,
|
||||
tenantLabel,
|
||||
tenantSlug,
|
||||
}: {
|
||||
tenantId?: string;
|
||||
tenantLabel: string;
|
||||
tenantSlug?: string;
|
||||
}) {
|
||||
const [viewMode, setViewMode] = useState<'overview' | 'edit'>('overview');
|
||||
const [section, setSection] = useState<BrandbookSectionId>('identidad');
|
||||
const [book, setBook] = useState<Brandbook>(() => emptyBrandbook(tenantLabel));
|
||||
@@ -120,11 +128,12 @@ export default function BrandZone({ tenantLabel }: { tenantLabel: string }) {
|
||||
setLoading(true);
|
||||
setErr(null);
|
||||
try {
|
||||
const r = await fetch('/api/brandbook');
|
||||
const query = tenantId ? `?tenant_id=${encodeURIComponent(tenantId)}` : '';
|
||||
const r = await fetch(`/api/brandbook${query}`);
|
||||
const j = await r.json();
|
||||
if (!r.ok) throw new Error(j.error || 'No se pudo cargar');
|
||||
setBook(j.brandbook || emptyBrandbook(tenantLabel));
|
||||
setSlug(j.slug || '');
|
||||
setSlug(j.slug || tenantSlug || '');
|
||||
setScore(j.completeness?.score ?? 0);
|
||||
setMissing(j.completeness?.missing || []);
|
||||
setExists(!!j.exists);
|
||||
@@ -133,7 +142,7 @@ export default function BrandZone({ tenantLabel }: { tenantLabel: string }) {
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [tenantLabel]);
|
||||
}, [tenantId, tenantLabel, tenantSlug]);
|
||||
|
||||
useEffect(() => {
|
||||
void load();
|
||||
@@ -149,10 +158,11 @@ export default function BrandZone({ tenantLabel }: { tenantLabel: string }) {
|
||||
setErr(null);
|
||||
setOkMsg(null);
|
||||
try {
|
||||
const r = await fetch('/api/brandbook', {
|
||||
const query = tenantId ? `?tenant_id=${encodeURIComponent(tenantId)}` : '';
|
||||
const r = await fetch(`/api/brandbook${query}`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ brandbook: book }),
|
||||
body: JSON.stringify({ brandbook: book, tenant_id: tenantId }),
|
||||
});
|
||||
const j = await r.json();
|
||||
if (!r.ok) throw new Error(j.error || 'No se pudo guardar');
|
||||
@@ -160,7 +170,7 @@ export default function BrandZone({ tenantLabel }: { tenantLabel: string }) {
|
||||
setScore(j.completeness?.score ?? 0);
|
||||
setMissing(j.completeness?.missing || []);
|
||||
setExists(true);
|
||||
setOkMsg('Brand kit guardado');
|
||||
setOkMsg('Brand kit guardado correctamente y sincronizado');
|
||||
} catch (e) {
|
||||
setErr(e instanceof Error ? e.message : 'Error al guardar');
|
||||
} finally {
|
||||
@@ -172,10 +182,11 @@ export default function BrandZone({ tenantLabel }: { tenantLabel: string }) {
|
||||
setSeeding(true);
|
||||
setErr(null);
|
||||
try {
|
||||
const r = await fetch('/api/brandbook', {
|
||||
const query = tenantId ? `?tenant_id=${encodeURIComponent(tenantId)}` : '';
|
||||
const r = await fetch(`/api/brandbook${query}`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ seed: true }),
|
||||
body: JSON.stringify({ seed: true, tenant_id: tenantId }),
|
||||
});
|
||||
const j = await r.json();
|
||||
if (!r.ok) throw new Error(j.error || 'No se pudo sembrar');
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user