Calendario: si vedono tutti gli orari validi, e il PDF ne fa una pagina ciascuno

Il popup mostrava solo l'orario del momento: chi guarda ad agosto vuole sapere
com'e' settembre per intero, e una griglia sola gli fa credere che valga per
sempre. Ora ci sono tutti quelli non ancora scaduti, ognuno con la sua etichetta
- In vigore, oppure la data da cui parte - e quelli passati spariscono da soli.

Il bottone scarica /calendario/tutti.pdf: un orario per pagina, con le giornate
fuori griglia ripetute su ognuna, perche' un foglio stampato da solo deve bastare
a se stesso. Gli id singoli restano validi per mandarne uno solo.

calendarioDaMostrare (uno) diventa calendariDaMostrare (tutti): una funzione
sola, non due che dicono cose simili - e' lo stesso difetto dei due link della
pagina servizi, che erano due espressioni per lo stesso indirizzo e sono
divergute.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-17 17:22:22 +02:00
parent d03c62641e
commit 69bd17a296
7 changed files with 207 additions and 147 deletions
+17 -9
View File
@@ -1,29 +1,37 @@
import type { APIRoute } from 'astro';
import { avvisiAttivi, calendari } from '../../data/calendari';
import { generaCalendarioPdf, nomeFilePdf } from '../../lib/calendario-pdf';
import { avvisiAttivi, calendari, calendariDaMostrare } from '../../data/calendari';
import { generaCalendariPdf, nomeFilePdf } from '../../lib/calendario-pdf';
export const prerender = false;
// Il PDF del calendario, come file scaricabile: `/calendario/<id>.pdf`.
// Il PDF degli orari, come file scaricabile.
//
// /calendario/tutti.pdf tutti gli orari validi, uno per pagina
// /calendario/settembre-1-18.pdf un orario solo
//
// Pubblico come il resto del sito — è lo stesso orario che chiunque legge nel popup, e un file
// dietro autenticazione non si potrebbe girare su WhatsApp, che è come questo centro
// comunica coi clienti.
// dietro autenticazione non si potrebbe girare su WhatsApp, che è come questo centro comunica
// coi clienti.
export const GET: APIRoute = async ({ params }) => {
const cal = calendari.find((c) => c.id === params.id);
// "tutti" segue la stessa regola della pagina: gli orari scaduti non ci sono. Un PDF che si
// porta dietro il periodo finito farebbe cercare lezioni che non esistono più.
const cals = params.id === 'tutti'
? calendariDaMostrare(new Date()).map((m) => m.cal)
: calendari.filter((c) => c.id === params.id);
// Un id sconosciuto è 404, non un PDF vuoto: un file che si apre e non contiene niente fa
// credere che il calendario sia vuoto.
if (!cal) return new Response('Calendario inesistente.', { status: 404 });
if (cals.length === 0) return new Response('Calendario inesistente.', { status: 404 });
// Le giornate fuori griglia stanno anche nel PDF: è il file che gira su WhatsApp, e chi lo
// riceve non ha davanti la pagina dove sono scritte.
const pdf = await generaCalendarioPdf(cal, avvisiAttivi(new Date()));
const pdf = await generaCalendariPdf(cals, avvisiAttivi(new Date()));
return new Response(new Uint8Array(pdf), {
headers: {
'Content-Type': 'application/pdf',
// `attachment` perché il bottone dice "scarica": senza, il browser lo aprirebbe in una
// scheda e su telefono resterebbe lì, senza finire fra i file.
'Content-Disposition': `attachment; filename="${nomeFilePdf(cal)}"`,
'Content-Disposition': `attachment; filename="${nomeFilePdf(cals)}"`,
'Content-Length': String(pdf.length),
// Un'ora: basta a non rigenerarlo a ogni click, e non abbastanza perché un avviso ormai
// scaduto (le giornate fuori griglia spariscono da sole) resti in giro un giorno intero.
+2 -2
View File
@@ -8,7 +8,7 @@ import { serviceIcons } from '../../data/service-icons';
import T from '../../components/content/T.astro';
import TImg from '../../components/content/TImg.astro';
import CalendarioModal from '../../components/CalendarioModal.astro';
import { calendarioDaMostrare, SERVIZI_CON_CALENDARIO } from '../../data/calendari';
import { calendariDaMostrare, SERVIZI_CON_CALENDARIO } from '../../data/calendari';
import { t } from '../../lib/content';
export const prerender = false;
@@ -18,7 +18,7 @@ const slug = service.slug;
// Il calendario della sala si apre dalle pagine delle class. La voce compare solo se c'è
// davvero una griglia da mostrare (in vigore o prossima): un link che apre un popup vuoto è
// peggio di nessun link.
const conCalendario = SERVIZI_CON_CALENDARIO.includes(slug) && calendarioDaMostrare(new Date()) !== null;
const conCalendario = SERVIZI_CON_CALENDARIO.includes(slug) && calendariDaMostrare(new Date()).length > 0;
const others = services.filter((p) => p.slug !== service.slug && p.grid !== false);
const images = import.meta.glob<{ default: ImageMetadata }>('../../assets/img/program-*.jpg', { eager: true });
const imgOf = (slug: string) => Object.entries(images).find(([p]) => p.includes(`program-${slug}.`))?.[1]?.default;