Files
CRAPP/src/components/crapp/Pagelle.tsx
T

128 lines
5.1 KiB
TypeScript
Raw Normal View History

2026-08-06 08:36:47 +02:00
import { useState } from "react";
import { ClipboardCheck, Lock } from "lucide-react";
import { toast } from "sonner";
import { cn } from "@/lib/utils";
import { Card } from "@/components/crapp/ui-bits";
2026-08-06 08:36:47 +02:00
import { Avatar } from "@/components/crapp/Avatar";
import type { Giocatore } from "@/lib/crapp-data";
2026-09-24 14:54:35 +02:00
import { useGiocatoreInCampo } from "@/lib/user-store";
2026-08-06 08:36:47 +02:00
import { mieiVoti, pagellePartita, usePagelle, useVotaPagella } from "@/lib/pagelle";
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,
2026-08-06 08:36:47 +02:00
chiuse = false,
}: {
matchId: string;
convocati: Giocatore[];
2026-08-06 08:36:47 +02:00
chiuse?: boolean;
}) {
2026-09-24 14:54:35 +02:00
// Solo `.id` serve qui, niente statistiche; `null` per l'allenatore, che non gioca (DD-034).
const io = useGiocatoreInCampo();
2026-08-06 08:36:47 +02:00
const { voti: tutti, isPending } = usePagelle();
const vota = useVotaPagella();
const [apertoPer, setApertoPer] = useState<string | null>(null);
const medie = pagellePartita(tutti, matchId);
const miei = io ? mieiVoti(tutti, matchId, io.id) : {};
const daVotare = convocati.filter((g) => g.id !== io?.id);
const fatti = daVotare.filter((g) => miei[g.id] !== undefined).length;
async function invia(votatoId: string, voto: number) {
if (!io) return;
setApertoPer(null);
try {
await vota.mutateAsync({ match_id: matchId, votante_id: io.id, votato_id: votatoId, voto });
toast.success("Voto registrato (resta anonimo)");
} catch {
toast.error("Voto non riuscito, riprova");
}
}
return (
<Card>
2026-08-06 08:36:47 +02:00
<div className="flex items-center justify-between gap-2">
<p className="inline-flex items-center gap-1.5 text-xs font-bold uppercase tracking-wide text-muted-foreground">
2026-08-06 08:36:47 +02:00
<ClipboardCheck className="h-3.5 w-3.5" /> Pagelle anonime
</p>
<span className="rounded-full bg-secondary px-2.5 py-1 text-xs font-bold uppercase text-muted-foreground">
2026-08-06 08:36:47 +02:00
{chiuse ? "Votazioni chiuse" : `${fatti}/${daVotare.length} votati`}
</span>
</div>
<p className="mt-1 text-xs text-muted-foreground">
2026-08-06 08:36:47 +02:00
Dai un voto da 1 a 10 ai compagni: nessuno vedrà chi ha votato cosa, solo la media.
</p>
{isPending ? (
<p aria-busy="true" className="mt-3 text-xs text-muted-foreground">
Carico le pagelle…
</p>
2026-08-06 08:36:47 +02:00
) : (
<div className="mt-3 space-y-1.5">
{convocati.map((g) => {
const media = medie[g.id];
const mio = miei[g.id];
const sonoIo = g.id === io?.id;
const aperto = apertoPer === g.id;
return (
<div key={g.id} className="rounded-2xl bg-secondary/60 p-2.5">
<div className="flex items-center gap-2.5">
<Avatar id={g.id} fallback={String(g.numero)} className="h-8 w-8 text-xs" />
<span className="min-w-0 flex-1">
<span className="block truncate text-sm font-bold leading-tight">{g.nome}</span>
<span className="text-xs text-muted-foreground">
2026-08-06 08:36:47 +02:00
{media ? `Media ${media.media} · ${media.voti} voti` : "Nessun voto"}
</span>
</span>
{sonoIo ? (
<span className="shrink-0 text-xs font-semibold uppercase text-muted-foreground">
2026-08-06 08:36:47 +02:00
Sei tu
</span>
) : chiuse ? (
<Lock className="h-4 w-4 shrink-0 text-muted-foreground" />
) : (
<button
type="button"
disabled={!io || vota.isPending}
onClick={() => setApertoPer(aperto ? null : g.id)}
className={cn(
"shrink-0 rounded-full px-3 py-1 text-xs font-bold uppercase transition-colors disabled:opacity-50",
2026-08-06 08:36:47 +02:00
mio !== undefined
? "bg-accent text-accent-foreground"
: "bg-card text-foreground",
)}
>
{mio !== undefined ? `Hai dato ${mio}` : "Vota"}
</button>
)}
</div>
{aperto && !chiuse ? (
<div className="mt-2 grid grid-cols-10 gap-1">
{voti.map((v) => (
<button
key={v}
type="button"
onClick={() => invia(g.id, v)}
className={cn(
"rounded-lg py-1.5 text-xs font-bold tabular-nums transition-transform active:scale-90",
mio === v
? "bg-accent text-accent-foreground"
: "bg-card text-foreground",
2026-08-06 08:36:47 +02:00
)}
>
{v}
</button>
))}
</div>
) : null}
</div>
);
})}
</div>
)}
</Card>
2026-08-06 08:36:47 +02:00
);
}