2026-09-08 09:39:40 +02:00
|
|
|
|
import { useMemo } from "react";
|
2026-08-06 08:36:47 +02:00
|
|
|
|
import { createFileRoute, Link } from "@tanstack/react-router";
|
2026-09-08 12:01:32 +02:00
|
|
|
|
import { AlertTriangle, ChevronRight, RefreshCw } from "lucide-react";
|
2026-08-06 08:36:47 +02:00
|
|
|
|
import { cn } from "@/lib/utils";
|
2026-09-09 11:29:34 +02:00
|
|
|
|
import { formatData, type RigaClassifica } from "@/lib/crapp-data";
|
2026-09-09 21:01:49 +02:00
|
|
|
|
import { PageHeader, TeamLogo } from "@/components/crapp/ui-bits";
|
2026-09-05 21:18:19 +02:00
|
|
|
|
import { BarraSottosezioni } from "@/components/crapp/BarraSottosezioni";
|
2026-09-01 13:38:33 +02:00
|
|
|
|
import { useScoutMatches } from "@/lib/scout-store";
|
2026-08-30 12:20:51 +02:00
|
|
|
|
import { useCsi } from "@/lib/csi";
|
2026-09-01 13:38:33 +02:00
|
|
|
|
import { isNostraSquadra, matchDaPartitaCsi, partiteGiocate } from "@/lib/csi-core";
|
2026-09-04 11:40:03 +02:00
|
|
|
|
import { useVotiMvp, vincitoriMvp } from "@/lib/mvp-voti";
|
|
|
|
|
|
import { useEventi } from "@/lib/eventi";
|
2026-09-09 12:07:03 +02:00
|
|
|
|
import { LogoSquadra } from "@/components/crapp/DettaglioCsi";
|
2026-08-06 08:36:47 +02:00
|
|
|
|
|
2026-09-09 21:01:49 +02:00
|
|
|
|
const LOGO_NOI = "/logo-nerorosso.svg";
|
|
|
|
|
|
const NOME_NOI = "CRAP Volley";
|
|
|
|
|
|
|
|
|
|
|
|
/** Logo CRAP o avversario; cerchio con iniziali se il CSI non ha l'immagine. */
|
|
|
|
|
|
function LogoPartita({
|
|
|
|
|
|
nostro,
|
|
|
|
|
|
logoAvversario,
|
|
|
|
|
|
avversario,
|
|
|
|
|
|
}: {
|
|
|
|
|
|
nostro: boolean;
|
|
|
|
|
|
logoAvversario: string;
|
|
|
|
|
|
avversario: string;
|
|
|
|
|
|
}) {
|
|
|
|
|
|
if (nostro) {
|
2026-09-24 12:14:37 +02:00
|
|
|
|
return <TeamLogo src={LOGO_NOI} className="h-8 w-8 rounded-lg shadow-none" />;
|
2026-09-09 21:01:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
if (logoAvversario) {
|
|
|
|
|
|
return <LogoSquadra src={logoAvversario} alt={avversario} className="h-8 w-8" />;
|
|
|
|
|
|
}
|
|
|
|
|
|
const iniziali = avversario
|
|
|
|
|
|
.split(/\s+/)
|
|
|
|
|
|
.filter(Boolean)
|
|
|
|
|
|
.slice(0, 2)
|
|
|
|
|
|
.map((p) => p[0]?.toUpperCase() ?? "")
|
|
|
|
|
|
.join("");
|
|
|
|
|
|
return (
|
|
|
|
|
|
<span
|
|
|
|
|
|
className="grid h-8 w-8 shrink-0 place-items-center rounded-lg bg-secondary text-[10px] font-bold text-muted-foreground"
|
|
|
|
|
|
aria-hidden
|
|
|
|
|
|
>
|
|
|
|
|
|
{iniziali || "?"}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-06 10:34:03 +02:00
|
|
|
|
const TAB_CLASSIFICA = ["classifica", "storico"] as const;
|
|
|
|
|
|
type TabClassifica = (typeof TAB_CLASSIFICA)[number];
|
|
|
|
|
|
|
|
|
|
|
|
function isTabClassifica(v: unknown): v is TabClassifica {
|
|
|
|
|
|
return typeof v === "string" && (TAB_CLASSIFICA as readonly string[]).includes(v);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-08-06 08:36:47 +02:00
|
|
|
|
export const Route = createFileRoute("/classifica")({
|
2026-09-06 10:34:03 +02:00
|
|
|
|
validateSearch: (search: Record<string, unknown>): { tab?: TabClassifica } => {
|
|
|
|
|
|
const tab = isTabClassifica(search["tab"]) ? search["tab"] : undefined;
|
|
|
|
|
|
return tab ? { tab } : {};
|
|
|
|
|
|
},
|
2026-08-06 08:36:47 +02:00
|
|
|
|
head: () => ({
|
|
|
|
|
|
meta: [
|
|
|
|
|
|
{ title: "Classifica campionato — CrAPP" },
|
|
|
|
|
|
{
|
|
|
|
|
|
name: "description",
|
|
|
|
|
|
content: "Classifica e risultati del girone CSI seguiti in tempo reale dal CRAP Volley.",
|
|
|
|
|
|
},
|
|
|
|
|
|
{ property: "og:title", content: "Classifica campionato — CrAPP" },
|
|
|
|
|
|
{ property: "og:description", content: "Posizioni, set e risultati aggiornati del girone." },
|
|
|
|
|
|
],
|
|
|
|
|
|
}),
|
|
|
|
|
|
component: Classifica,
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-09-09 11:29:34 +02:00
|
|
|
|
function TabellaClassifica({ righe, vuoto }: { righe: RigaClassifica[]; vuoto: string }) {
|
|
|
|
|
|
return (
|
|
|
|
|
|
<div className="overflow-hidden rounded-3xl bg-card shadow-card">
|
|
|
|
|
|
<div className="grid grid-cols-[2rem_minmax(0,1fr)_2rem_2.5rem_2.5rem] gap-2 border-b border-border px-3 py-2 text-xs font-bold uppercase text-muted-foreground">
|
|
|
|
|
|
<span>#</span>
|
|
|
|
|
|
<span>Squadra</span>
|
|
|
|
|
|
<span className="text-center">G</span>
|
|
|
|
|
|
<span className="text-center">Set</span>
|
|
|
|
|
|
<span className="text-center">Pt</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
{righe.length === 0 ? (
|
|
|
|
|
|
<p className="px-3 py-4 text-center text-xs text-muted-foreground">{vuoto}</p>
|
|
|
|
|
|
) : (
|
|
|
|
|
|
righe.map((r) => {
|
|
|
|
|
|
const noi = isNostraSquadra(r.squadra) || r.squadra === "CRAP Volley";
|
|
|
|
|
|
return (
|
|
|
|
|
|
<div
|
|
|
|
|
|
key={r.pos}
|
|
|
|
|
|
className={cn(
|
|
|
|
|
|
"grid grid-cols-[2rem_minmax(0,1fr)_2rem_2.5rem_2.5rem] items-center gap-2 border-b border-border px-3 py-2.5 text-sm last:border-0",
|
|
|
|
|
|
noi && "bg-accent/10",
|
|
|
|
|
|
)}
|
|
|
|
|
|
>
|
|
|
|
|
|
<span className={cn("font-display text-base", noi && "text-accent")}>{r.pos}</span>
|
|
|
|
|
|
<span className={cn("truncate", noi ? "font-bold" : "font-medium")}>{r.squadra}</span>
|
|
|
|
|
|
<span className="text-center text-xs text-muted-foreground">{r.giocate}</span>
|
|
|
|
|
|
<span className="text-center text-xs tabular-nums text-muted-foreground">
|
|
|
|
|
|
{r.setFatti}:{r.setSubiti}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
<span className="text-center font-bold tabular-nums">{r.punti}</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
);
|
|
|
|
|
|
})
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-08-30 12:20:51 +02:00
|
|
|
|
function formatAggiornamento(iso: string) {
|
|
|
|
|
|
const d = new Date(iso);
|
|
|
|
|
|
const oggi = new Date().toDateString() === d.toDateString();
|
|
|
|
|
|
const ora = d.toLocaleTimeString("it-IT", { hour: "2-digit", minute: "2-digit" });
|
|
|
|
|
|
return oggi ? `oggi alle ${ora}` : `il ${d.toLocaleDateString("it-IT")} alle ${ora}`;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-08-06 08:36:47 +02:00
|
|
|
|
function Classifica() {
|
2026-09-06 10:34:03 +02:00
|
|
|
|
const { tab } = Route.useSearch();
|
2026-08-06 08:36:47 +02:00
|
|
|
|
const scoutMatches = useScoutMatches();
|
2026-08-30 12:20:51 +02:00
|
|
|
|
const { data: csi } = useCsi();
|
2026-09-04 11:40:03 +02:00
|
|
|
|
const votiMvp = useVotiMvp();
|
|
|
|
|
|
const { eventi } = useEventi();
|
2026-08-30 12:20:51 +02:00
|
|
|
|
|
2026-09-08 09:39:40 +02:00
|
|
|
|
const classifica = useMemo(() => csi?.classifica ?? [], [csi]);
|
2026-09-09 11:29:34 +02:00
|
|
|
|
const classificaCoppa = useMemo(() => csi?.classificaCoppa ?? [], [csi]);
|
2026-09-08 09:39:40 +02:00
|
|
|
|
|
|
|
|
|
|
const mvpPerMatch = useMemo(() => vincitoriMvp(votiMvp.data ?? []), [votiMvp.data]);
|
|
|
|
|
|
const eventoIdPerData = useMemo(
|
|
|
|
|
|
() => new Map(eventi.filter((e) => e.tipo === "partita").map((e) => [e.data, e.id])),
|
|
|
|
|
|
[eventi],
|
2026-09-04 11:40:03 +02:00
|
|
|
|
);
|
2026-09-06 22:11:26 +02:00
|
|
|
|
// I voti MVP sono legati all'evento CrAPP, non al referto CSI né allo scout.
|
2026-09-08 09:39:40 +02:00
|
|
|
|
const tuttiMatch = useMemo(() => {
|
|
|
|
|
|
const mvpPerData = (data: string) => mvpPerMatch[eventoIdPerData.get(data) ?? ""] ?? "";
|
|
|
|
|
|
const csiGiocate = csi ? partiteGiocate(csi.partite) : [];
|
|
|
|
|
|
return csiGiocate.length
|
|
|
|
|
|
? csiGiocate.map((p) => {
|
|
|
|
|
|
const m = matchDaPartitaCsi(p);
|
|
|
|
|
|
return { ...m, mvp: mvpPerData(m.data), scout: false };
|
|
|
|
|
|
})
|
|
|
|
|
|
: scoutMatches.map((m) => ({
|
|
|
|
|
|
id: m.id,
|
|
|
|
|
|
data: m.data,
|
|
|
|
|
|
avversario: m.avversario,
|
2026-09-09 12:07:03 +02:00
|
|
|
|
logoAvversario: "",
|
2026-09-08 09:39:40 +02:00
|
|
|
|
casa: m.casa,
|
|
|
|
|
|
setNostri: m.setNostri,
|
|
|
|
|
|
setLoro: m.setLoro,
|
|
|
|
|
|
parziali: m.parziali,
|
|
|
|
|
|
mvp: mvpPerData(m.data),
|
|
|
|
|
|
scout: true,
|
|
|
|
|
|
}));
|
|
|
|
|
|
}, [csi, scoutMatches, mvpPerMatch, eventoIdPerData]);
|
|
|
|
|
|
|
|
|
|
|
|
// Ogni tab è memoizzata: un refetch in background di uno solo dei dati (CSI,
|
|
|
|
|
|
// scout, eventi, voti MVP) non deve ricostruire il JSX dell'altra tab.
|
|
|
|
|
|
const contenutoClassifica = useMemo(
|
|
|
|
|
|
() => (
|
|
|
|
|
|
<>
|
2026-09-08 12:01:32 +02:00
|
|
|
|
<div
|
|
|
|
|
|
className={cn(
|
|
|
|
|
|
"mb-3 flex items-center gap-2 rounded-2xl px-3 py-2 text-xs",
|
|
|
|
|
|
csi?.formatoSospetto
|
|
|
|
|
|
? "bg-warning/15 text-warning"
|
|
|
|
|
|
: "bg-secondary text-muted-foreground",
|
|
|
|
|
|
)}
|
|
|
|
|
|
>
|
|
|
|
|
|
{csi?.formatoSospetto ? (
|
|
|
|
|
|
<AlertTriangle className="h-3.5 w-3.5 shrink-0 text-warning" aria-hidden />
|
|
|
|
|
|
) : (
|
|
|
|
|
|
<RefreshCw className="h-3.5 w-3.5 shrink-0 text-accent" aria-hidden />
|
|
|
|
|
|
)}
|
|
|
|
|
|
{csi?.formatoSospetto
|
|
|
|
|
|
? "Il portale CSI potrebbe aver cambiato formato: dati da verificare."
|
|
|
|
|
|
: csi
|
|
|
|
|
|
? `Dati CSI aggiornati ${formatAggiornamento(csi.aggiornato)}`
|
|
|
|
|
|
: "Dati CSI in arrivo"}
|
2026-09-08 09:39:40 +02:00
|
|
|
|
</div>
|
2026-09-09 11:29:34 +02:00
|
|
|
|
{classificaCoppa.length > 0 ? (
|
|
|
|
|
|
<>
|
|
|
|
|
|
<h3 className="mb-2 px-1 text-sm font-bold text-foreground">Coppa</h3>
|
|
|
|
|
|
<TabellaClassifica righe={classificaCoppa} vuoto="Classifica non disponibile." />
|
|
|
|
|
|
<h3 className="mb-2 mt-4 px-1 text-sm font-bold text-foreground">Girone</h3>
|
|
|
|
|
|
</>
|
|
|
|
|
|
) : null}
|
|
|
|
|
|
<TabellaClassifica righe={classifica} vuoto="Classifica non ancora disponibile." />
|
2026-09-08 09:39:40 +02:00
|
|
|
|
</>
|
|
|
|
|
|
),
|
2026-09-09 11:29:34 +02:00
|
|
|
|
[csi, classifica, classificaCoppa],
|
2026-09-08 09:39:40 +02:00
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
const contenutoStorico = useMemo(
|
|
|
|
|
|
() =>
|
|
|
|
|
|
tuttiMatch.length === 0 ? (
|
|
|
|
|
|
<p className="rounded-3xl bg-card p-4 text-center text-xs text-muted-foreground shadow-card">
|
|
|
|
|
|
Nessuna partita disponibile.
|
|
|
|
|
|
</p>
|
|
|
|
|
|
) : (
|
|
|
|
|
|
<div className="space-y-3">
|
|
|
|
|
|
{tuttiMatch.map((m) => {
|
|
|
|
|
|
const vinta = m.setNostri > m.setLoro;
|
|
|
|
|
|
const eventoId = eventoIdPerData.get(m.data);
|
2026-09-09 21:01:49 +02:00
|
|
|
|
const cliccabile = Boolean(eventoId) || !m.scout;
|
|
|
|
|
|
const casa = {
|
|
|
|
|
|
nome: m.casa ? NOME_NOI : m.avversario,
|
|
|
|
|
|
nostro: m.casa,
|
|
|
|
|
|
};
|
|
|
|
|
|
const trasferta = {
|
|
|
|
|
|
nome: m.casa ? m.avversario : NOME_NOI,
|
|
|
|
|
|
nostro: !m.casa,
|
|
|
|
|
|
};
|
|
|
|
|
|
// Badge e parziali in ordine casa–ospite; il colore resta sulla vittoria CRAP.
|
|
|
|
|
|
const setCasa = m.casa ? m.setNostri : m.setLoro;
|
|
|
|
|
|
const setOspite = m.casa ? m.setLoro : m.setNostri;
|
2026-09-08 09:39:40 +02:00
|
|
|
|
const contenuto = (
|
|
|
|
|
|
<>
|
2026-09-09 21:01:49 +02:00
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
|
<div className="min-w-0 flex-1 space-y-1.5">
|
|
|
|
|
|
<div className="flex min-w-0 items-center gap-2">
|
|
|
|
|
|
<LogoPartita
|
|
|
|
|
|
nostro={casa.nostro}
|
|
|
|
|
|
logoAvversario={m.logoAvversario}
|
|
|
|
|
|
avversario={m.avversario}
|
|
|
|
|
|
/>
|
|
|
|
|
|
<p className="truncate text-sm font-bold">{casa.nome}</p>
|
2026-09-09 12:07:03 +02:00
|
|
|
|
</div>
|
2026-09-09 21:01:49 +02:00
|
|
|
|
<div className="flex min-w-0 items-center gap-2">
|
|
|
|
|
|
<LogoPartita
|
|
|
|
|
|
nostro={trasferta.nostro}
|
|
|
|
|
|
logoAvversario={m.logoAvversario}
|
|
|
|
|
|
avversario={m.avversario}
|
|
|
|
|
|
/>
|
|
|
|
|
|
<p className="truncate text-sm font-bold">{trasferta.nome}</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p className="text-xs text-muted-foreground">
|
|
|
|
|
|
{formatData(m.data)} · MVP {m.mvp || "da votare"}
|
|
|
|
|
|
{m.scout ? " · scoutata" : ""}
|
|
|
|
|
|
</p>
|
2026-09-08 09:39:40 +02:00
|
|
|
|
</div>
|
|
|
|
|
|
<span
|
|
|
|
|
|
className={cn(
|
2026-09-09 21:01:49 +02:00
|
|
|
|
"shrink-0 rounded-xl px-2.5 py-1 font-display text-xl",
|
2026-09-08 09:39:40 +02:00
|
|
|
|
vinta
|
|
|
|
|
|
? "bg-success text-success-foreground"
|
|
|
|
|
|
: "bg-destructive text-destructive-foreground",
|
|
|
|
|
|
)}
|
|
|
|
|
|
>
|
2026-09-09 21:01:49 +02:00
|
|
|
|
{setCasa}-{setOspite}
|
2026-09-08 09:39:40 +02:00
|
|
|
|
</span>
|
2026-09-09 21:01:49 +02:00
|
|
|
|
{cliccabile ? (
|
2026-09-24 12:14:37 +02:00
|
|
|
|
<ChevronRight className="h-5 w-5 shrink-0 text-muted-foreground" aria-hidden />
|
2026-09-09 21:01:49 +02:00
|
|
|
|
) : null}
|
2026-09-08 09:39:40 +02:00
|
|
|
|
</div>
|
|
|
|
|
|
<div className="mt-3 flex flex-wrap items-center gap-1.5">
|
2026-09-09 21:01:49 +02:00
|
|
|
|
{m.parziali.map((p, i) => {
|
|
|
|
|
|
const puntiCasa = m.casa ? p[0] : p[1];
|
|
|
|
|
|
const puntiOspite = m.casa ? p[1] : p[0];
|
|
|
|
|
|
const setVintoDaNoi = p[0] > p[1];
|
|
|
|
|
|
return (
|
|
|
|
|
|
<span
|
|
|
|
|
|
key={i}
|
|
|
|
|
|
className={cn(
|
|
|
|
|
|
"rounded-lg px-2 py-1 text-xs font-semibold tabular-nums",
|
2026-09-24 12:14:37 +02:00
|
|
|
|
setVintoDaNoi ? "bg-secondary" : "bg-muted text-muted-foreground",
|
2026-09-09 21:01:49 +02:00
|
|
|
|
)}
|
|
|
|
|
|
>
|
|
|
|
|
|
{puntiCasa}-{puntiOspite}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
);
|
|
|
|
|
|
})}
|
2026-09-08 09:39:40 +02:00
|
|
|
|
{eventoId && !m.mvp ? (
|
|
|
|
|
|
<span className="ml-auto inline-flex items-center gap-0.5 text-xs font-bold uppercase text-accent">
|
2026-09-09 21:01:49 +02:00
|
|
|
|
Vota MVP <ChevronRight className="h-3.5 w-3.5" aria-hidden />
|
2026-09-08 09:39:40 +02:00
|
|
|
|
</span>
|
|
|
|
|
|
) : null}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</>
|
|
|
|
|
|
);
|
2026-09-09 21:01:49 +02:00
|
|
|
|
const aria = `${casa.nome} vs ${trasferta.nome}: dettaglio partita`;
|
2026-09-09 12:07:03 +02:00
|
|
|
|
if (eventoId) {
|
|
|
|
|
|
return (
|
|
|
|
|
|
<Link
|
|
|
|
|
|
key={m.id}
|
|
|
|
|
|
to="/partita/$id"
|
|
|
|
|
|
params={{ id: eventoId }}
|
2026-09-09 21:01:49 +02:00
|
|
|
|
aria-label={aria}
|
|
|
|
|
|
className="premi block rounded-3xl bg-card p-4 shadow-card ring-1 ring-transparent transition-[box-shadow,transform] active:scale-[0.99] hover:ring-accent/30"
|
2026-09-09 12:07:03 +02:00
|
|
|
|
>
|
|
|
|
|
|
{contenuto}
|
|
|
|
|
|
</Link>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
// Senza evento CrAPP collegato (nessuna creazione automatica dal calendario
|
|
|
|
|
|
// CSI, vedi "Evoluzioni possibili"): le gare scoutate localmente non hanno un
|
|
|
|
|
|
// corrispettivo sul portale da mostrare, quelle CSI sì.
|
|
|
|
|
|
return m.scout ? (
|
|
|
|
|
|
<article key={m.id} className="rounded-3xl bg-card p-4 shadow-card">
|
|
|
|
|
|
{contenuto}
|
|
|
|
|
|
</article>
|
|
|
|
|
|
) : (
|
2026-09-08 09:39:40 +02:00
|
|
|
|
<Link
|
|
|
|
|
|
key={m.id}
|
2026-09-09 12:07:03 +02:00
|
|
|
|
to="/partita-csi/$id"
|
|
|
|
|
|
params={{ id: m.id }}
|
2026-09-09 21:01:49 +02:00
|
|
|
|
aria-label={aria}
|
|
|
|
|
|
className="premi block rounded-3xl bg-card p-4 shadow-card ring-1 ring-transparent transition-[box-shadow,transform] active:scale-[0.99] hover:ring-accent/30"
|
2026-09-08 09:39:40 +02:00
|
|
|
|
>
|
|
|
|
|
|
{contenuto}
|
|
|
|
|
|
</Link>
|
|
|
|
|
|
);
|
|
|
|
|
|
})}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
),
|
|
|
|
|
|
[tuttiMatch, eventoIdPerData],
|
|
|
|
|
|
);
|
2026-08-30 12:20:51 +02:00
|
|
|
|
|
2026-08-06 08:36:47 +02:00
|
|
|
|
return (
|
|
|
|
|
|
<>
|
2026-08-30 12:20:51 +02:00
|
|
|
|
<PageHeader
|
|
|
|
|
|
titolo="Campionato"
|
|
|
|
|
|
sottotitolo={csi ? `${csi.girone} · CSI Bologna` : "CSI Bologna"}
|
|
|
|
|
|
/>
|
2026-08-06 08:36:47 +02:00
|
|
|
|
|
2026-09-05 21:18:19 +02:00
|
|
|
|
<BarraSottosezioni
|
2026-09-06 10:34:03 +02:00
|
|
|
|
defaultId={tab ?? "classifica"}
|
2026-09-06 18:50:11 +02:00
|
|
|
|
variante="sottolineatura"
|
2026-09-07 18:35:35 +02:00
|
|
|
|
riempiLarghezza
|
2026-09-05 21:18:19 +02:00
|
|
|
|
voci={[
|
2026-09-08 09:39:40 +02:00
|
|
|
|
{ id: "classifica", label: "Classifica", contenuto: contenutoClassifica },
|
|
|
|
|
|
{ id: "storico", label: "Storico partite", contenuto: contenutoStorico },
|
2026-09-05 21:18:19 +02:00
|
|
|
|
]}
|
|
|
|
|
|
/>
|
2026-08-06 08:36:47 +02:00
|
|
|
|
</>
|
|
|
|
|
|
);
|
2026-08-30 12:20:51 +02:00
|
|
|
|
}
|