Implementa il modulo che unisce i due database (identity e longevity)
rispettando la separazione richiesta dal cliente: pseudonimizzazione
dei dati clinici. Il modulo esporta tre funzioni:
- creaCliente: registra un soggetto con il suo codice progressivo ISL-NNNN
- codicePerUtente: risale dal user_id del sito al client_code
- etaAllaData: calcola l'eta' al momento della misura (non la data di nascita)
Un test verifica che nessun altro modulo apra entrambe le connessioni,
preservando il firewall fra dati clinici e identita'.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sette task in TDD: schema dei due database, registro test e pesi versionati,
registraMisure come unico varco, anagrafica che tiene separata identita' e
clinica, ruoli cliente/trainer, salvataggio del questionario, export.
Alla fine esiste una libreria testata e nessuna pagina: il motore di calcolo
e le schermate sono i due piani successivi.
Fascicolo unico del cliente dentro il sito esistente, ramo separato e nessun
deploy. Tre database: sito, identity (codice <-> nome) e longevity (misure,
mai un nome) - cosi' l'export per le statistiche e' gia' pseudonimizzato per
costruzione, come chiesto da Nicola il 21/08.
Le misure sono un registro (una riga per valore) e i test sono dato, non
codice: togliere l'agilita' o passare l'HRR da 2' a 1' non e' una migrazione.
I pesi stanno in tabella versionata, cosi' il congelamento degli score
storici ha davvero qualcosa dietro.
Punteggi congelati con due versioni distinte (domande e modello di calcolo).
Il tipo di ritorno del motore rende impossibile mostrare un numero pieno dove
la regola vuole il tratteggio.
Chiesto da Adriano il 19/08: era 0,82rem sotto un titolo che arriva a 2,6rem, e
sembrava una didascalia. Ora cresce con la finestra come il titolo, restando un
gradino sotto.
Chiesto da Adriano il 19/08. Sono due tag e non un testo con un a capo dentro:
la data si cambia da sola se l'evento si sposta, e un ritorno a capo dentro un
valore di testo non sopravvive all'editor del pannello.
Il titolo vive anche nel database: in produzione va aggiornato
eventi.inaugurazione.title a "Inaugurazione", altrimenti resta la riga unica.
Il tag nuovo (.date) non e' in tabella e viene dal seed.
La foto di Eva in pagina non veniva dal repo ma da un file caricato dal
pannello il 12/07 (tag team.eva.image-about): lo scatto giusto, ritagliato piu'
largo, ma con il logo ancora nell'angolo. Il file del repo era l'altro ritaglio,
piu' stretto, e non lo vedeva nessuno.
Ora team-eva-dark.jpg E' quella foto, col logo rimosso: maschera dei pixel del
logo allargata di 4 px per prendere i bordi sfumati, ricostruzione biharmonic
(scikit-image) e grana del tessuto ripresa dalla stoffa 80 px piu' in alto —
una toppa liscia si sarebbe notata.
Cosi' tutte le foto stanno in un posto solo e passano dall'ottimizzazione della
build (54 KB webp invece di 177 KB jpeg servito com'e'). In produzione va
svuotato il tag team.eva.image-about, altrimenti continua a vincere il file
caricato. Originale col logo su
wasabi://.../foto-team/team-eva-dark-ORIGINALE-col-logo.jpeg; il file su
uploads/ resta dov'e'.
Il ritaglio e' 864x1240 (0.697) invece di 1547x1920 (0.806): le card ritagliano
a 3:4, quindi qui si perde qualcosa sopra e sotto invece che ai lati. Verificato
sulla card renderizzata.
Adriano ha tolto un piccolo logo dall'angolo in basso a destra delle sette foto
scure della pagina About: alessandro, alice, donata, eleonora, eva, giulia,
nicola. Originali in wasabi://.../Sito_Web/foto-team-originali-20260819.zip.
Stesso formato di prima (JPEG 1547x1920, RGB): l'unica differenza e' un riquadro
di ~80x80 px nell'angolo, verificato confrontando le immagini. I file pesano il
doppio perche' risalvati a qualita' piu' alta, ma in pagina non cambia nulla: la
build li ricomprime in webp attorno agli 85 KB.
Nota: la foto di Eva in pagina NON e' questa, e' un ritaglio piu' largo dello
stesso scatto caricato dal pannello il 12/07 (tag team.eva.image-about), che il
logo ce l'ha ancora.
Segnalato da Adriano il 19/08. Il valore vive anche nel database dei contenuti,
dove viene letto per primo: il seed da solo non avrebbe cambiato la pagina, e
l'aggiornamento del tag about.hero.title va fatto in produzione.
Segnalato da Adriano il 19/08: "Check-up Avanzato" era appiccicato alla foto
sopra e si leggeva come una didascalia invece che come l'inizio di una parte
nuova.
Lo stacco e' un padding e non un margine: sopra c'e' gia' il margine
dell'immagine, e due margini verticali adiacenti collassano fra loro — con
margin-top lo spazio sarebbe rimasto quello di prima.
Chiesta da Adriano il 19/08. Restava in coda alle FAQ e ripeteva, in versione
piu' povera, cio' che la sezione Check-up Avanzato racconta per esteso.
In pagina resta una sola domanda. I due tag del pannello
(service.profilazione.faq.2.q e .a) non sono piu' letti da nessuno.
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.
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>
Quei due giorni non stanno in nessuna delle due griglie perche' non hanno class:
il 19 c'e' la presentazione della nuova apertura, il 20 si sta chiusi. Scritto
come avviso con la sua finestra, il vuoto smette di sembrare una trascrizione
incompleta e diventa un'informazione.
Gli avvisi compaiono in cima al popup e in fondo al PDF - e' il file che gira su
WhatsApp, e chi lo riceve non ha la pagina davanti. Spariscono da soli quando
sono passati: un avviso scaduto dice che il centro e' chiuso in un giorno gia'
trascorso, e chi legge non sa piu' se valga per quest'anno o per il prossimo.
Lo spazio riservato in fondo alla pagina cresce con gli avvisi, altrimenti la
griglia si allunga fino a spingerli su una seconda pagina.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Le due celle coperte dal riquadro di Excel nel foglio dal 21 settembre: Adriano
conferma che non c'e' nulla. Il dato non cambia, cambia il commento - quel vuoto
adesso e' una risposta, non una lettura mancata.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
"Scarica in PDF" non apre piu' la stampa del browser: e' un link a
/calendario/<id>.pdf che restituisce un documento disegnato dal dato con PDFKit,
con Content-Disposition attachment. Serve perche' il calendario si gira su
WhatsApp, ed e' cosi' che questo centro parla coi clienti: chi lo riceve non ha
il sito aperto davanti.
Niente browser headless: Chromium in un container da 2 vCPU costa centinaia di
megabyte e un processo per richiesta, per una griglia di sette colonne.
Le linee della griglia si tracciano in una passata a parte, dopo i riempimenti:
disegnandole riga per riga, il riempimento della riga seguente copriva quella
appena fatta e due lezioni uguali in fasce consecutive si leggevano come un
blocco unico lungo il doppio.
Quattro test: intestazione %PDF- e coda %%EOF (un documento non chiuso arriva
monco e il browser mostra una pagina bianca invece di un errore), una pagina
sola, nomi di file distinti. Un id sconosciuto e' 404, non un PDF vuoto.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Le due griglie fornite dal cliente (1-18 settembre, dal 21 in poi) sono
trascritte come dato e ridisegnate nello stile del sito: si leggono da telefono,
si stampano in PDF e per correggere una cella non serve rifare uno screenshot.
Quale griglia compare lo decide la data, come per le edizioni della promo: quella
in vigore, oppure - se nessuna lo e' ancora - la prossima, dichiarando da quando
entra. Serve perche' il link esiste da subito e ad agosto un "nessun calendario"
non direbbe niente a chi vuole gli orari di settembre.
Il popup e' un <dialog> nativo: fondale, Esc e trappola del focus senza
librerie. "Scarica in PDF" stampa la sola griglia, gia' impaginata in orizzontale
dal CSS di stampa. Le discipline hanno una barra colorata a sinistra oltre al
fondo, cosi' restano distinguibili anche quando il browser stampa senza sfondi.
Quattordici test sul dato: confini al secondo in ora italiana, fasce orarie
uniche e in ordine, nessuna lezione in un giorno di chiusura, e il buco vero fra
il 19 e il 20 settembre resta dichiarato invece che nascosto.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Erano due espressioni diverse per lo stesso link e sono divergute: su One to One e
Performance Class la foto apriva la scheda del servizio mentre il testo mandava a
/training, quindi la destinazione dipendeva da dove capitava il dito. Adesso
l'indirizzo lo calcola una funzione sola, usata da entrambi. Verificato sulla
pagina renderizzata: sei card, zero link discordanti.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Senza login (o con una data illeggibile) il parametro veniva ignorato e la pagina
rispondeva con l'edizione corrente: due date diverse davano lo stesso risultato,
e si legge come una funzione rotta invece che come un accesso mancante. Ora una
barra rossa dichiara il motivo e come rimediare.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
La pagina non mostra piu' una promozione sola scritta nel codice: sceglie
l'edizione la cui finestra contiene l'istante della richiesta. Il 20 agosto alle
00:00 la promo agosto lascia il posto agli Opening Bundles senza deploy, e dopo
il 4 settembre la pagina dichiara che non c'e' nessuna promozione invece di
lasciare online prezzi scaduti.
Le edizioni sono dato (data/promo-edizioni.ts): finestra, quante card, quali
campi ha ciascuna. I testi restano tag modificabili dal pannello, col prefisso
dell'edizione. I bundle aggiungono domanda, descrizione e riquadro del periodo
di utilizzo, presi dal volantino del cliente.
Le date portano l'offset esplicito: il server gira in UTC e senza offset lo
scambio avverrebbe due ore prima di quanto scritto sul sito. La pagina esce con
Cache-Control: no-store, perche' una copia in cache la congelerebbe all'edizione
vecchia.
Dieci test: le finestre non si sovrappongono e non lasciano buchi, il countdown
scade quando finisce l'edizione, e ogni tag richiesto esiste nel seed - un tag
mancante non romperebbe la pagina, lascerebbe una card senza prezzo.
Anteprima con ?anteprima=<data>, solo per chi e' loggato.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
MethodSteps passa a una sola colonna con step in orizzontale, PartnersStrip
riduce loghi e spaziature, TrainingCards ridimensiona testi e padding.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Nuovo tag promo.faq.0 (q/a) numerato 0 per evitare di rinumerare le FAQ
esistenti: il seed è INSERT OR IGNORE e non riscriverebbe i tag già
presenti nei DB, causando disallineamento tra domanda e risposta.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Documenta il ruolo piattaforme al posto di campus, la piattaforma Stress
Index con le scelte fatte sullo stile e sulle isole React, il cestino
degli articoli e il caricamento degli articoli via script, oltre alle
trappole incontrate: il percorso nuovo del progetto Campus, la cache
dell'indice in sviluppo e la lettura del database in WAL dopo il deploy.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Porta in main il lavoro di questa sessione: il ruolo piattaforme al posto
di campus con il menu dedicato, la dashboard Stress Index tradotta nello
stile del sito, l'area riservata ridisegnata con cestino degli articoli e
gestione utenti rivista, il Campus riallineato alle 167 guide del corso,
venti articoli divulgativi nel blog e la promo di agosto con countdown.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
I filtri non dicevano dove ci fosse materiale: si sceglieva una categoria
per scoprirla vuota. Ora ognuna porta il proprio conteggio, cestino e
bozze esclusi, e il numero si aggiorna insieme ai filtri quando si cambia
tematica senza ricaricare.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Dieci articoli tecnici a firma Nicola Antonelli e dieci contenuti
educativi a firma Donata Dal Pastro, ricavati dalle lezioni del corso
base: sonno, movimento, proteine, freddo, respirazione, luce, recupero,
dati, alimentazione e salute del cervello. Taglio breve e concreto, per
chi frequenta la palestra e tiene alla salute senza essere del mestiere.
Gli articoli arrivano da uno script versionato invece che dal pannello,
perché il database in produzione è un volume del VPS e non viene toccato
dal deploy: creati a mano in locale sarebbero rimasti in locale. Lo script
salta gli slug già presenti, quindi si può rieseguire senza danni.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Il progetto Biohacking Campus è stato spostato in Wasabi-Adp-Work, mentre
sync-campus puntava ancora al vecchio percorso, ormai vuoto: da lì in poi
nessun aggiornamento è più arrivato. Il capitolo Q&A si era fermato alla
numero 35, mentre nel progetto ne esistono 84.
Ora la sezione conta 167 guide invece di 118. L'elenco dei Q&A è
rigenerato per intero dal filesystem, in ordine di numero, così il prossimo
allineamento è una sola riga di script.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Ogni clic su una categoria ricaricava l'intera pagina, header e hero
compresi, con lo sfarfallio che ne consegue. Ora si scarica la stessa
pagina e si sostituiscono soltanto la griglia e i filtri, con una breve
dissolvenza; l'indirizzo resta corretto e avanti/indietro funzionano.
I link restano link veri: senza JavaScript, o se la risposta non ha la
forma attesa, la navigazione avviene come prima.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Eliminare un articolo non lasciava nulla di recuperabile: ora la riga
finisce nel cestino (colonna deleted_at, aggiunta anche ai database già
esistenti) e sparisce da sito e pannello, ma resta ripristinabile dalla
nuova pagina /admin/cestino, dove sta anche la cancellazione definitiva.
L'API la accetta solo su articoli già cestinati, così il contenuto non si
perde per un clic distratto.
Ogni lettura pubblica — elenco, recenti, archivio per mese, pagina del
singolo articolo — filtra il cestino, e chi non gestisce vede nel cestino
soltanto i propri articoli.
Le azioni di riga diventano icone (matita e cestino) come nell'elenco
utenti, con etichetta accessibile che nomina l'articolo.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Le due azioni di riga diventano icone (chiave e cestino) con etichetta
accessibile: pesavano quanto il contenuto della tabella. Il ruolo smette
di essere il menu a tendina di sistema e prende la forma di una pillola
colorata per livello, restando un <select> che salva al cambio.
La chiave apre un pannello dove la password si può scrivere a mano oppure
far generare: l'endpoint accetta ora una password scelta, con lo stesso
minimo di otto caratteri della creazione, e continua a restituirla in
chiaro una volta sola perché nel database ne resta solo l'hash. Il valore
si copia con un pulsante invece che dal prompt del browser.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
L'elenco aveva il modulo di creazione appiccicato sopra la tabella: ora
c'è il pulsante "+ Nuovo utente" che porta a /admin/users/new, dove il
modulo ha spazio per etichette vere e per spiegare cosa comporta ogni
ruolo. A creazione avvenuta si torna all'elenco.
Nel menu la voce "Sito" diventa "Vai a sito", più esplicita sul fatto che
porta fuori dal pannello.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Il menu in alto si riduce alle voci che servono davvero — Articoli,
Contenuti, Utenti, Sito, Esci — con la voce corrente evidenziata; ognuna
resta visibile solo a chi ha il ruolo per aprirla, e in pagina di login la
barra mostra il solo marchio. "Nuovo" esce dal menu perché il pulsante per
creare un articolo è già in cima all'elenco Articoli.
Il pannello smette di essere un foglio di stile a sé: adotta i font e la
palette del sito, come Campus e Stress Index, con tabelle, pulsanti, campi
e pillole di stato riportati allo stesso linguaggio.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>