feat: export PDF del piano settimanale
GET /me/meal-plans/export.pdf restituisce un PDF stampabile del piano ACTIVE: 7 giorni × pasti con macros giornalieri + per-pasto + tempo preparazione, gestione free-meal e giorni di digiuno completo, ultima pagina con lista spesa aggregata per reparto. - apps/api/.../plan/export.routes.ts: pdfkit, layout A4 con paginazione automatica e footer disclaimer su tutte le pagine - apps/web/.../api/plan-export: proxy Next.js per same-origin + cookie - apps/web/.../plan/page.tsx: aggiunto link "Esporta PDF ↗" accanto a "Lista della spesa" nell'header del piano Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
83bab7b6d5
commit
e015abff33
5 files changed
+282
-6
No files matched your search
@@ -90,12 +90,22 @@ function PlanPageContent({ plan }: { plan: Awaited<ReturnType<typeof fetchCurren
|
||||
</Button>
|
||||
</form>
|
||||
{plan ? (
|
||||
<Link
|
||||
href="/shopping"
|
||||
className="text-ink-soft decoration-pomodoro hover:text-ink font-mono text-[11px] uppercase tracking-widest underline decoration-[1.5px] underline-offset-[5px] transition-colors"
|
||||
>
|
||||
Lista della spesa →
|
||||
</Link>
|
||||
<>
|
||||
<Link
|
||||
href="/shopping"
|
||||
className="text-ink-soft decoration-pomodoro hover:text-ink font-mono text-[11px] uppercase tracking-widest underline decoration-[1.5px] underline-offset-[5px] transition-colors"
|
||||
>
|
||||
{t('linkShopping')} →
|
||||
</Link>
|
||||
<a
|
||||
href="/api/plan-export"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-ink-soft decoration-pomodoro hover:text-ink font-mono text-[11px] uppercase tracking-widest underline decoration-[1.5px] underline-offset-[5px] transition-colors"
|
||||
>
|
||||
{t('exportPdf')} ↗
|
||||
</a>
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
// Proxy verso l'API: scarica il PDF del piano settimanale.
|
||||
|
||||
import { headers } from 'next/headers';
|
||||
import { NextResponse } from 'next/server';
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
const API_URL = process.env.API_URL ?? 'http://localhost:4000';
|
||||
|
||||
export async function GET(): Promise<Response> {
|
||||
const cookie = headers().get('cookie') ?? '';
|
||||
const upstream = await fetch(`${API_URL}/me/meal-plans/export.pdf`, {
|
||||
headers: { cookie },
|
||||
cache: 'no-store',
|
||||
});
|
||||
if (!upstream.ok) {
|
||||
return NextResponse.json(
|
||||
{ error: `api_error_${upstream.status}` },
|
||||
{ status: upstream.status },
|
||||
);
|
||||
}
|
||||
const buffer = await upstream.arrayBuffer();
|
||||
return new NextResponse(buffer, {
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'application/pdf',
|
||||
'Content-Disposition':
|
||||
upstream.headers.get('content-disposition') ?? 'attachment; filename="ketopath-piano.pdf"',
|
||||
'Cache-Control': 'no-store',
|
||||
},
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user