I giocatori a pari valore condividono ora la stessa posizione (dense rank) invece di essere numerati in sequenza, e il sottotitolo di ogni riga segue il criterio scelto invece di mostrare sempre le presenze consecutive. Per Palloni mostra le volte consecutive in cui il giocatore li ha portati (nuovo Giocatore.seriePalloni). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
4.1 KiB
Modulo — Palloni
Stato: implementato
File principali: src/lib/palloni.ts, src/lib/palloni-core.ts,
src/components/crapp/TurnoPalloni.tsx, src/components/crapp/PromemoriaPalloni.tsx,
src/routes/api/public/promemoria-palloni.ts
Obiettivo
Gestire un turno a rotazione condiviso per chi porta e riporta i palloni ad allenamenti e partite, con proposta automatica, possibilità di modifica manuale e promemoria push il giorno stesso.
Dati
Tabella turni_palloni (evento_id, giocatore_id, aggiornato_da, aggiornato_il) —
contiene solo i turni confermati manualmente; le proposte automatiche non salvate non vi
compaiono.
Implementazione
completaTurni()(palloni-core.ts) propone, per ogni partita o evento extra senza turno già salvato, il candidato con meno turni fatti, poi quello che non lo fa da più tempo, poi per ordine alfabetico — un algoritmo greedy, non un ordine fisso né solo per data. Gli allenamenti non ricevono proposta automatica: restano «da assegnare» finché qualcuno non sceglie un incaricato inTurnoPalloni(scelta della squadra).useAssegnaTurno()(palloni.ts) conferma una proposta o riassegna manualmente, con upsert suevento_id.- Il conteggio "quante volte hai portato i palloni" mostrato nel profilo e nei badge è
ricalcolato a runtime da
conteggioTurni()sui soli turni confermati (turniSalvatiinrosa.ts) — non è uno storico in tabella dedicata, ma non include le proposte automatiche dicompletaTurni()(quelle restano solo per la UI di rotazione,TurnoPalloni.tsx/PromemoriaPalloni.tsx). Conta solo gli eventi già passati (e.data < oggi, stesso criterio delle presenze): un turno assegnato in anticipo per un allenamento futuro non è ancora "portato", quindi non sale finché quel giorno non arriva. serieConsecutivaPalloni()(palloni-core.ts) calcola le volte consecutive in cui il giocatore ha portato i palloni (Giocatore.seriePalloniinrosa.ts), mostrate nel sottotitolo della classifica interna di Squadra quando si ordina per Palloni. Stesso criterio "solo eventi già passati" diconteggioTurni(); un evento passato senza turno confermato non spezza la serie di nessuno (viene saltato, non conta come "non portati").TurnoPalloni.tsxmostra/assegna il turno sulla card di un evento;PromemoriaPalloni.tsxè il banner in Home per il giocatore di turno.
Route API pubblica /api/public/promemoria-palloni
La fa partire un amministratore dal pulsante «Avvisa chi è di turno» dentro il riquadro
palloni dell'evento (TurnoPalloni.tsx), riservato agli admin (DD-025). Riceve l'eventoId,
e avvisiPalloniEvento() calcola i due destinatari di quell'evento: chi deve prendere i
palloni e chi deve riportarli (l'incaricato dell'evento precedente), con un testo diverso
per ciascuno.
Titolo e testo viaggiano cifrati dentro la push, quindi il service worker li mostra senza
nessuna chiamata di rete. Stesso meccanismo di apri-sondaggio (vedi
Notifiche).
Limiti noti
- L'invio è manuale: nessun cron manda il promemoria da solo, se l'admin non preme il
pulsante non parte niente (DD-025).
destinatariPromemoriaPalloni()— la versione "chi è di turno oggi" — resta inpalloni-core.tsma non la chiama più nessuno. - La rotazione non considera le assenze dichiarate: può proporre il turno a chi ha risposto "assente" o "infortunato" per quell'evento.
conteggioTurni()non filtra per tipo evento (a differenza dieventiPalloni(), che scarta i compleanni): guarda soloe.data < oggi. Un turno registrato per errore su un evento fuori dal dominio "richiede i palloni" conterebbe comunque per il badge Sherpa dei palloni (badge.md§ Problemi noti). Rischio basso — l'UI non offre questa combinazione — ma il comportamento attuale è pinnato da un test dedicato inpalloni-core.test.ts.
Evoluzioni possibili
- Versionare il cron (es. una migration con
cron.schedule) invece di configurarlo solo lato dashboard. - Escludere dalla rotazione chi ha già dichiarato assenza per l'evento.