Initial independent version of CrAPP
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
import { useState } from "react";
|
||||
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 { useGiocatoreCorrente } from "@/lib/user-store";
|
||||
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 = giocatori,
|
||||
chiuse = false,
|
||||
}: {
|
||||
matchId: string;
|
||||
convocati?: typeof giocatori;
|
||||
chiuse?: boolean;
|
||||
}) {
|
||||
const io = useGiocatoreCorrente();
|
||||
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 (
|
||||
<div className="rounded-3xl bg-card p-4 shadow-card">
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<p className="inline-flex items-center gap-1.5 text-[11px] font-bold uppercase tracking-wide text-muted-foreground">
|
||||
<ClipboardCheck className="h-3.5 w-3.5" /> Pagelle anonime
|
||||
</p>
|
||||
<span className="rounded-full bg-secondary px-2.5 py-1 text-[10px] font-bold uppercase text-muted-foreground">
|
||||
{chiuse ? "Votazioni chiuse" : `${fatti}/${daVotare.length} votati`}
|
||||
</span>
|
||||
</div>
|
||||
<p className="mt-1 text-[11px] text-muted-foreground">
|
||||
Dai un voto da 1 a 10 ai compagni: nessuno vedrà chi ha votato cosa, solo la media.
|
||||
</p>
|
||||
|
||||
{isPending ? (
|
||||
<p className="mt-3 text-xs text-muted-foreground">Carico le pagelle…</p>
|
||||
) : (
|
||||
<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-[11px] text-muted-foreground">
|
||||
{media ? `Media ${media.media} · ${media.voti} voti` : "Nessun voto"}
|
||||
</span>
|
||||
</span>
|
||||
{sonoIo ? (
|
||||
<span className="shrink-0 text-[10px] font-semibold uppercase text-muted-foreground">
|
||||
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-[10px] font-bold uppercase transition-colors disabled:opacity-50",
|
||||
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-[11px] font-bold tabular-nums transition-transform active:scale-90",
|
||||
mio === v ? "bg-accent text-accent-foreground" : "bg-card text-foreground",
|
||||
)}
|
||||
>
|
||||
{v}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user