diff --git a/src/lib/obiettivi.ts b/src/lib/obiettivi.ts index 4c7f020..900a780 100644 --- a/src/lib/obiettivi.ts +++ b/src/lib/obiettivi.ts @@ -28,12 +28,12 @@ export const contestoVuoto: ContestoObiettivi = { eventi: [], presenze: {}, page const MESE = "2026-08"; -function percentualePresenzeMese(ctx: ContestoObiettivi) { +function percentualePresenzeMese(ctx: ContestoObiettivi, rosaSize: number) { const delMese = ctx.eventi.filter( (e) => e.data.startsWith(MESE) && (e.tipo === "partita" || e.tipo === "allenamento"), ); - if (delMese.length === 0) return 0; - const posti = delMese.length * giocatori.length; + if (delMese.length === 0 || rosaSize === 0) return 0; + const posti = delMese.length * rosaSize; const presenti = delMese.reduce((s, e) => { const risposte = ctx.presenze[e.id] ?? {}; return s + Object.values(risposte).filter((x) => x === "presente" || x === "ritardo").length; @@ -41,10 +41,10 @@ function percentualePresenzeMese(ctx: ContestoObiettivi) { return Math.round((presenti / posti) * 100); } -function percentualeRisposte(ctx: ContestoObiettivi) { +function percentualeRisposte(ctx: ContestoObiettivi, rosaSize: number) { const daRispondere = ctx.eventi.filter((e) => e.tipo !== "compleanno"); - if (daRispondere.length === 0) return 0; - const posti = daRispondere.length * giocatori.length; + if (daRispondere.length === 0 || rosaSize === 0) return 0; + const posti = daRispondere.length * rosaSize; const risposte = daRispondere.reduce( (s, e) => s + Object.keys(ctx.presenze[e.id] ?? {}).length, 0, @@ -65,7 +65,7 @@ export function obiettiviSquadra( id: "o1", titolo: "90% di presenze ad agosto", descrizione: "Media presenze su partite e allenamenti del mese", - valore: percentualePresenzeMese(ctx), + valore: percentualePresenzeMese(ctx, rosa.length), target: 90, unita: "%", scadenza: "2026-08-31", @@ -76,7 +76,7 @@ export function obiettiviSquadra( id: "o2", titolo: "Tutti rispondono alle convocazioni", descrizione: "Percentuale di risposte date sugli eventi in programma", - valore: percentualeRisposte(ctx), + valore: percentualeRisposte(ctx, rosa.length), target: 90, unita: "%", scadenza: "2026-09-30", diff --git a/src/lib/palloni-core.ts b/src/lib/palloni-core.ts index e8188bb..1522d63 100644 --- a/src/lib/palloni-core.ts +++ b/src/lib/palloni-core.ts @@ -1,8 +1,11 @@ -import { formatData, giocatori } from "./crapp-data"; +import { formatData } from "./crapp-data"; import type { Evento } from "./eventi"; export type Turno = { evento_id: string; giocatore_id: string; aggiornato_da: string | null }; +/** Candidato al turno palloni: solo id e nome bastano per assegnare e ordinare. */ +export type CandidatoTurno = { id: string; nome: string }; + /** Eventi che richiedono i palloni (allenamenti, partite, extra), in ordine di data. */ export function eventiPalloni(eventi: Evento[]): Evento[] { return eventi @@ -18,10 +21,11 @@ export function eventiPalloni(eventi: Evento[]): Evento[] { export function completaTurni( turni: Record, eventi: Evento[], + rosa: CandidatoTurno[], ): Record { const risultato: Record = { ...turni }; - const conteggio = new Map(giocatori.map((g) => [g.id, 0])); - const ultimo = new Map(giocatori.map((g) => [g.id, -1])); + const conteggio = new Map(rosa.map((g) => [g.id, 0])); + const ultimo = new Map(rosa.map((g) => [g.id, -1])); eventiPalloni(eventi).forEach((evento, indice) => { const assegnato = risultato[evento.id]; @@ -32,7 +36,7 @@ export function completaTurni( } if (assegnato) return; - const scelto = giocatori.slice().sort((a, b) => { + const scelto = rosa.slice().sort((a, b) => { const ca = conteggio.get(a.id) ?? 0; const cb = conteggio.get(b.id) ?? 0; if (ca !== cb) return ca - cb; diff --git a/src/lib/palloni.ts b/src/lib/palloni.ts index e55c107..33a2627 100644 --- a/src/lib/palloni.ts +++ b/src/lib/palloni.ts @@ -2,6 +2,7 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { supabase } from "@/integrations/supabase/client"; import { completaTurni } from "./palloni-core"; import { useEventi } from "./eventi"; +import { nomeCompleto, useGiocatoriSquadra } from "./giocatori-squadra"; export const TURNI_KEY = ["turni-palloni"] as const; @@ -26,8 +27,10 @@ export function useTurniPalloni() { // Cambia raramente: una lettura per sessione è sufficiente. const query = useQuery({ queryKey: TURNI_KEY, queryFn: fetchTurni, staleTime: 30 * 60_000 }); const { eventi } = useEventi(); + const { righe: squadra } = useGiocatoriSquadra(); const salvati = query.data ?? {}; - return { ...query, salvati, turni: completaTurni(salvati, eventi) }; + const rosa = squadra.filter((g) => g.attivo).map((g) => ({ id: g.id, nome: nomeCompleto(g) })); + return { ...query, salvati, turni: completaTurni(salvati, eventi, rosa) }; } export function useAssegnaTurno() { diff --git a/src/lib/presenze-mese.ts b/src/lib/presenze-mese.ts index 3fbab8c..4ce106d 100644 --- a/src/lib/presenze-mese.ts +++ b/src/lib/presenze-mese.ts @@ -1,7 +1,7 @@ import { useMemo } from "react"; import { useEventi } from "./eventi"; import { useRispostePresenze } from "./presenze"; -import { giocatori } from "./crapp-data"; +import { useGiocatoriSquadra } from "./giocatori-squadra"; /** * Percentuale di presenze dell'ultimo mese (30 giorni), utile per le convocazioni. @@ -46,6 +46,7 @@ export function usePresenzeUltimoMeseTutti(): Record< > { const { eventi } = useEventi(); const { presenze } = useRispostePresenze(); + const { righe: squadra } = useGiocatoriSquadra(); return useMemo(() => { const oggi = new Date(); @@ -53,6 +54,7 @@ export function usePresenzeUltimoMeseTutti(): Record< inizio.setDate(inizio.getDate() - 30); const da = inizio.toISOString().slice(0, 10); const a = oggi.toISOString().slice(0, 10); + const idRosa = squadra.filter((g) => g.attivo).map((g) => g.id); const rilevanti = eventi.filter( (e) => (e.tipo === "partita" || e.tipo === "allenamento") && e.data >= da && e.data <= a, @@ -60,7 +62,7 @@ export function usePresenzeUltimoMeseTutti(): Record< const out: Record = {}; for (const e of rilevanti) { - const ids = e.convocati.length > 0 ? e.convocati : giocatori.map((g) => g.id); + const ids = e.convocati.length > 0 ? e.convocati : idRosa; for (const id of ids) { const rec = (out[id] ??= { presenti: 0, totali: 0, percentuale: 0 }); rec.totali += 1; @@ -72,5 +74,5 @@ export function usePresenzeUltimoMeseTutti(): Record< rec.percentuale = rec.totali ? Math.round((rec.presenti / rec.totali) * 100) : 0; } return out; - }, [eventi, presenze]); + }, [eventi, presenze, squadra]); } diff --git a/src/lib/user-store.ts b/src/lib/user-store.ts index f88179d..3b3c4c9 100644 --- a/src/lib/user-store.ts +++ b/src/lib/user-store.ts @@ -1,5 +1,6 @@ import { useSyncExternalStore } from "react"; -import { giocatori, type Giocatore } from "./crapp-data"; +import type { Giocatore } from "./crapp-data"; +import { useGiocatoriSquadra, type GiocatoreSquadra } from "./giocatori-squadra"; import { useIo } from "./rosa"; const KEY = "crapp-user-v1"; @@ -49,9 +50,10 @@ export function useGiocatoreId(): string | null { } /** Anagrafica base dal localStorage: le statistiche restano a zero finché non passa da `useIo`. */ -export function useGiocatoreBase(): Giocatore | null { +export function useGiocatoreBase(): GiocatoreSquadra | null { const id = useGiocatoreId(); - return id ? (giocatori.find((x) => x.id === id) ?? null) : null; + const { righe } = useGiocatoriSquadra(); + return id ? (righe.find((x) => x.id === id) ?? null) : null; } /** Giocatore corrente con statistiche calcolate da dati reali (presenze, MVP, badge, …). */ diff --git a/src/routes/api/public/promemoria-palloni.ts b/src/routes/api/public/promemoria-palloni.ts index 7ecca3a..6e0b5b4 100644 --- a/src/routes/api/public/promemoria-palloni.ts +++ b/src/routes/api/public/promemoria-palloni.ts @@ -1,4 +1,6 @@ import { createFileRoute } from "@tanstack/react-router"; +import { nomeCompleto } from "@/lib/giocatori-squadra"; +import { leggiGiocatoriSquadra } from "@/lib/giocatori-squadra.server"; import { completaTurni, destinatariPromemoriaPalloni, oggiISO } from "@/lib/palloni-core"; import { inviaPush } from "@/lib/webpush.server"; import { leggiEventi } from "@/lib/eventi.server"; @@ -15,7 +17,11 @@ export const Route = createFileRoute("/api/public/promemoria-palloni")({ const salvati: Record = {}; for (const riga of righe ?? []) salvati[riga.evento_id] = riga.giocatore_id; const eventi = await leggiEventi(); - const turni = completaTurni(salvati, eventi); + const squadra = await leggiGiocatoriSquadra(); + const rosa = squadra + .filter((g) => g.attivo) + .map((g) => ({ id: g.id, nome: nomeCompleto(g) })); + const turni = completaTurni(salvati, eventi, rosa); const oggi = oggiISO(); const destinatari = destinatariPromemoriaPalloni(turni, eventi, oggi); diff --git a/src/routes/api/public/push-messaggio.ts b/src/routes/api/public/push-messaggio.ts index 3907464..3c57a62 100644 --- a/src/routes/api/public/push-messaggio.ts +++ b/src/routes/api/public/push-messaggio.ts @@ -1,6 +1,7 @@ import { createFileRoute } from "@tanstack/react-router"; import { z } from "zod"; -import { giocatori } from "@/lib/crapp-data"; +import { nomeCompleto } from "@/lib/giocatori-squadra"; +import { leggiGiocatoriSquadra } from "@/lib/giocatori-squadra.server"; import { completaTurni, messaggioPalloniOggi, oggiISO } from "@/lib/palloni-core"; import { leggiEventi } from "@/lib/eventi.server"; @@ -43,11 +44,16 @@ export const Route = createFileRoute("/api/public/push-messaggio")({ const salvati: Record = {}; for (const riga of righe ?? []) salvati[riga.evento_id] = riga.giocatore_id; const eventi = await leggiEventi(); - const turni = completaTurni(salvati, eventi); + const squadra = await leggiGiocatoriSquadra(); + const rosa = squadra + .filter((g) => g.attivo) + .map((g) => ({ id: g.id, nome: nomeCompleto(g) })); + const turni = completaTurni(salvati, eventi, rosa); const oggi = oggiISO(); const mioId = iscrizione.giocatore_id; - const nome = giocatori.find((g) => g.id === mioId)?.nome ?? ""; + const giocatore = squadra.find((g) => g.id === mioId); + const nome = giocatore ? nomeCompleto(giocatore) : ""; return Response.json(messaggioPalloniOggi(turni, eventi, oggi, mioId, nome)); }, diff --git a/src/routes/api/public/sollecita-presenze.ts b/src/routes/api/public/sollecita-presenze.ts index 57d96ce..ad38505 100644 --- a/src/routes/api/public/sollecita-presenze.ts +++ b/src/routes/api/public/sollecita-presenze.ts @@ -1,7 +1,8 @@ import { createFileRoute } from "@tanstack/react-router"; import { z } from "zod"; -import { formatData, giocatori } from "@/lib/crapp-data"; +import { formatData } from "@/lib/crapp-data"; import { leggiEventi } from "@/lib/eventi.server"; +import { leggiGiocatoriSquadra } from "@/lib/giocatori-squadra.server"; import { inviaPush } from "@/lib/webpush.server"; const schema = z.object({ @@ -27,8 +28,10 @@ export const Route = createFileRoute("/api/public/sollecita-presenze")({ .select("giocatore_id, stato") .eq("evento_id", evento.id); + const squadra = await leggiGiocatoriSquadra(); const stati = new Map((righe ?? []).map((r) => [r.giocatore_id, r.stato])); - const destinatari = giocatori + const destinatari = squadra + .filter((g) => g.attivo) .filter((g) => { const stato = stati.get(g.id); return stato === undefined || stato === "forse";