Il giocatore senza certificato vede una card rossa cliccabile, l'admin l'elenco dei mancanti sopra scaduti e in scadenza. Nessuna migration. Aggiorna @tanstack/react-start alla 1.168.60 (con router, plugin e query) e chiude le vulnerabilità di npm audit: Vercel bloccava il deploy con la 1.168.34. Il tipo di error in __root.tsx diventa unknown, come nel nuovo tipo di TanStack. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
134 lines
3.8 KiB
TypeScript
134 lines
3.8 KiB
TypeScript
import { AlertCircle } from "lucide-react";
|
|
import { Link } from "@tanstack/react-router";
|
|
import { useProfili } from "@/lib/profili";
|
|
import {
|
|
avvisiCertificatiUtente,
|
|
formatDataBreve,
|
|
testoScadenza,
|
|
type AvvisoCertificato,
|
|
} from "@/lib/profili-core";
|
|
import { useGiocatoriSquadra } from "@/lib/giocatori-squadra";
|
|
import { oggiISO } from "@/lib/palloni-core";
|
|
import { useIsAdmin, useRuoliPronti } from "@/lib/ruoli";
|
|
import { useGiocatoreBase } from "@/lib/user-store";
|
|
|
|
function Card({
|
|
colore,
|
|
titolo,
|
|
righe,
|
|
cliccabile,
|
|
}: {
|
|
colore: "warning" | "primary" | "destructive";
|
|
titolo: string;
|
|
righe: string[];
|
|
cliccabile?: boolean;
|
|
}) {
|
|
const classi = `mx-5 mt-4 block rounded-3xl p-4 shadow-pop ${
|
|
colore === "warning"
|
|
? "bg-warning text-warning-foreground"
|
|
: colore === "destructive"
|
|
? "bg-destructive text-destructive-foreground"
|
|
: "bg-primary text-primary-foreground"
|
|
} ${cliccabile ? "premi" : ""}`;
|
|
const contenuto = (
|
|
<>
|
|
<p className="flex items-center gap-2 font-display text-lg uppercase leading-none">
|
|
<AlertCircle className="h-5 w-5" /> {titolo}
|
|
</p>
|
|
{righe.map((r) => (
|
|
<p key={r} className="mt-2 text-xs leading-snug opacity-90">
|
|
{r}
|
|
</p>
|
|
))}
|
|
</>
|
|
);
|
|
return cliccabile ? (
|
|
<Link to="/profilo" search={{ tab: "documenti" }} className={classi}>
|
|
{contenuto}
|
|
</Link>
|
|
) : (
|
|
<div className={classi}>{contenuto}</div>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Avviso certificati mancanti/scaduti/in scadenza (DD-035, DD-041, DD-046): calcolato al volo, niente push.
|
|
* Il giocatore lo vede da 30 giorni prima della scadenza, lo staff da 7.
|
|
*/
|
|
export function AvvisoCertificati() {
|
|
const admin = useIsAdmin();
|
|
const ruoliPronti = useRuoliPronti();
|
|
const base = useGiocatoreBase();
|
|
const { righe: rosa } = useGiocatoriSquadra();
|
|
const { profili, isPending } = useProfili();
|
|
|
|
if (!ruoliPronti || isPending) return null;
|
|
|
|
const oggi = oggiISO();
|
|
|
|
// Admin che è anche giocatore: vede il proprio avviso a 30 giorni e quello dello staff (DD-041).
|
|
const { personale, personaleMancante, staff } = avvisiCertificatiUtente(rosa, profili, oggi, {
|
|
admin,
|
|
base,
|
|
});
|
|
const cardPersonale = personaleMancante ? (
|
|
<Card
|
|
colore="destructive"
|
|
titolo="Certificato mancante"
|
|
cliccabile
|
|
righe={["Non hai ancora caricato il certificato medico: caricalo nei documenti"]}
|
|
/>
|
|
) : personale ? (
|
|
personale.giorni < 0 ? (
|
|
<Card
|
|
colore="primary"
|
|
titolo="Certificato scaduto"
|
|
cliccabile
|
|
righe={[
|
|
`Il tuo certificato medico è scaduto il ${formatDataBreve(personale.scadenza)}: caricane uno nuovo`,
|
|
]}
|
|
/>
|
|
) : (
|
|
<Card
|
|
colore="warning"
|
|
titolo="Certificato in scadenza"
|
|
cliccabile
|
|
righe={[`Il tuo certificato medico ${testoScadenza(personale.giorni)}`]}
|
|
/>
|
|
)
|
|
) : null;
|
|
|
|
if (!staff) return cardPersonale;
|
|
const { scaduti, inScadenza, mancanti } = staff;
|
|
return (
|
|
<>
|
|
{cardPersonale}
|
|
{mancanti.length > 0 ? (
|
|
<Card
|
|
colore="destructive"
|
|
titolo="Certificati mancanti"
|
|
righe={mancanti.map((m) => `${m.nome} ${m.cognome} — non caricato`)}
|
|
/>
|
|
) : null}
|
|
{scaduti.length > 0 ? (
|
|
<Card
|
|
colore="primary"
|
|
titolo="Certificati scaduti"
|
|
righe={scaduti.map((a) => rigaStaff(a, `scaduto il ${formatDataBreve(a.scadenza)}`))}
|
|
/>
|
|
) : null}
|
|
{inScadenza.length > 0 ? (
|
|
<Card
|
|
colore="warning"
|
|
titolo="Certificati in scadenza"
|
|
righe={inScadenza.map((a) => rigaStaff(a, testoScadenza(a.giorni)))}
|
|
/>
|
|
) : null}
|
|
</>
|
|
);
|
|
}
|
|
|
|
function rigaStaff(a: AvvisoCertificato, dettaglio: string): string {
|
|
return `${a.nome} ${a.cognome} — ${dettaglio}`;
|
|
}
|