Replace BPS Excel stub with multi-client calendar template.

Official download now generates a blank workbook with catalog autofill, notas_internas ignored on import, and the same template for every tenant.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
4Ventures
2026-10-08 17:38:38 +02:00
parent 025fc85175
commit 501f8203e8
8 changed files with 2999 additions and 34 deletions

View File

@@ -0,0 +1,55 @@
import { NextRequest, NextResponse } from 'next/server';
import { createClient } from '@/lib/supabase/server';
import { resolveActiveTenant } from '@/lib/tenant-context';
import { isAgencyRole, userRole } from '@/lib/agency';
import {
buildOfficialCsv,
buildOfficialXlsx,
} from '@/lib/calendar-template';
export const dynamic = 'force-dynamic';
export const runtime = 'nodejs';
/**
* GET /api/calendar/template?format=xlsx|csv
* Official blank multi-client template (NOT a filled BPS calendar).
*/
export async function GET(req: NextRequest) {
const supabase = await createClient();
const {
data: { user },
} = await supabase.auth.getUser();
if (!user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
if (!isAgencyRole(userRole(user))) {
return NextResponse.json({ error: 'Solo agencia' }, { status: 403 });
}
const ctx = await resolveActiveTenant(user);
const label =
ctx.tenantId && !ctx.portfolioWide ? ctx.label || 'CLIENTE' : 'CLIENTE';
const format = (req.nextUrl.searchParams.get('format') || 'xlsx').toLowerCase();
if (format === 'csv') {
const csv = buildOfficialCsv(label);
return new NextResponse(csv, {
headers: {
'Content-Type': 'text/csv; charset=utf-8',
'Content-Disposition':
'attachment; filename="plantilla-calendario-4ventures.csv"',
'Cache-Control': 'no-store',
},
});
}
const { buffer, filename } = await buildOfficialXlsx(label);
return new NextResponse(new Uint8Array(buffer), {
headers: {
'Content-Type':
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'Content-Disposition': `attachment; filename="${filename}"`,
'Cache-Control': 'no-store',
},
});
}

View File

@@ -9,11 +9,11 @@ import {
} from '@/lib/agency';
import {
parseJsonCalendar,
SAMPLE_CSV,
sheetsToPlans,
type ImportResult,
type SheetRows,
} from '@/lib/calendar-import';
import { buildOfficialCsv } from '@/lib/calendar-template';
import type { RrssMonthPlan } from '@/components/RrssMonthView';
export const dynamic = 'force-dynamic';
@@ -21,12 +21,14 @@ export const runtime = 'nodejs';
const MAX_BYTES = 8 * 1024 * 1024;
/** @deprecated Prefer GET /api/calendar/template?format=csv */
export async function GET() {
return new NextResponse(SAMPLE_CSV, {
return new NextResponse(buildOfficialCsv('CLIENTE'), {
headers: {
'Content-Type': 'text/csv; charset=utf-8',
'Content-Disposition':
'attachment; filename="plantilla-calendario-4ventures.csv"',
'Cache-Control': 'no-store',
},
});
}