Il pulsante delle schede di partita, allenamento ed evento dice «Apri su Maps». CHANGELOG con la sezione 1.4.2 (m34 applicata in produzione il 26/10/2026), PROJECT_STATE e package.json/package-lock alla 1.4.2. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
5.9 KiB
Modulo — Calendario ed Eventi
Stato: implementato
File principali: src/lib/eventi.ts, src/lib/eventi.server.ts, src/lib/calendario.ts
(griglia mensile condivisa), src/routes/calendario.tsx (vista mensile, tutti),
src/routes/eventi.tsx (creazione/modifica, admin e allenatori), src/components/crapp/EventoCard.tsx
(card condivisa), src/routes/allenamento.$id.tsx, src/routes/partita.$id.tsx,
src/routes/evento.$id.tsx (schede di dettaglio, una per tipo)
Test: test/unit/eventi.test.ts, test/unit/calendario.test.ts
Obiettivo
Un unico calendario condiviso per allenamenti, partite, amichevoli ed eventi extra
(riunioni, cene di squadra...), al posto di messaggi sparsi in chat. Ogni evento in
eventi_app diventa il punto a cui si agganciano presenze, convocazioni, MVP, pagelle,
scout e turno palloni — la maggior parte degli altri moduli dipende da un evento.id.
Due schermate, due pubblici
/calendario— vista mensile per tutta la squadra, sola lettura. Mostra allenamenti, partite, eventi ed eventi virtuali per i compleanni della rosa (compleanniEventi()ineventi.ts, generati a runtime dall'anagrafica diuseAnagraficaRosa({ conAllenatori: true })— ci sono anche gli allenatori, DD-034 — non righe vere dieventi_app): la spunta della vistagiorniIT/mesiITcolora la cella per tipo di evento, i giorni con più eventi si dividono lo spazio. In fondo alla pagina la sezione «Prossimi compleanni» elenca solo quelli del mese in corso non ancora passati (oggi compreso), a prescindere dal mese mostrato nella griglia: il 6 ottobre un compleanno del 3 non compare più. Le celle della griglia continuano a mostrare tutti i compleanni del mese selezionato./eventi— "Gestione eventi", riservata ad amministratori e allenatori (usePuoGestireEventi(), DD-034): crea, modifica ed elimina un evento, sceglie i convocati tra i soli giocatori (convocatiEvento(), vuoto = tutta la rosa). Da qui si distingue "partita" da "amichevole" tramite il flagcampionato(categoriaEvento()/daCategoria()ineventi.tsconvertono tra la categoria mostrata in interfaccia e la coppia{ tipo, campionato }salvata nel database). La pagina mostra solo una griglia mensile (stessa logica di/calendario, tramite le funzioni condivise disrc/lib/calendario.ts), che si scorre di mese in mese con le frecce o con lo swipe, con la stessa fisica a molla di/calendario. Ogni giorno è cliccabile, anche senza eventi, e apre un drawer con gli eventi di quel giorno (modifica/elimina) e un bottone "Nuovo evento in questo giorno" che apre il form con la data già precompilata: creare, modificare ed eliminare passano solo da lì. Nel form il giorno si legge come testo fisso e resta da scegliere solo l'ora; il campo data compare solo toccando «Cambia», per spostare l'evento. Fino a 0.9.2 sotto la griglia c'era anche la lista cronologica di tutti gli eventi, tolta perché ripeteva il calendario (DD-033).
Entrambe leggono la stessa cache (useEventi(), EVENTI_KEY, staleTime 10 minuti: il
calendario cambia raramente). EventoCard.tsx è la card riusata da entrambe le schermate;
linkPerEvento() decide dove porta il click — /partita/$id per una partita (con
/partita-csi/$id come alternativa "solo CSI" quando non c'è un evento collegato, vedi
collegamento-csi.md), /allenamento/$id per un allenamento, /evento/$id per un evento
extra-campo, nessun link per gli eventi virtuali (compleanni), che non hanno una riga in
eventi_app.
Link Google Maps. eventi_app.link_maps (M34, facoltativo) si imposta dal form di /eventi
sotto «Luogo»: si incolla il link di «Condividi» di Google Maps. linkMapsValido() in eventi.ts
accetta solo URL https di Google Maps (maps.app.goo.gl, goo.gl/maps, google.*/maps,
maps.google.*) e lo stesso criterio è un CHECK sul database. Le tre schede di dettaglio mostrano
«Apri su Maps» (nuova scheda) solo se il link c'è; non c'è un link generato dal testo del
luogo, e né la card né le notifiche lo mostrano.
Le tre schede di dettaglio (allenamento.$id.tsx, partita.$id.tsx, evento.$id.tsx)
condividono la stessa struttura — intestazione, note, conferme, RosaPresenze per l'elenco
nominativo di chi ha risposto — con TurnoPalloni in più per allenamenti e partite (vedi
palloni.md, non pertinente per gli eventi extra-campo) e lo stato infortunato escluso
dalle risposte solo sulla scheda evento (vedi presenze.md).
Lettura lato server
src/lib/eventi.server.ts (leggiEventi()) è la stessa conversione riga→modello di
eventi.ts, ma con supabaseAdmin per le route API che girano senza sessione utente (es.
sollecita-presenze.ts, promemoria-palloni.ts — vedi presenze.md e palloni.md) e per
notifiche-smart.ts, che decide i promemoria da mandare in base agli eventi del giorno.
Limiti noti
- Cancellare un evento è distruttivo per tutto ciò che vi era agganciato. Un trigger
(
m14_pulizia_dati_evento_cancellato, DD-029) pulisce a cascata presenze, cacche, voti MVP/pagelle/badge social, turni palloni e scout di quell'evento: non è recuperabile con un annulla, e prima di M14 quelle righe restavano orfane nel database (bonificate una tantum da M15/M16, vedi DATABASE.md). - Nessuna creazione automatica degli eventi partita dal calendario CSI. Le gare
ufficiali arrivano già come dati (
getEventsByTeamId.php, vedicollegamento-csi.md), ma un amministratore deve comunque creare a mano l'evento corrispondente in/eventiperché esistano convocazioni, presenze, MVP e pagelle per quella partita — altrimenti la gara resta visibile solo nello storico CSI, con un dettaglio "solo CSI" più povero (/partita-csi/$idinvece di/partita/$id). Indocs/ROADMAP.mdsotto "Prossimo".