From c44fd8495ef204535e91977422ff774767a02e42 Mon Sep 17 00:00:00 2001 From: Davide Grilli Date: Mon, 7 Sep 2026 09:59:03 +0200 Subject: [PATCH] =?UTF-8?q?Non=20conta=20i=20turni=20palloni=20finch=C3=A9?= =?UTF-8?q?=20l'evento=20non=20=C3=A8=20passato.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit conteggioTurni() contava ogni turno presente nella mappa, incluse le assegnazioni anticipate per eventi futuri: un allenamento di lunedì con turno già assegnato oggi (venerdì) veniva già sommato al badge "Sherpa dei palloni" prima ancora di essersi svolto. Ora filtra su e.data < oggi, lo stesso criterio già usato per presenze e serie — il turno resta assegnabile/ modificabile in anticipo, semplicemente non conta finché il giorno non arriva. oggiISO() (palloni-core.ts) diventa un alias di dataOggi(): stessa correzione di fuso della commit precedente, qui serviva anche per admin.tsx (scadenza documenti/certificati) e cacche.ts (statistiche giornaliere), che la usano. Co-Authored-By: Claude Sonnet 5 --- docs/modules/palloni.md | 5 ++++- src/lib/palloni-core.ts | 25 ++++++++++++++++------ src/lib/rosa.ts | 2 +- test/unit/palloni-core.test.ts | 38 ++++++++++++++++++++++++++++------ 4 files changed, 56 insertions(+), 14 deletions(-) diff --git a/docs/modules/palloni.md b/docs/modules/palloni.md index 0b6bca3..8fbcc63 100644 --- a/docs/modules/palloni.md +++ b/docs/modules/palloni.md @@ -34,7 +34,10 @@ compaiono. upsert su `evento_id`. - Il conteggio "quante volte hai portato i palloni" mostrato nel profilo e nei badge è ricalcolato a runtime da `conteggioTurni()` su turni salvati **più proposte non ancora - confermate** (partite/eventi) — non è uno storico in tabella dedicata. + confermate** (partite/eventi) — non è uno storico in tabella dedicata. 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. - `TurnoPalloni.tsx` mostra/assegna il turno sulla card di un evento; `PromemoriaPalloni.tsx` è il banner in Home per il giocatore di turno. diff --git a/src/lib/palloni-core.ts b/src/lib/palloni-core.ts index 4cb795f..02372a1 100644 --- a/src/lib/palloni-core.ts +++ b/src/lib/palloni-core.ts @@ -1,5 +1,6 @@ import { formatData } from "./crapp-data"; import type { Evento } from "./eventi"; +import { dataOggi } from "./scout-live"; export type Turno = { evento_id: string; giocatore_id: string; aggiornato_da: string | null }; @@ -60,10 +61,23 @@ export function completaTurni( return risultato; } -/** Quante volte ciascun giocatore è incaricato dei palloni. */ -export function conteggioTurni(turni: Record): Record { +/** + * Quante volte ciascun giocatore è incaricato dei palloni, solo per eventi già passati: + * un turno assegnato in anticipo per un allenamento futuro non è ancora "portato", quindi + * non deve contare finché quell'allenamento non è terminato (stesso criterio `e.data < oggi` + * usato per le presenze, così la conta non cambia da sola col passare della giornata). + */ +export function conteggioTurni( + turni: Record, + eventi: Evento[], + oggi: string = dataOggi(), +): Record { + const passati = new Set(eventi.filter((e) => e.data < oggi).map((e) => e.id)); const out: Record = {}; - for (const id of Object.values(turni)) out[id] = (out[id] ?? 0) + 1; + for (const [eventoId, giocatoreId] of Object.entries(turni)) { + if (!passati.has(eventoId)) continue; + out[giocatoreId] = (out[giocatoreId] ?? 0) + 1; + } return out; } @@ -84,10 +98,9 @@ export function eventoSuccessivo(eventi: Evento[], eventoId: string): Evento | u return i >= 0 ? lista[i + 1] : undefined; } +/** Alias di `dataOggi()`, nel fuso di Roma: qui per non toccare gli import esistenti. */ export function oggiISO(d = new Date()): string { - const mm = String(d.getMonth() + 1).padStart(2, "0"); - const dd = String(d.getDate()).padStart(2, "0"); - return `${d.getFullYear()}-${mm}-${dd}`; + return dataOggi(d); } /** Chi deve ricevere l'avviso push, oggi: chi porta i palloni e chi li riprende. */ diff --git a/src/lib/rosa.ts b/src/lib/rosa.ts index ef8f524..38b4df9 100644 --- a/src/lib/rosa.ts +++ b/src/lib/rosa.ts @@ -46,7 +46,7 @@ export function useRosa(): Giocatore[] { return useMemo(() => { const medie = mediePagelle(pagelle); const statCacche = statisticheCacche(cacche); - const palloni = conteggioTurni(turni); + const palloni = conteggioTurni(turni, eventi); const mvpVinti = mvpVintiPerGiocatore(votiMvp); return squadra diff --git a/test/unit/palloni-core.test.ts b/test/unit/palloni-core.test.ts index 16a4950..09f2eab 100644 --- a/test/unit/palloni-core.test.ts +++ b/test/unit/palloni-core.test.ts @@ -74,7 +74,8 @@ assert.equal(conAllenamentoSalvato["e2"], "g5"); const molti = Array.from({ length: giocatori.length + 2 }, (_, i) => evento(`x${i}`, `2026-10-${String(i + 1).padStart(2, "0")}`, "partita"), ); -const rotazione = conteggioTurni(completaTurni({}, molti, rosa)); +// Tutte "passate" rispetto a questa data, altrimenti conteggioTurni le scarterebbe. +const rotazione = conteggioTurni(completaTurni({}, molti, rosa), molti, "2026-11-01"); const carichi = Object.values(rotazione); assert.equal( Math.max(...carichi) - Math.min(...carichi), @@ -102,13 +103,38 @@ assert.equal(conFantasma["e1"], "gXX", "il turno storico resta com'è"); assert.ok(conFantasma["e2"], "la partita riceve comunque una proposta"); assert.equal(conFantasma["e3"], undefined, "l'allenamento senza salvataggio resta vuoto"); -// --- conteggioTurni ---------------------------------------------------------- -assert.deepEqual(conteggioTurni({ a: "g1", b: "g1", c: "g2" }), { g1: 2, g2: 1 }); -assert.deepEqual(conteggioTurni({}), {}); +// --- conteggioTurni: solo eventi già passati ---------------------------------- +const eventiConteggio: Evento[] = [ + evento("c1", "2026-09-01", "partita"), + evento("c2", "2026-09-05", "partita"), + evento("c3", "2026-09-10", "allenamento"), // futuro rispetto a OGGI_CONTEGGIO +]; +const OGGI_CONTEGGIO = "2026-09-08"; +assert.deepEqual( + conteggioTurni({ c1: "g1", c2: "g1", c3: "g2" }, eventiConteggio, OGGI_CONTEGGIO), + { g1: 2 }, + "il turno di un evento futuro non è ancora contato, anche se già assegnato", +); +assert.deepEqual( + conteggioTurni({}, eventiConteggio, OGGI_CONTEGGIO), + {}, + "nessun turno assegnato: conteggio vuoto", +); -// --- oggiISO ----------------------------------------------------------------- +// --- oggiISO ------------------------------------------------------------------- assert.match(oggiISO(), /^\d{4}-\d{2}-\d{2}$/); -assert.equal(oggiISO(), new Date().toLocaleDateString("sv-SE"), "data locale, non UTC"); +// Stesso controllo di dataOggi() in scout-live.test.ts: oggiISO() ne è un alias, il fuso +// deve restare Europe/Rome anche passando da qui. +assert.equal( + oggiISO(new Date("2026-01-15T23:30:00Z")), + "2026-01-16", + "CET: mezzanotte italiana precede quella UTC di un'ora", +); +assert.equal( + oggiISO(new Date("2026-07-15T22:30:00Z")), + "2026-07-16", + "CEST: il cambio ora legale porta lo scarto a due ore, non resta fisso a uno", +); // --- destinatariPromemoriaPalloni -------------------------------------------- const eventiPush: Evento[] = [