Modulo, DD-053, changelog e stato del progetto descrivono la schermata senza tasti dei punti, tabellone e blocco avversario, e il limite attuale su chiusura di set e partita. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
7.7 KiB
Modulo — Scout Live
Stato: implementato (fix M7 per la persistenza condivisa)
File principali: src/lib/scout-live.ts, src/lib/scout-stato.ts, src/lib/scout-store.ts,
src/lib/scout-export.ts, src/lib/cacche.ts, src/components/crapp/ScoutEntry.tsx,
src/components/crapp/SondaggioCacche.tsx, src/routes/scout.tsx, src/routes/partita.$id.tsx
Obiettivo
Permettere a un solo referente per volta di registrare in tempo reale, durante la partita,
un giudizio − / + sui fondamentali (battuta, ricezione, attacco, muro) di ciascun convocato,
per set, con salvataggio condiviso su Supabase (non più solo localStorage, fix M7) così che tutta la squadra veda lo stato
aggiornato da qualunque dispositivo.
Dati
scout_sessioni— chi ha il controllo dello Scout Live per una partita (una riga perevento_id, quindi un solo detentore).scout_live— stato in corso (azioni non ancora concluse) di una sessione.scout_partite— archivio delle partite scoutate concluse (risultato, parziali, azioni, valutazioni − / +), mai più modificato una volta salvato (solo eliminabile per intero).cacche_partita— sondaggio goliardico pre-partita, un voto per giocatore/evento (UNIQUE evento_id, giocatore_id).
Chi può usarlo
Chiunque sia autenticato: non è più riservato agli admin. A tenere l'ordine basta il lock di
sessione — scoutizza uno per volta, gli altri vedono "In uso da …". Questo allinea l'interfaccia
alle policy RLS di scout_sessioni/scout_live/scout_partite, che sono sempre state aperte a
qualunque utente autenticato (la migration M4 toglie l'accesso solo al ruolo anon).
Da dove ci si arriva
ScoutEntry.tsx è l'unico accesso a /scout: sta nella pagina della partita
(partita.$id.tsx, sezione «Scout live»), visibile a tutta la squadra. Si accende solo se
quella partita è quella di oggi — la prop eventoId confronta l'evento aperto con
partitaDiOggi() — e se nessun altro ha il lock; negli altri casi resta una card grigia non
cliccabile («Si attiva il giorno della partita» / «In uso da …»). Dalla home è stato tolto
perché occupava spazio 6 giorni su 7.
Meccanismo di lock condiviso
useApriSessioneScout()(scout-live.ts) prende il controllo con un upsert suscout_sessioni(chiaveevento_id), rifiutando se un altro giocatore ha già una sessione non scaduta.- Una sessione scade dopo 5 minuti di inattività;
useHeartbeatScout()la rinnova ogni 60 secondi finché lo scout resta aperto. - Il rilascio (
useChiudiSessioneScout()) avviene al bottone "Rilascia", a fine partita, e sull'eventopagehidedella finestra (per liberare il lock se il browser viene chiuso senza uscire esplicitamente). - Nessun realtime: la sessione si rilegge solo all'apertura/focus pagina o al bottone
"Aggiorna" (
staleTime30s).
Cosa registra
La schermata /scout ha due passi: 1. Tocca il giocatore (griglia dei convocati, filtrati sulle
risposte "presente"/"ritardo", con fallback a tutta la rosa se nessuno ha risposto) e 2. Valuta
(vedi sotto), seguiti da «Ultime azioni», «Chiudi set» e «Fine partita». Salvataggio automatico su
scout_live con debounce di 800ms a ogni cambiamento.
Punteggio e azioni. L'interfaccia non registra più il punteggio: sono stati tolti i tasti delle
azioni (punto attacco, ace, muro, errore nostro, punto e errore avversario), il tabellone con punti
e set e il blocco avversario / in casa / trasferta. Avversario e campo restano quelli ricavati dal
titolo della partita (o dallo stato già salvato) e non si modificano. I tipi di azione
(AzioneTipo, scout-store.ts: attacco, ace, muro, errore, punto_avv, errore_avv),
i totali totaliPerGiocatore() e il campo scout_partite.azioni restano nel codice e nei dati:
leggono le partite scoutate prima di questa modifica, ma oggi nessuna schermata crea nuove azioni.
Valutazioni dei fondamentali (− / +)
Per ogni convocato lo scout può segnare un − o un + su battuta, ricezione, attacco e muro (DD-053). Non sono punti: restano fuori dal punteggio, dai parziali e dai totali P/A/M/E. Si seleziona il giocatore (passo 1) e si tocca − o + sul fondamentale (passo 2, il cui titolo mostra cognome e numero del giocatore e il set in corso); accanto a ogni tasto compare il conteggio di quel giocatore nel set in corso. «Annulla» nella sezione toglie l'ultima valutazione del set in corso (l'«Annulla» in alto agisce solo sulle azioni, quindi oggi è sempre spento).
Ogni tocco è una riga Valutazione (giocatoreId, fondamentale, segno, set, ts) in una
lista separata da azioni; i conteggi per set si calcolano con contaValutazioni()
(scout-store.ts). Lo stato in corso le salva in scout_live.stato.valutazioni (assente negli
stati salvati prima: vale lista vuota); a fine partita finiscono in scout_partite.valutazioni.
Fine partita
Limite attuale: chiudiSet() e finePartita() si basano sui punti, che non si registrano più. «Chiudi
set» risponde «Set senza azioni» (quindi il set resta sempre il 1) e «Fine partita» «Nessun set da
salvare»: le valutazioni restano nello stato in corso e non arrivano in scout_partite.
finePartita() (scout.tsx) compone i parziali finali, inserisce la partita in
scout_partite (INSERT, non upsert), poi cancella la riga da scout_live (stato consumato)
e rilascia la sessione.
Export CSV
scout-export.ts genera un CSV (separatore ;, BOM UTF-8) con parziali, riepilogo per
giocatore e log cronologico delle azioni. Se ci sono valutazioni, aggiunge una tabella per set e giocatore con i
- e i − di ogni fondamentale (solo per chi ne ha). Scaricabile dagli admin dalla pagina partita, sezione "Report tecnico".
Sondaggio cacche
SondaggioCacche.tsx chiede "quante cacche hai fatto prima di questa partita" (0-5+), sempre
modificabile. sondaggioAperto() (cacche.ts) lo apre alle 8:00 del giorno della partita
(ora locale del dispositivo) e da lì lo lascia aperto per sempre; prima la card mostra solo
l'avviso di apertura. Quando è aperto, gli amministratori vedono nella card il pulsante
«Avvisa tutti del sondaggio»: chiama POST /api/public/apri-sondaggio e manda la push a tutti
i dispositivi iscritti, come il sollecito presenze (vedi Notifiche). Nessun
invio automatico: parte solo quando un admin lo preme.
statisticheCacche() (cacche.ts) calcola media, record e giornateTop
(giornate con ≥3), soglia usata per un badge segreto — coerente con DD-007 (badge
calcolati a runtime).
Regole rispettate
- DD-008 (gamification equa): i dati tecnici (punti/ace/muri) restano confinati allo Scout
Live come statistica di squadra e non entrano nel tipo
Giocatoreusato per badge o classifiche individuali.
Limiti noti
- Senza punteggio, set e partita non si chiudono (vedi «Fine partita»): da risolvere, per esempio chiudendo il set anche senza punti e salvando la partita con le sole valutazioni.
- Scout aperto a tutta la squadra: nessun filtro su chi può registrare le azioni, l'unica garanzia è il lock di sessione (vedi sopra).
- Possibile, per quanto improbabile, doppio "successo" applicativo nel prendere il lock: lettura e upsert non sono atomici.
- Avversario e campo non si correggono dallo scout.
scout_partitesi inserisce ma non si corregge dall'interfaccia: solo eliminazione totale.- Abbinamento partita↔scout fatto anche per uguaglianza di data come fallback: ambiguo se due partite cadono lo stesso giorno.
Evoluzioni possibili
- Mostrare le valutazioni anche nella pagina della partita (oggi solo nel CSV admin).
- Realtime (Supabase Realtime) per aggiornare la sessione condivisa senza refresh manuale.