Allinea le notifiche del profilo al canale intero e stacca Esci in rosso chiaro.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Ivan Cacciari
2026-09-05 22:31:56 +02:00
co-authored by Cursor
parent ce1c7cfd4b
commit 2345a45f14
4 changed files with 76 additions and 66 deletions
+5
View File
@@ -6,6 +6,11 @@ qui: sta in [ROADMAP.md](ROADMAP.md).
## Versione attuale — agosto 2026 ## Versione attuale — agosto 2026
### Profilo: etichetta notifiche allineata al comportamento
- Linterruttore in Impostazioni non è più «Notifiche turno palloni»: iscrive il dispositivo
a tutte le push (palloni, solleciti) e alle smart in app. Testo e docs aggiornati.
### Revisione dell'interfaccia: accessibilità, movimento, peso ### Revisione dell'interfaccia: accessibilità, movimento, peso
- **Contrasto**: `--success`, `--info` e `--training` erano tra 3.3:1 e 3.5:1 con il testo - **Contrasto**: `--success`, `--info` e `--training` erano tra 3.3:1 e 3.5:1 con il testo
+10 -9
View File
@@ -1,7 +1,6 @@
# Modulo — Notifiche # Modulo — Notifiche
**Stato:** implementato parzialmente — solo il canale "turno palloni" è realmente collegato **Stato:** implementato — un unico opt-in dispositivo abilita tutto il canale push
(vedi Limiti noti)
**File principali:** `src/lib/notifiche-smart.ts`, `src/lib/push-client.ts`, **File principali:** `src/lib/notifiche-smart.ts`, `src/lib/push-client.ts`,
`src/lib/webpush.server.ts`, `src/routes/api/public/push-config.ts`, `src/lib/webpush.server.ts`, `src/routes/api/public/push-config.ts`,
`src/routes/api/public/push-messaggio.ts`, `src/routes/api/public/push-subscribe.ts`, `src/routes/api/public/push-messaggio.ts`, `src/routes/api/public/push-subscribe.ts`,
@@ -30,7 +29,12 @@ persistente: la riga viene eliminata non appena letta dal service worker).
## Iscrizione alle notifiche push ## Iscrizione alle notifiche push
1. Il giocatore attiva "Notifiche turno palloni" in `/profilo` → richiesta permesso browser. In Profilo → Impostazioni c’è **un solo interruttore** («Notifiche»). Non esistono preferenze
separate per tipo di messaggio: liscrizione registra il dispositivo e lo rende destinatario
di **tutte** le push (promemoria palloni, solleciti presenze) e abilita anche le notifiche
smart in app, che usano lo stesso service worker.
1. Il giocatore attiva «Notifiche» in `/profilo` → richiesta permesso browser.
2. `GET /api/public/push-config` restituisce solo la chiave pubblica VAPID. 2. `GET /api/public/push-config` restituisce solo la chiave pubblica VAPID.
3. Registrazione del service worker `public/push-sw.js` e `pushManager.subscribe()`. 3. Registrazione del service worker `public/push-sw.js` e `pushManager.subscribe()`.
4. `POST /api/public/push-subscribe` registra endpoint e chiavi in `push_subscriptions` 4. `POST /api/public/push-subscribe` registra endpoint e chiavi in `push_subscriptions`
@@ -66,11 +70,8 @@ ripetersi — deduplica puramente locale al dispositivo, non sincronizzata.
## Limiti noti ## Limiti noti
- **Le 4 voci "Notifiche convocazioni", "Promemoria allenamenti", "Cambi orario", "Bacheca - Non ci sono preferenze granulari (solo palloni / solo presenze / solo smart): un dispositivo
squadra" in `/profilo` sono placeholder statici**: checkbox non controllati è iscritto o no. Separare i canali richiederebbe schema e UI dedicati.
(`defaultChecked`, nessun `onChange`), non collegati a nessuno stato, nessuna colonna DB
per queste preferenze. L'unica preferenza realmente funzionante è "Notifiche turno
palloni".
- `promemoria_push` è descritta altrove come "storico" ma nel codice è una coda che si - `promemoria_push` è descritta altrove come "storico" ma nel codice è una coda che si
autocancella alla lettura: non conserva nulla. autocancella alla lettura: non conserva nulla.
- Nessuna verifica di autenticazione su `push-messaggio` (chiunque conosca un endpoint push - Nessuna verifica di autenticazione su `push-messaggio` (chiunque conosca un endpoint push
@@ -88,6 +89,6 @@ ripetersi — deduplica puramente locale al dispositivo, non sincronizzata.
## Evoluzioni possibili ## Evoluzioni possibili
- Collegare (o rimuovere) le 4 preferenze placeholder in `/profilo`. - Preferenze per canale (palloni, solleciti, smart), se servono davvero alla squadra.
- Aggiungere autenticazione alle route pubbliche coinvolte. - Aggiungere autenticazione alle route pubbliche coinvolte.
- Gestire esplicitamente il caso iOS (messaggio se l'app non è installata da Home). - Gestire esplicitamente il caso iOS (messaggio se l'app non è installata da Home).
+3 -1
View File
@@ -144,7 +144,9 @@ Contiene tutti i badge ottenuti e quelli ancora da sbloccare.
Contiene. Contiene.
- Logout - Logout
- Preferenze notifiche - Preferenze notifiche: un solo interruttore che iscrive il dispositivo a **tutte** le push
(turno palloni, solleciti presenze) e abilita le notifiche smart in app — non è limitato
ai soli palloni (vedi [Notifiche](notifiche.md))
- Impostazioni applicazione - Impostazioni applicazione
- Segnala un bug e Suggerisci una nuova funzionalità: due link che aprono una issue GitHub - Segnala un bug e Suggerisci una nuova funzionalità: due link che aprono una issue GitHub
già impostata sul template giusto (`.github/ISSUE_TEMPLATE/bug_report.yml` e già impostata sul template giusto (`.github/ISSUE_TEMPLATE/bug_report.yml` e
+58 -56
View File
@@ -80,7 +80,7 @@ function Profilo() {
} else { } else {
await attivaNotifiche(g.id); await attivaNotifiche(g.id);
setNotifiche(true); setNotifiche(true);
toast.success("Notifiche palloni attive"); toast.success("Notifiche attive");
} }
} catch (error) { } catch (error) {
toast.error(error instanceof Error ? error.message : "Notifiche non disponibili"); toast.error(error instanceof Error ? error.message : "Notifiche non disponibili");
@@ -227,68 +227,70 @@ function Profilo() {
id: "impostazioni", id: "impostazioni",
label: "Impostazioni", label: "Impostazioni",
contenuto: ( contenuto: (
<div className="divide-y divide-border overflow-hidden rounded-3xl bg-card shadow-card"> <div className="space-y-3">
<button <div className="divide-y divide-border overflow-hidden rounded-3xl bg-card shadow-card">
type="button" <button
onClick={cambiaNotifiche} type="button"
disabled={!supportate || inCorso} onClick={cambiaNotifiche}
className="flex min-h-11 w-full items-center justify-between gap-3 px-4 py-3 text-left text-sm disabled:opacity-60" disabled={!supportate || inCorso}
> className="flex min-h-11 w-full items-center justify-between gap-3 px-4 py-3 text-left text-sm disabled:opacity-60"
<span className="min-w-0">
<span className="block truncate">Notifiche turno palloni</span>
<span className="block text-xs text-muted-foreground">
{supportate
? notifiche
? "Attive su questo dispositivo"
: "Ricevi l'avviso il giorno stesso e la volta dopo"
: "Non supportate su questo dispositivo"}
</span>
</span>
<span
className={cn(
"grid h-8 w-8 shrink-0 place-items-center rounded-xl",
notifiche
? "bg-accent-grad text-accent-foreground"
: "bg-secondary text-muted-foreground",
)}
> >
<Bell className="h-4 w-4" /> <span className="min-w-0">
</span> <span className="block truncate">Notifiche</span>
</button> <span className="block text-xs text-muted-foreground">
{admin ? ( {supportate
<Link ? notifiche
to="/admin" ? "Attive su questo dispositivo"
: "Palloni, solleciti presenze e avvisi in app"
: "Non supportate su questo dispositivo"}
</span>
</span>
<span
className={cn(
"grid h-8 w-8 shrink-0 place-items-center rounded-xl",
notifiche
? "bg-accent-grad text-accent-foreground"
: "bg-secondary text-muted-foreground",
)}
>
<Bell className="h-4 w-4" />
</span>
</button>
{admin ? (
<Link
to="/admin"
className="flex min-h-11 w-full items-center justify-between gap-3 px-4 py-3 text-sm transition-colors hover:bg-accent/5"
>
<span className="min-w-0 truncate">Dashboard amministratore</span>
<ShieldCheck className="h-4 w-4 text-muted-foreground" />
</Link>
) : null}
<a
href="https://github.com/ivancacciari1995-a11y/CRAPP/issues/new?template=bug_report.yml"
target="_blank"
rel="noopener noreferrer"
className="flex min-h-11 w-full items-center justify-between gap-3 px-4 py-3 text-sm transition-colors hover:bg-accent/5" className="flex min-h-11 w-full items-center justify-between gap-3 px-4 py-3 text-sm transition-colors hover:bg-accent/5"
> >
<span className="min-w-0 truncate">Dashboard amministratore</span> <span className="min-w-0 truncate">Segnala un bug</span>
<ShieldCheck className="h-4 w-4 text-muted-foreground" /> <Bug className="h-4 w-4 text-muted-foreground" />
</Link> </a>
) : null} <a
<a href="https://github.com/ivancacciari1995-a11y/CRAPP/issues/new?template=feature_request.yml"
href="https://github.com/ivancacciari1995-a11y/CRAPP/issues/new?template=bug_report.yml" target="_blank"
target="_blank" rel="noopener noreferrer"
rel="noopener noreferrer" className="flex min-h-11 w-full items-center justify-between gap-3 px-4 py-3 text-sm transition-colors hover:bg-accent/5"
className="flex min-h-11 w-full items-center justify-between gap-3 px-4 py-3 text-sm transition-colors hover:bg-accent/5" >
> <span className="min-w-0 truncate">Suggerisci una nuova funzionalità</span>
<span className="min-w-0 truncate">Segnala un bug</span> <Lightbulb className="h-4 w-4 text-muted-foreground" />
<Bug className="h-4 w-4 text-muted-foreground" /> </a>
</a> </div>
<a
href="https://github.com/ivancacciari1995-a11y/CRAPP/issues/new?template=feature_request.yml"
target="_blank"
rel="noopener noreferrer"
className="flex min-h-11 w-full items-center justify-between gap-3 px-4 py-3 text-sm transition-colors hover:bg-accent/5"
>
<span className="min-w-0 truncate">Suggerisci una nuova funzionalità</span>
<Lightbulb className="h-4 w-4 text-muted-foreground" />
</a>
<button <button
type="button" type="button"
onClick={logout} onClick={logout}
className="flex min-h-11 w-full items-center justify-between gap-3 px-4 py-3 text-sm transition-colors hover:bg-accent/5" className="premi flex min-h-11 w-full items-center justify-center gap-2 rounded-3xl bg-destructive/15 px-4 py-3 text-sm font-bold text-destructive shadow-card"
> >
<span className="min-w-0 truncate">Esci</span> <LogOut className="h-4 w-4" />
<LogOut className="h-4 w-4 text-muted-foreground" /> Esci
</button> </button>
</div> </div>
), ),