From 0a04025fe972b463b02a500c9ec3710a0d28c9bc Mon Sep 17 00:00:00 2001 From: Davide Grilli Date: Tue, 1 Sep 2026 13:38:33 +0200 Subject: [PATCH] Rimuove i dati dummy della classifica e usa i risultati CSI reali La classifica e lo storico partite mostravano dati inventati (squadre e risultati finti) come base, poi sovrascritti dai dati CSI quando disponibili. Ora classifica, ultimi risultati, storico match e obiettivi di squadra legati alle vittorie usano solo dati reali (CSI o scout live), con stati vuoti quando i dati CSI non sono ancora disponibili. Include anche la correzione di tutti gli errori di formattazione prettier segnalati da `npm run lint` sul resto del codice sorgente. Co-Authored-By: Claude Sonnet 5 --- public/push-sw.js | 2 +- src/components/crapp/BadgeDrawer.tsx | 24 +- src/components/crapp/BottomNav.tsx | 2 +- src/components/crapp/CelebrazioneBadge.tsx | 2 +- src/components/crapp/CollezioneBadge.tsx | 13 +- src/components/crapp/EventoCard.tsx | 56 +- src/components/crapp/Pagelle.tsx | 4 +- src/components/crapp/PromemoriaPalloni.tsx | 9 +- src/components/crapp/RosaPresenze.tsx | 3 +- src/components/crapp/ScoutEntry.tsx | 20 +- src/components/crapp/SerieCard.tsx | 10 +- src/components/crapp/SondaggioCacche.tsx | 5 +- src/components/crapp/TurnoPalloni.tsx | 2 +- src/components/crapp/VotazioneMvp.tsx | 2 +- src/components/crapp/VotoSocial.tsx | 5 +- src/components/crapp/ui-bits.tsx | 4 +- src/components/motion/Barra.tsx | 2 +- src/components/motion/Numero.tsx | 2 +- src/components/motion/Reveal.tsx | 2 +- src/integrations/lovable/index.ts | 5 +- src/integrations/supabase/auth-attacher.ts | 14 +- src/integrations/supabase/auth-middleware.ts | 88 +- src/integrations/supabase/client.server.ts | 29 +- src/integrations/supabase/client.ts | 34 +- src/integrations/supabase/types.ts | 874 +++++++++---------- src/lib/badge-social.ts | 5 +- src/lib/badges.ts | 25 +- src/lib/cacche.ts | 8 +- src/lib/crapp-data.ts | 40 +- src/lib/csi-core.ts | 13 + src/lib/infortuni.ts | 2 +- src/lib/motion.ts | 2 +- src/lib/mvp-voti.ts | 2 +- src/lib/notifiche-smart.ts | 15 +- src/lib/obiettivi.ts | 7 +- src/lib/presenze-mese.ts | 5 +- src/lib/push-client.ts | 2 +- src/lib/rosa.ts | 8 +- src/lib/scout-export.ts | 9 +- src/lib/scout-store.ts | 92 +- src/lib/serie.ts | 2 +- src/lib/webpush.server.ts | 9 +- src/routes/__root.tsx | 24 +- src/routes/allenamento.$id.tsx | 10 +- src/routes/api/public/push-config.ts | 2 +- src/routes/api/public/push-subscribe.ts | 7 +- src/routes/calendario.tsx | 11 +- src/routes/classifica.tsx | 122 +-- src/routes/eventi.tsx | 13 +- src/routes/index.tsx | 69 +- src/routes/partita.$id.tsx | 40 +- src/routes/profilo.tsx | 34 +- src/routes/scout.tsx | 27 +- src/routes/squadra.tsx | 92 +- src/styles.css | 7 +- 55 files changed, 1038 insertions(+), 879 deletions(-) diff --git a/public/push-sw.js b/public/push-sw.js index 55d84e7..fcd8f0a 100644 --- a/public/push-sw.js +++ b/public/push-sw.js @@ -43,4 +43,4 @@ self.addEventListener("notificationclick", (event) => { return self.clients.openWindow("/"); }), ); -}); \ No newline at end of file +}); diff --git a/src/components/crapp/BadgeDrawer.tsx b/src/components/crapp/BadgeDrawer.tsx index e45f4b3..a6417b1 100644 --- a/src/components/crapp/BadgeDrawer.tsx +++ b/src/components/crapp/BadgeDrawer.tsx @@ -14,13 +14,7 @@ import { DrawerTrigger, } from "@/components/ui/drawer"; -function DettaglioBadge({ - def, - stato, -}: { - def: BadgeDef; - stato?: BadgeStato; -}) { +function DettaglioBadge({ def, stato }: { def: BadgeDef; stato?: BadgeStato }) { const Icon = def.icon; const grado = stato?.grado ?? null; const meta = grado ? gradoMeta[grado] : null; @@ -94,10 +88,20 @@ function DettaglioBadge({ raggiunto ? `${gm.bg} ${gm.ring}` : "bg-secondary ring-border", )} > -

+

{gm.label}

-

+

{def.soglie[g]}

{def.unita}

@@ -130,7 +134,7 @@ export function BadgeDrawer({ return ( {children} - + {def.nome} {def.descrizione} diff --git a/src/components/crapp/BottomNav.tsx b/src/components/crapp/BottomNav.tsx index a1917e2..9db2121 100644 --- a/src/components/crapp/BottomNav.tsx +++ b/src/components/crapp/BottomNav.tsx @@ -27,4 +27,4 @@ export function BottomNav() { ); -} \ No newline at end of file +} diff --git a/src/components/crapp/CelebrazioneBadge.tsx b/src/components/crapp/CelebrazioneBadge.tsx index 0960d5a..92632e3 100644 --- a/src/components/crapp/CelebrazioneBadge.tsx +++ b/src/components/crapp/CelebrazioneBadge.tsx @@ -76,4 +76,4 @@ export function CelebrazioneBadge() { ); -} \ No newline at end of file +} diff --git a/src/components/crapp/CollezioneBadge.tsx b/src/components/crapp/CollezioneBadge.tsx index 9adb9d5..27ab58d 100644 --- a/src/components/crapp/CollezioneBadge.tsx +++ b/src/components/crapp/CollezioneBadge.tsx @@ -10,7 +10,12 @@ import { prossimoTraguardo, type BadgeStato, } from "@/lib/badges"; -import { badgeSocialVinti, categorieSocial, type VotoSocial, type CategoriaSocial } from "@/lib/badge-social"; +import { + badgeSocialVinti, + categorieSocial, + type VotoSocial, + type CategoriaSocial, +} from "@/lib/badge-social"; import { Reveal } from "@/components/motion/Reveal"; import { Barra } from "@/components/motion/Barra"; import { Numero } from "@/components/motion/Numero"; @@ -91,7 +96,9 @@ function SocialDrawer({
-

Vinto

+

+ Vinto +

{conteggio}{" "} @@ -246,4 +253,4 @@ export function CollezioneBadge({

); -} \ No newline at end of file +} diff --git a/src/components/crapp/EventoCard.tsx b/src/components/crapp/EventoCard.tsx index f583865..aa476ce 100644 --- a/src/components/crapp/EventoCard.tsx +++ b/src/components/crapp/EventoCard.tsx @@ -118,33 +118,35 @@ export function EventoCard({ {io ? ( -
- {stati.map((s) => { - const meta = statoMeta[s]; - const attivo = stato === s; - return ( - - ); - })} -
+
+ {stati.map((s) => { + const meta = statoMeta[s]; + const attivo = stato === s; + return ( + + ); + })} +
) : null} ); -} \ No newline at end of file +} diff --git a/src/components/crapp/Pagelle.tsx b/src/components/crapp/Pagelle.tsx index 5ebfe43..fe6ceb3 100644 --- a/src/components/crapp/Pagelle.tsx +++ b/src/components/crapp/Pagelle.tsx @@ -104,7 +104,9 @@ export function Pagelle({ 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", + mio === v + ? "bg-accent text-accent-foreground" + : "bg-card text-foreground", )} > {v} diff --git a/src/components/crapp/PromemoriaPalloni.tsx b/src/components/crapp/PromemoriaPalloni.tsx index 7cb2e4c..1819daa 100644 --- a/src/components/crapp/PromemoriaPalloni.tsx +++ b/src/components/crapp/PromemoriaPalloni.tsx @@ -1,11 +1,6 @@ import { AlertCircle } from "lucide-react"; import { formatData } from "@/lib/crapp-data"; -import { - eventiPalloni, - eventoPrecedente, - eventoSuccessivo, - oggiISO, -} from "@/lib/palloni-core"; +import { eventiPalloni, eventoPrecedente, eventoSuccessivo, oggiISO } from "@/lib/palloni-core"; import { useTurniPalloni } from "@/lib/palloni"; import { useEventi } from "@/lib/eventi"; import { useGiocatoreCorrente } from "@/lib/user-store"; @@ -59,4 +54,4 @@ export function PromemoriaPalloni() { ))} ); -} \ No newline at end of file +} diff --git a/src/components/crapp/RosaPresenze.tsx b/src/components/crapp/RosaPresenze.tsx index e0df50a..b16c018 100644 --- a/src/components/crapp/RosaPresenze.tsx +++ b/src/components/crapp/RosaPresenze.tsx @@ -21,7 +21,8 @@ export function RosaPresenze({ eventoId }: { eventoId: string }) { const mancanti = giocatori.filter((g) => !risposte[g.id]); const risposteN = giocatori.length - mancanti.length; const perc = Math.round((risposteN / giocatori.length) * 100); - const daSollecitare = mancanti.length + giocatori.filter((g) => risposte[g.id] === "forse").length; + const daSollecitare = + mancanti.length + giocatori.filter((g) => risposte[g.id] === "forse").length; async function sollecita() { setSollecito(true); diff --git a/src/components/crapp/ScoutEntry.tsx b/src/components/crapp/ScoutEntry.tsx index d5452be..d9fccb7 100644 --- a/src/components/crapp/ScoutEntry.tsx +++ b/src/components/crapp/ScoutEntry.tsx @@ -19,12 +19,20 @@ export function ScoutEntry({ variante = "grande" }: { variante?: "grande" | "com const occupato = !!attiva && attiva.giocatore_id !== io?.id; const disponibile = abilitato && pronto && !!partita && !occupato; - const titolo = !abilitato ? "Scout live" : !partita ? "Scout live non attivo" : occupato ? "Scout occupato" : "Scout live"; - const sottotitolo = !abilitato ? "Riservato ad allenatori e referenti" : !partita - ? "Si attiva il giorno della partita" - : occupato - ? `In uso da ${attiva!.giocatore_nome}` - : "Segna punti, ace e muri in tempo reale"; + const titolo = !abilitato + ? "Scout live" + : !partita + ? "Scout live non attivo" + : occupato + ? "Scout occupato" + : "Scout live"; + const sottotitolo = !abilitato + ? "Riservato ad allenatori e referenti" + : !partita + ? "Si attiva il giorno della partita" + : occupato + ? `In uso da ${attiva!.giocatore_nome}` + : "Segna punti, ace e muri in tempo reale"; const contenuto = ( <> diff --git a/src/components/crapp/SerieCard.tsx b/src/components/crapp/SerieCard.tsx index 92bde87..4e1bcfd 100644 --- a/src/components/crapp/SerieCard.tsx +++ b/src/components/crapp/SerieCard.tsx @@ -19,7 +19,9 @@ export function SerieGriglia({ g }: { g: Giocatore }) { @@ -29,7 +31,9 @@ export function SerieGriglia({ g }: { g: Giocatore }) {

{s.def.descrizione}

- + {s.valore} @@ -74,4 +78,4 @@ export function SerieHome({ g }: { g: Giocatore }) { ); -} \ No newline at end of file +} diff --git a/src/components/crapp/SondaggioCacche.tsx b/src/components/crapp/SondaggioCacche.tsx index 40cb9b1..e7004a6 100644 --- a/src/components/crapp/SondaggioCacche.tsx +++ b/src/components/crapp/SondaggioCacche.tsx @@ -68,7 +68,10 @@ export function SondaggioCacche({ eventoId }: { eventoId: string }) { Media squadra {media}
{classifica.map((r, i) => ( - + {i === 0 ? "🥇" : i === 1 ? "🥈" : "🥉"} {r.nome} · {r.quantita} ))} diff --git a/src/components/crapp/TurnoPalloni.tsx b/src/components/crapp/TurnoPalloni.tsx index e0a299e..da02d8a 100644 --- a/src/components/crapp/TurnoPalloni.tsx +++ b/src/components/crapp/TurnoPalloni.tsx @@ -78,4 +78,4 @@ export function TurnoPalloni({ eventoId }: { eventoId: string }) { ) : null} ); -} \ No newline at end of file +} diff --git a/src/components/crapp/VotazioneMvp.tsx b/src/components/crapp/VotazioneMvp.tsx index a2ab299..f48c9ee 100644 --- a/src/components/crapp/VotazioneMvp.tsx +++ b/src/components/crapp/VotazioneMvp.tsx @@ -97,4 +97,4 @@ export function VotazioneMvp({ matchId }: { matchId: string }) { ) : null} ); -} \ No newline at end of file +} diff --git a/src/components/crapp/VotoSocial.tsx b/src/components/crapp/VotoSocial.tsx index 8ccbf64..5d66460 100644 --- a/src/components/crapp/VotoSocial.tsx +++ b/src/components/crapp/VotoSocial.tsx @@ -122,8 +122,7 @@ export function VotoSocial({ matchId }: { matchId: string }) {

- {vincitore.nome} · {vincitore.voti}{" "} - {vincitore.voti === 1 ? "voto" : "voti"} + {vincitore.nome} · {vincitore.voti} {vincitore.voti === 1 ? "voto" : "voti"}

) : (

@@ -137,4 +136,4 @@ export function VotoSocial({ matchId }: { matchId: string }) { })} ); -} \ No newline at end of file +} diff --git a/src/components/crapp/ui-bits.tsx b/src/components/crapp/ui-bits.tsx index c55d517..6532173 100644 --- a/src/components/crapp/ui-bits.tsx +++ b/src/components/crapp/ui-bits.tsx @@ -62,7 +62,7 @@ export function StatoBadge({ stato, className }: { stato: Stato; className?: str className, )} > -{meta.label} + {meta.label} ); } @@ -87,4 +87,4 @@ export function StatTile({ {hint ?

{hint}

: null} ); -} \ No newline at end of file +} diff --git a/src/components/motion/Barra.tsx b/src/components/motion/Barra.tsx index 082f20e..291f2fc 100644 --- a/src/components/motion/Barra.tsx +++ b/src/components/motion/Barra.tsx @@ -22,4 +22,4 @@ export function Barra({ /> ); -} \ No newline at end of file +} diff --git a/src/components/motion/Numero.tsx b/src/components/motion/Numero.tsx index aed09bb..b64b489 100644 --- a/src/components/motion/Numero.tsx +++ b/src/components/motion/Numero.tsx @@ -17,4 +17,4 @@ export function Numero({ {suffisso}
); -} \ No newline at end of file +} diff --git a/src/components/motion/Reveal.tsx b/src/components/motion/Reveal.tsx index 47d2211..34ce949 100644 --- a/src/components/motion/Reveal.tsx +++ b/src/components/motion/Reveal.tsx @@ -26,4 +26,4 @@ export function Reveal({ {children} ); -} \ No newline at end of file +} diff --git a/src/integrations/lovable/index.ts b/src/integrations/lovable/index.ts index ccf28b5..5fe6340 100644 --- a/src/integrations/lovable/index.ts +++ b/src/integrations/lovable/index.ts @@ -11,7 +11,10 @@ type SignInOptions = { export const lovable = { auth: { - signInWithOAuth: async (provider: "google" | "apple" | "microsoft" | "lovable", opts?: SignInOptions) => { + signInWithOAuth: async ( + provider: "google" | "apple" | "microsoft" | "lovable", + opts?: SignInOptions, + ) => { const result = await lovableAuth.signInWithOAuth(provider, { redirect_uri: opts?.redirect_uri ?? window.location.origin, extraParams: { diff --git a/src/integrations/supabase/auth-attacher.ts b/src/integrations/supabase/auth-attacher.ts index 5bc57b7..cbedca6 100644 --- a/src/integrations/supabase/auth-attacher.ts +++ b/src/integrations/supabase/auth-attacher.ts @@ -1,15 +1,15 @@ // This file is automatically generated. Do not edit it directly. -import { createMiddleware } from '@tanstack/react-start' -import { supabase } from './client' +import { createMiddleware } from "@tanstack/react-start"; +import { supabase } from "./client"; // Must be registered as a global `functionMiddleware` in `src/start.ts`; otherwise // the browser never attaches the bearer token to serverFn RPCs. -export const attachSupabaseAuth = createMiddleware({ type: 'function' }).client( +export const attachSupabaseAuth = createMiddleware({ type: "function" }).client( async ({ next }) => { - const { data } = await supabase.auth.getSession() - const token = data.session?.access_token + const { data } = await supabase.auth.getSession(); + const token = data.session?.access_token; return next({ headers: token ? { Authorization: `Bearer ${token}` } : {}, - }) + }); }, -) +); diff --git a/src/integrations/supabase/auth-middleware.ts b/src/integrations/supabase/auth-middleware.ts index a6bc029..24daf97 100644 --- a/src/integrations/supabase/auth-middleware.ts +++ b/src/integrations/supabase/auth-middleware.ts @@ -1,19 +1,17 @@ // This file is automatically generated. Do not edit it directly. -import { createMiddleware } from '@tanstack/react-start' -import { getRequest } from '@tanstack/react-start/server' -import { createClient } from '@supabase/supabase-js' -import type { Database } from './types' - - +import { createMiddleware } from "@tanstack/react-start"; +import { getRequest } from "@tanstack/react-start/server"; +import { createClient } from "@supabase/supabase-js"; +import type { Database } from "./types"; function isNewSupabaseApiKey(value: string): boolean { - return value.startsWith('sb_publishable_') || value.startsWith('sb_secret_'); + return value.startsWith("sb_publishable_") || value.startsWith("sb_secret_"); } function createSupabaseFetch(supabaseKey: string): typeof fetch { return (input, init) => { const headers = new Headers( - typeof Request !== 'undefined' && input instanceof Request ? input.headers : undefined, + typeof Request !== "undefined" && input instanceof Request ? input.headers : undefined, ); if (init?.headers) { @@ -21,81 +19,79 @@ function createSupabaseFetch(supabaseKey: string): typeof fetch { } // New Supabase API keys are opaque strings, not bearer JWTs. - if (isNewSupabaseApiKey(supabaseKey) && headers.get('Authorization') === `Bearer ${supabaseKey}`) { - headers.delete('Authorization'); + if ( + isNewSupabaseApiKey(supabaseKey) && + headers.get("Authorization") === `Bearer ${supabaseKey}` + ) { + headers.delete("Authorization"); } - headers.set('apikey', supabaseKey); + headers.set("apikey", supabaseKey); return fetch(input, { ...init, headers }); }; } -export const requireSupabaseAuth = createMiddleware({ type: 'function' }).server( +export const requireSupabaseAuth = createMiddleware({ type: "function" }).server( async ({ next }) => { - - const SUPABASE_URL = process.env['SUPABASE_URL']; - const SUPABASE_PUBLISHABLE_KEY = process.env['SUPABASE_PUBLISHABLE_KEY']; + const SUPABASE_URL = process.env["SUPABASE_URL"]; + const SUPABASE_PUBLISHABLE_KEY = process.env["SUPABASE_PUBLISHABLE_KEY"]; if (!SUPABASE_URL || !SUPABASE_PUBLISHABLE_KEY) { const missing = [ - ...(!SUPABASE_URL ? ['SUPABASE_URL'] : []), - ...(!SUPABASE_PUBLISHABLE_KEY ? ['SUPABASE_PUBLISHABLE_KEY'] : []), + ...(!SUPABASE_URL ? ["SUPABASE_URL"] : []), + ...(!SUPABASE_PUBLISHABLE_KEY ? ["SUPABASE_PUBLISHABLE_KEY"] : []), ]; - const message = `Missing Supabase environment variable(s): ${missing.join(', ')}. Connect Supabase in Lovable Cloud.`; + const message = `Missing Supabase environment variable(s): ${missing.join(", ")}. Connect Supabase in Lovable Cloud.`; console.error(`[Supabase] ${message}`); throw new Error(message); } - + const request = getRequest(); if (!request?.headers) { - throw new Error('Unauthorized: No request headers available'); + throw new Error("Unauthorized: No request headers available"); } - const authHeader = request.headers.get('authorization'); + const authHeader = request.headers.get("authorization"); if (!authHeader) { - throw new Error('Unauthorized: No authorization header provided'); + throw new Error("Unauthorized: No authorization header provided"); } - if (!authHeader.startsWith('Bearer ')) { - throw new Error('Unauthorized: Only Bearer tokens are supported'); + if (!authHeader.startsWith("Bearer ")) { + throw new Error("Unauthorized: Only Bearer tokens are supported"); } - const token = authHeader.replace('Bearer ', ''); + const token = authHeader.replace("Bearer ", ""); if (!token) { - throw new Error('Unauthorized: No token provided'); + throw new Error("Unauthorized: No token provided"); } - if (token.split('.').length !== 3) { - throw new Error('Unauthorized: Invalid token'); + if (token.split(".").length !== 3) { + throw new Error("Unauthorized: Invalid token"); } - const supabase = createClient( - SUPABASE_URL!, - SUPABASE_PUBLISHABLE_KEY!, - { - global: { - fetch: createSupabaseFetch(SUPABASE_PUBLISHABLE_KEY!), - headers: { - Authorization: `Bearer ${token}`, - }, + const supabase = createClient(SUPABASE_URL!, SUPABASE_PUBLISHABLE_KEY!, { + global: { + fetch: createSupabaseFetch(SUPABASE_PUBLISHABLE_KEY!), + headers: { + Authorization: `Bearer ${token}`, }, - auth: { - storage: undefined, - persistSession: false, - autoRefreshToken: false, - }, - } - ); + }, + auth: { + storage: undefined, + persistSession: false, + autoRefreshToken: false, + }, + }); const { data, error } = await supabase.auth.getClaims(token); if (error || !data?.claims) { - throw new Error('Unauthorized: Invalid token'); + throw new Error("Unauthorized: Invalid token"); } if (!data.claims.sub) { - throw new Error('Unauthorized: No user ID found in token'); + throw new Error("Unauthorized: No user ID found in token"); } return next({ diff --git a/src/integrations/supabase/client.server.ts b/src/integrations/supabase/client.server.ts index 45a04b9..7199f4d 100644 --- a/src/integrations/supabase/client.server.ts +++ b/src/integrations/supabase/client.server.ts @@ -2,17 +2,17 @@ // Server-side Supabase client with service role key - bypasses RLS. // Use this for admin operations in server functions and server routes only. // For user-authenticated queries (with RLS), use the auth middleware instead. -import { createClient } from '@supabase/supabase-js'; -import type { Database } from './types'; +import { createClient } from "@supabase/supabase-js"; +import type { Database } from "./types"; function isNewSupabaseApiKey(value: string): boolean { - return value.startsWith('sb_publishable_') || value.startsWith('sb_secret_'); + return value.startsWith("sb_publishable_") || value.startsWith("sb_secret_"); } function createSupabaseFetch(supabaseKey: string): typeof fetch { return (input, init) => { const headers = new Headers( - typeof Request !== 'undefined' && input instanceof Request ? input.headers : undefined, + typeof Request !== "undefined" && input instanceof Request ? input.headers : undefined, ); if (init?.headers) { @@ -20,25 +20,28 @@ function createSupabaseFetch(supabaseKey: string): typeof fetch { } // New Supabase API keys are opaque strings, not bearer JWTs. - if (isNewSupabaseApiKey(supabaseKey) && headers.get('Authorization') === `Bearer ${supabaseKey}`) { - headers.delete('Authorization'); + if ( + isNewSupabaseApiKey(supabaseKey) && + headers.get("Authorization") === `Bearer ${supabaseKey}` + ) { + headers.delete("Authorization"); } - headers.set('apikey', supabaseKey); + headers.set("apikey", supabaseKey); return fetch(input, { ...init, headers }); }; } function createSupabaseAdminClient() { - const SUPABASE_URL = process.env['SUPABASE_URL']; - const SUPABASE_SERVICE_ROLE_KEY = process.env['SUPABASE_SERVICE_ROLE_KEY']; + const SUPABASE_URL = process.env["SUPABASE_URL"]; + const SUPABASE_SERVICE_ROLE_KEY = process.env["SUPABASE_SERVICE_ROLE_KEY"]; if (!SUPABASE_URL || !SUPABASE_SERVICE_ROLE_KEY) { const missing = [ - ...(!SUPABASE_URL ? ['SUPABASE_URL'] : []), - ...(!SUPABASE_SERVICE_ROLE_KEY ? ['SUPABASE_SERVICE_ROLE_KEY'] : []), + ...(!SUPABASE_URL ? ["SUPABASE_URL"] : []), + ...(!SUPABASE_SERVICE_ROLE_KEY ? ["SUPABASE_SERVICE_ROLE_KEY"] : []), ]; - const message = `Missing Supabase environment variable(s): ${missing.join(', ')}. Connect Supabase in Lovable Cloud.`; + const message = `Missing Supabase environment variable(s): ${missing.join(", ")}. Connect Supabase in Lovable Cloud.`; console.error(`[Supabase] ${message}`); throw new Error(message); } @@ -51,7 +54,7 @@ function createSupabaseAdminClient() { storage: undefined, persistSession: false, autoRefreshToken: false, - } + }, }); } diff --git a/src/integrations/supabase/client.ts b/src/integrations/supabase/client.ts index 0034e50..0e2014d 100644 --- a/src/integrations/supabase/client.ts +++ b/src/integrations/supabase/client.ts @@ -1,15 +1,15 @@ // This file is automatically generated. Do not edit it directly. -import { createClient } from '@supabase/supabase-js'; -import type { Database } from './types'; +import { createClient } from "@supabase/supabase-js"; +import type { Database } from "./types"; function isNewSupabaseApiKey(value: string): boolean { - return value.startsWith('sb_publishable_') || value.startsWith('sb_secret_'); + return value.startsWith("sb_publishable_") || value.startsWith("sb_secret_"); } function createSupabaseFetch(supabaseKey: string): typeof fetch { return (input, init) => { const headers = new Headers( - typeof Request !== 'undefined' && input instanceof Request ? input.headers : undefined, + typeof Request !== "undefined" && input instanceof Request ? input.headers : undefined, ); if (init?.headers) { @@ -17,28 +17,31 @@ function createSupabaseFetch(supabaseKey: string): typeof fetch { } // New Supabase API keys are opaque strings, not bearer JWTs. - if (isNewSupabaseApiKey(supabaseKey) && headers.get('Authorization') === `Bearer ${supabaseKey}`) { - headers.delete('Authorization'); + if ( + isNewSupabaseApiKey(supabaseKey) && + headers.get("Authorization") === `Bearer ${supabaseKey}` + ) { + headers.delete("Authorization"); } - headers.set('apikey', supabaseKey); + headers.set("apikey", supabaseKey); return fetch(input, { ...init, headers }); }; } - function createSupabaseClient() { // Use import.meta.env for client-side (Vite build-time replacement) // Fall back to process.env for SSR (server-side rendering) - const SUPABASE_URL = import.meta.env['VITE_SUPABASE_URL'] || process.env['SUPABASE_URL']; - const SUPABASE_PUBLISHABLE_KEY = import.meta.env['VITE_SUPABASE_PUBLISHABLE_KEY'] || process.env['SUPABASE_PUBLISHABLE_KEY']; + const SUPABASE_URL = import.meta.env["VITE_SUPABASE_URL"] || process.env["SUPABASE_URL"]; + const SUPABASE_PUBLISHABLE_KEY = + import.meta.env["VITE_SUPABASE_PUBLISHABLE_KEY"] || process.env["SUPABASE_PUBLISHABLE_KEY"]; if (!SUPABASE_URL || !SUPABASE_PUBLISHABLE_KEY) { const missing = [ - ...(!SUPABASE_URL ? ['SUPABASE_URL'] : []), - ...(!SUPABASE_PUBLISHABLE_KEY ? ['SUPABASE_PUBLISHABLE_KEY'] : []), + ...(!SUPABASE_URL ? ["SUPABASE_URL"] : []), + ...(!SUPABASE_PUBLISHABLE_KEY ? ["SUPABASE_PUBLISHABLE_KEY"] : []), ]; - const message = `Missing Supabase environment variable(s): ${missing.join(', ')}. Connect Supabase in Lovable Cloud.`; + const message = `Missing Supabase environment variable(s): ${missing.join(", ")}. Connect Supabase in Lovable Cloud.`; console.error(`[Supabase] ${message}`); throw new Error(message); } @@ -48,10 +51,10 @@ function createSupabaseClient() { fetch: createSupabaseFetch(SUPABASE_PUBLISHABLE_KEY), }, auth: { - storage: typeof window !== 'undefined' ? localStorage : undefined, + storage: typeof window !== "undefined" ? localStorage : undefined, persistSession: true, autoRefreshToken: true, - } + }, }); } @@ -65,4 +68,3 @@ export const supabase = new Proxy({} as ReturnType, return Reflect.get(_supabase, prop, receiver); }, }); - diff --git a/src/integrations/supabase/types.ts b/src/integrations/supabase/types.ts index 60b0e17..76aa6da 100644 --- a/src/integrations/supabase/types.ts +++ b/src/integrations/supabase/types.ts @@ -1,595 +1,583 @@ -export type Json = - | string - | number - | boolean - | null - | { [key: string]: Json | undefined } - | Json[] +export type Json = string | number | boolean | null | { [key: string]: Json | undefined } | Json[]; export type Database = { // Allows to automatically instantiate createClient with right options // instead of createClient(URL, KEY) __InternalSupabase: { - PostgrestVersion: "14.15" - } + PostgrestVersion: "14.15"; + }; public: { Tables: { badge_social_voti: { Row: { - categoria: string - created_at: string - id: string - match_id: string - updated_at: string - votante_id: string - votato_id: string - votato_nome: string - } + categoria: string; + created_at: string; + id: string; + match_id: string; + updated_at: string; + votante_id: string; + votato_id: string; + votato_nome: string; + }; Insert: { - categoria: string - created_at?: string - id?: string - match_id: string - updated_at?: string - votante_id: string - votato_id: string - votato_nome: string - } + categoria: string; + created_at?: string; + id?: string; + match_id: string; + updated_at?: string; + votante_id: string; + votato_id: string; + votato_nome: string; + }; Update: { - categoria?: string - created_at?: string - id?: string - match_id?: string - updated_at?: string - votante_id?: string - votato_id?: string - votato_nome?: string - } - Relationships: [] - } + categoria?: string; + created_at?: string; + id?: string; + match_id?: string; + updated_at?: string; + votante_id?: string; + votato_id?: string; + votato_nome?: string; + }; + Relationships: []; + }; cacche_partita: { Row: { - created_at: string - evento_id: string - giocatore_id: string - id: string - quantita: number - updated_at: string - } + created_at: string; + evento_id: string; + giocatore_id: string; + id: string; + quantita: number; + updated_at: string; + }; Insert: { - created_at?: string - evento_id: string - giocatore_id: string - id?: string - quantita?: number - updated_at?: string - } + created_at?: string; + evento_id: string; + giocatore_id: string; + id?: string; + quantita?: number; + updated_at?: string; + }; Update: { - created_at?: string - evento_id?: string - giocatore_id?: string - id?: string - quantita?: number - updated_at?: string - } - Relationships: [] - } + created_at?: string; + evento_id?: string; + giocatore_id?: string; + id?: string; + quantita?: number; + updated_at?: string; + }; + Relationships: []; + }; eventi: { Row: { - aggiornato_il: string - avversario: string | null - casa: boolean | null - creato_da: string | null - creato_il: string - data: string - id: string - luogo: string - ora: string - tipo: string - titolo: string - } + aggiornato_il: string; + avversario: string | null; + casa: boolean | null; + creato_da: string | null; + creato_il: string; + data: string; + id: string; + luogo: string; + ora: string; + tipo: string; + titolo: string; + }; Insert: { - aggiornato_il?: string - avversario?: string | null - casa?: boolean | null - creato_da?: string | null - creato_il?: string - data: string - id?: string - luogo: string - ora: string - tipo: string - titolo: string - } + aggiornato_il?: string; + avversario?: string | null; + casa?: boolean | null; + creato_da?: string | null; + creato_il?: string; + data: string; + id?: string; + luogo: string; + ora: string; + tipo: string; + titolo: string; + }; Update: { - aggiornato_il?: string - avversario?: string | null - casa?: boolean | null - creato_da?: string | null - creato_il?: string - data?: string - id?: string - luogo?: string - ora?: string - tipo?: string - titolo?: string - } - Relationships: [] - } + aggiornato_il?: string; + avversario?: string | null; + casa?: boolean | null; + creato_da?: string | null; + creato_il?: string; + data?: string; + id?: string; + luogo?: string; + ora?: string; + tipo?: string; + titolo?: string; + }; + Relationships: []; + }; eventi_app: { Row: { - aggiornato_il: string - campionato: boolean - casa: boolean - convocati: string[] - creato_il: string - data: string - id: string - luogo: string - note: string - ora: string - pagelle_chiuse: boolean - tipo: string - titolo: string - } + aggiornato_il: string; + campionato: boolean; + casa: boolean; + convocati: string[]; + creato_il: string; + data: string; + id: string; + luogo: string; + note: string; + ora: string; + pagelle_chiuse: boolean; + tipo: string; + titolo: string; + }; Insert: { - aggiornato_il?: string - campionato?: boolean - casa?: boolean - convocati?: string[] - creato_il?: string - data: string - id: string - luogo?: string - note?: string - ora?: string - pagelle_chiuse?: boolean - tipo: string - titolo: string - } + aggiornato_il?: string; + campionato?: boolean; + casa?: boolean; + convocati?: string[]; + creato_il?: string; + data: string; + id: string; + luogo?: string; + note?: string; + ora?: string; + pagelle_chiuse?: boolean; + tipo: string; + titolo: string; + }; Update: { - aggiornato_il?: string - campionato?: boolean - casa?: boolean - convocati?: string[] - creato_il?: string - data?: string - id?: string - luogo?: string - note?: string - ora?: string - pagelle_chiuse?: boolean - tipo?: string - titolo?: string - } - Relationships: [] - } + aggiornato_il?: string; + campionato?: boolean; + casa?: boolean; + convocati?: string[]; + creato_il?: string; + data?: string; + id?: string; + luogo?: string; + note?: string; + ora?: string; + pagelle_chiuse?: boolean; + tipo?: string; + titolo?: string; + }; + Relationships: []; + }; giocatori: { Row: { - aggiornato_il: string - auth_user_id: string | null - creato_il: string - foto: string | null - id: string - nascita: string - nome: string - numero: number - ruolo: string - } + aggiornato_il: string; + auth_user_id: string | null; + creato_il: string; + foto: string | null; + id: string; + nascita: string; + nome: string; + numero: number; + ruolo: string; + }; Insert: { - aggiornato_il?: string - auth_user_id?: string | null - creato_il?: string - foto?: string | null - id?: string - nascita: string - nome: string - numero: number - ruolo: string - } + aggiornato_il?: string; + auth_user_id?: string | null; + creato_il?: string; + foto?: string | null; + id?: string; + nascita: string; + nome: string; + numero: number; + ruolo: string; + }; Update: { - aggiornato_il?: string - auth_user_id?: string | null - creato_il?: string - foto?: string | null - id?: string - nascita?: string - nome?: string - numero?: number - ruolo?: string - } - Relationships: [] - } + aggiornato_il?: string; + auth_user_id?: string | null; + creato_il?: string; + foto?: string | null; + id?: string; + nascita?: string; + nome?: string; + numero?: number; + ruolo?: string; + }; + Relationships: []; + }; mvp_voti: { Row: { - created_at: string - id: string - match_id: string - updated_at: string - votante_id: string - votato_id: string - votato_nome: string - } + created_at: string; + id: string; + match_id: string; + updated_at: string; + votante_id: string; + votato_id: string; + votato_nome: string; + }; Insert: { - created_at?: string - id?: string - match_id: string - updated_at?: string - votante_id: string - votato_id: string - votato_nome: string - } + created_at?: string; + id?: string; + match_id: string; + updated_at?: string; + votante_id: string; + votato_id: string; + votato_nome: string; + }; Update: { - created_at?: string - id?: string - match_id?: string - updated_at?: string - votante_id?: string - votato_id?: string - votato_nome?: string - } - Relationships: [] - } + created_at?: string; + id?: string; + match_id?: string; + updated_at?: string; + votante_id?: string; + votato_id?: string; + votato_nome?: string; + }; + Relationships: []; + }; pagelle_voti: { Row: { - created_at: string - id: string - match_id: string - updated_at: string - votante_id: string - votato_id: string - voto: number - } + created_at: string; + id: string; + match_id: string; + updated_at: string; + votante_id: string; + votato_id: string; + voto: number; + }; Insert: { - created_at?: string - id?: string - match_id: string - updated_at?: string - votante_id: string - votato_id: string - voto: number - } + created_at?: string; + id?: string; + match_id: string; + updated_at?: string; + votante_id: string; + votato_id: string; + voto: number; + }; Update: { - created_at?: string - id?: string - match_id?: string - updated_at?: string - votante_id?: string - votato_id?: string - voto?: number - } - Relationships: [] - } + created_at?: string; + id?: string; + match_id?: string; + updated_at?: string; + votante_id?: string; + votato_id?: string; + voto?: number; + }; + Relationships: []; + }; presenze: { Row: { - aggiornato_da: string | null - aggiornato_il: string - evento_id: string - giocatore_id: string - id: string - stato: string - } + aggiornato_da: string | null; + aggiornato_il: string; + evento_id: string; + giocatore_id: string; + id: string; + stato: string; + }; Insert: { - aggiornato_da?: string | null - aggiornato_il?: string - evento_id: string - giocatore_id: string - id?: string - stato: string - } + aggiornato_da?: string | null; + aggiornato_il?: string; + evento_id: string; + giocatore_id: string; + id?: string; + stato: string; + }; Update: { - aggiornato_da?: string | null - aggiornato_il?: string - evento_id?: string - giocatore_id?: string - id?: string - stato?: string - } + aggiornato_da?: string | null; + aggiornato_il?: string; + evento_id?: string; + giocatore_id?: string; + id?: string; + stato?: string; + }; Relationships: [ { - foreignKeyName: "presenze_evento_id_fkey" - columns: ["evento_id"] - isOneToOne: false - referencedRelation: "eventi" - referencedColumns: ["id"] + foreignKeyName: "presenze_evento_id_fkey"; + columns: ["evento_id"]; + isOneToOne: false; + referencedRelation: "eventi"; + referencedColumns: ["id"]; }, { - foreignKeyName: "presenze_giocatore_id_fkey" - columns: ["giocatore_id"] - isOneToOne: false - referencedRelation: "giocatori" - referencedColumns: ["id"] + foreignKeyName: "presenze_giocatore_id_fkey"; + columns: ["giocatore_id"]; + isOneToOne: false; + referencedRelation: "giocatori"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; promemoria_push: { Row: { - creato_il: string - endpoint: string - id: string - testo: string - titolo: string - } + creato_il: string; + endpoint: string; + id: string; + testo: string; + titolo: string; + }; Insert: { - creato_il?: string - endpoint: string - id?: string - testo: string - titolo: string - } + creato_il?: string; + endpoint: string; + id?: string; + testo: string; + titolo: string; + }; Update: { - creato_il?: string - endpoint?: string - id?: string - testo?: string - titolo?: string - } - Relationships: [] - } + creato_il?: string; + endpoint?: string; + id?: string; + testo?: string; + titolo?: string; + }; + Relationships: []; + }; push_subscriptions: { Row: { - auth: string - created_at: string - endpoint: string - giocatore_id: string - id: string - p256dh: string - } + auth: string; + created_at: string; + endpoint: string; + giocatore_id: string; + id: string; + p256dh: string; + }; Insert: { - auth: string - created_at?: string - endpoint: string - giocatore_id: string - id?: string - p256dh: string - } + auth: string; + created_at?: string; + endpoint: string; + giocatore_id: string; + id?: string; + p256dh: string; + }; Update: { - auth?: string - created_at?: string - endpoint?: string - giocatore_id?: string - id?: string - p256dh?: string - } - Relationships: [] - } + auth?: string; + created_at?: string; + endpoint?: string; + giocatore_id?: string; + id?: string; + p256dh?: string; + }; + Relationships: []; + }; risposte_presenze: { Row: { - aggiornato_il: string - evento_id: string - giocatore_id: string - stato: string - } + aggiornato_il: string; + evento_id: string; + giocatore_id: string; + stato: string; + }; Insert: { - aggiornato_il?: string - evento_id: string - giocatore_id: string - stato: string - } + aggiornato_il?: string; + evento_id: string; + giocatore_id: string; + stato: string; + }; Update: { - aggiornato_il?: string - evento_id?: string - giocatore_id?: string - stato?: string - } - Relationships: [] - } + aggiornato_il?: string; + evento_id?: string; + giocatore_id?: string; + stato?: string; + }; + Relationships: []; + }; scout_live: { Row: { - aggiornato_il: string - evento_id: string - stato: Json - } + aggiornato_il: string; + evento_id: string; + stato: Json; + }; Insert: { - aggiornato_il?: string - evento_id: string - stato?: Json - } + aggiornato_il?: string; + evento_id: string; + stato?: Json; + }; Update: { - aggiornato_il?: string - evento_id?: string - stato?: Json - } - Relationships: [] - } + aggiornato_il?: string; + evento_id?: string; + stato?: Json; + }; + Relationships: []; + }; scout_sessioni: { Row: { - aggiornato_il: string - evento_id: string - giocatore_id: string - giocatore_nome: string - } + aggiornato_il: string; + evento_id: string; + giocatore_id: string; + giocatore_nome: string; + }; Insert: { - aggiornato_il?: string - evento_id: string - giocatore_id: string - giocatore_nome: string - } + aggiornato_il?: string; + evento_id: string; + giocatore_id: string; + giocatore_nome: string; + }; Update: { - aggiornato_il?: string - evento_id?: string - giocatore_id?: string - giocatore_nome?: string - } - Relationships: [] - } + aggiornato_il?: string; + evento_id?: string; + giocatore_id?: string; + giocatore_nome?: string; + }; + Relationships: []; + }; turni_palloni: { Row: { - aggiornato_da: string | null - aggiornato_il: string - evento_id: string - giocatore_id: string - } + aggiornato_da: string | null; + aggiornato_il: string; + evento_id: string; + giocatore_id: string; + }; Insert: { - aggiornato_da?: string | null - aggiornato_il?: string - evento_id: string - giocatore_id: string - } + aggiornato_da?: string | null; + aggiornato_il?: string; + evento_id: string; + giocatore_id: string; + }; Update: { - aggiornato_da?: string | null - aggiornato_il?: string - evento_id?: string - giocatore_id?: string - } - Relationships: [] - } + aggiornato_da?: string | null; + aggiornato_il?: string; + evento_id?: string; + giocatore_id?: string; + }; + Relationships: []; + }; user_roles: { Row: { - id: string - role: Database["public"]["Enums"]["app_role"] - user_id: string - } + id: string; + role: Database["public"]["Enums"]["app_role"]; + user_id: string; + }; Insert: { - id?: string - role: Database["public"]["Enums"]["app_role"] - user_id: string - } + id?: string; + role: Database["public"]["Enums"]["app_role"]; + user_id: string; + }; Update: { - id?: string - role?: Database["public"]["Enums"]["app_role"] - user_id?: string - } - Relationships: [] - } - } + id?: string; + role?: Database["public"]["Enums"]["app_role"]; + user_id?: string; + }; + Relationships: []; + }; + }; Views: { - [_ in never]: never - } + [_ in never]: never; + }; Functions: { has_role: { Args: { - _role: Database["public"]["Enums"]["app_role"] - _user_id: string - } - Returns: boolean - } - } + _role: Database["public"]["Enums"]["app_role"]; + _user_id: string; + }; + Returns: boolean; + }; + }; Enums: { - app_role: "admin" | "user" - } + app_role: "admin" | "user"; + }; CompositeTypes: { - [_ in never]: never - } - } -} + [_ in never]: never; + }; + }; +}; -type DatabaseWithoutInternals = Omit +type DatabaseWithoutInternals = Omit; -type DefaultSchema = DatabaseWithoutInternals[Extract] +type DefaultSchema = DatabaseWithoutInternals[Extract]; export type Tables< DefaultSchemaTableNameOrOptions extends | keyof (DefaultSchema["Tables"] & DefaultSchema["Views"]) | { schema: keyof DatabaseWithoutInternals }, - TableName extends DefaultSchemaTableNameOrOptions extends { - schema: keyof DatabaseWithoutInternals + TableName extends (DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals; } ? keyof (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] & DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Views"]) - : never = never, + : never) = never, > = DefaultSchemaTableNameOrOptions extends { - schema: keyof DatabaseWithoutInternals + schema: keyof DatabaseWithoutInternals; } ? (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] & DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Views"])[TableName] extends { - Row: infer R + Row: infer R; } ? R : never - : DefaultSchemaTableNameOrOptions extends keyof (DefaultSchema["Tables"] & - DefaultSchema["Views"]) - ? (DefaultSchema["Tables"] & - DefaultSchema["Views"])[DefaultSchemaTableNameOrOptions] extends { - Row: infer R + : DefaultSchemaTableNameOrOptions extends keyof (DefaultSchema["Tables"] & DefaultSchema["Views"]) + ? (DefaultSchema["Tables"] & DefaultSchema["Views"])[DefaultSchemaTableNameOrOptions] extends { + Row: infer R; } ? R : never - : never + : never; export type TablesInsert< DefaultSchemaTableNameOrOptions extends - | keyof DefaultSchema["Tables"] - | { schema: keyof DatabaseWithoutInternals }, - TableName extends DefaultSchemaTableNameOrOptions extends { - schema: keyof DatabaseWithoutInternals + keyof DefaultSchema["Tables"] | { schema: keyof DatabaseWithoutInternals }, + TableName extends (DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals; } ? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] - : never = never, + : never) = never, > = DefaultSchemaTableNameOrOptions extends { - schema: keyof DatabaseWithoutInternals + schema: keyof DatabaseWithoutInternals; } ? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends { - Insert: infer I + Insert: infer I; } ? I : never : DefaultSchemaTableNameOrOptions extends keyof DefaultSchema["Tables"] ? DefaultSchema["Tables"][DefaultSchemaTableNameOrOptions] extends { - Insert: infer I + Insert: infer I; } ? I : never - : never + : never; export type TablesUpdate< DefaultSchemaTableNameOrOptions extends - | keyof DefaultSchema["Tables"] - | { schema: keyof DatabaseWithoutInternals }, - TableName extends DefaultSchemaTableNameOrOptions extends { - schema: keyof DatabaseWithoutInternals + keyof DefaultSchema["Tables"] | { schema: keyof DatabaseWithoutInternals }, + TableName extends (DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals; } ? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] - : never = never, + : never) = never, > = DefaultSchemaTableNameOrOptions extends { - schema: keyof DatabaseWithoutInternals + schema: keyof DatabaseWithoutInternals; } ? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends { - Update: infer U + Update: infer U; } ? U : never : DefaultSchemaTableNameOrOptions extends keyof DefaultSchema["Tables"] ? DefaultSchema["Tables"][DefaultSchemaTableNameOrOptions] extends { - Update: infer U + Update: infer U; } ? U : never - : never + : never; export type Enums< DefaultSchemaEnumNameOrOptions extends - | keyof DefaultSchema["Enums"] - | { schema: keyof DatabaseWithoutInternals }, - EnumName extends DefaultSchemaEnumNameOrOptions extends { - schema: keyof DatabaseWithoutInternals + keyof DefaultSchema["Enums"] | { schema: keyof DatabaseWithoutInternals }, + EnumName extends (DefaultSchemaEnumNameOrOptions extends { + schema: keyof DatabaseWithoutInternals; } ? keyof DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"] - : never = never, + : never) = never, > = DefaultSchemaEnumNameOrOptions extends { - schema: keyof DatabaseWithoutInternals + schema: keyof DatabaseWithoutInternals; } ? DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"][EnumName] : DefaultSchemaEnumNameOrOptions extends keyof DefaultSchema["Enums"] ? DefaultSchema["Enums"][DefaultSchemaEnumNameOrOptions] - : never + : never; export type CompositeTypes< PublicCompositeTypeNameOrOptions extends - | keyof DefaultSchema["CompositeTypes"] - | { schema: keyof DatabaseWithoutInternals }, - CompositeTypeName extends PublicCompositeTypeNameOrOptions extends { - schema: keyof DatabaseWithoutInternals + keyof DefaultSchema["CompositeTypes"] | { schema: keyof DatabaseWithoutInternals }, + CompositeTypeName extends (PublicCompositeTypeNameOrOptions extends { + schema: keyof DatabaseWithoutInternals; } ? keyof DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"] - : never = never, + : never) = never, > = PublicCompositeTypeNameOrOptions extends { - schema: keyof DatabaseWithoutInternals + schema: keyof DatabaseWithoutInternals; } ? DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"][CompositeTypeName] : PublicCompositeTypeNameOrOptions extends keyof DefaultSchema["CompositeTypes"] ? DefaultSchema["CompositeTypes"][PublicCompositeTypeNameOrOptions] - : never + : never; export const Constants = { public: { @@ -597,4 +585,4 @@ export const Constants = { app_role: ["admin", "user"], }, }, -} as const +} as const; diff --git a/src/lib/badge-social.ts b/src/lib/badge-social.ts index 346d001..2f96d02 100644 --- a/src/lib/badge-social.ts +++ b/src/lib/badge-social.ts @@ -139,8 +139,7 @@ export function mioVotoSocial( ) { return ( voti.find( - (v) => - v.match_id === matchId && v.categoria === categoria && v.votante_id === votanteId, + (v) => v.match_id === matchId && v.categoria === categoria && v.votante_id === votanteId, ) ?? null ); } @@ -156,4 +155,4 @@ export function badgeSocialVinti(voti: VotoSocial[], giocatoreId: string) { } } return out; -} \ No newline at end of file +} diff --git a/src/lib/badges.ts b/src/lib/badges.ts index e645c52..dfac5c7 100644 --- a/src/lib/badges.ts +++ b/src/lib/badges.ts @@ -19,10 +19,7 @@ export type Grado = "bronzo" | "argento" | "oro"; export const gradiOrdine: Grado[] = ["bronzo", "argento", "oro"]; -export const gradoMeta: Record< - Grado, - { label: string; text: string; bg: string; ring: string } -> = { +export const gradoMeta: Record = { bronzo: { label: "Bronzo", text: "text-bronzo", bg: "bg-bronzo/15", ring: "ring-bronzo/40" }, argento: { label: "Argento", text: "text-argento", bg: "bg-argento/20", ring: "ring-argento/50" }, oro: { label: "Oro", text: "text-oro", bg: "bg-oro/20", ring: "ring-oro/50" }, @@ -50,7 +47,8 @@ export const badgeDefs: BadgeDef[] = [ { id: "mvp", nome: "MVP", - descrizione: "Riconoscimento per il miglior giocatore della partita, scelto dai compagni a fine match.", + descrizione: + "Riconoscimento per il miglior giocatore della partita, scelto dai compagni a fine match.", unita: "MVP", icon: Trophy, soglie: { bronzo: 1, argento: 3, oro: 5 }, @@ -59,7 +57,8 @@ export const badgeDefs: BadgeDef[] = [ { id: "pagella", nome: "Pagellone", - descrizione: "Media dei voti che i compagni ti danno a fine partita: conta come giochi, non quanti punti fai.", + descrizione: + "Media dei voti che i compagni ti danno a fine partita: conta come giochi, non quanti punti fai.", unita: "di media voto", icon: ClipboardCheck, soglie: { bronzo: 6.5, argento: 7.5, oro: 8.5 }, @@ -68,7 +67,8 @@ export const badgeDefs: BadgeDef[] = [ { id: "palloni", nome: "Sherpa dei palloni", - descrizione: "Quante volte ti sei caricato la sacca dei palloni: lavoro oscuro, badge luminoso.", + descrizione: + "Quante volte ti sei caricato la sacca dei palloni: lavoro oscuro, badge luminoso.", unita: "turni palloni", icon: CircleDot, soglie: { bronzo: 3, argento: 6, oro: 10 }, @@ -86,7 +86,8 @@ export const badgeDefs: BadgeDef[] = [ { id: "serie-allenamenti", nome: "Sempre in palestra", - descrizione: "Allenamenti consecutivi a cui sei stato presente: la costanza paga più del talento.", + descrizione: + "Allenamenti consecutivi a cui sei stato presente: la costanza paga più del talento.", unita: "allenamenti di fila", icon: Rocket, soglie: { bronzo: 3, argento: 6, oro: 10 }, @@ -108,7 +109,8 @@ export const badgeSegreti: BadgeDef[] = [ { id: "s-tiebreak", nome: "Uomo tie-break", - descrizione: "Sbloccato da chi ha almeno 2 MVP e una media voto alta: nei momenti caldi ci sei sempre.", + descrizione: + "Sbloccato da chi ha almeno 2 MVP e una media voto alta: nei momenti caldi ci sei sempre.", unita: "MVP con media alta", icon: Ghost, segreto: true, @@ -119,7 +121,8 @@ export const badgeSegreti: BadgeDef[] = [ { id: "s-mai-forfait", nome: "Mai un forfait", - descrizione: "Sbloccato con 10 conferme rapide consecutive e 15 presenze: su di te la squadra può contare a occhi chiusi.", + descrizione: + "Sbloccato con 10 conferme rapide consecutive e 15 presenze: su di te la squadra può contare a occhi chiusi.", unita: "requisito nascosto", icon: Anchor, segreto: true, @@ -268,4 +271,4 @@ export function badgeSbloccati(g: Giocatore): BadgeStato[] { export function descrizioneSoglie(def: BadgeDef) { return `${def.soglie.bronzo}/${def.soglie.argento}/${def.soglie.oro} ${def.unita}`; -} \ No newline at end of file +} diff --git a/src/lib/cacche.ts b/src/lib/cacche.ts index 886da7d..bbdafd9 100644 --- a/src/lib/cacche.ts +++ b/src/lib/cacche.ts @@ -63,7 +63,13 @@ function arrotonda(n: number) { export function statisticheCacche(righe: RigaCacche[]): Record { const out: Record = {}; for (const r of righe) { - const cur = out[r.giocatore_id] ?? { totale: 0, giornate: 0, media: 0, record: 0, giornateTop: 0 }; + const cur = out[r.giocatore_id] ?? { + totale: 0, + giornate: 0, + media: 0, + record: 0, + giornateTop: 0, + }; cur.totale += r.quantita; cur.giornate += 1; cur.record = Math.max(cur.record, r.quantita); diff --git a/src/lib/crapp-data.ts b/src/lib/crapp-data.ts index 2890bec..829f00f 100644 --- a/src/lib/crapp-data.ts +++ b/src/lib/crapp-data.ts @@ -2,10 +2,18 @@ export type Stato = "presente" | "assente" | "forse" | "ritardo" | "infortunato" export const statoMeta: Record = { presente: { label: "Presente", emoji: "✅", className: "bg-success text-success-foreground" }, - assente: { label: "Assente", emoji: "❌", className: "bg-destructive text-destructive-foreground" }, + assente: { + label: "Assente", + emoji: "❌", + className: "bg-destructive text-destructive-foreground", + }, forse: { label: "Forse", emoji: "🤔", className: "bg-warning text-warning-foreground" }, ritardo: { label: "In ritardo", emoji: "⏱️", className: "bg-info text-info-foreground" }, - infortunato: { label: "Infortunato", emoji: "🩹", className: "bg-primary text-primary-foreground" }, + infortunato: { + label: "Infortunato", + emoji: "🩹", + className: "bg-primary text-primary-foreground", + }, }; /** @@ -125,24 +133,6 @@ export const giocatori: Giocatore[] = rosaCSI.map((r, i) => ({ ...serieDa(statsDemo[r.nome]), })); -export type Match = { - id: string; - data: string; - avversario: string; - casa: boolean; - setNostri: number; - setLoro: number; - parziali: Array<[number, number]>; - mvp: string; -}; - -export const storicoMatch: Match[] = [ - { id: "m1", data: "2026-07-25", avversario: "Pallavolo Sesto", casa: true, setNostri: 3, setLoro: 1, parziali: [[25, 19], [23, 25], [25, 21], [25, 18]], mvp: "Davide Grilli" }, - { id: "m2", data: "2026-07-18", avversario: "ASD Rondinella", casa: false, setNostri: 2, setLoro: 3, parziali: [[25, 22], [19, 25], [25, 23], [20, 25], [12, 15]], mvp: "Ivan Cacciari" }, - { id: "m3", data: "2026-07-11", avversario: "Virtus Cinisello", casa: true, setNostri: 3, setLoro: 0, parziali: [[25, 15], [25, 20], [25, 17]], mvp: "Laura Passabì" }, - { id: "m4", data: "2026-07-04", avversario: "Nuova Bovisa", casa: false, setNostri: 3, setLoro: 2, parziali: [[21, 25], [25, 23], [18, 25], [25, 20], [15, 11]], mvp: "Nicola Pezzoli" }, -]; - export type RigaClassifica = { pos: number; squadra: string; @@ -154,16 +144,6 @@ export type RigaClassifica = { punti: number; }; -export const classifica: RigaClassifica[] = [ - { pos: 1, squadra: "ASD Rondinella", giocate: 12, vinte: 10, perse: 2, setFatti: 33, setSubiti: 12, punti: 29 }, - { pos: 2, squadra: "CRAP Volley", giocate: 12, vinte: 9, perse: 3, setFatti: 31, setSubiti: 16, punti: 26 }, - { pos: 3, squadra: "Pallavolo Sesto", giocate: 12, vinte: 8, perse: 4, setFatti: 29, setSubiti: 19, punti: 24 }, - { pos: 4, squadra: "Volley Bruzzano", giocate: 12, vinte: 7, perse: 5, setFatti: 26, setSubiti: 21, punti: 21 }, - { pos: 5, squadra: "Aurora Nera", giocate: 12, vinte: 5, perse: 7, setFatti: 22, setSubiti: 25, punti: 16 }, - { pos: 6, squadra: "Virtus Cinisello", giocate: 12, vinte: 3, perse: 9, setFatti: 15, setSubiti: 30, punti: 10 }, - { pos: 7, squadra: "Nuova Bovisa", giocate: 12, vinte: 2, perse: 10, setFatti: 13, setSubiti: 32, punti: 7 }, -]; - export function formatData(iso: string) { const d = new Date(iso + "T00:00:00"); return d.toLocaleDateString("it-IT", { weekday: "short", day: "2-digit", month: "long" }); diff --git a/src/lib/csi-core.ts b/src/lib/csi-core.ts index e4a232b..f2bf897 100644 --- a/src/lib/csi-core.ts +++ b/src/lib/csi-core.ts @@ -161,3 +161,16 @@ export function partiteDaEventi(eventi: unknown): PartitaCsi[] { export function partiteGiocate(partite: PartitaCsi[]): PartitaCsi[] { return partite.filter((p) => p.setNostri !== null && p.setLoro !== null); } + +/** Converte una gara CSI già giocata nella forma comune usata nelle liste risultati. */ +export function matchDaPartitaCsi(p: PartitaCsi) { + return { + id: p.id, + data: p.data, + avversario: p.avversario, + casa: p.casa, + setNostri: p.setNostri ?? 0, + setLoro: p.setLoro ?? 0, + parziali: p.parziali, + }; +} diff --git a/src/lib/infortuni.ts b/src/lib/infortuni.ts index 37ba231..3eca0dc 100644 --- a/src/lib/infortuni.ts +++ b/src/lib/infortuni.ts @@ -57,4 +57,4 @@ export function useInfortuniERitardi(): { infortuni: ContoInfortuni; ritardi: Co () => ({ infortuni: contaInfortuni(presenze), ritardi: contaRitardi(presenze) }), [presenze], ); -} \ No newline at end of file +} diff --git a/src/lib/motion.ts b/src/lib/motion.ts index cc35bb4..bd3b716 100644 --- a/src/lib/motion.ts +++ b/src/lib/motion.ts @@ -90,4 +90,4 @@ export async function coriandoli(ridotto = false) { origin: { y: 0.7 }, disableForReducedMotion: true, }); -} \ No newline at end of file +} diff --git a/src/lib/mvp-voti.ts b/src/lib/mvp-voti.ts index 5b71d2b..a41d857 100644 --- a/src/lib/mvp-voti.ts +++ b/src/lib/mvp-voti.ts @@ -83,4 +83,4 @@ export function vincitoriMvp(voti: VotoMvp[]): Record { export function mioVoto(voti: VotoMvp[], matchId: string, votanteId: string) { return voti.find((v) => v.match_id === matchId && v.votante_id === votanteId) ?? null; -} \ No newline at end of file +} diff --git a/src/lib/notifiche-smart.ts b/src/lib/notifiche-smart.ts index abc2617..f1df44a 100644 --- a/src/lib/notifiche-smart.ts +++ b/src/lib/notifiche-smart.ts @@ -1,16 +1,7 @@ import { useCallback, useEffect, useState } from "react"; import type { Giocatore } from "./crapp-data"; -import { - microcopyObiettivo, - progressoObiettivo, - type ObiettivoSquadra, -} from "./obiettivi"; -import { - badgeGiocatore, - badgeSegretiSbloccati, - gradoMeta, - prossimoTraguardo, -} from "./badges"; +import { microcopyObiettivo, progressoObiettivo, type ObiettivoSquadra } from "./obiettivi"; +import { badgeGiocatore, badgeSegretiSbloccati, gradoMeta, prossimoTraguardo } from "./badges"; import { serieGiocatore } from "./serie"; import { badgeSocialVinti, categorieSocial, type VotoSocial } from "./badge-social"; @@ -166,4 +157,4 @@ export function useNotificheSmart(g: Giocatore | null, votiSocial: VotoSocial[] const chiudi = useCallback(() => setCoda((c) => c.slice(1)), []); return { notifica: coda[0] ?? null, restanti: Math.max(0, coda.length - 1), chiudi }; -} \ No newline at end of file +} diff --git a/src/lib/obiettivi.ts b/src/lib/obiettivi.ts index 7766581..4c7f020 100644 --- a/src/lib/obiettivi.ts +++ b/src/lib/obiettivi.ts @@ -1,4 +1,4 @@ -import { giocatori, storicoMatch, type Giocatore } from "./crapp-data"; +import { giocatori, type Giocatore } from "./crapp-data"; import type { Evento } from "./eventi"; import type { MappaPresenze } from "./presenze"; import { mediaSquadra, type VotoPagella } from "./pagelle"; @@ -20,6 +20,8 @@ export type ContestoObiettivi = { eventi: Evento[]; presenze: MappaPresenze; pagelle: VotoPagella[]; + /** Vittorie ufficiali in campionato (dato CSI). */ + vittorie?: number; }; export const contestoVuoto: ContestoObiettivi = { eventi: [], presenze: {}, pagelle: [] }; @@ -50,8 +52,6 @@ function percentualeRisposte(ctx: ContestoObiettivi) { return Math.round((risposte / posti) * 100); } -const vittorie = storicoMatch.filter((m) => m.setNostri > m.setLoro).length; - /** Obiettivi collaborativi: si muovono con il contributo di tutta la rosa. */ export function obiettiviSquadra( rosa: Giocatore[] = giocatori, @@ -59,6 +59,7 @@ export function obiettiviSquadra( ): ObiettivoSquadra[] { const somma = (f: (g: Giocatore) => number) => rosa.reduce((s, g) => s + f(g), 0); const continui = rosa.filter((g) => g.serieAllenamenti >= 3).length; + const vittorie = ctx.vittorie ?? 0; return [ { id: "o1", diff --git a/src/lib/presenze-mese.ts b/src/lib/presenze-mese.ts index 8716484..3fbab8c 100644 --- a/src/lib/presenze-mese.ts +++ b/src/lib/presenze-mese.ts @@ -60,8 +60,7 @@ export function usePresenzeUltimoMeseTutti(): Record< const out: Record = {}; for (const e of rilevanti) { - const ids = - e.convocati.length > 0 ? e.convocati : giocatori.map((g) => g.id); + const ids = e.convocati.length > 0 ? e.convocati : giocatori.map((g) => g.id); for (const id of ids) { const rec = (out[id] ??= { presenti: 0, totali: 0, percentuale: 0 }); rec.totali += 1; @@ -74,4 +73,4 @@ export function usePresenzeUltimoMeseTutti(): Record< } return out; }, [eventi, presenze]); -} \ No newline at end of file +} diff --git a/src/lib/push-client.ts b/src/lib/push-client.ts index 616b650..b8adad5 100644 --- a/src/lib/push-client.ts +++ b/src/lib/push-client.ts @@ -69,4 +69,4 @@ export async function disattivaNotifiche(): Promise { await sub.unsubscribe(); } await reg?.unregister(); -} \ No newline at end of file +} diff --git a/src/lib/rosa.ts b/src/lib/rosa.ts index d8b1ea4..8482a49 100644 --- a/src/lib/rosa.ts +++ b/src/lib/rosa.ts @@ -11,6 +11,8 @@ import { useGiocatoreBase } from "./user-store"; import { useEventi } from "./eventi"; import { useRispostePresenze } from "./presenze"; import { obiettiviOrdinati } from "./obiettivi"; +import { useCsi } from "./csi"; +import { partiteGiocate } from "./csi-core"; /** * Rosa completa con tutte le statistiche personali (presenze, MVP, media voto, @@ -57,7 +59,11 @@ export function useObiettivi() { const { eventi } = useEventi(); const { presenze } = useRispostePresenze(); const { voti: pagelle } = usePagelle(); - return obiettiviOrdinati(rosa, { eventi, presenze, pagelle }); + const { data: csi } = useCsi(); + const vittorie = csi + ? partiteGiocate(csi.partite).filter((p) => (p.setNostri ?? 0) > (p.setLoro ?? 0)).length + : 0; + return obiettiviOrdinati(rosa, { eventi, presenze, pagelle, vittorie }); } export { giocatori }; diff --git a/src/lib/scout-export.ts b/src/lib/scout-export.ts index ae46931..9cf4e22 100644 --- a/src/lib/scout-export.ts +++ b/src/lib/scout-export.ts @@ -14,7 +14,14 @@ export function rigaCsv(campi: Array) { /** Esporta la scoutizzazione di una partita in CSV (separatore ";" per Excel IT). */ export function csvScoutMatch(match: ScoutMatch): string { const righe: string[] = []; - righe.push(rigaCsv(["Partita", match.casa ? "CRAP Volley" : match.avversario, "vs", match.casa ? match.avversario : "CRAP Volley"])); + righe.push( + rigaCsv([ + "Partita", + match.casa ? "CRAP Volley" : match.avversario, + "vs", + match.casa ? match.avversario : "CRAP Volley", + ]), + ); righe.push(rigaCsv(["Data", match.data, "Set", `${match.setNostri}-${match.setLoro}`])); righe.push(""); righe.push(rigaCsv(["Set", "Parziale nostro", "Parziale loro"])); diff --git a/src/lib/scout-store.ts b/src/lib/scout-store.ts index 797198c..6d3602b 100644 --- a/src/lib/scout-store.ts +++ b/src/lib/scout-store.ts @@ -1,24 +1,54 @@ import { useSyncExternalStore } from "react"; -import { giocatori, classifica, type Giocatore, type RigaClassifica } from "./crapp-data"; +import { giocatori, type Giocatore } from "./crapp-data"; -export type AzioneTipo = - | "attacco" - | "ace" - | "muro" - | "errore" - | "punto_avv" - | "errore_avv"; +export type AzioneTipo = "attacco" | "ace" | "muro" | "errore" | "punto_avv" | "errore_avv"; export const azioniMeta: Record< AzioneTipo, { label: string; short: string; nostro: boolean; richiedeGiocatore: boolean; className: string } > = { - attacco: { label: "Punto attacco", short: "Punto", nostro: true, richiedeGiocatore: true, className: "bg-accent text-accent-foreground" }, - ace: { label: "Ace", short: "Ace", nostro: true, richiedeGiocatore: true, className: "bg-success text-success-foreground" }, - muro: { label: "Muro", short: "Muro", nostro: true, richiedeGiocatore: true, className: "bg-info text-info-foreground" }, - errore: { label: "Errore nostro", short: "Errore", nostro: false, richiedeGiocatore: true, className: "bg-destructive text-destructive-foreground" }, - punto_avv: { label: "Punto avversario", short: "Punto avv.", nostro: false, richiedeGiocatore: false, className: "bg-muted text-muted-foreground" }, - errore_avv: { label: "Errore avversario", short: "Err. avv.", nostro: true, richiedeGiocatore: false, className: "bg-secondary text-foreground" }, + attacco: { + label: "Punto attacco", + short: "Punto", + nostro: true, + richiedeGiocatore: true, + className: "bg-accent text-accent-foreground", + }, + ace: { + label: "Ace", + short: "Ace", + nostro: true, + richiedeGiocatore: true, + className: "bg-success text-success-foreground", + }, + muro: { + label: "Muro", + short: "Muro", + nostro: true, + richiedeGiocatore: true, + className: "bg-info text-info-foreground", + }, + errore: { + label: "Errore nostro", + short: "Errore", + nostro: false, + richiedeGiocatore: true, + className: "bg-destructive text-destructive-foreground", + }, + punto_avv: { + label: "Punto avversario", + short: "Punto avv.", + nostro: false, + richiedeGiocatore: false, + className: "bg-muted text-muted-foreground", + }, + errore_avv: { + label: "Errore avversario", + short: "Err. avv.", + nostro: true, + richiedeGiocatore: false, + className: "bg-secondary text-foreground", + }, }; export type Azione = { @@ -143,37 +173,3 @@ export function giocatoriConScout( }; }); } - -/** Classifica demo aggiornata con i match scoutati (solo la nostra riga). */ -export function classificaConScout(matches: ScoutMatch[]): RigaClassifica[] { - if (matches.length === 0) return classifica; - const agg = matches.reduce( - (s, m) => { - const vinta = m.setNostri > m.setLoro; - s.giocate += 1; - s.vinte += vinta ? 1 : 0; - s.perse += vinta ? 0 : 1; - s.setFatti += m.setNostri; - s.setSubiti += m.setLoro; - s.punti += vinta ? (m.setLoro <= 1 ? 3 : 2) : m.setLoro === 3 && m.setNostri === 2 ? 1 : 0; - return s; - }, - { giocate: 0, vinte: 0, perse: 0, setFatti: 0, setSubiti: 0, punti: 0 }, - ); - return classifica - .map((r) => - r.squadra === "CRAP Volley" - ? { - ...r, - giocate: r.giocate + agg.giocate, - vinte: r.vinte + agg.vinte, - perse: r.perse + agg.perse, - setFatti: r.setFatti + agg.setFatti, - setSubiti: r.setSubiti + agg.setSubiti, - punti: r.punti + agg.punti, - } - : r, - ) - .sort((a, b) => b.punti - a.punti || b.setFatti - b.setSubiti - (a.setFatti - a.setSubiti)) - .map((r, i) => ({ ...r, pos: i + 1 })); -} \ No newline at end of file diff --git a/src/lib/serie.ts b/src/lib/serie.ts index ebd5c3c..30c3554 100644 --- a/src/lib/serie.ts +++ b/src/lib/serie.ts @@ -87,4 +87,4 @@ export function serieGiocatore(g: Giocatore): SerieStato[] { /** La serie migliore da mostrare in home. */ export function serieMigliore(g: Giocatore): SerieStato { return serieGiocatore(g).sort((a, b) => b.valore - a.valore)[0]!; -} \ No newline at end of file +} diff --git a/src/lib/webpush.server.ts b/src/lib/webpush.server.ts index 5899792..63ea259 100644 --- a/src/lib/webpush.server.ts +++ b/src/lib/webpush.server.ts @@ -39,7 +39,12 @@ async function importaChiave(publicKey: string, privateKey: string) { ); } -async function creaVapidJwt(audience: string, subject: string, publicKey: string, privateKey: string) { +async function creaVapidJwt( + audience: string, + subject: string, + publicKey: string, + privateKey: string, +) { const header = encodeJson({ typ: "JWT", alg: "ES256" }); const payload = encodeJson({ aud: audience, @@ -74,4 +79,4 @@ export async function inviaPush(endpoint: string): Promise { }, }); return res.status; -} \ No newline at end of file +} diff --git a/src/routes/__root.tsx b/src/routes/__root.tsx index 2a8db4d..666ca13 100644 --- a/src/routes/__root.tsx +++ b/src/routes/__root.tsx @@ -88,22 +88,36 @@ export const Route = createRootRouteWithContext<{ queryClient: QueryClient }>()( { title: "CrAPP — L'app del CRAP Volley" }, { name: "description", - content: "Convocazioni, presenze, statistiche e classifica del CRAP Volley in un'unica app mobile.", + content: + "Convocazioni, presenze, statistiche e classifica del CRAP Volley in un'unica app mobile.", }, { name: "author", content: "CRAP Volley" }, { name: "theme-color", content: "#111111" }, { property: "og:title", content: "CrAPP — L'app del CRAP Volley" }, { property: "og:description", - content: "Convocazioni, presenze, statistiche e classifica del CRAP Volley in un'unica app mobile.", + content: + "Convocazioni, presenze, statistiche e classifica del CRAP Volley in un'unica app mobile.", }, { property: "og:type", content: "website" }, { name: "twitter:card", content: "summary_large_image" }, { name: "twitter:site", content: "@Lovable" }, { name: "twitter:title", content: "CrAPP — L'app del CRAP Volley" }, - { name: "twitter:description", content: "Convocazioni, presenze, statistiche e classifica del CRAP Volley in un'unica app mobile." }, - { property: "og:image", content: "https://pub-bb2e103a32db4e198524a2e9ed8f35b4.r2.dev/fd5ebd0b-6661-43fa-936c-35856d1068c4/id-preview-2942946e--8d07b0e4-6bd2-4a17-9dd2-bb2cf13f9f7c.lovable.app-1785491191791.png" }, - { name: "twitter:image", content: "https://pub-bb2e103a32db4e198524a2e9ed8f35b4.r2.dev/fd5ebd0b-6661-43fa-936c-35856d1068c4/id-preview-2942946e--8d07b0e4-6bd2-4a17-9dd2-bb2cf13f9f7c.lovable.app-1785491191791.png" }, + { + name: "twitter:description", + content: + "Convocazioni, presenze, statistiche e classifica del CRAP Volley in un'unica app mobile.", + }, + { + property: "og:image", + content: + "https://pub-bb2e103a32db4e198524a2e9ed8f35b4.r2.dev/fd5ebd0b-6661-43fa-936c-35856d1068c4/id-preview-2942946e--8d07b0e4-6bd2-4a17-9dd2-bb2cf13f9f7c.lovable.app-1785491191791.png", + }, + { + name: "twitter:image", + content: + "https://pub-bb2e103a32db4e198524a2e9ed8f35b4.r2.dev/fd5ebd0b-6661-43fa-936c-35856d1068c4/id-preview-2942946e--8d07b0e4-6bd2-4a17-9dd2-bb2cf13f9f7c.lovable.app-1785491191791.png", + }, ], links: [ { diff --git a/src/routes/allenamento.$id.tsx b/src/routes/allenamento.$id.tsx index d645847..fcd32eb 100644 --- a/src/routes/allenamento.$id.tsx +++ b/src/routes/allenamento.$id.tsx @@ -13,9 +13,15 @@ export const Route = createFileRoute("/allenamento/$id")({ return { meta: [ { title: `${titolo} — CrAPP` }, - { name: "description", content: "Dettaglio allenamento, orario, luogo e presenze del CRAP Volley." }, + { + name: "description", + content: "Dettaglio allenamento, orario, luogo e presenze del CRAP Volley.", + }, { property: "og:title", content: `${titolo} — CrAPP` }, - { property: "og:description", content: "Dettaglio allenamento, orario, luogo e presenze del CRAP Volley." }, + { + property: "og:description", + content: "Dettaglio allenamento, orario, luogo e presenze del CRAP Volley.", + }, { property: "og:type", content: "website" }, { name: "twitter:card", content: "summary" }, ], diff --git a/src/routes/api/public/push-config.ts b/src/routes/api/public/push-config.ts index 7cf1ca2..b7f2a2d 100644 --- a/src/routes/api/public/push-config.ts +++ b/src/routes/api/public/push-config.ts @@ -9,4 +9,4 @@ export const Route = createFileRoute("/api/public/push-config")({ }, }, }, -}); \ No newline at end of file +}); diff --git a/src/routes/api/public/push-subscribe.ts b/src/routes/api/public/push-subscribe.ts index 2433ded..b413fe0 100644 --- a/src/routes/api/public/push-subscribe.ts +++ b/src/routes/api/public/push-subscribe.ts @@ -38,9 +38,12 @@ export const Route = createFileRoute("/api/public/push-subscribe")({ if (!parsed.success) return new Response("Dati non validi", { status: 400 }); const { supabaseAdmin } = await import("@/integrations/supabase/client.server"); - await supabaseAdmin.from("push_subscriptions").delete().eq("endpoint", parsed.data.endpoint); + await supabaseAdmin + .from("push_subscriptions") + .delete() + .eq("endpoint", parsed.data.endpoint); return Response.json({ ok: true }); }, }, }, -}); \ No newline at end of file +}); diff --git a/src/routes/calendario.tsx b/src/routes/calendario.tsx index 190b13e..1c932dc 100644 --- a/src/routes/calendario.tsx +++ b/src/routes/calendario.tsx @@ -122,7 +122,9 @@ function Calendario() { setDrawerAperto(true); } - const eventiGiornoSelezionato = giornoSelezionato ? eventiPerGiorno.get(giornoSelezionato) ?? [] : []; + const eventiGiornoSelezionato = giornoSelezionato + ? (eventiPerGiorno.get(giornoSelezionato) ?? []) + : []; return ( <> @@ -202,7 +204,8 @@ function Calendario() { .join(", ")})` : undefined; const tipo = tipiGiorno.length === 1 ? tipiGiorno[0] : undefined; - const isOggi = !!oggi && oggi.anno === anno && oggi.mese === mese && oggi.giorno === giorno; + const isOggi = + !!oggi && oggi.anno === anno && oggi.mese === mese && oggi.giorno === giorno; const Cella = haEventi ? "button" : "div"; return ( - {giorno} + + {giorno} + ); })} diff --git a/src/routes/classifica.tsx b/src/routes/classifica.tsx index 30ba547..6240250 100644 --- a/src/routes/classifica.tsx +++ b/src/routes/classifica.tsx @@ -2,10 +2,9 @@ import { createFileRoute, Link } from "@tanstack/react-router"; import { RefreshCw } from "lucide-react"; import { cn } from "@/lib/utils"; import { PageHeader, Section } from "@/components/crapp/ui-bits"; -import { storicoMatch } from "@/lib/crapp-data"; -import { classificaConScout, useScoutMatches } from "@/lib/scout-store"; +import { useScoutMatches } from "@/lib/scout-store"; import { useCsi } from "@/lib/csi"; -import { isNostraSquadra, partiteGiocate } from "@/lib/csi-core"; +import { isNostraSquadra, matchDaPartitaCsi, partiteGiocate } from "@/lib/csi-core"; import { ScoutEntry } from "@/components/crapp/ScoutEntry"; export const Route = createFileRoute("/classifica")({ @@ -34,25 +33,18 @@ function Classifica() { const scoutMatches = useScoutMatches(); const { data: csi } = useCsi(); - const classifica = csi?.classifica.length ? csi.classifica : classificaConScout(scoutMatches); + const classifica = csi?.classifica ?? []; const risultati = csi?.partite.length - ? partiteGiocate(csi.partite).map((p) => ({ - id: p.id, - avversario: p.avversario, - casa: p.casa, - setNostri: p.setNostri ?? 0, - setLoro: p.setLoro ?? 0, - })) - : [ - ...scoutMatches.map((m) => ({ - id: m.id, - avversario: m.avversario, - casa: m.casa, - setNostri: m.setNostri, - setLoro: m.setLoro, - })), - ...storicoMatch, - ]; + ? partiteGiocate(csi.partite).map(matchDaPartitaCsi) + : scoutMatches.map((m) => ({ + id: m.id, + data: m.data, + avversario: m.avversario, + casa: m.casa, + setNostri: m.setNostri, + setLoro: m.setLoro, + parziali: m.parziali, + })); return ( <> @@ -82,47 +74,61 @@ function Classifica() { Set Pt - {classifica.map((r) => { - const noi = isNostraSquadra(r.squadra) || r.squadra === "CRAP Volley"; - return ( -
- {r.pos} - - {r.squadra} - - {r.giocate} - - {r.setFatti}:{r.setSubiti} - - {r.punti} -
- ); - })} + {classifica.length === 0 ? ( +

+ Classifica non ancora disponibile. +

+ ) : ( + classifica.map((r) => { + const noi = isNostraSquadra(r.squadra) || r.squadra === "CRAP Volley"; + return ( +
+ + {r.pos} + + + {r.squadra} + + {r.giocate} + + {r.setFatti}:{r.setSubiti} + + {r.punti} +
+ ); + }) + )}
-
- {risultati.map((m) => ( -
-

- {m.casa ? "CRAP Volley" : m.avversario} — {m.casa ? m.avversario : "CRAP Volley"} -

- - {m.casa ? `${m.setNostri}-${m.setLoro}` : `${m.setLoro}-${m.setNostri}`} - -
- ))} -
+ {risultati.length === 0 ? ( +

+ Nessun risultato disponibile. +

+ ) : ( +
+ {risultati.map((m) => ( +
+

+ {m.casa ? "CRAP Volley" : m.avversario} — {m.casa ? m.avversario : "CRAP Volley"} +

+ + {m.casa ? `${m.setNostri}-${m.setLoro}` : `${m.setLoro}-${m.setNostri}`} + +
+ ))} +
+ )}
); diff --git a/src/routes/eventi.tsx b/src/routes/eventi.tsx index 58f152e..b870418 100644 --- a/src/routes/eventi.tsx +++ b/src/routes/eventi.tsx @@ -120,7 +120,9 @@ function GestioneEventi() { {bozza ? ( -
e.id === bozza.id) ? "Modifica evento" : "Nuovo evento"}> +
e.id === bozza.id) ? "Modifica evento" : "Nuovo evento"} + >
{tipi.map((t) => ( @@ -241,7 +243,9 @@ function GestioneEventi() { } className={cn( "truncate rounded-xl px-2.5 py-2 text-left text-xs font-semibold transition-colors", - scelto ? "bg-accent text-accent-foreground" : "bg-secondary text-muted-foreground", + scelto + ? "bg-accent text-accent-foreground" + : "bg-secondary text-muted-foreground", )} > {g.nome} @@ -278,7 +282,10 @@ function GestioneEventi() { ) : (
{eventi.map((e) => ( -
+

{e.titolo}

diff --git a/src/routes/index.tsx b/src/routes/index.tsx index 544b6cd..e7bdb4e 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -8,10 +8,13 @@ import { CompletaProfilo } from "@/components/crapp/ProfiloAmministrativo"; import { Reveal } from "@/components/motion/Reveal"; import { Barra } from "@/components/motion/Barra"; import { Numero } from "@/components/motion/Numero"; -import { classifica, storicoMatch } from "@/lib/crapp-data"; import { microcopyObiettivo, progressoObiettivo } from "@/lib/obiettivi"; import { useEventi, type Evento } from "@/lib/eventi"; import { useIo, useObiettivi } from "@/lib/rosa"; +import { useCsi } from "@/lib/csi"; +import { isNostraSquadra, matchDaPartitaCsi, partiteGiocate } from "@/lib/csi-core"; +import { useScoutMatches } from "@/lib/scout-store"; +import { useVotiMvp, vincitoriMvp } from "@/lib/mvp-voti"; export const Route = createFileRoute("/")({ head: () => ({ @@ -40,8 +43,17 @@ function Index() { const prossimi: Evento[] = eventi.filter((e) => e.data >= oggi).slice(0, 3); const prossimo = prossimi[0] ?? null; const linkProssimo = prossimo ? linkPerEvento(prossimo) : null; - const noi = classifica.find((r) => r.squadra === "CRAP Volley")!; - const ultima = storicoMatch[0]!; + const { data: csi } = useCsi(); + const noi = csi?.classifica.find((r) => isNostraSquadra(r.squadra)); + const scoutMatches = useScoutMatches(); + const votiMvp = useVotiMvp(); + const mvpPerMatch = vincitoriMvp(votiMvp.data ?? []); + const csiGiocate = csi ? partiteGiocate(csi.partite) : []; + const ultima = csiGiocate[0] + ? { ...matchDaPartitaCsi(csiGiocate[0]), mvp: mvpPerMatch[csiGiocate[0].id] ?? "" } + : scoutMatches[0] + ? { ...scoutMatches[0], mvp: mvpPerMatch[scoutMatches[0].id] ?? scoutMatches[0].mvp } + : null; const obiettivi = useObiettivi(); const obiettivo = obiettivi.find((o) => progressoObiettivo(o) < 100) ?? obiettivi[0] ?? null; @@ -62,12 +74,12 @@ function Index() {

-

{noi.pos}º

+

{noi ? `${noi.pos}º` : "—"}

In classifica

- {noi.vinte}-{noi.perse} + {noi ? `${noi.vinte}-${noi.perse}` : "—"}

Bilancio

@@ -130,29 +142,36 @@ function Index() { } > -
-
-
-

CRAP Volley vs {ultima.avversario}

-

- {ultima.casa ? "In casa" : "Trasferta"} · MVP {ultima.mvp} + {ultima ? ( +

+
+
+

CRAP Volley vs {ultima.avversario}

+

+ {ultima.casa ? "In casa" : "Trasferta"} + {ultima.mvp ? ` · MVP ${ultima.mvp}` : ""} +

+
+

+ {ultima.setNostri}-{ultima.setLoro}

-

- {ultima.setNostri}-{ultima.setLoro} -

+
+ {ultima.parziali.map((p, i) => ( + + {p[0]}-{p[1]} + + ))} +
-
- {ultima.parziali.map((p, i) => ( - - {p[0]}-{p[1]} - - ))} -
-
+ ) : ( +

+ Nessun risultato disponibile. +

+ )}
risposte[g.id] === "presente" || risposte[g.id] === "ritardo", @@ -60,6 +69,9 @@ function PartitaDetail() { const convocati = convocatiEvento(evento); const scout = scoutMatches.find((m) => m.id === evento.id || m.data === evento.data) ?? null; + const csiMatch = csi + ? partiteGiocate(csi.partite).find((p) => p.data === evento.data) + : undefined; const match = scout ? { id: scout.id, @@ -70,10 +82,12 @@ function PartitaDetail() { setLoro: scout.setLoro, parziali: scout.parziali, } - : storicoMatch.find((m) => m.data === evento.data); + : csiMatch + ? matchDaPartitaCsi(csiMatch) + : undefined; const totaliTeam = scout ? totaliSquadra([scout]) : null; const avversario = evento.titolo.includes(" vs ") - ? evento.titolo.split(" vs ").find((p) => !p.includes("CRAP")) ?? evento.titolo + ? (evento.titolo.split(" vs ").find((p) => !p.includes("CRAP")) ?? evento.titolo) : evento.titolo; const casa = evento.casa; const vinta = match && match.setNostri > match.setLoro; @@ -140,7 +154,9 @@ function PartitaDetail() { {match.setNostri} - {match.setLoro} @@ -149,7 +165,10 @@ function PartitaDetail() {
{match.parziali.map(([noi, loro], i) => ( -
+
{noi} Set {i + 1} {loro} @@ -171,7 +190,8 @@ function PartitaDetail() { ) : (

- La partita non è ancora stata disputata. Torna qui dopo il fischio finale per vedere il risultato. + La partita non è ancora stata disputata. Torna qui dopo il fischio finale per vedere il + risultato.

)} @@ -229,7 +249,9 @@ function PartitaDetail() { {admin ? ( - {["Notifiche convocazioni", "Promemoria allenamenti", "Cambi orario", "Bacheca squadra"].map( - (v) => ( - - ), - )} + {[ + "Notifiche convocazioni", + "Promemoria allenamenti", + "Cambi orario", + "Bacheca squadra", + ].map((v) => ( + + ))} {admin ? ( ); -} \ No newline at end of file +} diff --git a/src/routes/scout.tsx b/src/routes/scout.tsx index f34ac4e..caa1ad2 100644 --- a/src/routes/scout.tsx +++ b/src/routes/scout.tsx @@ -53,7 +53,17 @@ export const Route = createFileRoute("/scout")({ const ordineAzioni: AzioneTipo[] = ["attacco", "ace", "muro", "errore"]; -function Blocco({ icona, titolo, testo, children }: { icona: React.ReactNode; titolo: string; testo: string; children?: React.ReactNode }) { +function Blocco({ + icona, + titolo, + testo, + children, +}: { + icona: React.ReactNode; + titolo: string; + testo: string; + children?: React.ReactNode; +}) { return (
@@ -101,7 +111,9 @@ function Scout() { } if (!pronto || sessione.isLoading || statoSalvato.isLoading) { - return } titolo="Scout live" testo="Caricamento…" />; + return ( + } titolo="Scout live" testo="Caricamento…" /> + ); } if (!partita) { @@ -187,7 +199,10 @@ function ScoutBoard({ const base = iniziale ?? statoIniziale( - partita.titolo.replace(/CRAP Volley/gi, "").replace(/\s*vs\s*/i, "").trim() || "Avversario", + partita.titolo + .replace(/CRAP Volley/gi, "") + .replace(/\s*vs\s*/i, "") + .trim() || "Avversario", partita.titolo.trim().toLowerCase().startsWith("crap"), ); const [avversario, setAvversario] = useState(base.avversario); @@ -439,9 +454,7 @@ function ScoutBoard({ i === 0 && "anim-riga", )} > - - {g ? `#${g.numero} ${g.nome}` : "Avversario"} - + {g ? `#${g.numero} ${g.nome}` : "Avversario"} ); -} \ No newline at end of file +} diff --git a/src/routes/squadra.tsx b/src/routes/squadra.tsx index c2b2680..a7d36cf 100644 --- a/src/routes/squadra.tsx +++ b/src/routes/squadra.tsx @@ -4,11 +4,13 @@ import { Cake, ChevronDown, Crown } from "lucide-react"; import { cn } from "@/lib/utils"; import { PageHeader, Section } from "@/components/crapp/ui-bits"; import { Avatar } from "@/components/crapp/Avatar"; -import { formatData, storicoMatch } from "@/lib/crapp-data"; +import { formatData } from "@/lib/crapp-data"; import { microcopyObiettivo, progressoObiettivo } from "@/lib/obiettivi"; import { useRosa, useObiettivi } from "@/lib/rosa"; import { usePresenzeUltimoMeseTutti } from "@/lib/presenze-mese"; import { totaliSquadra } from "@/lib/scout-store"; +import { useCsi } from "@/lib/csi"; +import { matchDaPartitaCsi, partiteGiocate } from "@/lib/csi-core"; import { mediaSquadra, usePagelle } from "@/lib/pagelle"; import { ScoutEntry } from "@/components/crapp/ScoutEntry"; import { StatTile } from "@/components/crapp/ui-bits"; @@ -43,7 +45,8 @@ export const Route = createFileRoute("/squadra")({ { title: "Squadra CRAP Volley — CrAPP" }, { name: "description", - content: "Rosa completa del CRAP Volley: giocatori, dati anagrafici, statistiche stagionali e badge sbloccati.", + content: + "Rosa completa del CRAP Volley: giocatori, dati anagrafici, statistiche stagionali e badge sbloccati.", }, { property: "og:title", content: "Squadra CRAP Volley — CrAPP" }, { @@ -55,7 +58,6 @@ export const Route = createFileRoute("/squadra")({ component: Squadra, }); - const criteri = [ { id: "presenze", label: "Presenze" }, { id: "mediaVoto", label: "Media voto" }, @@ -85,24 +87,31 @@ function Squadra() { const obiettivi = useObiettivi(); const team = totaliSquadra(scoutMatches); const mediaPresenze = rosa.length - ? Math.round((rosa.reduce((s, g) => s + g.presenze / (g.totaliEventi || 1), 0) / rosa.length) * 100) + ? Math.round( + (rosa.reduce((s, g) => s + g.presenze / (g.totaliEventi || 1), 0) / rosa.length) * 100, + ) : 0; const ordinati = [...rosa].sort((a, b) => valore(b, criterio) - valore(a, criterio)); const max = ordinati[0] ? valore(ordinati[0], criterio) || 1 : 1; - const tuttiMatch = [ - ...scoutMatches.map((m) => ({ - id: m.id, - data: m.data, - avversario: m.avversario, - casa: m.casa, - setNostri: m.setNostri, - setLoro: m.setLoro, - parziali: m.parziali, - mvp: mvpPerMatch[m.id] ?? "", - scout: true, - })), - ...storicoMatch.map((m) => ({ ...m, scout: false })), - ]; + const { data: csi } = useCsi(); + const csiGiocate = csi ? partiteGiocate(csi.partite) : []; + const tuttiMatch = csiGiocate.length + ? csiGiocate.map((p) => ({ + ...matchDaPartitaCsi(p), + mvp: mvpPerMatch[p.id] ?? "", + scout: false, + })) + : scoutMatches.map((m) => ({ + id: m.id, + data: m.data, + avversario: m.avversario, + casa: m.casa, + setNostri: m.setNostri, + setLoro: m.setLoro, + parziali: m.parziali, + mvp: mvpPerMatch[m.id] ?? "", + scout: true, + })); const completati = obiettivi.filter((o) => progressoObiettivo(o) >= 100).length; const mediaObiettivi = obiettivi.length ? Math.round(obiettivi.reduce((s, o) => s + progressoObiettivo(o), 0) / obiettivi.length) @@ -116,7 +125,10 @@ function Squadra() {
{rosa.map((g) => { const stati = badgeGiocatore(g); - const sbloccati = [...stati.filter((b) => b.grado !== null), ...badgeSegretiSbloccati(g)]; + const sbloccati = [ + ...stati.filter((b) => b.grado !== null), + ...badgeSegretiSbloccati(g), + ]; const isOpen = aperto === g.id; return (
@@ -126,7 +138,11 @@ function Squadra() { className="flex w-full items-center gap-3 p-3 text-left active:scale-[0.99]" aria-expanded={isOpen} > - + {g.nome} @@ -160,19 +176,20 @@ function Squadra() {

- {formatData(g.nascita)} {g.nascita.slice(0, 4)} + {formatData(g.nascita)}{" "} + {g.nascita.slice(0, 4)}

- {[ - { l: "Presenze", v: `${g.presenze}/${g.totaliEventi}` }, - { l: "Presenze 30gg", v: `${mese[g.id]?.percentuale ?? 0}%` }, - { l: "Presenze di fila", v: g.streak }, - { l: "Media voto", v: g.mediaVoto || "—" }, - { l: "MVP", v: g.mvp }, - { l: "Cacche/partita 💩", v: g.cacchePartita || "—" }, - ].map((s) => ( + {[ + { l: "Presenze", v: `${g.presenze}/${g.totaliEventi}` }, + { l: "Presenze 30gg", v: `${mese[g.id]?.percentuale ?? 0}%` }, + { l: "Presenze di fila", v: g.streak }, + { l: "Media voto", v: g.mediaVoto || "—" }, + { l: "MVP", v: g.mvp }, + { l: "Cacche/partita 💩", v: g.cacchePartita || "—" }, + ].map((s) => (

{s.v}

@@ -201,9 +218,16 @@ function Squadra() {

{b.def.nome}

{b.valore} {b.def.unita} - {b.prossimaSoglia ? ` · ${b.prossimaSoglia} per ${gradoMeta[b.prossimo!].label.toLowerCase()}` : ""} + {b.prossimaSoglia + ? ` · ${b.prossimaSoglia} per ${gradoMeta[b.prossimo!].label.toLowerCase()}` + : ""}

-

+

{meta.label}

@@ -223,7 +247,7 @@ function Squadra() {
- + @@ -411,7 +435,9 @@ function Squadra() { const meta = gradoMeta[grado]; const quanti = rosa.filter((g) => { const raggiunto = gradoRaggiunto(b, b.valore(g)); - return raggiunto ? gradiOrdine.indexOf(raggiunto) >= gradiOrdine.indexOf(grado) : false; + return raggiunto + ? gradiOrdine.indexOf(raggiunto) >= gradiOrdine.indexOf(grado) + : false; }).length; return (