Rimuove la serie «Conferme 24h» e i badge collegati

Toglie la serie dal profilo, dalla Home e dalle notifiche di traguardo, insieme
ai badge «Risposta lampo» e «Mai un forfait» e al calcolo di serieConferme.
La colonna risposto_il e il suo trigger restano nel database, inutilizzati.
Test e documentazione allineati.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
davideandClaude Sonnet 5.5 committed 2026-10-06 13:21:14 +02:00
1 parent 8d0a8c7971
commit bfb768fb5a
17 files changed
+69 -718

No files matched your search

-23
View File
@@ -3,9 +3,7 @@ import {
Repeat,
Lock,
Ghost,
Zap,
Rocket,
Anchor,
Stethoscope,
AlarmClock,
ClipboardCheck,
@@ -114,15 +112,6 @@ export const badgeDefs: BadgeDef[] = [
soglie: { bronzo: 3, argento: 6, oro: 10 },
valore: (g) => g.serieAllenamenti,
},
{
id: "serie-conferme",
nome: "Risposta lampo",
descrizione: "Conferme rapide agli eventi: la squadra sa subito che ci sei.",
unita: "conferme entro 24h",
icon: Zap,
soglie: { bronzo: 3, argento: 8, oro: 15 },
valore: (g) => g.serieConferme,
},
];
/** Badge segreti: non compaiono nella UI finché non vengono sbloccati. */
@@ -141,18 +130,6 @@ export const badgeSegreti: BadgeDef[] = [
valore: (g) => (g.mvp >= 2 && g.votiPagella >= VOTI_MINIMI_PAGELLA && g.mediaVoto >= 8 ? 1 : 0),
celebrazione: "Nei momenti caldi ci sei sempre.",
},
{
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.",
unita: "requisito nascosto",
icon: Anchor,
segreto: true,
soglie: { bronzo: 1, argento: 1, oro: 1 },
valore: (g) => (g.serieConferme >= 10 && g.presenze >= 15 ? 1 : 0),
celebrazione: "Su di te la squadra può contare a occhi chiusi.",
},
{
id: "s-infermeria",
nome: "Cliente VIP dell'Infermeria",
-2
View File
@@ -34,7 +34,6 @@ export type Giocatore = {
/** Serie consecutive per tipo: si azzerano in modo indipendente. */
serieAllenamenti: number;
seriePartite: number;
serieConferme: number;
/** MVP eletti dalla squadra. */
mvp: number;
/** Media delle pagelle ricevute dai compagni (1-10). */
@@ -109,7 +108,6 @@ export const giocatori: Giocatore[] = rosaCSI
streak: 0,
serieAllenamenti: 0,
seriePartite: 0,
serieConferme: 0,
mvp: 0,
mediaVoto: 0,
votiPagella: 0,
+1 -26
View File
@@ -155,31 +155,6 @@ export function serieConsecutiva(
);
}
const ORE_24 = 24 * 60 * 60 * 1000;
/**
* Serie di conferme rapide: risposte arrivate entro 24 ore dalla convocazione
* (`creatoIl` dell'evento). Gli eventi senza istante di creazione — quelli generati
* dal client, non salvati a database — non spezzano la serie: vengono saltati.
*/
export function serieConferme(
giocatoreId: string,
eventi: Evento[],
tempi: MappaTempiRisposta,
oggi: string = dataOggi(),
): number {
return serieSu(
giocatoreId,
eventi.filter((e) => e.creatoIl),
oggi,
undefined,
(e) => {
const risposto = tempi[e.id]?.[giocatoreId];
return risposto !== undefined && Date.parse(risposto) - Date.parse(e.creatoIl!) <= ORE_24;
},
);
}
/** Scorre gli eventi già passati in ordine di data applicando la regola delle serie. */
function serieSu(
giocatoreId: string,
@@ -228,7 +203,7 @@ export function usePresenzeEvento(eventoId: string) {
*
* - l'istante si scrive **solo se manca** (`??=`), come fa il database, dove `risposto_il`
* non viene inviato sull'upsert e un trigger lo congela: è la prima risposta, non l'ultima,
* e un ripensamento non deve far ripartire il cronometro della serie "Conferme 24h";
* e un ripensamento non deve cambiare l'istante della prima risposta;
* - cancellare la risposta (`stato: null`) elimina **anche** l'istante, così se il giocatore
* risponde di nuovo il cronometro riparte davvero — ha ritirato la risposta.
*/
+2 -15
View File
@@ -12,7 +12,6 @@ import { useEventi } from "./eventi";
import {
contaPartiteGiocate,
contaPresenzeGiocatore,
serieConferme,
serieConsecutiva,
totaliEventiGiocatore,
useRispostePresenze,
@@ -68,7 +67,7 @@ export function useRosa(): Giocatore[] {
const { salvati: turniSalvati } = useTurniPalloni();
const { infortuni, ritardi } = useInfortuniERitardi();
const { eventi } = useEventi();
const { presenze: mappaPresenze, tempi } = useRispostePresenze();
const { presenze: mappaPresenze } = useRispostePresenze();
const votiMvp = voti.data;
@@ -94,7 +93,6 @@ export function useRosa(): Giocatore[] {
streak: serieConsecutiva(g.id, eventi, mappaPresenze),
serieAllenamenti: serieConsecutiva(g.id, eventi, mappaPresenze, "allenamento"),
seriePartite: serieConsecutiva(g.id, eventi, mappaPresenze, "partita"),
serieConferme: serieConferme(g.id, eventi, tempi),
mvp: mvpVinti[g.id] ?? 0,
mediaVoto: medie[g.id]?.media ?? 0,
votiPagella: medie[g.id]?.voti ?? 0,
@@ -105,18 +103,7 @@ export function useRosa(): Giocatore[] {
infortuni: infortuni[g.id] ?? 0,
ritardi: ritardi[g.id] ?? 0,
}));
}, [
squadra,
votiMvp,
pagelle,
cacche,
turniSalvati,
infortuni,
ritardi,
eventi,
mappaPresenze,
tempi,
]);
}, [squadra, votiMvp, pagelle, cacche, turniSalvati, infortuni, ritardi, eventi, mappaPresenze]);
}
/** Criteri di ordinamento della classifica interna di Squadra. */
+2 -10
View File
@@ -1,8 +1,8 @@
import { Dumbbell, Swords, Zap } from "lucide-react";
import { Dumbbell, Swords } from "lucide-react";
import type { LucideIcon } from "lucide-react";
import type { Giocatore } from "./crapp-data";
export type SerieTipo = "allenamenti" | "partite" | "conferme";
export type SerieTipo = "allenamenti" | "partite";
export type SerieDef = {
tipo: SerieTipo;
@@ -31,14 +31,6 @@ export const serieDefs: SerieDef[] = [
traguardi: [2, 5, 8, 12],
valore: (g) => g.seriePartite,
},
{
tipo: "conferme",
label: "Conferme 24h",
descrizione: "Risposte date entro 24 ore dalla convocazione",
icon: Zap,
traguardi: [3, 8, 15, 20],
valore: (g) => g.serieConferme,
},
];
export type SerieStato = {