2026-08-06 08:36:47 +02:00
|
|
|
import { createFileRoute, useNavigate } from "@tanstack/react-router";
|
2026-08-30 17:57:59 +02:00
|
|
|
import { useEffect, useState } from "react";
|
|
|
|
|
import { LogIn } from "lucide-react";
|
|
|
|
|
import { toast } from "sonner";
|
2026-08-06 08:36:47 +02:00
|
|
|
import { TeamLogo } from "@/components/crapp/ui-bits";
|
|
|
|
|
import { giocatori } from "@/lib/crapp-data";
|
2026-08-30 17:57:59 +02:00
|
|
|
import { accediConGoogle, authObbligatoria, useSessione } from "@/lib/auth";
|
|
|
|
|
import {
|
|
|
|
|
nomeCompleto,
|
|
|
|
|
slotDi,
|
|
|
|
|
slotLiberi,
|
|
|
|
|
useCollegaGiocatore,
|
|
|
|
|
useGiocatoriSquadra,
|
|
|
|
|
type GiocatoreSquadra,
|
|
|
|
|
} from "@/lib/giocatori-squadra";
|
2026-08-06 08:36:47 +02:00
|
|
|
import { impostaGiocatore, useGiocatoreCorrente } from "@/lib/user-store";
|
|
|
|
|
|
|
|
|
|
export const Route = createFileRoute("/benvenuto")({
|
|
|
|
|
head: () => ({
|
|
|
|
|
meta: [
|
2026-08-07 12:23:27 +02:00
|
|
|
{ title: "Benvenuto — CrAPP DEVELOP" },
|
2026-08-06 08:36:47 +02:00
|
|
|
{
|
|
|
|
|
name: "description",
|
2026-08-30 17:57:59 +02:00
|
|
|
content: "Accedi e collega il tuo profilo giocatore per iniziare.",
|
2026-08-06 08:36:47 +02:00
|
|
|
},
|
2026-08-07 12:23:27 +02:00
|
|
|
{ property: "og:title", content: "Benvenuto — CrAPP DEVELOP" },
|
2026-08-06 08:36:47 +02:00
|
|
|
{
|
|
|
|
|
property: "og:description",
|
2026-08-30 17:57:59 +02:00
|
|
|
content: "Accedi e collega il tuo profilo giocatore per iniziare.",
|
2026-08-06 08:36:47 +02:00
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
}),
|
|
|
|
|
component: Benvenuto,
|
|
|
|
|
});
|
|
|
|
|
|
2026-08-30 17:57:59 +02:00
|
|
|
function Scheda({
|
|
|
|
|
titolo,
|
|
|
|
|
sottotitolo,
|
|
|
|
|
onClick,
|
|
|
|
|
iniziali,
|
|
|
|
|
}: {
|
|
|
|
|
titolo: string;
|
|
|
|
|
sottotitolo: string;
|
|
|
|
|
onClick: () => void;
|
|
|
|
|
iniziali: string;
|
|
|
|
|
}) {
|
|
|
|
|
return (
|
|
|
|
|
<button
|
|
|
|
|
type="button"
|
|
|
|
|
onClick={onClick}
|
|
|
|
|
className="flex w-full items-center gap-4 rounded-2xl bg-card p-4 shadow-card transition-transform active:scale-[0.98]"
|
|
|
|
|
>
|
|
|
|
|
<div className="grid h-12 w-12 shrink-0 place-items-center rounded-xl bg-secondary font-display text-lg">
|
|
|
|
|
{iniziali}
|
|
|
|
|
</div>
|
|
|
|
|
<div className="min-w-0 flex-1 text-left">
|
|
|
|
|
<p className="font-semibold leading-tight">{titolo}</p>
|
|
|
|
|
<p className="text-xs text-muted-foreground">{sottotitolo}</p>
|
|
|
|
|
</div>
|
|
|
|
|
</button>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-06 08:36:47 +02:00
|
|
|
function Benvenuto() {
|
|
|
|
|
const navigate = useNavigate();
|
|
|
|
|
const giocatore = useGiocatoreCorrente();
|
2026-08-30 17:57:59 +02:00
|
|
|
const { pronta, utenteId } = useSessione();
|
|
|
|
|
const { righe } = useGiocatoriSquadra();
|
|
|
|
|
const collega = useCollegaGiocatore();
|
|
|
|
|
const [inCorso, setInCorso] = useState(false);
|
|
|
|
|
|
|
|
|
|
const mioSlot = slotDi(righe, utenteId);
|
|
|
|
|
// Con l'auth obbligatoria si entra solo da loggati; finché non lo è, la selezione
|
|
|
|
|
// diretta resta come ponte per chi non ha ancora collegato l'account (DD-011).
|
|
|
|
|
const puoEntrare = !!giocatore && (!!utenteId || !authObbligatoria());
|
2026-08-06 08:36:47 +02:00
|
|
|
|
|
|
|
|
useEffect(() => {
|
2026-08-30 17:57:59 +02:00
|
|
|
if (puoEntrare) navigate({ to: "/" });
|
|
|
|
|
}, [puoEntrare, navigate]);
|
|
|
|
|
|
|
|
|
|
// L'account è già collegato a uno slot: nessuna scelta da fare.
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (mioSlot) impostaGiocatore(mioSlot.id);
|
|
|
|
|
}, [mioSlot]);
|
|
|
|
|
|
|
|
|
|
async function accedi() {
|
|
|
|
|
setInCorso(true);
|
|
|
|
|
try {
|
|
|
|
|
await accediConGoogle();
|
|
|
|
|
} catch (error) {
|
|
|
|
|
toast.error(error instanceof Error ? error.message : "Accesso non riuscito");
|
|
|
|
|
setInCorso(false);
|
2026-08-06 08:36:47 +02:00
|
|
|
}
|
2026-08-30 17:57:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function reclama(g: GiocatoreSquadra) {
|
|
|
|
|
if (!utenteId) return;
|
|
|
|
|
try {
|
|
|
|
|
await collega.mutateAsync({ giocatoreId: g.id, utenteId });
|
|
|
|
|
impostaGiocatore(g.id);
|
|
|
|
|
} catch {
|
|
|
|
|
toast.error("Profilo già collegato a un altro account. Chiedi a un amministratore.");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const liberi = slotLiberi(righe);
|
2026-08-06 08:36:47 +02:00
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="flex min-h-screen flex-col items-center justify-center px-6 py-12">
|
|
|
|
|
<TeamLogo className="h-20 w-20" />
|
|
|
|
|
<h1 className="mt-6 text-center font-display text-4xl uppercase leading-none">
|
2026-08-07 12:23:27 +02:00
|
|
|
Benvenuto in CrAPP DEVELOP
|
2026-08-06 08:36:47 +02:00
|
|
|
</h1>
|
2026-08-30 17:57:59 +02:00
|
|
|
|
|
|
|
|
{!pronta ? null : !utenteId ? (
|
|
|
|
|
<>
|
|
|
|
|
<p className="mt-2 text-center text-sm text-muted-foreground">
|
|
|
|
|
Accedi con il tuo account Google per collegare il profilo giocatore.
|
|
|
|
|
</p>
|
2026-08-06 08:36:47 +02:00
|
|
|
<button
|
|
|
|
|
type="button"
|
2026-08-30 17:57:59 +02:00
|
|
|
onClick={accedi}
|
|
|
|
|
disabled={inCorso}
|
|
|
|
|
className="premi mt-8 flex w-full max-w-sm items-center justify-center gap-2 rounded-2xl bg-accent-grad py-3.5 text-sm font-bold uppercase text-accent-foreground shadow-pop disabled:opacity-60"
|
2026-08-06 08:36:47 +02:00
|
|
|
>
|
2026-08-30 17:57:59 +02:00
|
|
|
<LogIn className="h-4 w-4" /> Accedi con Google
|
2026-08-06 08:36:47 +02:00
|
|
|
</button>
|
2026-08-30 17:57:59 +02:00
|
|
|
|
|
|
|
|
{authObbligatoria() ? null : (
|
|
|
|
|
<div className="mt-10 w-full max-w-sm">
|
|
|
|
|
<p className="mb-3 text-center text-xs text-muted-foreground">
|
|
|
|
|
Oppure entra scegliendo il tuo nome, come prima.
|
|
|
|
|
</p>
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
{giocatori.map((g) => (
|
|
|
|
|
<Scheda
|
|
|
|
|
key={g.id}
|
|
|
|
|
titolo={g.nome}
|
|
|
|
|
sottotitolo={`#${g.numero} · ${g.ruolo}`}
|
|
|
|
|
iniziali={g.iniziali}
|
|
|
|
|
onClick={() => impostaGiocatore(g.id)}
|
|
|
|
|
/>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</>
|
|
|
|
|
) : (
|
|
|
|
|
<>
|
|
|
|
|
<p className="mt-2 text-center text-sm text-muted-foreground">
|
|
|
|
|
Sei entrato. Scegli il tuo nome: resterà collegato a questo account.
|
|
|
|
|
</p>
|
|
|
|
|
<div className="mt-8 w-full max-w-sm space-y-2">
|
|
|
|
|
{liberi.map((g) => (
|
|
|
|
|
<Scheda
|
|
|
|
|
key={g.id}
|
|
|
|
|
titolo={nomeCompleto(g)}
|
|
|
|
|
sottotitolo={`#${g.numero} · ${g.ruolo}`}
|
|
|
|
|
iniziali={`${g.nome[0] ?? ""}${g.cognome[0] ?? ""}`.toUpperCase()}
|
|
|
|
|
onClick={() => void reclama(g)}
|
|
|
|
|
/>
|
|
|
|
|
))}
|
|
|
|
|
{liberi.length === 0 ? (
|
|
|
|
|
<p className="rounded-2xl bg-card p-4 text-center text-sm text-muted-foreground shadow-card">
|
|
|
|
|
Nessun profilo libero: chiedi a un amministratore di collegarti.
|
|
|
|
|
</p>
|
|
|
|
|
) : null}
|
|
|
|
|
</div>
|
|
|
|
|
</>
|
|
|
|
|
)}
|
2026-08-06 08:36:47 +02:00
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|