Struttura: il sito esistente si sposta sotto apps/sito
Primo passo della separazione sito / piattaforme decisa da Adriano il 03/09: due applicazioni autosufficienti, senza workspace di root, cosi' che ognuna resti un progetto Astro completo ed estraibile. Qui non cambia niente del codice: solo la radice. I pattern del .gitignore che avevano una barra erano ancorati alla radice e dopo lo spostamento non avrebbero piu' coperto niente (data/*.db*, uploads/*): resi validi a qualunque profondita'. Suite: 45 file, 347 test verdi prima e dopo lo spostamento. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EndnceRA5WnA6rvA5iV9WL
This commit is contained in:
@@ -1,195 +0,0 @@
|
||||
// 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' },
|
||||
// ⚠️ Il VENERDI' non ha nessuna class in questo periodo, ed e' voluto: le tre che
|
||||
// c'erano (MOBILITY 8.30, REFORMER 9.15 e 10.00) sono state tolte su richiesta del
|
||||
// cliente l'01/09/2026, ed erano tutte di mattina. Non e' una trascrizione monca:
|
||||
// confrontando questa griglia con quella dal 21 settembre — dove il venerdi' c'e' —
|
||||
// la differenza salta all'occhio e sembra un errore. Non lo e'.
|
||||
// La colonna resta VUOTA e non barrata: togliere le class non vuol dire che la sala
|
||||
// sia chiusa, e nessuno ce l'ha detto (scelta di Adriano, 01/09).
|
||||
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' } },
|
||||
{ ora: '9.15', celle: { SAB: 'MOBILITY' } },
|
||||
{ ora: '10.00', celle: { 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' }));
|
||||
}
|
||||
Reference in New Issue
Block a user