Promemoria senza ritorni e tre canali per ogni notifica (DD-037, DD-038)

m23: un registro (promemoria_eventi_generati) ricorda i promemoria a 24 e 3 ore
già generati, così eliminare la notifica non li fa rigenerare dal cron né
riaccodare la mail a ogni giro.

m24: la push dei promemoria, che nascono nel database, la manda il worker
mailer/ con lo stesso modulo dell'app (coda notifiche_push_coda, esiti, backoff,
iscrizioni scadute eliminate, push scartate oltre 3 ore). Il sondaggio
pre-partita scrive anche notifiche_utente (tipo sondaggio_cacche): compare
in-app e arriva per email. L'immagine del worker si costruisce dalla radice
del repository per includere src/lib/webpush.server.ts.

Test: 9 di integrazione per m23, 17 per la coda push, 6 per la route del
sondaggio, nuovi casi unitari sul worker; prova end-to-end con un servizio
push finto. Documentazione e catalogo delle notifiche aggiornati.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
davideandClaude Sonnet 5.5 committed 2026-09-29 14:19:54 +02:00
1 parent 58eba642f7
commit e9013b703b
27 files changed
+2076 -92

No files matched your search

+26
View File
@@ -1,7 +1,33 @@
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { supabase } from "@/integrations/supabase/client";
import { inRosa, type TipoMembro } from "./giocatori-squadra";
import { oggiISO } from "./palloni-core";
/**
* Avviso «sondaggio pre-partita aperto» (push, centro notifiche ed email, DD-038): stesso testo su
* tutti e tre i canali.
*/
export function avvisoSondaggio(partita: { titolo: string; ora: string }): {
titolo: string;
testo: string;
} {
return {
titolo: "💩 Sondaggio pre-partita aperto",
testo: `${partita.titolo} · ore ${partita.ora}. Quante cacche hai fatto? Rispondi prima del fischio d'inizio.`,
};
}
/**
* Chi riceve l'avviso in-app e per email: i giocatori attivi, mai gli allenatori, che il sondaggio
* non riguarda (DD-034). Non dipende dai dispositivi iscritti alla push: l'avviso in-app e la mail
* arrivano anche a chi non ha attivato le notifiche.
*/
export function destinatariSondaggio(
squadra: Array<{ id: string; attivo: boolean; tipo: TipoMembro }>,
): string[] {
return squadra.filter(inRosa).map((g) => g.id);
}
/** Ora di apertura del sondaggio, il giorno stesso della partita. */
export const ORA_APERTURA_SONDAGGIO = 8;
+3 -2
View File
@@ -4,7 +4,7 @@ import { useGiocatoreId } from "./user-store";
/**
* Centro notifiche in-app (M17): pallino sull'avatar del profilo, con il numero delle non
* lette. Quattro sorgenti riempiono `notifiche_utente` (lato database, mai dal client): un
* lette. Cinque sorgenti riempiono `notifiche_utente` (lato database, mai dal client): un
* messaggio libero dell'admin, un promemoria automatico prima di un evento (24h e 3h), il
* turno palloni e il sollecito presenze (questi due avviati a mano da un admin, in
* parallelo alla push esistente). Qui si legge e si segna come letto, con
@@ -16,7 +16,8 @@ export type TipoNotifica =
| "evento_promemoria_24h"
| "evento_promemoria_3h"
| "turno_palloni"
| "sollecita_presenze";
| "sollecita_presenze"
| "sondaggio_cacche";
export type NotificaUtente = {
id: string;