From ead310e783eb733bcdea696369a93e02bc2a9263 Mon Sep 17 00:00:00 2001 From: 4Ventures <4ventures@MacBook-Air-de-4Ventures.local> Date: Tue, 6 Oct 2026 12:25:47 +0200 Subject: [PATCH] Keep editorial calendars private to each tenant. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stop seeding Agua Miia’s October plan for every client and restrict the portal publish action to that tenant only. Co-authored-by: Cursor --- src/app/api/calendar/publish/route.ts | 14 ++++++ .../calendario/PublishRrssButton.tsx | 7 ++- src/app/dashboard/calendario/page.tsx | 43 +++++++++++++++---- 3 files changed, 55 insertions(+), 9 deletions(-) diff --git a/src/app/api/calendar/publish/route.ts b/src/app/api/calendar/publish/route.ts index e8afe47..9aeea94 100644 --- a/src/app/api/calendar/publish/route.ts +++ b/src/app/api/calendar/publish/route.ts @@ -30,6 +30,20 @@ export async function POST() { ); } + const t = await restGet<{ slug?: string; name?: string }[]>( + `/rest/v1/tenants?id=eq.${encodeURIComponent(ctx.tenantId)}&select=slug,name` + ); + const slug = t.data?.[0]?.slug || ''; + if (slug !== 'agua-miia') { + return NextResponse.json( + { + error: + 'Este botón solo carga el plan de octubre de Agua Miia en su propio portal. No publica en Instagram ni copia el calendario a otros clientes.', + }, + { status: 400 } + ); + } + const headers = serviceHeaders(); if (!headers) { return NextResponse.json({ error: 'no service role' }, { status: 500 }); diff --git a/src/app/dashboard/calendario/PublishRrssButton.tsx b/src/app/dashboard/calendario/PublishRrssButton.tsx index 3d0bfe3..b515850 100644 --- a/src/app/dashboard/calendario/PublishRrssButton.tsx +++ b/src/app/dashboard/calendario/PublishRrssButton.tsx @@ -34,8 +34,13 @@ export default function PublishRrssButton() { className="inline-flex items-center justify-center min-h-[52px] px-6 rounded-xl border border-emerald-500/40 bg-emerald-500/15 text-emerald-300 text-sm font-bold hover:bg-emerald-500/25 disabled:opacity-50" > - {busy ? 'Publicando…' : 'Publicar mes en portal'} + {busy ? 'Publicando…' : 'Dejar octubre visible para el cliente'} +

+ No publica en Instagram ni Facebook. Solo guarda el plan de este mes en + el portal de Agua Miia para que el cliente lo vea en + Calendario. +

{msg ?

{msg}

: null} ); diff --git a/src/app/dashboard/calendario/page.tsx b/src/app/dashboard/calendario/page.tsx index 2ebb243..5446151 100644 --- a/src/app/dashboard/calendario/page.tsx +++ b/src/app/dashboard/calendario/page.tsx @@ -51,6 +51,15 @@ export default async function CalendarioPage({ ); } + let tenantSlug: string | null = null; + if (ctx.tenantId) { + const t = await restGet<{ slug?: string }[]>( + `/rest/v1/tenants?id=eq.${encodeURIComponent(ctx.tenantId)}&select=slug` + ); + tenantSlug = t.data?.[0]?.slug || null; + } + const isAguaMiia = tenantSlug === 'agua-miia'; + let calendarDocs: Array<{ title?: string; content?: unknown }> = []; if (agency && ctx.viaView && ctx.tenantId) { const tid = encodeURIComponent(ctx.tenantId); @@ -68,7 +77,6 @@ export default async function CalendarioPage({ calendarDocs = data || []; } - // Prefer structured month plans; fallback to bundled Octubre Miia seed const plans: RrssMonthPlan[] = []; for (const doc of calendarDocs) { const raw = @@ -82,12 +90,18 @@ export default async function CalendarioPage({ })() : doc.content; const plan = asPlan(raw); - if (plan) plans.push(plan); + if (!plan) continue; + const blob = JSON.stringify(plan).toLowerCase(); + if (!isAguaMiia && blob.includes('agua miia')) continue; + plans.push(plan); } - if (!plans.length) { + if (!plans.length && isAguaMiia) { plans.push(octPlan as unknown as RrssMonthPlan); } + const canPublishMiia = agency && isAguaMiia; + const publishBtn = canPublishMiia ? : null; + const monthParam = sp.month || plans[0]?.month; const active = plans.find((p) => p.month === monthParam) || plans[0]; // Portada = archivo anual estilo /rrss/ig-fb/ @@ -111,8 +125,8 @@ export default async function CalendarioPage({ {ctx.label}

- Calendario Instagram/Facebook y estrategia LinkedIn. Sin publicación - pública: solo portal + doble check agencia/cliente. + Calendario editorial de {ctx.label}. No se publica en redes: solo el + cliente (y la agencia en su contexto) lo ve aquí.

@@ -128,7 +142,7 @@ export default async function CalendarioPage({ > LinkedIn - {agency ? : null} + {publishBtn}
); @@ -142,7 +156,7 @@ export default async function CalendarioPage({ year={year} plans={plans} tenantLabel={ctx.label} - agencyPublish={agency ? : null} + agencyPublish={publishBtn} /> ); } @@ -159,13 +173,26 @@ export default async function CalendarioPage({ ); } - // Legacy flat posts fallback const legacy = asLegacyPosts( typeof calendarDocs[0]?.content === 'string' ? JSON.parse(calendarDocs[0].content as string) : calendarDocs[0]?.content ); + if (!legacy.length) { + return ( +
+

+ Sin calendario editorial +

+

+ {ctx.label} no tiene un plan RRSS en el portal. Cada cliente solo ve + el suyo; no se muestra el de otros tenants. +

+
+ ); + } + return (