import { useState } from "react"; import { createFileRoute, Link } from "@tanstack/react-router"; import { Cake, ChevronDown, ChevronRight, 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 } 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"; import { Reveal } from "@/components/motion/Reveal"; import { Barra } from "@/components/motion/Barra"; import { Numero } from "@/components/motion/Numero"; import { useScoutMatches } from "@/lib/scout-store"; import { useVotiMvp, vincitoriMvp } from "@/lib/mvp-voti"; import { useEventi } from "@/lib/eventi"; import { BadgeDrawer } from "@/components/crapp/BadgeDrawer"; import { badgeDefs, badgeGiocatore, badgeSegretiSbloccati, descrizioneSoglie, gradiOrdine, gradoMeta, gradoRaggiunto, collezioneBadge, } from "@/lib/badges"; function RuoloBadge({ ruolo }: { ruolo: string }) { return ( {ruolo} ); } export const Route = createFileRoute("/squadra")({ head: () => ({ meta: [ { title: "Squadra CRAP Volley — CrAPP" }, { name: "description", content: "Rosa completa del CRAP Volley: giocatori, dati anagrafici, statistiche stagionali e badge sbloccati.", }, { property: "og:title", content: "Squadra CRAP Volley — CrAPP" }, { property: "og:description", content: "Tutti i giocatori della rosa con dati, statistiche e obiettivi raggiunti.", }, ], }), component: Squadra, }); const criteri = [ { id: "presenze", label: "Presenze" }, { id: "mediaVoto", label: "Media voto" }, { id: "mvp", label: "MVP" }, { id: "palloni", label: "Palloni" }, { id: "cacchePartita", label: "Cacche" }, ] as const; type Criterio = (typeof criteri)[number]["id"]; function valore( g: { presenze: number; mediaVoto: number; mvp: number; palloni: number; cacchePartita: number }, c: Criterio, ) { return g[c] ?? 0; } function Squadra() { const rosa = useRosa(); const mese = usePresenzeUltimoMeseTutti(); const scoutMatches = useScoutMatches(); const votiMvp = useVotiMvp(); const { voti: pagelle } = usePagelle(); const mvpPerMatch = vincitoriMvp(votiMvp.data ?? []); const [aperto, setAperto] = useState(null); const [criterio, setCriterio] = useState("presenze"); 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, ) : 0; const ordinati = [...rosa].sort((a, b) => valore(b, criterio) - valore(a, criterio)); const max = ordinati[0] ? valore(ordinati[0], criterio) || 1 : 1; const { data: csi } = useCsi(); const csiGiocate = csi ? partiteGiocate(csi.partite) : []; const { eventi } = useEventi(); const eventoIdPerData = new Map( eventi.filter((e) => e.tipo === "partita").map((e) => [e.data, e.id]), ); 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) : 0; return ( <>
{rosa.map((g) => { const stati = badgeGiocatore(g); const sbloccati = [ ...stati.filter((b) => b.grado !== null), ...badgeSegretiSbloccati(g), ]; const isOpen = aperto === g.id; return (
{isOpen ? (

{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) => (

{s.v}

{s.l}

))}

Badge sbloccati · {collezioneBadge(g).ottenuti}/{collezioneBadge(g).totali}

{sbloccati.length === 0 ? (

Nessun badge sbloccato per ora.

) : (
{sbloccati.map((b) => { const Icon = b.def.icon; const meta = gradoMeta[b.grado!]; return (

{b.def.nome}

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

{meta.label}

); })}
)}
) : null}
); })}
{criteri.map((c) => ( ))}
{ordinati.map((g, i) => (
{i + 1}

{g.nome} {i === 0 ? : null}

#{g.numero} · {g.ruolo} · {g.streak} presenze consecutive

{valore(g, criterio) || "—"}
))}
{tuttiMatch.map((m) => { const vinta = m.setNostri > m.setLoro; const eventoId = eventoIdPerData.get(m.data); const contenuto = ( <>

{m.casa ? "CRAP Volley" : m.avversario} vs{" "} {m.casa ? m.avversario : "CRAP Volley"}

{formatData(m.data)} · MVP {m.mvp || "da votare"} {m.scout ? " · scoutata" : ""}

{m.setNostri}-{m.setLoro}
{m.parziali.map((p, i) => ( p[1] ? "bg-secondary" : "bg-muted text-muted-foreground", )} > {p[0]}-{p[1]} ))} {eventoId && !m.mvp ? ( Vota MVP ) : null}
); return eventoId ? ( {contenuto} ) : (
{contenuto}
); })}

Progresso collettivo

{completati}/{obiettivi.length} completati

{obiettivi.map((o, i) => { const pct = progressoObiettivo(o); const fatto = pct >= 100; return ( = 90 ? "ring-accent/40" : "ring-transparent", )} >
{o.emoji}

{o.titolo}

{o.descrizione}

{fatto ? "Completato" : `${pct}%`}

{o.valore}/{o.target} {o.unita} · {pct}% {o.scadenza ? ` · entro il ${formatData(o.scadenza)}` : ""}

{microcopyObiettivo(o)}

{o.impatto}

); })}
{badgeDefs.map((b) => { const Icon = b.icon; return (

{b.nome}

{descrizioneSoglie(b)}

{gradiOrdine.map((grado) => { 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; }).length; return (

{meta.label}

{b.soglie[grado]}

{quanti}/{rosa.length}

); })}
); })}
); }