DD-018: collegamento automatico giocatore-account per email

Al primo accesso l'app collega da sola l'account Google al giocatore
la cui email registrata coincide (case-insensitive), invece di far
scegliere il nome da un elenco. Senza corrispondenza compare solo un
messaggio d'errore con un pulsante per uscire e riprovare con un altro
account: nessuna scelta manuale di ripiego.

- Migration m5: colonna `email` su giocatori_squadra, seed per Ivan
  Cacciari e Davide Grilli, trigger esteso per richiedere anche il
  match email oltre allo slot libero.
- slotPerEmail() sostituisce slotLiberi() (rimossa, senza più
  chiamanti in produzione).

Co-Authored-By: Claude Mythos  <noreply@anthropic.com>
This commit is contained in:
2026-09-01 10:46:54 +02:00
co-authored by Claude Mythos
parent 127e7c76e9
commit 215af4bbc4
11 changed files with 190 additions and 90 deletions
+6 -1
View File
@@ -39,7 +39,12 @@ export function useSessione() {
}
}, []);
return { sessione, pronta, utenteId: sessione?.user.id ?? null };
return {
sessione,
pronta,
utenteId: sessione?.user.id ?? null,
emailUtente: sessione?.user.email ?? null,
};
}
export async function accediConGoogle(): Promise<void> {
+13 -3
View File
@@ -15,6 +15,7 @@ export type GiocatoreSquadra = {
ruolo: string;
authUserId: string | null;
attivo: boolean;
email: string | null;
};
type RigaGiocatoreSquadra = {
@@ -25,6 +26,7 @@ type RigaGiocatoreSquadra = {
ruolo: string;
auth_user_id: string | null;
attivo: boolean;
email: string | null;
};
export const SQUADRA_KEY = ["giocatori-squadra"] as const;
@@ -45,6 +47,7 @@ export function rosaFallback(): GiocatoreSquadra[] {
ruolo: g.ruolo,
authUserId: null,
attivo: true,
email: null,
}));
}
@@ -61,14 +64,20 @@ export function slotDi(
return righe.find((g) => g.authUserId === utenteId) ?? null;
}
export function slotLiberi(righe: GiocatoreSquadra[]): GiocatoreSquadra[] {
return righe.filter((g) => g.attivo && !g.authUserId);
/** Lo slot libero la cui email coincide con quella dell'account Google (case-insensitive). */
export function slotPerEmail(
righe: GiocatoreSquadra[],
email: string | null,
): GiocatoreSquadra | null {
if (!email) return null;
const cercata = email.trim().toLowerCase();
return righe.find((g) => !g.authUserId && g.email?.trim().toLowerCase() === cercata) ?? null;
}
async function fetchSquadra(): Promise<GiocatoreSquadra[]> {
const { data, error } = await supabaseNuoveTabelle
.from("giocatori_squadra")
.select("id, nome, cognome, numero, ruolo, auth_user_id, attivo")
.select("id, nome, cognome, numero, ruolo, auth_user_id, attivo, email")
.order("id");
if (error) throw error;
const righe = (data ?? []) as RigaGiocatoreSquadra[];
@@ -80,6 +89,7 @@ async function fetchSquadra(): Promise<GiocatoreSquadra[]> {
ruolo: r.ruolo,
authUserId: r.auth_user_id,
attivo: r.attivo,
email: r.email,
}));
}