diff --git a/src/lib/crapp-data.ts b/src/lib/crapp-data.ts index 59f4607..e5b36d3 100644 --- a/src/lib/crapp-data.ts +++ b/src/lib/crapp-data.ts @@ -112,6 +112,14 @@ export const giocatori: Giocatore[] = rosaCSI })) .sort((a, b) => dividiNome(a.nome).cognome.localeCompare(dividiNome(b.nome).cognome, "it")); +/** + * Fallback per la data di nascita: `giocatori_squadra` non ha ancora questa colonna + * (DD-015 follow-up). Un giocatore aggiunto dopo la migrazione non ha nascita nota. + */ +export const nascitaPerId: Record = Object.fromEntries( + giocatori.map((g) => [g.id, g.nascita]), +); + export type RigaClassifica = { pos: number; squadra: string; diff --git a/src/lib/giocatori-squadra.server.ts b/src/lib/giocatori-squadra.server.ts new file mode 100644 index 0000000..5fef7bb --- /dev/null +++ b/src/lib/giocatori-squadra.server.ts @@ -0,0 +1,20 @@ +import type { SupabaseClient } from "@supabase/supabase-js"; +import { + COLONNE_SQUADRA, + daRigaSquadra, + type GiocatoreSquadra, + type RigaGiocatoreSquadra, +} from "./giocatori-squadra"; + +/** Lettura squadra lato server (route API): stessa conversione del client. */ +export async function leggiGiocatoriSquadra(): Promise { + const { supabaseAdmin } = await import("@/integrations/supabase/client.server"); + // `types.ts` non include ancora `giocatori_squadra` con le colonne di M8 (vedi client-nuove-tabelle.ts). + const client = supabaseAdmin as unknown as SupabaseClient; + const { data } = await client + .from("giocatori_squadra") + .select(COLONNE_SQUADRA) + .order("cognome") + .order("nome"); + return ((data ?? []) as RigaGiocatoreSquadra[]).map(daRigaSquadra); +} diff --git a/src/lib/giocatori-squadra.ts b/src/lib/giocatori-squadra.ts index 1de16d6..8775769 100644 --- a/src/lib/giocatori-squadra.ts +++ b/src/lib/giocatori-squadra.ts @@ -20,7 +20,7 @@ export type GiocatoreSquadra = { dataTessera: string | null; }; -type RigaGiocatoreSquadra = { +export type RigaGiocatoreSquadra = { id: string; nome: string; cognome: string; @@ -73,17 +73,12 @@ export function slotPerEmail( return righe.find((g) => !g.authUserId && g.email?.trim().toLowerCase() === cercata) ?? null; } -async function fetchSquadra(): Promise { - const { data, error } = await supabaseNuoveTabelle - .from("giocatori_squadra") - .select( - "id, nome, cognome, numero, ruolo, auth_user_id, attivo, email, numero_tessera, data_tessera", - ) - .order("cognome") - .order("nome"); - if (error) throw error; - const righe = (data ?? []) as RigaGiocatoreSquadra[]; - return righe.map((r) => ({ +export const COLONNE_SQUADRA = + "id, nome, cognome, numero, ruolo, auth_user_id, attivo, email, numero_tessera, data_tessera"; + +/** Conversione riga database -> modello applicativo (riusabile anche lato server). */ +export function daRigaSquadra(r: RigaGiocatoreSquadra): GiocatoreSquadra { + return { id: r.id, nome: r.nome, cognome: r.cognome, @@ -94,7 +89,18 @@ async function fetchSquadra(): Promise { email: r.email, numeroTessera: r.numero_tessera, dataTessera: r.data_tessera, - })); + }; +} + +async function fetchSquadra(): Promise { + const { data, error } = await supabaseNuoveTabelle + .from("giocatori_squadra") + .select(COLONNE_SQUADRA) + .order("cognome") + .order("nome"); + if (error) throw error; + const righe = (data ?? []) as RigaGiocatoreSquadra[]; + return righe.map(daRigaSquadra); } /** Anagrafica squadra: una lettura per sessione, cambia raramente. */ diff --git a/src/lib/rosa.ts b/src/lib/rosa.ts index d772104..27f2e44 100644 --- a/src/lib/rosa.ts +++ b/src/lib/rosa.ts @@ -1,5 +1,6 @@ import { useMemo } from "react"; -import { giocatori, type Giocatore } from "./crapp-data"; +import { nascitaPerId, type Giocatore } from "./crapp-data"; +import { nomeCompleto, useGiocatoriSquadra } from "./giocatori-squadra"; import { mvpVintiPerGiocatore, useVotiMvp } from "./mvp-voti"; import { mediePagelle, usePagelle } from "./pagelle"; import { statisticheCacche, useCacche } from "./cacche"; @@ -8,21 +9,24 @@ import { useTurniPalloni } from "./palloni"; import { useInfortuniERitardi } from "./infortuni"; import { useGiocatoreId } from "./user-store"; import { useEventi } from "./eventi"; -import { - contaPresenzeGiocatore, - totaliEventiGiocatore, - useRispostePresenze, -} from "./presenze"; +import { contaPresenzeGiocatore, totaliEventiGiocatore, useRispostePresenze } from "./presenze"; import { obiettiviOrdinati } from "./obiettivi"; import { useCsi } from "./csi"; import { partiteGiocate } from "./csi-core"; +function iniziali(nome: string, cognome: string): string { + return `${nome[0] ?? ""}${cognome[0] ?? ""}`.toUpperCase(); +} + /** * Rosa completa con tutte le statistiche personali (presenze, MVP, media voto, - * palloni, infortuni, ritardi, cacche). Usa solo cache già in memoria: - * nessuna query aggiuntiva rispetto a quelle che l'app fa comunque. + * palloni, infortuni, ritardi, cacche). Legge l'anagrafica da `giocatori_squadra` + * (DD-015): solo i giocatori attivi, gli altri restano nel database ma spariscono + * dagli elenchi correnti. Usa solo cache già in memoria: nessuna query aggiuntiva + * rispetto a quelle che l'app fa comunque. */ export function useRosa(): Giocatore[] { + const { righe: squadra } = useGiocatoriSquadra(); const voti = useVotiMvp(); const { voti: pagelle } = usePagelle(); const { righe: cacche } = useCacche(); @@ -39,28 +43,30 @@ export function useRosa(): Giocatore[] { const palloni = conteggioTurni(turni); const mvpVinti = mvpVintiPerGiocatore(votiMvp); - return giocatori.map((g) => ({ - ...g, - presenze: contaPresenzeGiocatore(g.id, eventi, mappaPresenze), - totaliEventi: totaliEventiGiocatore(g.id, eventi), - mvp: mvpVinti[g.id] ?? 0, - mediaVoto: medie[g.id]?.media ?? 0, - palloni: palloni[g.id] ?? 0, - cacche: statCacche[g.id]?.giornateTop ?? 0, - cacchePartita: statCacche[g.id]?.media ?? 0, - infortuni: infortuni[g.id] ?? 0, - ritardi: ritardi[g.id] ?? 0, - })); - }, [ - votiMvp, - pagelle, - cacche, - turni, - infortuni, - ritardi, - eventi, - mappaPresenze, - ]); + return squadra + .filter((g) => g.attivo) + .map((g) => ({ + id: g.id, + nome: nomeCompleto(g), + numero: g.numero, + ruolo: g.ruolo, + nascita: nascitaPerId[g.id] ?? "", + iniziali: iniziali(g.nome, g.cognome), + presenze: contaPresenzeGiocatore(g.id, eventi, mappaPresenze), + totaliEventi: totaliEventiGiocatore(g.id, eventi), + streak: 0, + serieAllenamenti: 0, + seriePartite: 0, + serieConferme: 0, + mvp: mvpVinti[g.id] ?? 0, + mediaVoto: medie[g.id]?.media ?? 0, + palloni: palloni[g.id] ?? 0, + cacche: statCacche[g.id]?.giornateTop ?? 0, + cacchePartita: statCacche[g.id]?.media ?? 0, + infortuni: infortuni[g.id] ?? 0, + ritardi: ritardi[g.id] ?? 0, + })); + }, [squadra, votiMvp, pagelle, cacche, turni, infortuni, ritardi, eventi, mappaPresenze]); } /** Il giocatore selezionato sul dispositivo, con le statistiche complete. */ @@ -83,5 +89,3 @@ export function useObiettivi() { : 0; return obiettiviOrdinati(rosa, { eventi, presenze, pagelle, vittorie }); } - -export { giocatori };