3741ce34ac
Il documento MODIFICHE_PER_SITO.pdf porta cinque cose.
1. "Small Group" si chiama "Personalized Training Group". Il nome vive anche
nel database dei contenuti, dove `resolveContent` legge prima della copia
nel codice: `scripts/testi-17-agosto.mjs` aggiorna le 19 righe rimaste,
una volta sola, correggendo anche gli articoli ("Lo Small Group" -> "Il
Personalized Training Group"). Lo slug della scheda resta `small-group`:
cambiarlo romperebbe i link gia' girati su WhatsApp.
2. Dalla pagina Servizi le card instradano dove serve. "One to One" apre la
vista Training con One to One, Personalized Training Group e Rehab;
"Class" quella con Holistic e Reformer. Le viste sono `/training/<vista>`;
una vista inventata e' 404, non l'elenco intero, altrimenti un indirizzo
rotto continuerebbe a sembrare buono.
3. La Performance Class non si eroga piu': la scheda che la spiegava
risponde 404, la card "Class" resta in griglia. Con la scheda e' caduta la
tabella prezzi, che era solo sua.
4. Il Check-up racconta Base e Avanzato: trafiletto nuovo nella card e
sezione "Check-up Avanzato" nella scheda, con la Wellness Tower e i cinque
punti. Il calendario delle class si apre ora anche dalla card Class in
griglia e dalla scheda Reformer, oltre che da Holistic.
5. "Promo ed eventi" diventa una pagina di scelta: le promozioni restano su
/promo-eventi/promo, l'inaugurazione del 19 settembre sta su
/promo-eventi/eventi. Il testo dice che il programma della giornata manca
ancora, ed e' editabile dal pannello.
Corretti due link del footer che portavano a schede inesistenti
(`/services/personal-training` non e' mai stato uno slug).
181 test verdi (157 + 24 nuovi), build ok, `astro check` invariato.
189 lines
8.1 KiB
TypeScript
189 lines
8.1 KiB
TypeScript
// Calendari delle class in Sala Piccola.
|
|
//
|
|
// Trascritti dai due fogli forniti dal cliente il 10/08/2026 (immagini su Wasabi, cartella
|
|
// del progetto). Sono dato e non immagini di proposito: così la griglia si legge da telefono,
|
|
// si stampa in PDF e si corregge una cella senza rifare uno screenshot.
|
|
//
|
|
// Ogni calendario porta la sua finestra di validità: la pagina mostra quello in vigore, e
|
|
// quando nessuno lo è ancora mostra il prossimo dichiarando da quando entra. Il passaggio da
|
|
// un orario all'altro avviene da solo, senza deploy — stesso principio delle edizioni della
|
|
// pagina promo (`data/promo-edizioni.ts`).
|
|
|
|
export const GIORNI = ['LUN', 'MAR', 'MER', 'GIO', 'VEN', 'SAB', 'DOM'] as const;
|
|
export type Giorno = (typeof GIORNI)[number];
|
|
|
|
/** Le discipline della sala: il colore di ognuna è definito nel componente del calendario. */
|
|
export const DISCIPLINE = ['REFORMER', 'PILATES', 'YOGA', 'MOBILITY'] as const;
|
|
export type Disciplina = (typeof DISCIPLINE)[number];
|
|
|
|
export interface RigaCalendario {
|
|
ora: string;
|
|
/** Fascia messa in risalto nel foglio del cliente (le righe gialle). */
|
|
clou?: boolean;
|
|
celle: Partial<Record<Giorno, Disciplina>>;
|
|
}
|
|
|
|
export interface Calendario {
|
|
id: string;
|
|
/** Come lo chiama il cliente: è anche l'etichetta mostrata in pagina. */
|
|
titolo: string;
|
|
sala: string;
|
|
/** Validità, ISO con offset esplicito (il server gira in UTC). `to: null` = a tempo indeterminato. */
|
|
from: string;
|
|
to: string | null;
|
|
/** Giorni in cui la sala è chiusa: colonna barrata, non vuota — vuoto e chiuso non sono lo stesso. */
|
|
chiusi: Giorno[];
|
|
/** Precisazione su una colonna, es. il sabato aperto una volta sola. */
|
|
noteGiorno?: Partial<Record<Giorno, string>>;
|
|
righe: RigaCalendario[];
|
|
}
|
|
|
|
export const calendari: Calendario[] = [
|
|
{
|
|
id: 'settembre-1-18',
|
|
titolo: "Dall'1 al 18 settembre",
|
|
sala: 'Sala piccola',
|
|
from: '2026-09-01T00:00:00+02:00',
|
|
to: '2026-09-18T23:59:59+02:00',
|
|
chiusi: ['DOM'],
|
|
noteGiorno: { SAB: 'solo il 12 settembre' },
|
|
righe: [
|
|
{ ora: '6.15', celle: {} },
|
|
{ ora: '7.00', clou: true, celle: { MAR: 'YOGA' } },
|
|
{ ora: '7.45', clou: true, celle: { MAR: 'PILATES', MER: 'REFORMER' } },
|
|
{ ora: '8.30', celle: { MER: 'REFORMER', VEN: 'MOBILITY' } },
|
|
{ ora: '9.15', celle: { VEN: 'REFORMER', SAB: 'MOBILITY' } },
|
|
{ ora: '10.00', celle: { VEN: 'REFORMER', SAB: 'PILATES' } },
|
|
{ ora: '10.45', celle: { SAB: 'REFORMER' } },
|
|
{ ora: '11.30', celle: { SAB: 'REFORMER' } },
|
|
{ ora: '12.15', clou: true, celle: { MAR: 'REFORMER', MER: 'REFORMER', GIO: 'REFORMER' } },
|
|
{ ora: '13.00', clou: true, celle: { MAR: 'REFORMER', MER: 'PILATES', GIO: 'REFORMER' } },
|
|
{ ora: '13.45', celle: {} },
|
|
{ ora: '14.30', celle: { MAR: 'REFORMER' } },
|
|
{ ora: '15.15', celle: { MAR: 'MOBILITY' } },
|
|
{ ora: '16.00', celle: {} },
|
|
{ ora: '16.45', celle: { MAR: 'REFORMER' } },
|
|
{ ora: '17.30', clou: true, celle: { MAR: 'REFORMER', GIO: 'REFORMER' } },
|
|
{ ora: '18.15', clou: true, celle: { MAR: 'REFORMER', GIO: 'REFORMER' } },
|
|
{ ora: '19.00', clou: true, celle: { MAR: 'REFORMER', GIO: 'REFORMER' } },
|
|
{ ora: '19.45', clou: true, celle: { MAR: 'REFORMER', GIO: 'PILATES' } },
|
|
],
|
|
},
|
|
{
|
|
id: 'settembre-21-in-poi',
|
|
titolo: 'Dal 21 settembre in poi',
|
|
sala: 'Sala piccola',
|
|
from: '2026-09-21T00:00:00+02:00',
|
|
to: null,
|
|
chiusi: ['DOM'],
|
|
righe: [
|
|
{ ora: '6.15', celle: { MER: 'MOBILITY' } },
|
|
{ ora: '7.00', clou: true, celle: { MAR: 'YOGA', MER: 'REFORMER' } },
|
|
{ ora: '7.45', clou: true, celle: { MAR: 'PILATES', MER: 'REFORMER', VEN: 'PILATES' } },
|
|
{ ora: '8.30', celle: { MAR: 'REFORMER', MER: 'REFORMER', VEN: 'MOBILITY' } },
|
|
{ ora: '9.15', celle: { VEN: 'REFORMER', SAB: 'MOBILITY' } },
|
|
{ ora: '10.00', celle: { VEN: 'REFORMER', SAB: 'PILATES' } },
|
|
{ ora: '10.45', celle: { SAB: 'REFORMER' } },
|
|
{ ora: '11.30', celle: { GIO: 'YOGA', SAB: 'REFORMER' } },
|
|
{ ora: '12.15', clou: true, celle: { MAR: 'REFORMER', MER: 'REFORMER', GIO: 'REFORMER' } },
|
|
{ ora: '13.00', clou: true, celle: { MAR: 'REFORMER', MER: 'PILATES', GIO: 'REFORMER' } },
|
|
{ ora: '13.45', celle: {} },
|
|
{ ora: '14.30', celle: { MAR: 'REFORMER' } },
|
|
{ ora: '15.15', celle: { MAR: 'MOBILITY' } },
|
|
{ ora: '16.00', celle: {} },
|
|
{ ora: '16.45', celle: { MAR: 'REFORMER' } },
|
|
// Mercoledì 17.30 e 18.15 sono vuote: nel foglio ricevuto le due celle erano coperte
|
|
// dal riquadro "Opzioni Incolla" di Excel, e il 17/08/2026 Adriano ha confermato che
|
|
// lì non c'è niente. Il vuoto qui è una risposta, non una lettura mancata.
|
|
{ ora: '17.30', clou: true, celle: { LUN: 'PILATES', MAR: 'REFORMER', GIO: 'REFORMER' } },
|
|
{ ora: '18.15', clou: true, celle: { LUN: 'REFORMER', MAR: 'REFORMER', GIO: 'REFORMER' } },
|
|
{ ora: '19.00', clou: true, celle: { LUN: 'REFORMER', MAR: 'REFORMER', GIO: 'REFORMER' } },
|
|
{ ora: '19.45', clou: true, celle: { LUN: 'REFORMER', MAR: 'REFORMER', GIO: 'PILATES' } },
|
|
],
|
|
},
|
|
];
|
|
|
|
/**
|
|
* Giornate che non stanno in nessuna griglia: eventi e chiusure.
|
|
*
|
|
* Il 19 e il 20 settembre non compaiono in nessuno dei due fogli, e la ragione non è una
|
|
* dimenticanza — il sabato c'è la presentazione della nuova apertura e la domenica si sta
|
|
* chiusi (detto da Adriano il 17/08/2026). Scritto qui, quel vuoto smette di sembrare un
|
|
* errore di trascrizione e diventa un'informazione per chi legge gli orari.
|
|
*/
|
|
export interface AvvisoCalendario {
|
|
id: string;
|
|
/** `null` = visibile da subito. */
|
|
from: string | null;
|
|
/** Ultimo istante in cui ha senso mostrarlo: dopo, sparisce da solo. */
|
|
to: string;
|
|
quando: string;
|
|
testo: string;
|
|
}
|
|
|
|
export const avvisi: AvvisoCalendario[] = [
|
|
{
|
|
id: 'presentazione-19-settembre',
|
|
from: null,
|
|
to: '2026-09-19T23:59:59+02:00',
|
|
quando: 'Sabato 19 settembre',
|
|
testo: 'Presentazione della nuova apertura: nessuna class in programma.',
|
|
},
|
|
{
|
|
id: 'chiusura-20-settembre',
|
|
from: null,
|
|
to: '2026-09-20T23:59:59+02:00',
|
|
quando: 'Domenica 20 settembre',
|
|
testo: 'Chiuso.',
|
|
},
|
|
];
|
|
|
|
/** Gli avvisi ancora validi, in ordine di scadenza: i più imminenti per primi. */
|
|
export function avvisiAttivi(now: Date, list: AvvisoCalendario[] = avvisi): AvvisoCalendario[] {
|
|
const t = now.getTime();
|
|
return list
|
|
.filter((a) => (a.from === null || t >= Date.parse(a.from)) && t <= Date.parse(a.to))
|
|
.sort((x, y) => Date.parse(x.to) - Date.parse(y.to));
|
|
}
|
|
|
|
/**
|
|
* Le schede servizio da cui si apre il calendario della sala.
|
|
*
|
|
* Sono le due class che si fanno davvero: la Performance Class è uscita di scena il
|
|
* 17/08/2026 (il cliente non la eroga più) e con lei la sua scheda.
|
|
*/
|
|
export const PAGINE_CON_CALENDARIO = ['holistic-class', 'reformer-class'];
|
|
|
|
/**
|
|
* Le card della griglia /services che portano il bottone del calendario.
|
|
*
|
|
* È un elenco a parte, e non un doppione del precedente: nella griglia la card si chiama
|
|
* «Class» ma ha slug `performance-class`, e da lì si va alla vista Training con Holistic e
|
|
* Reformer. Il calendario è lo stesso, il punto da cui si apre no.
|
|
*/
|
|
export const CARD_CON_CALENDARIO = ['performance-class'];
|
|
|
|
export interface CalendarioMostrato {
|
|
cal: Calendario;
|
|
stato: 'in-corso' | 'futuro';
|
|
}
|
|
|
|
/**
|
|
* I calendari da mostrare: quello in vigore e quelli che verranno, in ordine di partenza.
|
|
*
|
|
* Si vedono **tutti insieme**, non solo quello del momento: chi guarda a fine agosto vuole
|
|
* sapere com'è settembre per intero, e vedere una griglia sola gli farebbe credere che sia
|
|
* quella per sempre. Ognuna porta il proprio stato, così l'orario in vigore adesso non si
|
|
* confonde con quello che comincerà dopo.
|
|
*
|
|
* Gli orari **scaduti spariscono da soli**: dal 21 settembre resta il solo foglio valido, e
|
|
* nessuno rischia di leggere le lezioni di un periodo finito.
|
|
*/
|
|
export function calendariDaMostrare(now: Date, list: Calendario[] = calendari): CalendarioMostrato[] {
|
|
const t = now.getTime();
|
|
return list
|
|
.filter((c) => c.to === null || t <= Date.parse(c.to))
|
|
.sort((a, b) => Date.parse(a.from) - Date.parse(b.from))
|
|
.map((cal) => ({ cal, stato: t >= Date.parse(cal.from) ? 'in-corso' : 'futuro' }));
|
|
}
|