/** * Rename portfolio brand slugs to canonical codes: * talento-y-deporte → tyd * talento-scholar-program → tsp * de-tu-a-tu-formacion → dtatf * ecp → ecp (unchanged) * * npx tsx scripts/rename_brand_slugs.ts */ import { config } from 'dotenv'; import { resolve } from 'path'; config({ path: resolve(process.cwd(), '.env.local') }); import { enrichTenantBrandbook } from '../src/lib/brandbook-enrich'; import { ensureCreativesDriveFolders } from '../src/lib/creatives-drive'; import { BRAND_DISPLAY_NAMES, brandSiblingNote, } from '../src/lib/tenant-brand-aliases'; const url = process.env.NEXT_PUBLIC_SUPABASE_URL!; const key = process.env.SUPABASE_SERVICE_ROLE_KEY!; const h = { apikey: key, Authorization: `Bearer ${key}`, 'Content-Type': 'application/json', Prefer: 'return=representation', }; const RENAMES: { from: string; to: keyof typeof BRAND_DISPLAY_NAMES }[] = [ { from: 'talento-y-deporte', to: 'tyd' }, { from: 'talento-scholar-program', to: 'tsp' }, { from: 'de-tu-a-tu-formacion', to: 'dtatf' }, ]; async function rest(path: string, init?: RequestInit): Promise { const r = await fetch(`${url}/rest/v1/${path}`, { ...init, headers: { ...h, ...(init?.headers || {}) }, }); const text = await r.text(); if (!r.ok) throw new Error(`${path} ${r.status}: ${text.slice(0, 400)}`); return text ? (JSON.parse(text) as T) : ([] as T); } async function main() { const today = new Date().toISOString().slice(0, 10); const tenants = await rest<{ id: string; slug: string; name: string; agency_notes?: string }[]>( 'tenants?select=id,slug,name,agency_notes' ); const bySlug = Object.fromEntries(tenants.map((t) => [t.slug, t])); for (const { from, to } of RENAMES) { const existingTo = bySlug[to]; const fromRow = bySlug[from]; if (existingTo && !fromRow) { console.log(`[${to}] already canonical`); continue; } if (!fromRow) { console.log(`[skip] ${from} not found`); continue; } if (existingTo && existingTo.id !== fromRow.id) { throw new Error(`Conflict: both ${from} and ${to} exist`); } const sib = brandSiblingNote(to); const notes = [ `[${today}] Slug canónico: ${to} · ${BRAND_DISPLAY_NAMES[to]}.`, sib, fromRow.agency_notes || '', ] .filter(Boolean) .join('\n') .slice(0, 4000); await rest(`tenants?id=eq.${fromRow.id}`, { method: 'PATCH', headers: { Prefer: 'return=minimal' }, body: JSON.stringify({ slug: to, name: BRAND_DISPLAY_NAMES[to], agency_followed: true, agency_notes: notes, }), }); console.log(`[rename] ${from} → ${to}`); bySlug[to] = { ...fromRow, slug: to, name: BRAND_DISPLAY_NAMES[to] }; delete bySlug[from]; } // Ensure ECP display name const ecp = bySlug['ecp']; if (ecp) { await rest(`tenants?id=eq.${ecp.id}`, { method: 'PATCH', headers: { Prefer: 'return=minimal' }, body: JSON.stringify({ name: BRAND_DISPLAY_NAMES.ecp, agency_followed: true, agency_notes: [ `[${today}] Marca canónica ECP (separada de DTATF).`, brandSiblingNote('ecp'), ecp.agency_notes || '', ] .filter(Boolean) .join('\n') .slice(0, 4000), }), }); console.log('[ecp] display name refreshed'); } for (const slug of ['tyd', 'tsp', 'ecp', 'dtatf'] as const) { const t = bySlug[slug] || (await rest<{ id: string; slug: string }[]>( `tenants?slug=eq.${slug}&select=id,slug&limit=1` ))[0]; if (!t) { console.log(`[missing] ${slug}`); continue; } const r = await enrichTenantBrandbook({ tenantId: t.id, forceSeed: true }); const drive = await ensureCreativesDriveFolders(t.id, slug); console.log( `[enrich] ${slug} ${r.completenessBefore}→${r.completenessAfter} drive=${drive.ok ? 'ok' : drive.warning}` ); } console.log('Done'); } main().catch((e) => { console.error(e); process.exit(1); });