diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 7b9cb04..19c65c9 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -6,6 +6,15 @@ qui: sta in [ROADMAP.md](ROADMAP.md). ## Versione attuale — agosto 2026 +### Il voto MVP apre due ore dopo il fischio d'inizio, e vota solo chi c'era + +- La votazione non dipende più dal referto CSI né dallo Scout: il pannello ha una sezione sua + in `/partita/$id` e si apre due ore dopo `data`+`ora` dell'evento, anche senza risultato + caricato. +- Votano — e sono votabili — solo i giocatori dati `presente` o `ritardo` a quell'evento. +- I voti sono ora legati all'id dell'evento CrAPP: le righe scritte prima con l'id dello + scout o della partita CSI restano nel database ma non vengono più lette. + ### Tre riletture in meno dopo ogni salvataggio - «Aggiungi giocatore», salvataggio ed eliminazione di una partita scoutata e cambio della diff --git a/docs/modules/mvp.md b/docs/modules/mvp.md index a9e7a57..8d11dbf 100644 --- a/docs/modules/mvp.md +++ b/docs/modules/mvp.md @@ -17,12 +17,19 @@ calcolato a runtime. Tabella `mvp_voti`, vincolo `UNIQUE (match_id, votante_id)` — un solo voto per giocatore per partita, sovrascrivibile. +`match_id` è l'**id dell'evento CrAPP**, non quello del referto CSI né dello Scout: la +votazione non dipende più da nessuna delle due fonti (i voti scritti prima con l'id scout/CSI +restano nel database ma non vengono più letti da nessuna schermata). + --- ## Implementazione -- Il pannello compare in `partita.$id.tsx` solo se esiste un risultato (Scout Live salvato) - per la partita, altrimenti mostra "la partita non è ancora stata disputata". +- Il pannello sta in `partita.$id.tsx` in una sezione sua, sempre presente: `votoMvpAperto()` + lo apre `ORE_ATTESA_MVP` (2) ore dopo `data`+`ora` dell'evento, prima di allora mostra solo + quando aprirà. Nessun legame con il risultato caricato. +- Votano e sono votabili solo i **presenti** di quell'evento (`presente` o `ritardo` in + `usePresenzeEvento`): chi non c'era ha il bottone disabilitato e non compare nell'elenco. - `useVotaMvp()` fa upsert `onConflict: match_id, votante_id`: il voto è modificabile senza limiti, senza storico. - Nessuno vota sé stesso: `VotazioneMvp.tsx` toglie il votante dall'elenco e il vincolo @@ -38,11 +45,12 @@ partita, sovrascrivibile. ## Limiti noti -- Nessuna scadenza o chiusura della votazione: resta aperta indefinitamente. +- Nessuna scadenza o chiusura della votazione: una volta aperta resta aperta indefinitamente. - Il voto è legato a chi lo scrive: da `m11_scritture_per_ruolo` la policy impone che `votante_id` sia lo slot collegato all'account (DD-023). Su chi viene votato l'unico - vincolo è che non sia il votante stesso (`mvp_no_autovoto`): che sia un convocato di - quella partita resta un filtro solo applicativo. + vincolo è che non sia il votante stesso (`mvp_no_autovoto`): che votante e votato fossero + presenti a quella partita, e che siano passate due ore dall'inizio, restano filtri solo + applicativi — chi scrive su PostgREST li aggira. - In caso di parità, nessun MVP viene assegnato per quella partita. --- diff --git a/src/components/crapp/VotazioneMvp.tsx b/src/components/crapp/VotazioneMvp.tsx index 87f7601..a19753c 100644 --- a/src/components/crapp/VotazioneMvp.tsx +++ b/src/components/crapp/VotazioneMvp.tsx @@ -4,15 +4,34 @@ import { toast } from "sonner"; import { cn } from "@/lib/utils"; import { nomeCompleto, useGiocatoriSquadra } from "@/lib/giocatori-squadra"; import { useGiocatoreCorrente } from "@/lib/user-store"; -import { conteggioPartita, mioVoto, useVotaMvp, useVotiMvp, type VotoMvp } from "@/lib/mvp-voti"; +import { usePresenzeEvento } from "@/lib/presenze"; +import type { Evento } from "@/lib/eventi"; +import { + conteggioPartita, + mioVoto, + ORE_ATTESA_MVP, + useVotaMvp, + useVotiMvp, + votoMvpAperto, + type VotoMvp, +} from "@/lib/mvp-voti"; -/** Pannello di votazione MVP di una partita: un voto a testa, modificabile. */ -export function VotazioneMvp({ matchId }: { matchId: string }) { +/** + * Pannello di votazione MVP di una partita: un voto a testa, modificabile. + * + * I voti sono legati all'evento CrAPP, non al referto CSI né allo scout: si vota anche + * senza risultato caricato, dalle due ore dopo il fischio d'inizio. Votano e sono votabili + * solo i presenti (o in ritardo) di quell'evento. + */ +export function VotazioneMvp({ evento }: { evento: Evento }) { + const matchId = evento.id; const io = useGiocatoreCorrente(); const voti = useVotiMvp(); const vota = useVotaMvp(); const { righe: squadra } = useGiocatoriSquadra(); - const rosa = squadra.filter((g) => g.attivo); + const { risposte } = usePresenzeEvento(evento.id); + const presente = (id: string) => risposte[id] === "presente" || risposte[id] === "ritardo"; + const rosa = squadra.filter((g) => g.attivo && presente(g.id)); const [aperto, setAperto] = useState(false); const tutti: VotoMvp[] = voti.data ?? []; @@ -21,6 +40,20 @@ export function VotazioneMvp({ matchId }: { matchId: string }) { const totale = conteggio.reduce((s, c) => s + c.voti, 0); const testa = conteggio[0]; const pareggio = conteggio.length > 1 && conteggio[1]!.voti === testa?.voti; + const puoVotare = !!io && presente(io.id); + + if (!votoMvpAperto(evento.data, evento.ora)) { + return ( +
+
+ Apre {ORE_ATTESA_MVP} ore dopo l'inizio della partita. +
+