Punti in striscia e nomi in "Formazione di partenza" ora usano lo
stesso giallo (#f5c518) per la squadra di casa e blu (#2196f3) per
gli ospiti già usati in display/controller, invece di una palette
scollegata dal resto dell'app.
Aggiunge un'icona nera (nuovo asset serv-nero.png, più visibile del
serv.png bianco su sfondo chiaro) accanto al nome della squadra che
serviva all'inizio di ogni set nel referto stampabile.
Corregge inoltre generaReferto: la stampa partiva subito dopo
document.close(), prima che l'icona (risorsa esterna asincrona)
finisse di caricare, risultando in un riquadro vuoto. Ora attende
l'evento 'load' della finestra popup prima di chiamare print().
Cornice nera, barre teal "SET N" per set, badge A/B per squadre e
box risultato finale in stile tabella, per avvicinare il referto
stampabile all'aspetto del referto cartaceo FIPAV mantenendo solo
i dati che l'app traccia realmente.
La striscia salva ora storico cambi formazione e time out per ogni
set, con riferimento al punteggio al momento dell'evento anziché a
un timestamp: startTimeout accumula in set.timeouts, confermaCambi
in set.cambi. Il referto elenca entrambi ordinati per punteggio.
L'overlay ora riproduce il video scelto dal controller mostrando un
countdown 00:30→00:00 calcolato dallo stato condiviso. Se il video finisce
prima dei 30s subentra un'immagine "TIME OUT + nome squadra" fino allo
scadere; se dura più di 30s si applica una dissolvenza CSS negli ultimi
istanti. In caso di riconnessione a metà time out il video viene
riallineato (currentTime) al punto corretto.
Il bottone Time Out apre un modal invece di attivare direttamente
l'overlay: si sceglie la squadra che lo ha richiesto e uno dei video
in spot/ (durata rilevata lato client via <video preload=metadata>).
A time out attivo il bottone diventa "Termina" con countdown, per
poterlo interrompere manualmente prima dei 30s.
Serve una squadra richiedente e un video selezionabile, non più un semplice
flag. Il timer dei 30s è armato lato server (non nel controller) così
sopravvive a refresh/disconnessione/standby del tablet che pilota il gioco.
Il tabellone (display) usava blocchi di colore pieni e testo semplice, molto
diverso dallo stile scuro con card e accenti oro/blu del controller.
Riallinea la palette (#f5c518/#2196f3), la tipografia (Inter, grassetto,
maiuscolo) e i colori di stato connessione/time out, poi aggiunge dettagli da
tabellone reale (linea centrale, badge SET, bagliore sul punteggio, striscia
punti come pannello con bordo/ombra) senza toccare layout o struttura.
Aggiunge il flag `timeout` nello stato e l'action `toggleTimeout`: dal
controller il pulsante Time Out commuta la modalità e il display copre il
tabellone con i video .mp4 della cartella `spot/` (non versionata).
- un solo video → loop; più video → sequenza alfabetica con ritorno al primo;
cartella vuota/assente → overlay "TIME OUT".
- `src/spot-utils.js` (listSpotVideos) elenca i .mp4; endpoint `/spot/list`
+ file statici `/spot/<file>.mp4` serviti sia in prod (server.js) sia in
dev (vite-plugin-websocket.js).
- il display avvia i video con audio e ripiega su muto se il browser blocca
l'autoplay con suono.
- il controller resta operativo ma evidenzia la modalità con banner e
pulsante arancione pulsante, in entrambi i layout (mobile ed esteso).
La suite era allineata a una vecchia forma dello stato (sp.punt/sp.set/
sp.servHome) e a una vecchia architettura e2e (controller su :3001).
Baseline iniziale: 77/170 test Vitest falliti.
Vitest (ora 212/212 verdi):
- gameState.test.js: riscritto con helper che derivano punteggio/set/
servizio dalla striscia; aggiunto blocco formInizio
- server-utils.js: getNetworkIPs accetta interfacce iniettabili e
printServerInfo accetta gli IP iniettabili (deterministico anche su WSL);
filtro LAN unificato (esclude 127./169.254./172.)
- websocket + stress: punteggio letto via punteggio(striscia)
- ControllerPage/DisplayPage: forzato layout mobile (viewport portrait),
punteggi impostati via striscia; aggiunto test bottone REFERTO
- nuovi: referto.test.js, persist.test.js (mock fs), wsMixin.test.js,
integration/server.test.js (routing)
Refactor di supporto:
- referto.js: estratta buildRefertoHtml(state, now) pura; generaReferto
resta wrapper con window.open/print
- server.js: estratti createApp()/startServer(); avvio solo se entrypoint
e2e (migrazione parziale, NON ancora verificata verde):
- tutti i riferimenti controller :3001 -> :3000/controller
- forzato viewport portrait sul controller prima del goto
- reset helper: chiude il dialog di configurazione che doReset apre
- game-simulation: gestione del dialog SET VINTO automatico a 25
Aggiunge un bottone "REFERTO" nel modal PARTITA FINITA che apre una
nuova scheda con il referto di gara in HTML e avvia il dialogo di
stampa del browser (Salva come PDF).
Il referto include: punteggio per set, formazione di partenza per ogni
set (salvata automaticamente al primo punto), andamento punto per punto
con colori per squadra, data e ora di generazione.
Nota: funzionalità prototipale — layout, contenuti e dettagli sono
ancora da perfezionare sulla base dell'utilizzo reale.
Le card punteggio mobile adottano lo stesso linguaggio visivo della
modalità estesa: pannello scuro #161618, stripe colorata in cima
(giallo home, blu guest), score come testo colorato senza riquadro.
I bottoni SET diventano a colore pieno; btn-ctrl e btn-danger
uniformati ai token colore della dashboard landscape.
Aggiunge una seconda modalità dashboard per schermi orizzontali,
rilevata automaticamente dall'orientamento (landscape → estesa,
portrait → mobile) tramite resize/orientationchange listener.
Layout estesa: due pannelli affiancati con stripe colorata in cima
(giallo/blu per identità squadra), score grande come elemento eroe,
diagramma campo in verde scuro, SET button a colori pieni in fondo.
Barra azioni compatta (40px) con tutti i controlli secondari.
Il pannello usa position:fixed ancorato al viewport per impedire
qualsiasi scroll, con @wheel.prevent e @touchmove.prevent.
Il pannello punteggio ora rispecchia l'ordine delle squadre definito da
state.order: premendo Inverti le card si scambiano di lato anche sul
controller, allineandosi al comportamento visivo del display.
Dopo aver azzerato la partita il dialog di configurazione si apre
subito, con nomi e modalità correnti e formazioni resettate ai default
— stesso comportamento già presente al termine di ogni set.
- Aggiunge modalità "Amichevole" in config: i set si vincono normalmente
ma la partita non termina mai automaticamente (checkVittoriaPartita
restituisce false), consentendo di giocare set illimitati
- Dev server ora espone su tutte le interfacce (vite --host)
- printServerInfo mostra Display + Controller per dispositivi remoti
- Su WSL2 getNetworkIPs() interroga PowerShell per ottenere gli IP
Windows reali invece degli IP interni WSL (172.x)
La striscia è ora l'unica source of truth: punt, set vinti e servizio
vengono calcolati via helper puri (punteggio/servizio/setVinti) invece
di essere mantenuti come campi ridondanti nello stato.
Il boilerplate WebSocket (~150 righe identiche in entrambi i componenti)
è estratto in src/wsMixin.js con createWsMixin(role); i componenti
diventano solo logica specifica del loro ruolo.
Aggiunge checkVittoriaPartita per rilevare la vittoria della partita
(2 set in 2/3, 3 set in 3/5). nuovoSet ora registra il set vincente
senza resettare il punteggio quando la partita è finita. Il controller
mostra "PARTITA FINITA" al posto di "SET VINTO" con solo il tasto CHIUDI.
La striscia diventa un array di set: ogni elemento è { serv, r[] }
dove r è la sequenza di scorer ('home'|'guest') del set.
- Un rally = un elemento in r: minimo non-derivabile
- Tutti i set (passati e corrente) sono conservati nell'array
- Dal set corrente si derivano: punteggio, servizio, cambio palla, rotazione
- Dal set completo si derivano: vincitore (r.at(-1)), score finale (count)
- storicoServizio eliminato: l'undo legge l'entry precedente di r
DisplayPage calcola le strip visive (home/guest) tramite computed
da striscia.at(-1).r senza dati ridondanti nel modello.
Quando una squadra raggiunge il punteggio di vittoria (25 con +2 di
scarto, 15 nel set decisivo), il controller mostra un dialog "SET VINTO"
con il nome della squadra vincente.
Alla conferma: invia l'azione nuovoSet (incrementa il set, azzera punti,
striscia, storico servizio e formazioni) e apre automaticamente il dialog
di configurazione per inserire le formazioni del set successivo.
- Inizializza striscia con { home: [0], guest: [0] } invece di usare
[" "] per il team non servente; corregge anche reset e cambiaPalla.
- Sostituisce JSON.parse/stringify con structuredClone (nativo, più veloce).
- Calcola cambioPalla una sola volta in incPunt invece di due volte.
Unifica i due server Express (display :3000, controller :3001) in un
unico processo su PORT (default 3000). Le route /display e /controller
servono rispettivamente index.html e controller.html.
In sviluppo elimina il server proxy su :3001; il plugin Vite riscrive
/display → / e /controller → /controller.html internamente.
printServerInfo aggiornata alla firma a porta singola.
- Layout ora usa CSS Grid (max-content 1fr) per allineare le colonne
dei nomi indipendentemente dalla loro lunghezza
- I punti crescono da sinistra verso destra; un watcher Vue imposta
scrollLeft al massimo ad ogni aggiornamento, mantenendo visibili
gli ultimi punti a destra quando la striscia va oltre lo schermo
- Le celle vuote (spazio al posto del punto) non mostrano più
lo sfondo giallo-verde (classe item-vuoto)
- All'inizio del set lo 0 compare solo nella riga della squadra che batte;
la squadra non servente mostra uno spazio per mantenere l'allineamento dei nomi
- cambiaPalla aggiorna la striscia dopo il flip del servizio (a 0-0)
- resetta inizializza la striscia in base al servente corrente anziché mostrare 0 per entrambe
- Corretto il guard dell'undo: usa storicoServizio.length > 0 invece di
striscia.home.length > 1, che avrebbe bloccato l'annulla quando la riga
della squadra non servente contiene un solo elemento
- aggiunge attributi alt alle icone di servizio su Display e Controller
- migliora il contrasto colore del pulsante Reset per rispettare i controlli a11y
- include piccoli aggiustamenti collegati ai test E2E/accessibility
- Introduce Vitest per Unit e Integration Test.
- Introduce Playwright per End-to-End Test.
- Aggiuge documentazione dettagliata in tests/README.md.
- Aggiorna .gitignore per escludere i report di coverage
Il controller invia un comando 'speak' via WebSocket. Il server inoltra il messaggio solo ai client display, che eseguono speechSynthesis con preferenza per voce italiana.
Usa fallback a 127.0.0.1 quando l'hostname è localhost/::1 nei client websocket display/controller.
Instrada il proxy del controller dev verso Vite tramite DEV_PROXY_HOST (default 127.0.0.1).
Mostra gli URL locali del server con 127.0.0.1 per una diagnostica coerente su Raspberry/Linux.
Permette di specificare manualmente l'host del WebSocket tramite il
parametro ?wsHost=[host:port], utile per scenari di sviluppo con WSL2
o quando si accede da dispositivi remoti.
- Aggiunge parsing del parametro wsHost in DisplayPage e ControllerPage
- Mantiene fallback automatico a location.host se non specificato
- Migliora diagnostica con log della URL WebSocket effettiva
- Creati entry point separati per il Display (porta 5173) e il Controller (porta 3001).
- Aggiunti controller.html e src/controller-main.js per l'app di controllo remoto.
- Semplificato src/main.js per montare direttamente DisplayPage, rimuovendo vue-router.
- Implementato un server di sviluppo proxy per il controller in vite-plugin-websocket.js.
- Aggiornato server.js per gestire due istanze Express (display e controller) in produzione.
- Aggiornata la configurazione di Vite per il supporto alla build multi-pagina
Aggiunge gestione riconnessione con backoff esponenziale e protezione da reconnect multipli.
Migliora cleanup su unmount/HMR per evitare listener e timeout pendenti.
Uniforma gestione errori e stato connessione lato client.
Semplifica etichette pulsanti controller rimuovendo emoji e aggiorna commenti.
Estrae la gestione dei messaggi WebSocket in un modulo dedicato.
Rende server.js più snello e focalizzato su bootstrap HTTP/WS.
Introduce utility per stampa URL di accesso e discovery IP di rete.
Mantiene la logica di stato partita condivisa in gameState.js.
- Aggiunto server Express + WebSocket (server.js)
- Creata pagina Display (solo visualizzazione punteggio)
- Creata pagina Controller (pannello comandi da mobile)
- Aggiunto Vue Router con rotte / e /controller
- Estratta logica di gioco condivisa in gameState.js
Implementa ordine tabindex personalizzato per inserimento formazioni:
- Focus automatico su primo campo all'apertura (Ctrl+M)
- Sequenza Tab: nomi squadre → zone 1-6 home → zone 1-6 guest
- Navigazione ciclica con Tab/Shift+Tab all'interno del dialog
- Bottoni esclusi dalla navigazione (tabindex="-1")
- Handler dedicato per gestione focus e prevenzione navigazione esterna
Consente di effettuare fino a 2 cambi simultanei per squadra con una nuova
interfaccia utente più compatta e visuale. Gli input IN sono colorati di
verde, gli OUT di rosso, e una freccia indica la direzione del cambio.
La validazione permette cambi parziali (campi vuoti) ma richiede che ogni
cambio inserito sia completo (sia IN che OUT) e che almeno un cambio sia
presente per confermare
Dettagli:
- richiesta squadra prima del cambio e tabella a riga singola
- scorciatoie Ctrl+C (home) e Shift+C (guest)
- conferma cambio con validazioni e sostituzione in formazione
- README aggiornato con nuovi shortcut e funzione cambi
- dialog “CAMBI” con tabella 2x2 e intestazioni IN/OUT
- etichette riga con nomi squadre
- conferma solo con righe complete (almeno un cambio)
- sostituzione OUT→IN in formazione con controlli errori