2026-08-06 08:36:47 +02:00
|
|
|
import { useState } from "react";
|
2026-09-06 12:15:39 +02:00
|
|
|
import { BellRing, Check, CircleDot, Loader2, Pencil } from "lucide-react";
|
2026-08-06 08:36:47 +02:00
|
|
|
import { toast } from "sonner";
|
|
|
|
|
import { cn } from "@/lib/utils";
|
|
|
|
|
import { Avatar } from "@/components/crapp/Avatar";
|
2026-09-06 12:15:39 +02:00
|
|
|
import { intestazioniAutenticate } from "@/lib/auth";
|
2026-09-03 10:17:03 +02:00
|
|
|
import { nomeCompleto, useGiocatoriSquadra } from "@/lib/giocatori-squadra";
|
2026-08-06 08:36:47 +02:00
|
|
|
import { useAssegnaTurno, useTurniPalloni } from "@/lib/palloni";
|
2026-09-06 12:15:39 +02:00
|
|
|
import { useIsAdmin } from "@/lib/ruoli";
|
2026-09-08 10:00:52 +02:00
|
|
|
import { useGiocatoreBase } from "@/lib/user-store";
|
2026-08-06 08:36:47 +02:00
|
|
|
|
|
|
|
|
export function TurnoPalloni({ eventoId }: { eventoId: string }) {
|
|
|
|
|
const [aperto, setAperto] = useState(false);
|
2026-09-06 12:15:39 +02:00
|
|
|
const [avviso, setAvviso] = useState(false);
|
2026-08-06 08:36:47 +02:00
|
|
|
const { salvati, turni, isPending } = useTurniPalloni();
|
|
|
|
|
const assegna = useAssegnaTurno();
|
2026-09-08 10:00:52 +02:00
|
|
|
// Solo `.nome` serve qui: `useGiocatoreBase` basta, niente statistiche.
|
|
|
|
|
const io = useGiocatoreBase();
|
2026-09-06 12:15:39 +02:00
|
|
|
const admin = useIsAdmin();
|
2026-09-03 10:17:03 +02:00
|
|
|
const { righe: squadra } = useGiocatoriSquadra();
|
|
|
|
|
const rosa = squadra.filter((g) => g.attivo);
|
2026-08-06 08:36:47 +02:00
|
|
|
|
|
|
|
|
const id = turni[eventoId];
|
|
|
|
|
const proposto = !salvati[eventoId];
|
2026-09-03 10:17:03 +02:00
|
|
|
const giocatore = rosa.find((g) => g.id === id);
|
2026-08-06 08:36:47 +02:00
|
|
|
|
|
|
|
|
function scegli(giocatoreId: string) {
|
|
|
|
|
setAperto(false);
|
|
|
|
|
assegna.mutate(
|
|
|
|
|
{ eventoId, giocatoreId, da: io?.nome ?? null },
|
|
|
|
|
{
|
|
|
|
|
onSuccess: () => toast.success("Turno palloni aggiornato"),
|
|
|
|
|
onError: () => toast.error("Non sono riuscito a salvare il turno"),
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-06 12:15:39 +02:00
|
|
|
/** Avvisa via push chi è di turno per questo evento, e chi deve riportare i palloni. */
|
|
|
|
|
async function avvisa() {
|
|
|
|
|
setAvviso(true);
|
|
|
|
|
try {
|
|
|
|
|
const res = await fetch("/api/public/promemoria-palloni", {
|
|
|
|
|
method: "POST",
|
|
|
|
|
headers: { "Content-Type": "application/json", ...(await intestazioniAutenticate()) },
|
|
|
|
|
body: JSON.stringify({ eventoId }),
|
|
|
|
|
});
|
|
|
|
|
if (!res.ok) throw new Error();
|
|
|
|
|
const dati = (await res.json()) as { inviate: number; destinatari: number };
|
2026-09-06 12:25:21 +02:00
|
|
|
if (dati.inviate > 0) {
|
|
|
|
|
toast.success(`Promemoria inviato a ${dati.inviate} dispositivi`);
|
|
|
|
|
} else if (dati.destinatari === 0) {
|
|
|
|
|
toast.info("Nessun dispositivo con le notifiche attive tra gli incaricati");
|
|
|
|
|
} else {
|
|
|
|
|
// I dispositivi ci sono ma l'invio non è riuscito: quasi sempre le chiavi VAPID
|
|
|
|
|
// mancanti (in locale non sono configurate). Dirlo, invece di dare la colpa
|
|
|
|
|
// all'assenza di iscrizioni.
|
|
|
|
|
toast.error(`Invio non riuscito verso ${dati.destinatari} dispositivi`);
|
|
|
|
|
}
|
2026-09-06 12:15:39 +02:00
|
|
|
} catch {
|
|
|
|
|
toast.error("Non sono riuscito a inviare il promemoria");
|
|
|
|
|
} finally {
|
|
|
|
|
setAvviso(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-06 08:36:47 +02:00
|
|
|
return (
|
|
|
|
|
<div className="mt-3 rounded-2xl bg-secondary/60 p-2.5">
|
|
|
|
|
<button
|
|
|
|
|
type="button"
|
|
|
|
|
onClick={() => setAperto((v) => !v)}
|
|
|
|
|
className="flex w-full items-center gap-2.5 text-left"
|
|
|
|
|
>
|
|
|
|
|
<span className="grid h-8 w-8 shrink-0 place-items-center rounded-xl bg-card text-accent">
|
|
|
|
|
<CircleDot className="h-4 w-4" />
|
|
|
|
|
</span>
|
|
|
|
|
<span className="min-w-0 flex-1">
|
2026-09-05 12:42:24 +02:00
|
|
|
<span className="block text-xs font-bold uppercase tracking-wide text-muted-foreground">
|
2026-08-06 08:36:47 +02:00
|
|
|
Palloni
|
|
|
|
|
</span>
|
|
|
|
|
<span className="flex items-center gap-1.5 text-sm font-bold leading-tight">
|
|
|
|
|
{isPending || assegna.isPending ? (
|
|
|
|
|
<Loader2 className="h-3.5 w-3.5 animate-spin text-muted-foreground" />
|
|
|
|
|
) : null}
|
2026-09-03 10:17:03 +02:00
|
|
|
<span className="truncate">{giocatore ? nomeCompleto(giocatore) : "Da assegnare"}</span>
|
2026-08-06 08:36:47 +02:00
|
|
|
{proposto && giocatore ? (
|
2026-09-05 12:42:24 +02:00
|
|
|
<span className="shrink-0 rounded-full bg-card px-1.5 py-0.5 text-xs font-bold uppercase text-muted-foreground">
|
2026-08-06 08:36:47 +02:00
|
|
|
proposto
|
|
|
|
|
</span>
|
|
|
|
|
) : null}
|
|
|
|
|
</span>
|
|
|
|
|
</span>
|
|
|
|
|
<Pencil className="h-4 w-4 shrink-0 text-muted-foreground" />
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
|
|
{aperto ? (
|
|
|
|
|
<div className="mt-2.5 max-h-56 space-y-1 overflow-y-auto rounded-xl bg-card p-1.5">
|
2026-09-03 10:17:03 +02:00
|
|
|
{rosa.map((g) => (
|
2026-08-06 08:36:47 +02:00
|
|
|
<button
|
|
|
|
|
key={g.id}
|
|
|
|
|
type="button"
|
|
|
|
|
onClick={() => scegli(g.id)}
|
|
|
|
|
className={cn(
|
|
|
|
|
"flex w-full items-center gap-2 rounded-lg px-2 py-1.5 text-left text-sm transition-colors",
|
|
|
|
|
g.id === id ? "bg-accent/10 font-bold" : "hover:bg-secondary",
|
|
|
|
|
)}
|
|
|
|
|
>
|
|
|
|
|
<Avatar id={g.id} fallback={String(g.numero)} className="h-7 w-7 text-xs" />
|
2026-09-03 10:17:03 +02:00
|
|
|
<span className="min-w-0 flex-1 truncate">{nomeCompleto(g)}</span>
|
2026-08-06 08:36:47 +02:00
|
|
|
{g.id === id ? <Check className="h-4 w-4 shrink-0 text-accent" /> : null}
|
|
|
|
|
</button>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
) : null}
|
2026-09-06 12:15:39 +02:00
|
|
|
|
|
|
|
|
{admin && giocatore ? (
|
|
|
|
|
<button
|
|
|
|
|
type="button"
|
|
|
|
|
onClick={avvisa}
|
|
|
|
|
disabled={avviso}
|
|
|
|
|
className="premi mt-2.5 flex w-full items-center justify-center gap-2 rounded-xl bg-card px-3 py-2 text-xs font-bold uppercase text-foreground disabled:opacity-50"
|
|
|
|
|
>
|
|
|
|
|
{avviso ? (
|
|
|
|
|
<Loader2 className="h-3.5 w-3.5 animate-spin" />
|
|
|
|
|
) : (
|
|
|
|
|
<BellRing className="h-3.5 w-3.5" />
|
|
|
|
|
)}
|
|
|
|
|
Avvisa chi è di turno
|
|
|
|
|
</button>
|
|
|
|
|
) : null}
|
2026-08-06 08:36:47 +02:00
|
|
|
</div>
|
|
|
|
|
);
|
2026-09-01 13:38:33 +02:00
|
|
|
}
|