Documenta DD-050: avviso ad admin e allenatori se una presenza cambia a meno di 6 ore dall'evento
Solo documentazione: nessun codice né migration. Decisione in valutazione. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
daa0dd9c60
commit
f0e6a3a6cc
3 files changed
+72
-2
No files matched your search
@@ -0,0 +1,67 @@
|
|||||||
|
# DD-050 — Avviso a admin e allenatori se una presenza cambia a meno di 6 ore dall'evento
|
||||||
|
|
||||||
|
**Data:** 8 ottobre 2026
|
||||||
|
**Stato:** In valutazione (solo documentata: nessun codice né migration scritti)
|
||||||
|
|
||||||
|
**Contesto**
|
||||||
|
Un giocatore può cambiare la risposta a un evento fino all'ultimo minuto. Chi organizza (admin e allenatore)
|
||||||
|
se ne accorge solo riaprendo l'evento: un «assente» detto due ore prima di una partita può far saltare la
|
||||||
|
formazione o il numero minimo per l'allenamento. Oggi nessuna notifica avvisa chi gestisce gli eventi di una
|
||||||
|
risposta (`notifiche.md`, «Cosa non genera nessuna notifica»).
|
||||||
|
|
||||||
|
**Decisione**
|
||||||
|
|
||||||
|
1. **Cosa fa scattare l'avviso.** Una riga di `risposte_presenze` che cambia — nuova, modificata o ritirata —
|
||||||
|
quando mancano **da 0 a 6 ore** all'inizio di una **partita o un allenamento** (non agli altri tipi di
|
||||||
|
evento). Scatta per **qualsiasi
|
||||||
|
cambio di stato**, da qualunque valore a qualunque altro (presente, assente, forse, in ritardo,
|
||||||
|
infortunato) e anche per la prima risposta e per il ritiro; non scatta solo se lo stato resta quello di
|
||||||
|
prima (nessun cambio reale). Vale per qualunque giocatore, anche se la
|
||||||
|
modifica la fa un admin per conto suo.
|
||||||
|
2. **Chi la riceve:** gli **admin e gli allenatori attivi** (chi ha il ruolo `admin` o `allenatore` in
|
||||||
|
`user_roles` e uno slot attivo in `giocatori_squadra`), **tranne la persona a cui appartiene la presenza**
|
||||||
|
(se un admin cambia la propria, non avvisa se stesso). Chi ha il ruolo ma nessuno slot collegato non può
|
||||||
|
ricevere notifiche, perché `notifiche_utente` è per slot.
|
||||||
|
3. **Come funziona.** Un trigger `AFTER INSERT OR UPDATE OF stato OR DELETE` su `risposte_presenze` chiama una
|
||||||
|
funzione `SECURITY DEFINER` (`avvisa_presenza_modificata()`, migration `m32`). La funzione:
|
||||||
|
1. ignora il caso in cui lo stato prima e dopo è lo stesso;
|
||||||
|
2. legge l'evento e continua solo se `tipo` è `partita` o `allenamento` e `inizio_evento(data, ora) - now()`
|
||||||
|
sta tra 0 e 6 ore (stessa `inizio_evento()` di M26: fuso `Europe/Rome`, ora illeggibile = nessun avviso);
|
||||||
|
3. inserisce una riga in `notifiche_utente` per ogni destinatario, con il tipo nuovo `presenza_modificata`.
|
||||||
|
|
||||||
|
Dal database in poi il resto è già fatto: il trigger di M22/M26 accoda l'**email**, il trigger delle push
|
||||||
|
(esteso con il tipo nuovo) accoda la **push**, la riga è il **centro notifiche in-app** (DD-038). Nessuna
|
||||||
|
route dell'app e nessun codice lato client oltre al nuovo valore di `TipoNotifica`.
|
||||||
|
|
||||||
|
4. **Testo.** Titolo `Presenza modificata: <Nome Cognome>`; corpo su righe: `Evento: <titolo>`,
|
||||||
|
`Data: <GG/MM/AAAA>, ore <ora>`, `Da: <stato prima>`, `A: <stato dopo>`. Gli stati sono «presente»,
|
||||||
|
«assente», «forse», «in ritardo», «infortunato», «nessuna risposta» (prima di una risposta nuova, o dopo
|
||||||
|
un ritiro). Non c'è un link all'evento: il pulsante «Apri CrAPP» della mail e il tocco sulla push aprono l'app.
|
||||||
|
5. **Una notifica per modifica, senza raggruppare.** `evento_id` resta NULL (come i messaggi `admin`), così la
|
||||||
|
`UNIQUE (giocatore_id, evento_id, tipo)` non si applica e due modifiche dello stesso evento non si
|
||||||
|
sovrascrivono. Contro: se la stessa persona cambia idea cinque volte, ognuno dei destinatari riceve cinque
|
||||||
|
notifiche e cinque email.
|
||||||
|
6. **Nessuna configurazione.** Né una soglia modificabile (6 ore è scritto nella funzione), né un interruttore
|
||||||
|
per ruolo: l'email e la push seguono le preferenze già esistenti di ciascuno (`preferenze_utente`, DD-036).
|
||||||
|
|
||||||
|
**Alternative scartate**
|
||||||
|
|
||||||
|
- **Una route dell'app invocata dal client dopo il salvataggio** (come `sollecita-presenze`) → il client
|
||||||
|
scrive su `risposte_presenze` direttamente con RLS: una route in più sarebbe aggirabile e dipenderebbe dal
|
||||||
|
fatto che l'app sia ancora aperta al momento del salvataggio. Il trigger copre ogni scrittura.
|
||||||
|
- **Raggruppare in una sola notifica per evento** (upsert con `evento_id`) → perde il dettaglio di chi ha
|
||||||
|
cambiato cosa, e l'upsert non rifa partire la push (il trigger delle push è solo `INSERT`).
|
||||||
|
- **Notificare anche chi ha il ruolo ma è il giocatore stesso** → inutile: sa già cosa ha fatto.
|
||||||
|
|
||||||
|
**Conseguenze**
|
||||||
|
|
||||||
|
- Nuova migration `m32` (CHECK dei tipi, due funzioni, due trigger); `docs/DATABASE.md` e il catalogo in
|
||||||
|
`notifiche.md` si aggiornano con essa. **Va applicata in produzione** con `npx supabase db push`.
|
||||||
|
- L'allenatore, che oggi non riceve nulla sulle presenze, riceve questa notifica: va aggiunta a
|
||||||
|
[allenatore.md](../modules/allenatore.md#notifiche).
|
||||||
|
- Rumore possibile a ridosso dell'evento (punto 5). Se in pratica dà fastidio, il primo ritocco è raggruppare
|
||||||
|
per evento, non togliere l'avviso.
|
||||||
|
|
||||||
|
**Riesame**
|
||||||
|
Dopo qualche settimana d'uso: se arrivano troppe notifiche per lo stesso giocatore, raggruppare; se 6 ore
|
||||||
|
sono troppe o poche, spostare la soglia (una costante nella funzione).
|
||||||
@@ -47,7 +47,7 @@ Una decisione sostituita non si cancella: si cambia il suo **Stato** e si rimand
|
|||||||
**Accettate**
|
**Accettate**
|
||||||
|
|
||||||
| ID | Titolo |
|
| ID | Titolo |
|
||||||
| ------------------- | ------------------------------------------- |
|
| ------------------- | --------------------------------------------------------- |
|
||||||
| [DD-001](DD-001.md) | Indipendenza da Lovable |
|
| [DD-001](DD-001.md) | Indipendenza da Lovable |
|
||||||
| [DD-002](DD-002.md) | Sviluppo document-first |
|
| [DD-002](DD-002.md) | Sviluppo document-first |
|
||||||
| [DD-004](DD-004.md) | Ogni versione aggiunge, non riscrive |
|
| [DD-004](DD-004.md) | Ogni versione aggiunge, non riscrive |
|
||||||
@@ -86,6 +86,7 @@ Una decisione sostituita non si cancella: si cambia il suo **Stato** e si rimand
|
|||||||
| [DD-038](DD-038.md) | Tre canali per ogni notifica |
|
| [DD-038](DD-038.md) | Tre canali per ogni notifica |
|
||||||
| [DD-039](DD-039.md) | Niente promemoria all'allenatore |
|
| [DD-039](DD-039.md) | Niente promemoria all'allenatore |
|
||||||
| [DD-040](DD-040.md) | Palloni e solleciti automatici |
|
| [DD-040](DD-040.md) | Palloni e solleciti automatici |
|
||||||
|
| [DD-050](DD-050.md) | Avviso se una presenza cambia a meno di 6 ore dall'evento |
|
||||||
| [DD-049](DD-049.md) | Caratteri più grandi in tutta l'app |
|
| [DD-049](DD-049.md) | Caratteri più grandi in tutta l'app |
|
||||||
| [DD-048](DD-048.md) | Via la tessera dalla dashboard |
|
| [DD-048](DD-048.md) | Via la tessera dalla dashboard |
|
||||||
| [DD-047](DD-047.md) | Notifica di compleanno |
|
| [DD-047](DD-047.md) | Notifica di compleanno |
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ quelle del turno palloni**, che restano solo push e in-app: DD-040).
|
|||||||
| 6 | **Sondaggio pre-partita** — «💩 Sondaggio pre-partita aperto» | Un admin, dalla pagina della partita. Route `apri-sondaggio` (vedi [Scout Live](scout-live.md)). | Push: tutti i dispositivi iscritti, tranne quelli degli allenatori. In-app ed email (tipo `sondaggio_cacche`): i giocatori attivi, allenatori esclusi, anche senza dispositivi iscritti. | sì | sì | sì |
|
| 6 | **Sondaggio pre-partita** — «💩 Sondaggio pre-partita aperto» | Un admin, dalla pagina della partita. Route `apri-sondaggio` (vedi [Scout Live](scout-live.md)). | Push: tutti i dispositivi iscritti, tranne quelli degli allenatori. In-app ed email (tipo `sondaggio_cacche`): i giocatori attivi, allenatori esclusi, anche senza dispositivi iscritti. | sì | sì | sì |
|
||||||
| 7 | **Notifiche smart** — badge appena sbloccato, serie a un traguardo, obiettivo di squadra tra il 90 e il 100%, badge social vinto | L'app, mentre è aperta: sono calcolate in locale da `calcolaNotifiche()`. | Chi ha l'app aperta con uno slot giocatore in rosa selezionato; **mai l'allenatore**. Nessun invio verso altri dispositivi. | no (solo locale) | no | no |
|
| 7 | **Notifiche smart** — badge appena sbloccato, serie a un traguardo, obiettivo di squadra tra il 90 e il 100%, badge social vinto | L'app, mentre è aperta: sono calcolate in locale da `calcolaNotifiche()`. | Chi ha l'app aperta con uno slot giocatore in rosa selezionato; **mai l'allenatore**. Nessun invio verso altri dispositivi. | no (solo locale) | no | no |
|
||||||
| 8 | **Compleanno** — auguri al festeggiato, avviso agli altri | **Automatica**, **una volta al giorno, dalle 8:00** (fuso `Europe/Rome`) per chi compie gli anni quel giorno (DD-047, `m31`): job `pg_cron` ogni ora, funzione `genera_avvisi_compleanno()`, registro `compleanni_notificati`; tipi `compleanno_auguri` e `compleanno`, push dal worker. Nessun pulsante manuale. | **Tutti i membri attivi**, allenatori compresi: il festeggiato riceve gli auguri, gli altri l'avviso. Con più compleanni lo stesso giorno, un solo messaggio a testa (agli altri con tutti i nomi, a ogni festeggiato solo i propri auguri). | sì | sì | sì |
|
| 8 | **Compleanno** — auguri al festeggiato, avviso agli altri | **Automatica**, **una volta al giorno, dalle 8:00** (fuso `Europe/Rome`) per chi compie gli anni quel giorno (DD-047, `m31`): job `pg_cron` ogni ora, funzione `genera_avvisi_compleanno()`, registro `compleanni_notificati`; tipi `compleanno_auguri` e `compleanno`, push dal worker. Nessun pulsante manuale. | **Tutti i membri attivi**, allenatori compresi: il festeggiato riceve gli auguri, gli altri l'avviso. Con più compleanni lo stesso giorno, un solo messaggio a testa (agli altri con tutti i nomi, a ogni festeggiato solo i propri auguri). | sì | sì | sì |
|
||||||
|
| 9 | **Presenza modificata** — «Presenza modificata: _Nome Cognome_» _(proposta, DD-050: non ancora implementata)_ | **Automatica**, a ogni cambio di una risposta in `risposte_presenze` (nuova, modificata o ritirata) quando mancano da 0 a 6 ore all'inizio di una partita o di un allenamento: trigger sul database, tipo `presenza_modificata`, una notifica per modifica, push dal worker. | Gli admin e gli allenatori attivi, tranne il giocatore a cui appartiene la presenza. | sì | sì | sì |
|
||||||
|
|
||||||
Le notifiche generate dal server (righe 1-6 e 8) hanno tutti e tre i canali (DD-038), tranne il turno palloni (riga 4), che non manda email (DD-040). La
|
Le notifiche generate dal server (righe 1-6 e 8) hanno tutti e tre i canali (DD-038), tranne il turno palloni (riga 4), che non manda email (DD-040). La
|
||||||
differenza sta in come parte la push: quelle dei pulsanti (3 e 6) la mandano le route dell'app, quelle
|
differenza sta in come parte la push: quelle dei pulsanti (3 e 6) la mandano le route dell'app, quelle
|
||||||
@@ -75,6 +76,7 @@ disattivazione). Tra `<…>` i valori che cambiano a ogni invio; le date sono `G
|
|||||||
| 6 | Sondaggio pre-partita | `💩 Sondaggio pre-partita aperto` | `<partita> · ore <ora>. Quante cacche hai fatto? Rispondi prima del fischio d'inizio.` |
|
| 6 | Sondaggio pre-partita | `💩 Sondaggio pre-partita aperto` | `<partita> · ore <ora>. Quante cacche hai fatto? Rispondi prima del fischio d'inizio.` |
|
||||||
| 8a | Compleanno, auguri al festeggiato | `Buon compleanno, <Nome>! 🎂` | Nessuno: solo il titolo |
|
| 8a | Compleanno, auguri al festeggiato | `Buon compleanno, <Nome>! 🎂` | Nessuno: solo il titolo |
|
||||||
| 8b | Compleanno, avviso agli altri | `Oggi è il compleanno di <Nome>` — con più festeggiati `<A> e <B>`, o `<A>, <B> e <C>`, in ordine alfabetico per nome | Nessuno: solo il titolo. L'età non compare né negli auguri né nell'avviso |
|
| 8b | Compleanno, avviso agli altri | `Oggi è il compleanno di <Nome>` — con più festeggiati `<A> e <B>`, o `<A>, <B> e <C>`, in ordine alfabetico per nome | Nessuno: solo il titolo. L'età non compare né negli auguri né nell'avviso |
|
||||||
|
| 9 | Presenza modificata (DD-050) | `Presenza modificata: <Nome Cognome>` | `Evento: <titolo>`<br>`Data: <GG/MM/AAAA>, ore <ora>`<br>`Da: <stato prima>`<br>`A: <stato dopo>` — stati: presente, assente, forse, in ritardo, infortunato, nessuna risposta |
|
||||||
|
|
||||||
Le righe 4a-4c e 5 (e le 1-2, finché esistevano) nascono nelle funzioni SQL della migration `m26`
|
Le righe 4a-4c e 5 (e le 1-2, finché esistevano) nascono nelle funzioni SQL della migration `m26`
|
||||||
(`genera_promemoria_eventi`, `genera_avvisi_palloni_fascia`, `genera_revoche_palloni`,
|
(`genera_promemoria_eventi`, `genera_avvisi_palloni_fascia`, `genera_revoche_palloni`,
|
||||||
@@ -102,7 +104,7 @@ dall'emoji. Non entrano nel centro notifiche e non generano email.
|
|||||||
- Creare, spostare, modificare o cancellare un evento. Dalla `m28` (DD-043) non parte nemmeno più un
|
- Creare, spostare, modificare o cancellare un evento. Dalla `m28` (DD-043) non parte nemmeno più un
|
||||||
promemoria prima dell'inizio: restano solo il turno palloni e il sollecito presenze.
|
promemoria prima dell'inizio: restano solo il turno palloni e il sollecito presenze.
|
||||||
- L'arrivo di un risultato, di una pagella o di un voto MVP.
|
- L'arrivo di un risultato, di una pagella o di un voto MVP.
|
||||||
- Un giocatore che risponde a un evento: nessun avviso a chi gestisce gli eventi.
|
- Un giocatore che risponde a un evento: nessun avviso a chi gestisce gli eventi, **salvo** quando la risposta cambia a meno di 6 ore da una partita o un allenamento (riga 9, DD-050).
|
||||||
- Assenze e infortuni.
|
- Assenze e infortuni.
|
||||||
- La registrazione o il collegamento di un nuovo giocatore.
|
- La registrazione o il collegamento di un nuovo giocatore.
|
||||||
- Il certificato in scadenza: resta una card in Home (DD-035).
|
- Il certificato in scadenza: resta una card in Home (DD-035).
|
||||||
|
|||||||
Reference in new issue
Block a user