diff --git a/src/components/crapp/BarraSottosezioni.tsx b/src/components/crapp/BarraSottosezioni.tsx
index 080a029..a2d4315 100644
--- a/src/components/crapp/BarraSottosezioni.tsx
+++ b/src/components/crapp/BarraSottosezioni.tsx
@@ -1,5 +1,5 @@
import { useEffect, useRef, useState, type ReactNode } from "react";
-import { motion } from "motion/react";
+import { motion, type Transition } from "motion/react";
import { cn } from "@/lib/utils";
import { proietta } from "@/lib/molla";
import { useMotoRidotto } from "@/lib/motion";
@@ -13,7 +13,7 @@ export type VoceSottosezione = {
};
/** Tween breve: evita molle + exit che tengono due pannelli in DOM insieme. */
-const transizioneTab = { type: "tween" as const, duration: 0.16, ease: [0.25, 0.1, 0.25, 1] };
+const transizioneTab: Transition = { type: "tween", duration: 0.16, ease: [0.25, 0.1, 0.25, 1] };
/**
* Barra di sottosezioni in un'unica fila e pannello che mostra una sola sezione
@@ -116,11 +116,7 @@ export function BarraSottosezioni({
}}
className={cn(
"min-h-11 touch-manipulation whitespace-nowrap text-sm font-bold uppercase tracking-wide transition-colors",
- riempiLarghezza
- ? "min-w-0 flex-1"
- : sottolineatura
- ? "shrink-0"
- : "w-full",
+ riempiLarghezza ? "min-w-0 flex-1" : sottolineatura ? "shrink-0" : "w-full",
sottolineatura
? cn(
"rounded-xl px-1 py-2.5 text-center",
diff --git a/src/lib/rosa.ts b/src/lib/rosa.ts
index 0c825d4..e92aead 100644
--- a/src/lib/rosa.ts
+++ b/src/lib/rosa.ts
@@ -67,7 +67,7 @@ export function useRosa(): Giocatore[] {
const { eventi } = useEventi();
const { presenze: mappaPresenze, tempi } = useRispostePresenze();
- const votiMvp = voti.data ?? [];
+ const votiMvp = voti.data;
return useMemo(() => {
const medie = mediePagelle(pagelle);
@@ -76,7 +76,7 @@ export function useRosa(): Giocatore[] {
// premiare chi ha davvero portato i palloni, non chi l'algoritmo di rotazione ha
// scelto per un evento passato senza che nessuno confermasse nulla.
const palloni = conteggioTurni(turniSalvati, eventi);
- const mvpVinti = mvpVintiPerGiocatore(votiMvp);
+ const mvpVinti = mvpVintiPerGiocatore(votiMvp ?? []);
return squadra
.filter((g) => g.attivo)
diff --git a/src/routes/classifica.tsx b/src/routes/classifica.tsx
index 364fdee..200ff41 100644
--- a/src/routes/classifica.tsx
+++ b/src/routes/classifica.tsx
@@ -26,9 +26,7 @@ function LogoPartita({
avversario: string;
}) {
if (nostro) {
- return (
-
- );
+ return ;
}
if (logoAvversario) {
return ;
@@ -254,10 +252,7 @@ function Classifica() {
{setCasa}-{setOspite}
{cliccabile ? (
-
+
) : null}
@@ -270,9 +265,7 @@ function Classifica() {
key={i}
className={cn(
"rounded-lg px-2 py-1 text-xs font-semibold tabular-nums",
- setVintoDaNoi
- ? "bg-secondary"
- : "bg-muted text-muted-foreground",
+ setVintoDaNoi ? "bg-secondary" : "bg-muted text-muted-foreground",
)}
>
{puntiCasa}-{puntiOspite}
diff --git a/test/integration/presenze-badge.test.ts b/test/integration/presenze-badge.test.ts
index 0c362cd..ae86bdb 100644
--- a/test/integration/presenze-badge.test.ts
+++ b/test/integration/presenze-badge.test.ts
@@ -27,6 +27,8 @@ import { daRiga, type RigaEvento, type Evento } from "@/lib/eventi";
import { giocatori, type Giocatore } from "@/lib/crapp-data";
import { statoLocale } from "../helpers/locale";
import { prova, riepilogo, salta } from "../helpers/prova";
+import type { Stato } from "@/lib/crapp-data";
+import type { MappaPresenze } from "@/lib/presenze";
const locale = statoLocale();
@@ -97,16 +99,16 @@ if (!locale) {
}
/** Rilegge esattamente come `fetchPresenze()`. */
- async function leggiPresenze(): Promise>> {
+ async function leggiPresenze(): Promise {
const res = await rest(
`risposte_presenze?evento_id=like.${PREFISSO}*&select=evento_id,giocatore_id,stato`,
);
const righe = (await res.json()) as Array<{
evento_id: string;
giocatore_id: string;
- stato: string;
+ stato: Stato;
}>;
- const mappa: Record> = {};
+ const mappa: MappaPresenze = {};
for (const r of righe) (mappa[r.evento_id] ??= {})[r.giocatore_id] = r.stato;
return mappa;
}
diff --git a/test/integration/s-infermeria-badge.test.ts b/test/integration/s-infermeria-badge.test.ts
index 0234bb0..00de1d4 100644
--- a/test/integration/s-infermeria-badge.test.ts
+++ b/test/integration/s-infermeria-badge.test.ts
@@ -20,6 +20,8 @@ import { daRiga, type RigaEvento, type Evento } from "@/lib/eventi";
import { giocatori, type Giocatore } from "@/lib/crapp-data";
import { statoLocale } from "../helpers/locale";
import { prova, riepilogo, salta } from "../helpers/prova";
+import type { Stato } from "@/lib/crapp-data";
+import type { MappaPresenze } from "@/lib/presenze";
const locale = statoLocale();
@@ -87,16 +89,16 @@ if (!locale) {
return righe.map(daRiga);
}
- async function leggiPresenze(): Promise>> {
+ async function leggiPresenze(): Promise {
const res = await rest(
`risposte_presenze?evento_id=like.${PREFISSO}*&select=evento_id,giocatore_id,stato`,
);
const righe = (await res.json()) as Array<{
evento_id: string;
giocatore_id: string;
- stato: string;
+ stato: Stato;
}>;
- const mappa: Record> = {};
+ const mappa: MappaPresenze = {};
for (const r of righe) (mappa[r.evento_id] ??= {})[r.giocatore_id] = r.stato;
return mappa;
}
diff --git a/test/integration/s-mai-forfait-badge.test.ts b/test/integration/s-mai-forfait-badge.test.ts
index 59a7c83..dbf804a 100644
--- a/test/integration/s-mai-forfait-badge.test.ts
+++ b/test/integration/s-mai-forfait-badge.test.ts
@@ -19,6 +19,8 @@ import { daRiga, type RigaEvento, type Evento } from "@/lib/eventi";
import { giocatori, type Giocatore } from "@/lib/crapp-data";
import { statoLocale } from "../helpers/locale";
import { prova, riepilogo, salta } from "../helpers/prova";
+import type { Stato } from "@/lib/crapp-data";
+import type { MappaPresenze } from "@/lib/presenze";
const locale = statoLocale();
@@ -90,7 +92,7 @@ if (!locale) {
}
async function leggiPresenzeETempi(): Promise<{
- presenze: Record>;
+ presenze: MappaPresenze;
tempi: Record>;
}> {
const res = await rest(
@@ -99,10 +101,10 @@ if (!locale) {
const righe = (await res.json()) as Array<{
evento_id: string;
giocatore_id: string;
- stato: string;
+ stato: Stato;
risposto_il: string;
}>;
- const presenze: Record> = {};
+ const presenze: MappaPresenze = {};
const tempi: Record> = {};
for (const r of righe) {
(presenze[r.evento_id] ??= {})[r.giocatore_id] = r.stato;
diff --git a/test/integration/s-ritardi-badge.test.ts b/test/integration/s-ritardi-badge.test.ts
index 3a7fbe4..7b48680 100644
--- a/test/integration/s-ritardi-badge.test.ts
+++ b/test/integration/s-ritardi-badge.test.ts
@@ -17,6 +17,8 @@ import { daRiga, type RigaEvento, type Evento } from "@/lib/eventi";
import { giocatori, type Giocatore } from "@/lib/crapp-data";
import { statoLocale } from "../helpers/locale";
import { prova, riepilogo, salta } from "../helpers/prova";
+import type { Stato } from "@/lib/crapp-data";
+import type { MappaPresenze } from "@/lib/presenze";
const locale = statoLocale();
@@ -81,16 +83,16 @@ if (!locale) {
return righe.map(daRiga);
}
- async function leggiPresenze(): Promise>> {
+ async function leggiPresenze(): Promise {
const res = await rest(
`risposte_presenze?evento_id=like.${PREFISSO}*&select=evento_id,giocatore_id,stato`,
);
const righe = (await res.json()) as Array<{
evento_id: string;
giocatore_id: string;
- stato: string;
+ stato: Stato;
}>;
- const mappa: Record> = {};
+ const mappa: MappaPresenze = {};
for (const r of righe) (mappa[r.evento_id] ??= {})[r.giocatore_id] = r.stato;
return mappa;
}
diff --git a/test/integration/schema-profili.test.ts b/test/integration/schema-profili.test.ts
index 76fee1a..da9ff9d 100644
--- a/test/integration/schema-profili.test.ts
+++ b/test/integration/schema-profili.test.ts
@@ -59,6 +59,14 @@ if (!URL_BASE || !CHIAVE_SERVIZIO || !CHIAVE_PUBBLICA) {
// Attenzione al falso verde: su un UPDATE che non tocca nessuna riga PostgREST
// risponde comunque 2xx. Quello che conta è che il dato non cambi.
await prova("un anonimo non si collega a uno slot della rosa", async () => {
+ const leggiSlot = async () => {
+ const r = await rest("giocatori_squadra?id=eq.g1&select=auth_user_id", CHIAVE_SERVIZIO);
+ const righe = (await r.json()) as Array<{ auth_user_id: string | null }>;
+ return righe[0]?.auth_user_id ?? null;
+ };
+ // Sul database di produzione g1 può essere già collegato a un giocatore vero:
+ // conta che il tentativo anonimo non cambi il valore, non che lo slot sia libero.
+ const prima = await leggiSlot();
const res = await rest("giocatori_squadra?id=eq.g1", CHIAVE_PUBBLICA, {
method: "PATCH",
headers: { Prefer: "return=representation" },
@@ -68,9 +76,7 @@ if (!URL_BASE || !CHIAVE_SERVIZIO || !CHIAVE_PUBBLICA) {
const aggiornate = (await res.json()) as unknown[];
assert.equal(aggiornate.length, 0, "nessuna riga aggiornata senza sessione");
}
- const dopo = await rest("giocatori_squadra?id=eq.g1&select=auth_user_id", CHIAVE_SERVIZIO);
- const righe = (await dopo.json()) as Array<{ auth_user_id: string | null }>;
- assert.equal(righe[0]?.auth_user_id ?? null, null, "lo slot g1 è rimasto libero");
+ assert.equal(await leggiSlot(), prima, "lo slot g1 non è stato modificato");
});
// --- M8: tracciamento tesseramento CSI --------------------------------------
diff --git a/test/integration/serie-allenamenti-badge.test.ts b/test/integration/serie-allenamenti-badge.test.ts
index e1659c9..c2abf80 100644
--- a/test/integration/serie-allenamenti-badge.test.ts
+++ b/test/integration/serie-allenamenti-badge.test.ts
@@ -26,6 +26,8 @@ import { daRiga, type RigaEvento, type Evento } from "@/lib/eventi";
import { giocatori, type Giocatore } from "@/lib/crapp-data";
import { statoLocale } from "../helpers/locale";
import { prova, riepilogo, salta } from "../helpers/prova";
+import type { Stato } from "@/lib/crapp-data";
+import type { MappaPresenze } from "@/lib/presenze";
const locale = statoLocale();
@@ -93,16 +95,16 @@ if (!locale) {
return righe.map(daRiga);
}
- async function leggiPresenze(): Promise>> {
+ async function leggiPresenze(): Promise {
const res = await rest(
`risposte_presenze?evento_id=like.${PREFISSO}*&select=evento_id,giocatore_id,stato`,
);
const righe = (await res.json()) as Array<{
evento_id: string;
giocatore_id: string;
- stato: string;
+ stato: Stato;
}>;
- const mappa: Record> = {};
+ const mappa: MappaPresenze = {};
for (const r of righe) (mappa[r.evento_id] ??= {})[r.giocatore_id] = r.stato;
return mappa;
}
diff --git a/test/unit/profili-core.test.ts b/test/unit/profili-core.test.ts
index e19b741..4810956 100644
--- a/test/unit/profili-core.test.ts
+++ b/test/unit/profili-core.test.ts
@@ -114,6 +114,7 @@ const squadra: GiocatoreSquadra[] = [
email: null,
numeroTessera: null,
dataTessera: null,
+ nascita: null,
},
{
id: "g2",
@@ -126,6 +127,7 @@ const squadra: GiocatoreSquadra[] = [
email: null,
numeroTessera: null,
dataTessera: null,
+ nascita: null,
},
];
const csv = csvTesseramento(squadra, { g1: completo });
diff --git a/test/unit/push-sw.test.ts b/test/unit/push-sw.test.ts
index c29c918..d188369 100644
--- a/test/unit/push-sw.test.ts
+++ b/test/unit/push-sw.test.ts
@@ -4,11 +4,16 @@ import { readFileSync } from "node:fs";
import { runInNewContext } from "node:vm";
const codice = readFileSync(new URL("../../public/push-sw.js", import.meta.url), "utf8");
-type Evento = { data?: { json: () => unknown } | null; waitUntil: (p: Promise) => void };
+type Evento = {
+ data?: { json: () => unknown } | null | undefined;
+ waitUntil: (p: Promise) => void;
+};
+// `renotify` è supportato da Chromium ma manca dai tipi DOM di TypeScript.
+type OpzioniNotifica = NotificationOptions & { renotify?: boolean };
function avviaWorker() {
const listener = new Map void>();
- const notifiche: Array<{ titolo: string; opzioni: NotificationOptions }> = [];
+ const notifiche: Array<{ titolo: string; opzioni: OpzioniNotifica }> = [];
let completa: (() => void) | undefined;
const mostrata = new Promise((resolve) => (completa = resolve));
const installata = Promise.resolve();
@@ -23,7 +28,7 @@ function avviaWorker() {
matchAll: () => assert.fail("la push deve funzionare senza consultare finestre aperte"),
},
registration: {
- showNotification(titolo: string, opzioni: NotificationOptions) {
+ showNotification(titolo: string, opzioni: OpzioniNotifica) {
notifiche.push({ titolo, opzioni });
return mostrata;
},
diff --git a/test/unit/scout-stato.test.ts b/test/unit/scout-stato.test.ts
index 09f451e..24ce890 100644
--- a/test/unit/scout-stato.test.ts
+++ b/test/unit/scout-stato.test.ts
@@ -4,7 +4,8 @@ import { SCOUT_STATO_KEY, statoIniziale } from "@/lib/scout-stato";
// --- statoIniziale: parte vuoto, con avversario e campo memorizzati -----------
const stato = statoIniziale("Avversari", true);
-assert.deepEqual(stato, { azioni: [], setChiusi: [], avversario: "Avversari", casa: true });
+// Confronta una copia: deepEqual restringerebbe `stato` al letterale, con `azioni: never[]`.
+assert.deepEqual({ ...stato }, { azioni: [], setChiusi: [], avversario: "Avversari", casa: true });
const trasferta = statoIniziale("Altra Squadra", false);
assert.equal(trasferta.casa, false);
diff --git a/vite.config.ts b/vite.config.ts
index 5a00681..5e13b28 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -1,4 +1,4 @@
-import { defineConfig, loadEnv, mergeConfig } from "vite";
+import { defineConfig, loadEnv, mergeConfig, type UserConfig } from "vite";
import tailwindcss from "@tailwindcss/vite";
import tsConfigPaths from "vite-tsconfig-paths";
import viteReact from "@vitejs/plugin-react";
@@ -46,14 +46,13 @@ export default defineConfig(async ({ command, mode }) => {
}
plugins.push(viteReact());
- let config = {
+ let config: UserConfig = {
define: envDefine,
...(isDevBuild
? {
environments: {
client: { define: { "process.env.NODE_ENV": JSON.stringify("development") } },
},
- esbuild: { keepNames: true },
}
: {}),
css: { transformer: "lightningcss" as const },