Aggancia Squadra, Presenze, Pagelle, Scout e calendario alla rosa reale
convocatiEvento() e compleanniEventi() prendono ora la rosa come parametro invece di leggere la lista statica; csvScoutMatch() idem. Le schermate di gestione eventi, dettaglio allenamento/partita, calendario e scout live, più i widget di presenze/pagelle/voto MVP/voto social/sondaggio cacche/turno palloni, passano tutte la rosa letta da useRosa() o useGiocatoriSquadra(): un giocatore aggiunto o disattivato dalla dashboard admin ora si riflette ovunque.
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 }) {
|
||||
<div className="rounded-3xl bg-card p-4 shadow-card">
|
||||
<div className="flex items-baseline justify-between">
|
||||
<p className="text-sm font-bold">
|
||||
Hanno risposto {risposteN}/{giocatori.length}
|
||||
Hanno risposto {risposteN}/{rosa.length}
|
||||
</p>
|
||||
<span className="font-display text-xl leading-none">{perc}%</span>
|
||||
</div>
|
||||
@@ -59,7 +60,7 @@ export function RosaPresenze({ eventoId }: { eventoId: string }) {
|
||||
|
||||
<div className="mt-3 flex flex-wrap gap-1.5">
|
||||
{ordine.map((s) => {
|
||||
const n = giocatori.filter((g) => risposte[g.id] === s).length;
|
||||
const n = rosa.filter((g) => risposte[g.id] === s).length;
|
||||
return (
|
||||
<span
|
||||
key={s}
|
||||
@@ -130,7 +131,7 @@ export function RosaPresenze({ eventoId }: { eventoId: string }) {
|
||||
) : null}
|
||||
|
||||
{ordine.map((s) => {
|
||||
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 (
|
||||
<Gruppo
|
||||
@@ -162,7 +163,7 @@ function Gruppo({
|
||||
}: {
|
||||
titolo: string;
|
||||
n: number;
|
||||
lista: typeof giocatori;
|
||||
lista: Giocatore[];
|
||||
attenzione?: boolean;
|
||||
}) {
|
||||
return (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
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 { mediaPartita, useCacche, useSalvaCacche } from "@/lib/cacche";
|
||||
|
||||
@@ -11,6 +11,7 @@ export function SondaggioCacche({ eventoId }: { eventoId: string }) {
|
||||
const io = useGiocatoreCorrente();
|
||||
const { righe } = useCacche();
|
||||
const salva = useSalvaCacche();
|
||||
const { righe: squadra } = useGiocatoriSquadra();
|
||||
|
||||
const dellaPartita = righe.filter((r) => 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;
|
||||
|
||||
@@ -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 ? (
|
||||
<Loader2 className="h-3.5 w-3.5 animate-spin text-muted-foreground" />
|
||||
) : null}
|
||||
<span className="truncate">{giocatore ? giocatore.nome : "Da assegnare"}</span>
|
||||
<span className="truncate">{giocatore ? nomeCompleto(giocatore) : "Da assegnare"}</span>
|
||||
{proposto && giocatore ? (
|
||||
<span className="shrink-0 rounded-full bg-card px-1.5 py-0.5 text-[9px] font-bold uppercase text-muted-foreground">
|
||||
proposto
|
||||
@@ -59,7 +61,7 @@ export function TurnoPalloni({ eventoId }: { eventoId: string }) {
|
||||
|
||||
{aperto ? (
|
||||
<div className="mt-2.5 max-h-56 space-y-1 overflow-y-auto rounded-xl bg-card p-1.5">
|
||||
{giocatori.map((g) => (
|
||||
{rosa.map((g) => (
|
||||
<button
|
||||
key={g.id}
|
||||
type="button"
|
||||
@@ -70,7 +72,7 @@ export function TurnoPalloni({ eventoId }: { eventoId: string }) {
|
||||
)}
|
||||
>
|
||||
<Avatar id={g.id} fallback={String(g.numero)} className="h-7 w-7 text-xs" />
|
||||
<span className="min-w-0 flex-1 truncate">{g.nome}</span>
|
||||
<span className="min-w-0 flex-1 truncate">{nomeCompleto(g)}</span>
|
||||
{g.id === id ? <Check className="h-4 w-4 shrink-0 text-accent" /> : null}
|
||||
</button>
|
||||
))}
|
||||
|
||||
@@ -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 ? (
|
||||
<div className="mt-3 grid max-h-60 grid-cols-2 gap-1.5 overflow-y-auto">
|
||||
{giocatori.map((g) => (
|
||||
{rosa.map((g) => (
|
||||
<button
|
||||
key={g.id}
|
||||
type="button"
|
||||
disabled={vota.isPending}
|
||||
onClick={() => invia(g.id, g.nome)}
|
||||
onClick={() => invia(g.id, nomeCompleto(g))}
|
||||
className={cn(
|
||||
"truncate rounded-xl px-2.5 py-2 text-left text-xs font-semibold transition-colors",
|
||||
mio?.votato_id === g.id
|
||||
@@ -82,7 +84,7 @@ export function VotazioneMvp({ matchId }: { matchId: string }) {
|
||||
: "bg-card text-foreground",
|
||||
)}
|
||||
>
|
||||
{g.nome}
|
||||
{nomeCompleto(g)}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -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<string | null>(null);
|
||||
|
||||
const tutti = voti.data ?? [];
|
||||
@@ -93,14 +95,14 @@ export function VotoSocial({ matchId }: { matchId: string }) {
|
||||
|
||||
{isOpen ? (
|
||||
<div className="grid max-h-56 grid-cols-2 gap-1.5 overflow-y-auto border-t border-border p-3">
|
||||
{giocatori
|
||||
{rosa
|
||||
.filter((g) => g.id !== io?.id)
|
||||
.map((g) => (
|
||||
<button
|
||||
key={g.id}
|
||||
type="button"
|
||||
disabled={vota.isPending}
|
||||
onClick={() => invia(cat.id, g.id, g.nome)}
|
||||
onClick={() => invia(cat.id, g.id, nomeCompleto(g))}
|
||||
className={cn(
|
||||
"truncate rounded-xl px-2.5 py-2 text-left text-xs font-semibold transition-colors",
|
||||
mio?.votato_id === g.id
|
||||
@@ -108,7 +110,7 @@ export function VotoSocial({ matchId }: { matchId: string }) {
|
||||
: "bg-secondary text-foreground",
|
||||
)}
|
||||
>
|
||||
{g.nome}
|
||||
{nomeCompleto(g)}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user