diff --git a/src/components/crapp/EventoCard.tsx b/src/components/crapp/EventoCard.tsx index aa476ce..51fabc4 100644 --- a/src/components/crapp/EventoCard.tsx +++ b/src/components/crapp/EventoCard.tsx @@ -1,9 +1,10 @@ import { MapPin, Clock, Users, Cake, ArrowRight } from "lucide-react"; import { Link } from "@tanstack/react-router"; import { cn } from "@/lib/utils"; -import { formatData, giocatori, statoMeta, type Stato } from "@/lib/crapp-data"; +import { formatData, statoMeta, type Stato } from "@/lib/crapp-data"; import type { Evento } from "@/lib/eventi"; import { Barra } from "@/components/motion/Barra"; +import { useGiocatoriSquadra } from "@/lib/giocatori-squadra"; import { usePresenzeEvento, useSalvaPresenza } from "@/lib/presenze"; import { useGiocatoreCorrente } from "@/lib/user-store"; @@ -36,16 +37,18 @@ export function EventoCard({ const { risposte } = usePresenzeEvento(evento.id); const salva = useSalvaPresenza(); const io = useGiocatoreCorrente(); + const { righe: squadra } = useGiocatoriSquadra(); + const rosa = squadra.filter((g) => g.attivo); const stato = io ? risposte[io.id] : undefined; - const presentiVeri = giocatori.filter( + const presentiVeri = rosa.filter( (g) => risposte[g.id] === "presente" || risposte[g.id] === "ritardo", ).length; const tipo = evento.tipo === "partita" && !evento.campionato ? { label: "Amichevole", className: "bg-accent/70 text-accent-foreground" } : tipoMeta[evento.tipo]; - const totale = giocatori.length; - const perc = Math.round((presentiVeri / totale) * 100); + const totale = rosa.length; + const perc = totale ? Math.round((presentiVeri / totale) * 100) : 0; const isCompleanno = evento.tipo === "compleanno"; if (isCompleanno) { diff --git a/src/components/crapp/Pagelle.tsx b/src/components/crapp/Pagelle.tsx index fe6ceb3..cb23062 100644 --- a/src/components/crapp/Pagelle.tsx +++ b/src/components/crapp/Pagelle.tsx @@ -3,7 +3,7 @@ import { ClipboardCheck, Lock } from "lucide-react"; import { toast } from "sonner"; import { cn } from "@/lib/utils"; import { Avatar } from "@/components/crapp/Avatar"; -import { giocatori } from "@/lib/crapp-data"; +import type { Giocatore } from "@/lib/crapp-data"; import { useGiocatoreCorrente } from "@/lib/user-store"; import { mieiVoti, pagellePartita, usePagelle, useVotaPagella } from "@/lib/pagelle"; @@ -12,11 +12,11 @@ const voti = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; /** Pagelle di fine partita: voto anonimo 1-10 a ciascun compagno. */ export function Pagelle({ matchId, - convocati = giocatori, + convocati, chiuse = false, }: { matchId: string; - convocati?: typeof giocatori; + convocati: Giocatore[]; chiuse?: boolean; }) { const io = useGiocatoreCorrente(); diff --git a/src/components/crapp/RosaPresenze.tsx b/src/components/crapp/RosaPresenze.tsx index b16c018..8cafd8d 100644 --- a/src/components/crapp/RosaPresenze.tsx +++ b/src/components/crapp/RosaPresenze.tsx @@ -4,8 +4,9 @@ import { toast } from "sonner"; import { cn } from "@/lib/utils"; import { Avatar } from "@/components/crapp/Avatar"; import { Barra } from "@/components/motion/Barra"; -import { giocatori, statoMeta, type Stato } from "@/lib/crapp-data"; +import { statoMeta, type Giocatore, type Stato } from "@/lib/crapp-data"; import { usePresenzeEvento, useSalvaPresenza } from "@/lib/presenze"; +import { useRosa } from "@/lib/rosa"; import { useGiocatoreCorrente } from "@/lib/user-store"; import { useIsAdmin } from "@/lib/ruoli"; @@ -16,13 +17,13 @@ export function RosaPresenze({ eventoId }: { eventoId: string }) { const salva = useSalvaPresenza(); const io = useGiocatoreCorrente(); const admin = useIsAdmin(); + const rosa = useRosa(); const [sollecito, setSollecito] = useState(false); - const mancanti = giocatori.filter((g) => !risposte[g.id]); - const risposteN = giocatori.length - mancanti.length; - const perc = Math.round((risposteN / giocatori.length) * 100); - const daSollecitare = - mancanti.length + giocatori.filter((g) => risposte[g.id] === "forse").length; + const mancanti = rosa.filter((g) => !risposte[g.id]); + const risposteN = rosa.length - mancanti.length; + const perc = rosa.length ? Math.round((risposteN / rosa.length) * 100) : 0; + const daSollecitare = mancanti.length + rosa.filter((g) => risposte[g.id] === "forse").length; async function sollecita() { setSollecito(true); @@ -51,7 +52,7 @@ export function RosaPresenze({ eventoId }: { eventoId: string }) {

- Hanno risposto {risposteN}/{giocatori.length} + Hanno risposto {risposteN}/{rosa.length}

{perc}%
@@ -59,7 +60,7 @@ export function RosaPresenze({ eventoId }: { eventoId: string }) {
{ordine.map((s) => { - const n = giocatori.filter((g) => risposte[g.id] === s).length; + const n = rosa.filter((g) => risposte[g.id] === s).length; return ( { - const lista = giocatori.filter((g) => risposte[g.id] === s); + const lista = rosa.filter((g) => risposte[g.id] === s); if (lista.length === 0) return null; return ( r.evento_id === eventoId); const mia = dellaPartita.find((r) => r.giocatore_id === io?.id); @@ -18,7 +19,10 @@ export function SondaggioCacche({ eventoId }: { eventoId: string }) { const classifica = [...dellaPartita] .sort((a, b) => b.quantita - a.quantita) .slice(0, 3) - .map((r) => ({ ...r, nome: giocatori.find((g) => g.id === r.giocatore_id)?.nome ?? "—" })); + .map((r) => { + const g = squadra.find((g) => g.id === r.giocatore_id); + return { ...r, nome: g ? nomeCompleto(g) : "—" }; + }); async function rispondi(quantita: number) { if (!io) return; diff --git a/src/components/crapp/TurnoPalloni.tsx b/src/components/crapp/TurnoPalloni.tsx index da02d8a..8428344 100644 --- a/src/components/crapp/TurnoPalloni.tsx +++ b/src/components/crapp/TurnoPalloni.tsx @@ -3,7 +3,7 @@ import { Check, CircleDot, Loader2, Pencil } from "lucide-react"; import { toast } from "sonner"; import { cn } from "@/lib/utils"; import { Avatar } from "@/components/crapp/Avatar"; -import { giocatori } from "@/lib/crapp-data"; +import { nomeCompleto, useGiocatoriSquadra } from "@/lib/giocatori-squadra"; import { useAssegnaTurno, useTurniPalloni } from "@/lib/palloni"; import { useGiocatoreCorrente } from "@/lib/user-store"; @@ -12,10 +12,12 @@ export function TurnoPalloni({ eventoId }: { eventoId: string }) { const { salvati, turni, isPending } = useTurniPalloni(); const assegna = useAssegnaTurno(); const io = useGiocatoreCorrente(); + const { righe: squadra } = useGiocatoriSquadra(); + const rosa = squadra.filter((g) => g.attivo); const id = turni[eventoId]; const proposto = !salvati[eventoId]; - const giocatore = giocatori.find((g) => g.id === id); + const giocatore = rosa.find((g) => g.id === id); function scegli(giocatoreId: string) { setAperto(false); @@ -46,7 +48,7 @@ export function TurnoPalloni({ eventoId }: { eventoId: string }) { {isPending || assegna.isPending ? ( ) : null} - {giocatore ? giocatore.nome : "Da assegnare"} + {giocatore ? nomeCompleto(giocatore) : "Da assegnare"} {proposto && giocatore ? ( proposto @@ -59,7 +61,7 @@ export function TurnoPalloni({ eventoId }: { eventoId: string }) { {aperto ? (
- {giocatori.map((g) => ( + {rosa.map((g) => ( ))} diff --git a/src/components/crapp/VotazioneMvp.tsx b/src/components/crapp/VotazioneMvp.tsx index f48c9ee..d678964 100644 --- a/src/components/crapp/VotazioneMvp.tsx +++ b/src/components/crapp/VotazioneMvp.tsx @@ -2,7 +2,7 @@ import { useState } from "react"; import { Crown, Vote } from "lucide-react"; import { toast } from "sonner"; import { cn } from "@/lib/utils"; -import { giocatori } from "@/lib/crapp-data"; +import { nomeCompleto, useGiocatoriSquadra } from "@/lib/giocatori-squadra"; import { useGiocatoreCorrente } from "@/lib/user-store"; import { conteggioPartita, mioVoto, useVotaMvp, useVotiMvp, type VotoMvp } from "@/lib/mvp-voti"; @@ -11,6 +11,8 @@ export function VotazioneMvp({ matchId }: { matchId: string }) { const io = useGiocatoreCorrente(); const voti = useVotiMvp(); const vota = useVotaMvp(); + const { righe: squadra } = useGiocatoriSquadra(); + const rosa = squadra.filter((g) => g.attivo); const [aperto, setAperto] = useState(false); const tutti: VotoMvp[] = voti.data ?? []; @@ -69,12 +71,12 @@ export function VotazioneMvp({ matchId }: { matchId: string }) { {aperto ? (
- {giocatori.map((g) => ( + {rosa.map((g) => ( ))}
diff --git a/src/components/crapp/VotoSocial.tsx b/src/components/crapp/VotoSocial.tsx index 5d66460..881b248 100644 --- a/src/components/crapp/VotoSocial.tsx +++ b/src/components/crapp/VotoSocial.tsx @@ -2,7 +2,7 @@ import { useState } from "react"; import { Check, Crown, Sparkles } from "lucide-react"; import { toast } from "sonner"; import { cn } from "@/lib/utils"; -import { giocatori } from "@/lib/crapp-data"; +import { nomeCompleto, useGiocatoriSquadra } from "@/lib/giocatori-squadra"; import { useGiocatoreCorrente } from "@/lib/user-store"; import { categorieSocial, @@ -18,6 +18,8 @@ export function VotoSocial({ matchId }: { matchId: string }) { const io = useGiocatoreCorrente(); const voti = useVotiSocial(); const vota = useVotaSocial(); + const { righe: squadra } = useGiocatoriSquadra(); + const rosa = squadra.filter((g) => g.attivo); const [aperta, setAperta] = useState(null); const tutti = voti.data ?? []; @@ -93,14 +95,14 @@ export function VotoSocial({ matchId }: { matchId: string }) { {isOpen ? (
- {giocatori + {rosa .filter((g) => g.id !== io?.id) .map((g) => ( ))}
diff --git a/src/lib/eventi.ts b/src/lib/eventi.ts index 78cce06..a3aa316 100644 --- a/src/lib/eventi.ts +++ b/src/lib/eventi.ts @@ -1,6 +1,6 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { supabase } from "@/integrations/supabase/client"; -import { giocatori } from "./crapp-data"; +import type { Giocatore } from "./crapp-data"; export type EventoTipo = "partita" | "allenamento" | "evento" | "compleanno"; @@ -158,8 +158,9 @@ export function useEliminaEvento() { } /** Compleanni della rosa, come eventi di calendario dell'anno indicato. */ -export function compleanniEventi(anno = new Date().getFullYear()): Evento[] { - return giocatori +export function compleanniEventi(rosa: Giocatore[], anno = new Date().getFullYear()): Evento[] { + return rosa + .filter((g) => g.nascita) .map((g) => { const md = g.nascita.slice(5); const eta = anno - Number(g.nascita.slice(0, 4)); @@ -181,7 +182,7 @@ export function compleanniEventi(anno = new Date().getFullYear()): Evento[] { } /** Rosa convocata per un evento: se non specificata vale tutta la rosa. */ -export function convocatiEvento(evento: Evento | null) { - if (!evento || evento.convocati.length === 0) return giocatori; - return giocatori.filter((g) => evento.convocati.includes(g.id)); +export function convocatiEvento(evento: Evento | null, rosa: Giocatore[]) { + if (!evento || evento.convocati.length === 0) return rosa; + return rosa.filter((g) => evento.convocati.includes(g.id)); } diff --git a/src/lib/scout-export.ts b/src/lib/scout-export.ts index 9cf4e22..bafe5cc 100644 --- a/src/lib/scout-export.ts +++ b/src/lib/scout-export.ts @@ -1,4 +1,4 @@ -import { giocatori } from "./crapp-data"; +import type { Giocatore } from "./crapp-data"; import { azioniMeta, totaliPerGiocatore, type ScoutMatch } from "./scout-store"; /** Riga CSV con separatore ";" (Excel IT), riusata anche dall'export tesseramento. */ @@ -12,7 +12,7 @@ export function rigaCsv(campi: Array) { } /** Esporta la scoutizzazione di una partita in CSV (separatore ";" per Excel IT). */ -export function csvScoutMatch(match: ScoutMatch): string { +export function csvScoutMatch(match: ScoutMatch, rosa: Giocatore[]): string { const righe: string[] = []; righe.push( rigaCsv([ @@ -29,7 +29,7 @@ export function csvScoutMatch(match: ScoutMatch): string { righe.push(""); righe.push(rigaCsv(["Numero", "Giocatore", "Ruolo", "Punti", "Ace", "Muri", "Errori"])); const totali = totaliPerGiocatore(match.azioni); - for (const g of giocatori) { + for (const g of rosa) { const t = totali.get(g.id); if (!t) continue; righe.push(rigaCsv([g.numero, g.nome, g.ruolo, t.punti, t.ace, t.muri, t.errori])); @@ -37,7 +37,7 @@ export function csvScoutMatch(match: ScoutMatch): string { righe.push(""); righe.push(rigaCsv(["Set", "Giocatore", "Azione"])); for (const a of match.azioni) { - const g = giocatori.find((x) => x.id === a.giocatoreId); + const g = rosa.find((x) => x.id === a.giocatoreId); righe.push(rigaCsv([a.set, g?.nome ?? "—", azioniMeta[a.tipo].label])); } return righe.join("\n"); diff --git a/src/routes/allenamento.$id.tsx b/src/routes/allenamento.$id.tsx index fcd32eb..456f8fe 100644 --- a/src/routes/allenamento.$id.tsx +++ b/src/routes/allenamento.$id.tsx @@ -1,8 +1,9 @@ import { createFileRoute, Link } from "@tanstack/react-router"; import { ArrowLeft, MapPin, Clock, Users, Dumbbell, CalendarDays } from "lucide-react"; import { PageHeader, Section } from "@/components/crapp/ui-bits"; -import { formatData, giocatori } from "@/lib/crapp-data"; +import { formatData } from "@/lib/crapp-data"; import { convocatiEvento, useEvento } from "@/lib/eventi"; +import { useRosa } from "@/lib/rosa"; import { TurnoPalloni } from "@/components/crapp/TurnoPalloni"; import { RosaPresenze } from "@/components/crapp/RosaPresenze"; import { usePresenzeEvento } from "@/lib/presenze"; @@ -34,7 +35,8 @@ function AllenamentoDetail() { const { id } = Route.useParams(); const { evento } = useEvento(id); const { risposte } = usePresenzeEvento(id); - const presentiVeri = giocatori.filter( + const rosa = useRosa(); + const presentiVeri = rosa.filter( (g) => risposte[g.id] === "presente" || risposte[g.id] === "ritardo", ).length; @@ -90,7 +92,7 @@ function AllenamentoDetail() {
- Conferme: {presentiVeri}/{convocatiEvento(evento).length} + Conferme: {presentiVeri}/{convocatiEvento(evento, rosa).length}
diff --git a/src/routes/calendario.tsx b/src/routes/calendario.tsx index 1c932dc..ed01735 100644 --- a/src/routes/calendario.tsx +++ b/src/routes/calendario.tsx @@ -6,6 +6,7 @@ import { cn } from "@/lib/utils"; import { EventoCard, linkPerEvento } from "@/components/crapp/EventoCard"; import { PageHeader, Section } from "@/components/crapp/ui-bits"; import { compleanniEventi, useEventi, type Evento } from "@/lib/eventi"; +import { useRosa } from "@/lib/rosa"; import { useGiocatoreCorrente } from "@/lib/user-store"; import { useIsAdmin } from "@/lib/ruoli"; import { @@ -100,11 +101,12 @@ function Calendario() { const io = useGiocatoreCorrente(); const admin = useIsAdmin(); const { eventi } = useEventi(); + const rosa = useRosa(); const { anno, mese, precedente, successivo } = useMeseNav(); const { giorni, offsetLunedi } = giorniDelMese(anno, mese); const mesePrefix = `${anno}-${pad2(mese + 1)}`; - const compleanni = compleanniEventi(anno); + const compleanni = compleanniEventi(rosa, anno); const compleanniMese = compleanni.filter((c) => c.data.startsWith(mesePrefix)); const eventiMese = eventi.filter((e) => e.data.startsWith(mesePrefix)); diff --git a/src/routes/eventi.tsx b/src/routes/eventi.tsx index b870418..39803ac 100644 --- a/src/routes/eventi.tsx +++ b/src/routes/eventi.tsx @@ -4,7 +4,8 @@ import { ArrowLeft, CalendarPlus, Loader2, Pencil, Trash2 } from "lucide-react"; import { toast } from "sonner"; import { cn } from "@/lib/utils"; import { PageHeader, Section } from "@/components/crapp/ui-bits"; -import { formatData, giocatori } from "@/lib/crapp-data"; +import { formatData } from "@/lib/crapp-data"; +import { nomeCompleto, useGiocatoriSquadra } from "@/lib/giocatori-squadra"; import { categoriaEvento, daCategoria, @@ -50,9 +51,11 @@ function GestioneEventi() { const io = useGiocatoreCorrente(); const admin = useIsAdmin(); const { eventi, isPending } = useEventi(); + const { righe: squadra } = useGiocatoriSquadra(); const salva = useSalvaEvento(); const elimina = useEliminaEvento(); const [bozza, setBozza] = useState(null); + const rosa = squadra.filter((g) => g.attivo); if (!io || !admin) { return ( @@ -228,7 +231,7 @@ function GestioneEventi() { label={`Convocati (${bozza.convocati.length === 0 ? "tutta la rosa" : bozza.convocati.length})`} >
- {giocatori.map((g) => { + {rosa.map((g) => { const scelto = bozza.convocati.includes(g.id); return ( ); })} diff --git a/src/routes/partita.$id.tsx b/src/routes/partita.$id.tsx index 64ac78d..f324424 100644 --- a/src/routes/partita.$id.tsx +++ b/src/routes/partita.$id.tsx @@ -2,8 +2,9 @@ import { createFileRoute, Link } from "@tanstack/react-router"; import { ArrowLeft, MapPin, Clock, Users, Trophy, Swords, Download } from "lucide-react"; import { cn } from "@/lib/utils"; import { PageHeader, Section } from "@/components/crapp/ui-bits"; -import { formatData, giocatori } from "@/lib/crapp-data"; +import { formatData } from "@/lib/crapp-data"; import { convocatiEvento, useEvento } from "@/lib/eventi"; +import { useRosa } from "@/lib/rosa"; import { useCsi } from "@/lib/csi"; import { matchDaPartitaCsi, partiteGiocate } from "@/lib/csi-core"; import { Pagelle } from "@/components/crapp/Pagelle"; @@ -49,7 +50,8 @@ function PartitaDetail() { const scoutMatches = useScoutMatches(); const { data: csi } = useCsi(); const { risposte } = usePresenzeEvento(id); - const presentiVeri = giocatori.filter( + const rosa = useRosa(); + const presentiVeri = rosa.filter( (g) => risposte[g.id] === "presente" || risposte[g.id] === "ritardo", ).length; @@ -67,7 +69,7 @@ function PartitaDetail() { ); } - const convocati = convocatiEvento(evento); + const convocati = convocatiEvento(evento, rosa); const scout = scoutMatches.find((m) => m.id === evento.id || m.data === evento.data) ?? null; const csiMatch = csi ? partiteGiocate(csi.partite).find((p) => p.data === evento.data) @@ -229,7 +231,7 @@ function PartitaDetail() {

{[...totaliPerGiocatore(scout.azioni).entries()].map(([gid, t]) => { - const g = giocatori.find((x) => x.id === gid); + const g = rosa.find((x) => x.id === gid); if (!g) return null; return (
- scaricaCsv(`scout-${scout.data}-${scout.avversario}.csv`, csvScoutMatch(scout)) + scaricaCsv( + `scout-${scout.data}-${scout.avversario}.csv`, + csvScoutMatch(scout, rosa), + ) } className="premi mt-4 flex w-full items-center justify-center gap-2 rounded-2xl bg-accent-grad py-3 text-sm font-bold uppercase text-accent-foreground shadow-pop" > diff --git a/src/routes/scout.tsx b/src/routes/scout.tsx index 7a45436..b7f2815 100644 --- a/src/routes/scout.tsx +++ b/src/routes/scout.tsx @@ -3,8 +3,9 @@ import { createFileRoute, useNavigate } from "@tanstack/react-router"; import { Undo2, Save, CheckCircle2, Radio, Lock, CalendarX2, LogOut } from "lucide-react"; import { toast } from "sonner"; import { cn } from "@/lib/utils"; -import { giocatori, formatData } from "@/lib/crapp-data"; +import { formatData } from "@/lib/crapp-data"; import type { Evento } from "@/lib/eventi"; +import { useRosa } from "@/lib/rosa"; import { useGiocatoreCorrente } from "@/lib/user-store"; import { useIsAdmin } from "@/lib/ruoli"; import { usePresenzeEvento } from "@/lib/presenze"; @@ -214,15 +215,16 @@ function ScoutBoard({ const cancella = useCancellaStatoScout(); const salvaMatch = useSalvaScoutMatch(); const { risposte } = usePresenzeEvento(partita.id); + const rosa = useRosa(); const finito = useRef(false); /** In campo solo chi ha confermato la presenza (anche in ritardo). */ const convocati = useMemo(() => { - const presenti = giocatori.filter( + const presenti = rosa.filter( (g) => risposte[g.id] === "presente" || risposte[g.id] === "ritardo", ); - return presenti.length > 0 ? presenti : giocatori; - }, [risposte]); + return presenti.length > 0 ? presenti : rosa; + }, [risposte, rosa]); // Salvataggio condiviso: se chi scouta si disconnette, il prossimo riprende da qui. useEffect(() => { @@ -453,7 +455,7 @@ function ScoutBoard({ ) : (
    {ultime.map((a, i) => { - const g = giocatori.find((x) => x.id === a.giocatoreId); + const g = rosa.find((x) => x.id === a.giocatoreId); return (