Compare commits
31 Commits
f6c50954f0
...
v1.0.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 04969a45ea | |||
| 3789f25d0d | |||
| d3698a506d | |||
| 1972fd37a4 | |||
| ea4d8ec523 | |||
| f190db2161 | |||
| 9df74a760f | |||
| 44617f2f86 | |||
| 33a1534319 | |||
| 2e66a6cf2a | |||
| c923bdbf64 | |||
| 139dcc9c5b | |||
| 24dda41b0d | |||
| 4cbb5fb48d | |||
| eae5cbf964 | |||
| 2c6416bfe0 | |||
| 9a808e566d | |||
| 6c6ac7fc29 | |||
| bbe0862241 | |||
| 26d647dce7 | |||
| a72bc1844e | |||
|
|
d57204f4c1 | ||
|
|
40b5751440 | ||
|
|
6824fb3539 | ||
|
|
81e93c8108 | ||
|
|
ef3886b9f3 | ||
|
|
8c59b3b115 | ||
|
|
44c0825a0a | ||
|
|
01b9a0748f | ||
|
|
8a4dc19542 | ||
|
|
6d58ed18c8 |
3
.gitignore
vendored
@@ -7,6 +7,9 @@ yarn-error.log*
|
|||||||
pnpm-debug.log*
|
pnpm-debug.log*
|
||||||
lerna-debug.log*
|
lerna-debug.log*
|
||||||
|
|
||||||
|
# DEscrizione ultimo commit (memoria corta...)
|
||||||
|
currentCommit.txt
|
||||||
|
|
||||||
node_modules
|
node_modules
|
||||||
dist
|
dist
|
||||||
dist-ssr
|
dist-ssr
|
||||||
|
|||||||
282
CHANGELOG.md
Normal file
@@ -0,0 +1,282 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
Tutte le modifiche significative a questo progetto sono documentate in questo file.
|
||||||
|
|
||||||
|
Il formato si basa su [Keep a Changelog](https://keepachangelog.com/it/1.0.0/),
|
||||||
|
e questo progetto aderisce al [Versionamento Semantico](https://semver.org/lang/it/).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [1.0.0] - 2026-02-10
|
||||||
|
|
||||||
|
Rilascio iniziale di **Segnapunti Anto**, un'applicazione web Progressive Web App (PWA) professionale per il tracciamento in tempo reale dei punteggi durante partite di pallavolo.
|
||||||
|
|
||||||
|
### Funzionalità Principali
|
||||||
|
|
||||||
|
#### Gestione Partite e Punteggi
|
||||||
|
- **Tracciamento punti in tempo reale** per entrambe le squadre (casa e ospite)
|
||||||
|
- **Conteggio automatico dei set** con supporto per modalità al meglio di 3 o al meglio di 5
|
||||||
|
- **Logica regolamentare completa**:
|
||||||
|
- Set regolari: primo a 25 punti con almeno 2 di vantaggio
|
||||||
|
- Set decisivo (tie-break): primo a 15 punti con almeno 2 di vantaggio
|
||||||
|
- Blocco automatico assegnazione punti al raggiungimento della vittoria
|
||||||
|
- **Indicatore visivo del servizio** per identificare quale squadra è al servizio
|
||||||
|
- **Cronologia punti** con striscia visiva per seguire l'andamento della partita
|
||||||
|
- Possibilità di **incrementare e decrementare punti** e **set**
|
||||||
|
- **Annullamento punti** con ripristino automatico del servizio precedente
|
||||||
|
|
||||||
|
#### Formazioni e Rotazioni
|
||||||
|
- **Visualizzazione interattiva** della formazione in campo con 6 giocatori per squadra
|
||||||
|
- **Rotazione automatica regolamentare** quando la squadra conquista il servizio (cambio palla)
|
||||||
|
- **Configurazione manuale dei numeri di maglia** per ogni giocatore
|
||||||
|
- **Sistema di cambi giocatori**:
|
||||||
|
- Dialog dedicato per effettuare i cambi
|
||||||
|
- Supporto per cambi singoli o multipli
|
||||||
|
- Tabella IN/OUT con validazione degli input
|
||||||
|
- Verifica che i numeri di maglia siano numerici
|
||||||
|
- Scorciatoie da tastiera dedicate per squadra casa e ospite
|
||||||
|
- **Limitazione cambio palla manuale** solo a inizio set (0-0) per prevenire errori nella rotazione
|
||||||
|
|
||||||
|
#### Interfaccia Utente e Personalizzazione
|
||||||
|
- **Interfaccia fullscreen touch-friendly** ottimizzata per tablet e smartphone
|
||||||
|
- **Layout responsive** con media queries per schermi piccoli (<768px)
|
||||||
|
- **Modalità di visualizzazione**:
|
||||||
|
- Punteggio semplice
|
||||||
|
- Formazioni complete con posizioni giocatori
|
||||||
|
- Toggle tra le due modalità con scorciatoia da tastiera
|
||||||
|
- **Personalizzazione squadre**:
|
||||||
|
- Configurazione dinamica dei nomi squadre
|
||||||
|
- Inversione layout orizzontale (scambio posizione casa/ospite)
|
||||||
|
- Configurazione numeri di maglia giocatori
|
||||||
|
- **Controlli nascondibili**: possibilità di nascondere/mostrare barra pulsanti e cronologia
|
||||||
|
- **Stabilizzazione UI**: dimensioni fisse per riquadri punteggio per evitare spostamenti durante gli aggiornamenti
|
||||||
|
|
||||||
|
#### Controlli e Accessibilità
|
||||||
|
- **Controlli da tastiera completi** con scorciatoie dedicate:
|
||||||
|
- **Squadra Casa**: `Ctrl + ↑/↓` (punti), `Ctrl + →` (set), `Ctrl + C` (cambi)
|
||||||
|
- **Squadra Ospite**: `Shift + ↑/↓` (punti), `Shift + →` (set), `Shift + C` (cambi)
|
||||||
|
- **Comandi globali**:
|
||||||
|
- `Ctrl + ←` (cambio palla, solo a 0-0)
|
||||||
|
- `Ctrl + M` (configurazione)
|
||||||
|
- `Ctrl + B` (toggle barra pulsanti)
|
||||||
|
- `Ctrl + F` (fullscreen)
|
||||||
|
- `Ctrl + S` (annuncio vocale)
|
||||||
|
- `Ctrl + Z` (switch visualizzazione)
|
||||||
|
- **Sintesi vocale** per annunci punteggio in italiano usando Web Speech API
|
||||||
|
- **Sistema di alert professionale** usando Wave UI per notifiche e conferme
|
||||||
|
|
||||||
|
#### Progressive Web App (PWA)
|
||||||
|
- **Installabile** come app nativa su smartphone, tablet e desktop
|
||||||
|
- **Funzionamento offline completo** grazie ai Service Worker
|
||||||
|
- **Auto-update automatico** per ricevere nuovi aggiornamenti senza reinstallazione
|
||||||
|
- **Display fullscreen** per massimizzare lo spazio visivo
|
||||||
|
- **Orientamento landscape ottimizzato** per utilizzo su tablet
|
||||||
|
- **Orientamento sensor landscape** con supporto rotazione 180°
|
||||||
|
- **Icone PWA personalizzate** (192x192 e 512x512)
|
||||||
|
- **Prevenzione scroll indesiderato** su mobile (overscroll-behavior)
|
||||||
|
- **Supporto 100dvh** e position:fixed per layout mobile stabile
|
||||||
|
- **Blocco scroll** per evitare ricariche accidentali con swipe-down
|
||||||
|
|
||||||
|
#### Build e Deployment
|
||||||
|
- **Build APK Android** tramite Capacitor per distribuzione nativa
|
||||||
|
- **Setup automatico icone Android** con script dedicato per multi-densità (ldpi, mdpi, hdpi, xhdpi, xxhdpi, xxxhdpi)
|
||||||
|
- **Configurazione Capacitor** ottimizzata per landscape senza splash screen
|
||||||
|
- **Output unificato** in `/dist/android` per build Android
|
||||||
|
- **Base path configurabile** (`/segnap`) per deployment su sottocartelle
|
||||||
|
|
||||||
|
### Tecnologie e Dipendenze
|
||||||
|
|
||||||
|
#### Stack Tecnologico
|
||||||
|
- **Vue 3.4.38** - Framework JavaScript reattivo
|
||||||
|
- **Vite 5.4.10** - Build tool e dev server veloce
|
||||||
|
- **Wave UI 3.17.0** - Libreria UI components per alert e dialogs
|
||||||
|
- **NoSleep.js 0.12.0** - Prevenzione standby durante le partite
|
||||||
|
- **vite-plugin-pwa 0.20.5** - Plugin per generazione PWA
|
||||||
|
- **Capacitor 6.2.0** - Framework per build app native Android/iOS
|
||||||
|
|
||||||
|
#### Ambiente di Sviluppo
|
||||||
|
- **Node.js 20.x LTS** (consigliato v20.2.0)
|
||||||
|
- **npm 9.0.0+** per gestione dipendenze
|
||||||
|
- **NVM support** per gestione versioni Node.js
|
||||||
|
- **Hot Module Replacement (HMR)** in modalità sviluppo
|
||||||
|
- **Source maps** per debugging
|
||||||
|
- **Vue DevTools** supportato
|
||||||
|
|
||||||
|
#### Browser Supportati
|
||||||
|
- **Chrome/Chromium 90+** - Supporto completo (consigliato)
|
||||||
|
- **Firefox 88+** - Supporto completo
|
||||||
|
|
||||||
|
### Build e Comandi
|
||||||
|
|
||||||
|
#### Comandi Disponibili
|
||||||
|
- `npm run dev` - Server di sviluppo con hot-reload su http://localhost:5173
|
||||||
|
- `npm run build` - Build di produzione ottimizzata in `/dist`
|
||||||
|
- `npm run preview` - Anteprima locale della build di produzione
|
||||||
|
|
||||||
|
#### Output Build
|
||||||
|
- File statici ottimizzati e minificati
|
||||||
|
- Service Worker generato automaticamente
|
||||||
|
- PWA manifest configurato
|
||||||
|
- Assets con hash per cache busting
|
||||||
|
- Permessi automatici per cartella dist in ambiente Docker
|
||||||
|
|
||||||
|
### Documentazione
|
||||||
|
- **README.md completo** con:
|
||||||
|
- Panoramica funzionalità
|
||||||
|
- Requisiti di sistema dettagliati
|
||||||
|
- Istruzioni di installazione e setup con NVM
|
||||||
|
- Guida ai comandi per sviluppo e build
|
||||||
|
- Tabella completa shortcuts tastiera
|
||||||
|
- Configurazione PWA documentata
|
||||||
|
- Spiegazione logica regolamentare pallavolo
|
||||||
|
- Browser testati e supportati
|
||||||
|
- **Encoding corretto** per caratteri accentati italiani
|
||||||
|
|
||||||
|
### Miglioramenti Architetturali
|
||||||
|
- **Separazione componenti**: HomePage estratta in componente dedicato
|
||||||
|
- **Rimozione codice legacy**: eliminati file non utilizzati (HelloWorld.vue)
|
||||||
|
- **Refactoring CSS**: file style.css organizzato e ottimizzato
|
||||||
|
- **Gestione servizio migliorata**: variabile booleana `servHome` per gestione cambio servizio
|
||||||
|
- **Validazione input**: controlli su numeri di maglia e cambi giocatori
|
||||||
|
- **Gestione errori**: prevenzione incrementi a set concluso senza notifiche spam
|
||||||
|
|
||||||
|
### Note di Sviluppo
|
||||||
|
- **Orientamento sensor landscape**: permette rotazione 180° del dispositivo
|
||||||
|
- **Fix tentati audio mobile**: implementati ma richiedono ancora debug (WIP)
|
||||||
|
- Aggiunto supporto @capacitor-community/text-to-speech per audio nativo
|
||||||
|
- Implementato fallback Web API su desktop, plugin nativo su mobile
|
||||||
|
- Correzione lingua da 'it_IT' a 'it-IT'
|
||||||
|
- **Java 17 → 21**: aggiornamento per compatibilità plugin TTS
|
||||||
|
- **ImageMagick in Dockerfile**: per generazione automatica icone Android
|
||||||
|
|
||||||
|
### Configurazione
|
||||||
|
- **Base path**: `/segnap` (configurabile in vite.config.js)
|
||||||
|
- **Tema PWA**: background `#eee`, theme color `#ffffff`
|
||||||
|
- **Display**: fullscreen landscape
|
||||||
|
- **Manifest name**: app_segnap
|
||||||
|
- **Short name**: segnap
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Architettura e Funzionamento Interno
|
||||||
|
|
||||||
|
### Come Funziona l'Applicazione Web
|
||||||
|
|
||||||
|
L'applicazione è una **Single Page Application (SPA)** completamente **client-side**:
|
||||||
|
|
||||||
|
- **Server**: Serve solo file statici (nessun backend, nessun database)
|
||||||
|
- **Esecuzione**: Tutto gira nel browser dell'utente
|
||||||
|
- **Stato**: Memorizzato solo nella RAM del browser (si perde al refresh)
|
||||||
|
- **Offline**: Funziona completamente offline dopo la prima visita (Service Worker)
|
||||||
|
|
||||||
|
#### Build e Deployment
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Vite compila il codice Vue/JavaScript
|
||||||
|
2. Ottimizza e minifica JavaScript e CSS
|
||||||
|
3. Genera Service Worker e manifest PWA
|
||||||
|
4. Output in `/dist` con file statici pronti
|
||||||
|
|
||||||
|
**Deploy**: Copia `/dist` su web server statico (nginx, Apache, Vercel, Netlify). Non serve Node.js, PHP o database sul server.
|
||||||
|
|
||||||
|
#### Funzionamento Runtime
|
||||||
|
|
||||||
|
**Prima visita:**
|
||||||
|
1. Browser scarica index.html dal server
|
||||||
|
2. Carica bundle JavaScript e CSS
|
||||||
|
3. Vue.js inizializza l'applicazione
|
||||||
|
4. Service Worker cachea tutti gli asset
|
||||||
|
5. App pronta (nessuna ulteriore chiamata al server)
|
||||||
|
|
||||||
|
**Visite successive:**
|
||||||
|
1. Service Worker serve i file dalla cache locale
|
||||||
|
2. App si avvia istantaneamente anche senza internet
|
||||||
|
3. In background verifica se esistono aggiornamenti
|
||||||
|
4. Aggiornamenti applicati al prossimo refresh
|
||||||
|
|
||||||
|
#### Gestione Stato
|
||||||
|
|
||||||
|
Tutto lo stato della partita vive nella memoria del browser:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
sp: {
|
||||||
|
punt: { home: 0, guest: 0 },
|
||||||
|
set: { home: 0, guest: 0 },
|
||||||
|
servHome: true,
|
||||||
|
form: { home: ["1","2","3","4","5","6"], guest: ["1","2","3","4","5","6"] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Limitazioni:**
|
||||||
|
- Refresh della pagina azzera tutto lo stato
|
||||||
|
- Nessuna sincronizzazione tra dispositivi
|
||||||
|
- Nessuno storico partite
|
||||||
|
|
||||||
|
**Vantaggi:**
|
||||||
|
- Privacy totale (nessun dato esce dal dispositivo)
|
||||||
|
- Velocità massima (nessuna latenza di rete)
|
||||||
|
- Funzionamento offline completo
|
||||||
|
|
||||||
|
**Possibili evoluzioni:**
|
||||||
|
- Persistenza con localStorage
|
||||||
|
- Backend con database per multi-dispositivo
|
||||||
|
- WebSocket per sincronizzazione real-time
|
||||||
|
|
||||||
|
### Note Tecniche
|
||||||
|
|
||||||
|
**Problemi noti:**
|
||||||
|
- Audio mobile: sintesi vocale non funziona su alcuni dispositivi Android
|
||||||
|
- Persistenza: lo stato si perde al refresh della pagina
|
||||||
|
- Codice legacy: presenza di file HomePage duplicati
|
||||||
|
|
||||||
|
**Architettura futura:**
|
||||||
|
- Stato centralizzato con Pinia/Vuex
|
||||||
|
- Architettura client-server con WebSocket per display remoto
|
||||||
|
- Persistenza con localStorage o database
|
||||||
|
- Testing con Vitest e Cypress
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Informazioni sul Progetto
|
||||||
|
|
||||||
|
**Nome progetto**: Segnapunti Anto
|
||||||
|
**Descrizione**: Applicazione web PWA per tracciare i punteggi di partite di pallavolo in tempo reale
|
||||||
|
**Sviluppato per**: Team Antoniana
|
||||||
|
**Licenza**: Privata
|
||||||
|
**Repository**: https://github.com/[username]/segnapunti
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Come Leggere Questo Changelog
|
||||||
|
|
||||||
|
- **Funzionalità Principali**: nuove caratteristiche aggiunte all'applicazione
|
||||||
|
- **Tecnologie e Dipendenze**: stack tecnologico e versioni utilizzate
|
||||||
|
- **Build e Comandi**: istruzioni per compilare ed eseguire il progetto
|
||||||
|
- **Documentazione**: miglioramenti alla documentazione utente e sviluppatore
|
||||||
|
- **Miglioramenti Architetturali**: refactoring e ottimizzazioni del codice
|
||||||
|
- **Note di Sviluppo**: work in progress e problemi noti
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Supporto e Contributi
|
||||||
|
|
||||||
|
Per segnalare bug o richiedere funzionalità, aprire una issue nel repository del progetto.
|
||||||
|
|
||||||
|
Per contribuire al progetto:
|
||||||
|
1. Fork del repository
|
||||||
|
2. Creare un branch per la feature (`git checkout -b feature/nome-feature`)
|
||||||
|
3. Commit delle modifiche (`git commit -m 'Aggiunge nome-feature'`)
|
||||||
|
4. Push al branch (`git push origin feature/nome-feature`)
|
||||||
|
5. Aprire una Pull Request
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Data primo commit**: 2024
|
||||||
|
**Data rilascio 1.0.0**: 2026-02-10
|
||||||
256
README.md
@@ -1,7 +1,255 @@
|
|||||||
# Vue 3 + Vite
|
# Segnapunti Anto
|
||||||
|
|
||||||
This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
|
Applicazione web **Progressive Web App (PWA)** per tracciare i punteggi di partite di pallavolo in tempo reale.
|
||||||
|
|
||||||
## Recommended IDE Setup
|
---
|
||||||
|
|
||||||
- [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
|
## Panoramica
|
||||||
|
|
||||||
|
**Segnapunti Anto** è un'applicazione digitale per il tracciamento dei punteggi durante partite di pallavolo, ottimizzata per l'uso su tablet e smartphone. Sviluppata per il team Antoniana, l'app fornisce un'interfaccia fullscreen touch-friendly con supporto offline e controlli da tastiera.
|
||||||
|
|
||||||
|
### Funzionalità Principali
|
||||||
|
|
||||||
|
- **Gestione Completa Partite**
|
||||||
|
- Tracciamento punti in tempo reale per entrambe le squadre
|
||||||
|
- Conteggio automatico dei set (modalità 2/3 o 3/5)
|
||||||
|
- Indicatore visivo del servizio
|
||||||
|
- Blocco incremento punti a set concluso
|
||||||
|
- Cronologia punti con striscia visiva
|
||||||
|
|
||||||
|
- **Formazioni Squadra**
|
||||||
|
- Visualizzazione interattiva dei 6 giocatori in campo
|
||||||
|
- Rotazione automatica regolamentare al cambio palla
|
||||||
|
- Configurazione manuale dei numeri di maglia
|
||||||
|
- Dialog cambi con uno o due cambi (IN → OUT) e validazioni
|
||||||
|
- Supporto logica pallavolo ufficiale (25 punti + 2 di vantaggio, tie-break a 15 nel set decisivo)
|
||||||
|
|
||||||
|
- **Controlli Multimodali**
|
||||||
|
- Scorciatoie da tastiera complete (vedi sezione [Shortcuts](#shortcuts))
|
||||||
|
- Sintesi vocale per annunci punteggio in italiano (Web Speech API)
|
||||||
|
|
||||||
|
- **Personalizzazione**
|
||||||
|
- Configurazione dinamica nomi squadre
|
||||||
|
- Selettore modalità partita: al meglio di 3 o al meglio di 5
|
||||||
|
- Toggle layout orizzontale (inverti home/guest)
|
||||||
|
- Modalità visualizzazione: punteggio semplice o formazioni complete
|
||||||
|
- Nascondi/mostra controlli e cronologia
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Requisiti
|
||||||
|
|
||||||
|
### Requisiti di Sistema
|
||||||
|
|
||||||
|
#### Per Sviluppo
|
||||||
|
- **Sistema Operativo**: Linux, macOS, Windows (WSL2 consigliato)
|
||||||
|
- **Node.js**: v20.2.0 o superiore (LTS consigliato)
|
||||||
|
- **npm**: v9.0.0 o superiore (incluso con Node.js)
|
||||||
|
- **RAM**: Minimo 2GB, consigliato 4GB
|
||||||
|
- **Spazio Disco**: ~500MB per dipendenze e build
|
||||||
|
|
||||||
|
#### Per Deployment
|
||||||
|
- **Server Web**: Qualsiasi server statico (nginx, Apache, Vercel, Netlify)
|
||||||
|
- **HTTPS**: Obbligatorio per Service Worker e PWA (eccetto localhost)
|
||||||
|
- **Connessione Internet**: Solo per primo caricamento (poi funziona offline)
|
||||||
|
|
||||||
|
### Requisiti Browser (Utente Finale)
|
||||||
|
|
||||||
|
| Requisito | Dettaglio | Necessità |
|
||||||
|
|-----------|-----------|-----------|
|
||||||
|
| **JavaScript ES6+** | Supporto moduli, arrow functions, async/await | Obbligatorio |
|
||||||
|
| **Service Worker API** | Per funzionalità offline PWA | Obbligatorio |
|
||||||
|
| **Fullscreen API** | Per modalità schermo intero | Consigliato |
|
||||||
|
| **Web Speech API** | Per sintesi vocale punteggi | Opzionale |
|
||||||
|
| **Local Storage** | Per persistenza configurazioni | Consigliato |
|
||||||
|
|
||||||
|
### Browser Testati e Supportati
|
||||||
|
|
||||||
|
| Browser | Versione Minima | Supporto | Note |
|
||||||
|
|---------|-----------------|----------|------|
|
||||||
|
| Chrome/Chromium | 90+ | ✅ Completo | Consigliato per tutte le features |
|
||||||
|
| Firefox | 88+ | ✅ Completo | Supporto completo PWA e Speech API |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Installazione e Setup
|
||||||
|
|
||||||
|
### Prerequisiti
|
||||||
|
|
||||||
|
- **Node.js** v20.2.0 (consigliato)
|
||||||
|
- **npm** o **yarn**
|
||||||
|
|
||||||
|
### Installazione con NVM (consigliato)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Installa la versione corretta di Node.js
|
||||||
|
nvm install v20.2.0
|
||||||
|
nvm use v20.2.0
|
||||||
|
|
||||||
|
# Clona il repository
|
||||||
|
git clone <repository-url>
|
||||||
|
cd segnapunti
|
||||||
|
|
||||||
|
# Installa le dipendenze
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Comandi per Sviluppo
|
||||||
|
|
||||||
|
### Dev Server
|
||||||
|
|
||||||
|
Avvia il server di sviluppo con hot-reload:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
L'applicazione sarà disponibile su [http://localhost:5173](http://localhost:5173)
|
||||||
|
|
||||||
|
### Modalità Sviluppo
|
||||||
|
- Hot Module Replacement (HMR) attivo
|
||||||
|
- Source maps per debugging
|
||||||
|
- Vue DevTools supportato
|
||||||
|
- Errori e warnings in console
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Comandi per Build
|
||||||
|
|
||||||
|
### Build Produzione
|
||||||
|
|
||||||
|
Genera i file ottimizzati per il deployment:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
**Output:**
|
||||||
|
- Cartella `/dist` con file statici ottimizzati
|
||||||
|
- Service Worker generato automaticamente
|
||||||
|
- PWA manifest configurato
|
||||||
|
- Assets minificati e con hash per cache busting
|
||||||
|
- Base path: `/segnap` (modificabile in `vite.config.js`)
|
||||||
|
|
||||||
|
### Preview Build
|
||||||
|
|
||||||
|
Anteprima locale della build di produzione:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run preview
|
||||||
|
```
|
||||||
|
|
||||||
|
Serve i file dalla cartella `/dist` per testare la build prima del deploy.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Shortcuts
|
||||||
|
|
||||||
|
### Controlli Tastiera Squadra Home
|
||||||
|
|
||||||
|
| Scorciatoia | Azione |
|
||||||
|
|-------------|--------|
|
||||||
|
| `Ctrl + ↑` | Incrementa punti |
|
||||||
|
| `Ctrl + ↓` | Decrementa punti |
|
||||||
|
| `Ctrl + →` | Incrementa set |
|
||||||
|
| `Ctrl + C` | Apri dialog cambi |
|
||||||
|
|
||||||
|
### Controlli Tastiera Squadra Guest
|
||||||
|
|
||||||
|
| Scorciatoia | Azione |
|
||||||
|
|-------------|--------|
|
||||||
|
| `Shift + ↑` | Incrementa punti |
|
||||||
|
| `Shift + ↓` | Decrementa punti |
|
||||||
|
| `Shift + →` | Incrementa set |
|
||||||
|
| `Shift + C` | Apri dialog cambi |
|
||||||
|
|
||||||
|
### Comandi Globali
|
||||||
|
|
||||||
|
| Scorciatoia | Azione |
|
||||||
|
|-------------|--------|
|
||||||
|
| `Ctrl + ←` | Cambio palla (servizio) - **solo a 0-0** |
|
||||||
|
| `Ctrl + M` | Apri configurazione nomi squadre e formazioni |
|
||||||
|
| `Ctrl + B` | Toggle visibilità barra pulsanti |
|
||||||
|
| `Ctrl + F` | Attiva/disattiva fullscreen |
|
||||||
|
| `Ctrl + S` | Annuncio vocale punteggio corrente |
|
||||||
|
| `Ctrl + Z` | Switch tra visualizzazione formazioni e punteggio |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Configurazione PWA
|
||||||
|
|
||||||
|
L'applicazione è configurata come **Progressive Web App** nel file [vite.config.js](vite.config.js):
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
VitePWA({
|
||||||
|
registerType: 'autoUpdate',
|
||||||
|
manifest: {
|
||||||
|
name: "app_segnap",
|
||||||
|
short_name: "segnap",
|
||||||
|
description: "Segnapunti standalone.",
|
||||||
|
background_color: "#eee",
|
||||||
|
theme_color: '#ffffff',
|
||||||
|
display: "fullscreen",
|
||||||
|
orientation: "landscape",
|
||||||
|
icons: [
|
||||||
|
{ src: 'segnap-192x192.png', sizes: '192x192', type: 'image/png' },
|
||||||
|
{ src: 'segnap-512x512.png', sizes: '512x512', type: 'image/png' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Caratteristiche PWA
|
||||||
|
|
||||||
|
- **Display**: Fullscreen per massimizzare lo spazio visivo
|
||||||
|
- **Orientamento**: Landscape (orizzontale) ottimizzato per tablet
|
||||||
|
- **Auto-update**: Service Worker con aggiornamento automatico
|
||||||
|
- **Offline**: Funzionamento completo senza connessione internet
|
||||||
|
- **Installabile**: Aggiungibile alla home screen come app nativa
|
||||||
|
|
||||||
|
### Installazione PWA
|
||||||
|
|
||||||
|
**Android/Desktop (Chrome):**
|
||||||
|
- Menu → "Installa app" o icona (⊕) nella barra degli indirizzi
|
||||||
|
|
||||||
|
**iOS (Safari):**
|
||||||
|
- Share (□↑) → "Aggiungi a Home"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Logica Regolamentare Pallavolo
|
||||||
|
|
||||||
|
### Vittoria Set
|
||||||
|
|
||||||
|
- **Set regolari (1-4)**: Primo a 25 punti con almeno 2 di vantaggio
|
||||||
|
- **Set decisivo**:
|
||||||
|
- Modalità 2/3: 3° set a 15 punti con almeno 2 di vantaggio
|
||||||
|
- Modalità 3/5: 5° set a 15 punti con almeno 2 di vantaggio
|
||||||
|
- **Blocco automatico**: Non consente assegnare punti oltre la vittoria
|
||||||
|
|
||||||
|
### Rotazione Formazione
|
||||||
|
|
||||||
|
La rotazione avviene **automaticamente** quando:
|
||||||
|
1. La squadra **conquista il servizio** (cambio palla)
|
||||||
|
2. Il punteggio è diverso da 0-0
|
||||||
|
|
||||||
|
**Limitazione cambio palla manuale:**
|
||||||
|
- Il cambio manuale del servizio (`Ctrl + ←`) è consentito **solo a 0-0**
|
||||||
|
- Questa limitazione previene errori nella rotazione delle formazioni
|
||||||
|
|
||||||
|
### Formazione in Campo
|
||||||
|
|
||||||
|
Visualizzazione a 6 posizioni standard:
|
||||||
|
|
||||||
|
```
|
||||||
|
Rete
|
||||||
|
┌─────┬─────┬─────┐
|
||||||
|
│ 4 │ 3 │ 2 │ ← Fila anteriore
|
||||||
|
├─────┼─────┼─────┤
|
||||||
|
│ 5 │ 6 │ 1 │ ← Fila posteriore
|
||||||
|
└─────┴─────┴─────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
La rotazione avviene in senso orario: 1→6→5→4→3→2→1
|
||||||
|
|||||||
1
dev-dist/registerSW.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
if('serviceWorker' in navigator) navigator.serviceWorker.register('/dev-sw.js?dev-sw', { scope: '/', type: 'classic' })
|
||||||
92
dev-dist/sw.js
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2018 Google Inc. All Rights Reserved.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// If the loader is already loaded, just stop.
|
||||||
|
if (!self.define) {
|
||||||
|
let registry = {};
|
||||||
|
|
||||||
|
// Used for `eval` and `importScripts` where we can't get script URL by other means.
|
||||||
|
// In both cases, it's safe to use a global var because those functions are synchronous.
|
||||||
|
let nextDefineUri;
|
||||||
|
|
||||||
|
const singleRequire = (uri, parentUri) => {
|
||||||
|
uri = new URL(uri + ".js", parentUri).href;
|
||||||
|
return registry[uri] || (
|
||||||
|
|
||||||
|
new Promise(resolve => {
|
||||||
|
if ("document" in self) {
|
||||||
|
const script = document.createElement("script");
|
||||||
|
script.src = uri;
|
||||||
|
script.onload = resolve;
|
||||||
|
document.head.appendChild(script);
|
||||||
|
} else {
|
||||||
|
nextDefineUri = uri;
|
||||||
|
importScripts(uri);
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
.then(() => {
|
||||||
|
let promise = registry[uri];
|
||||||
|
if (!promise) {
|
||||||
|
throw new Error(`Module ${uri} didn’t register its module`);
|
||||||
|
}
|
||||||
|
return promise;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
self.define = (depsNames, factory) => {
|
||||||
|
const uri = nextDefineUri || ("document" in self ? document.currentScript.src : "") || location.href;
|
||||||
|
if (registry[uri]) {
|
||||||
|
// Module is already loading or loaded.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let exports = {};
|
||||||
|
const require = depUri => singleRequire(depUri, uri);
|
||||||
|
const specialDeps = {
|
||||||
|
module: { uri },
|
||||||
|
exports,
|
||||||
|
require
|
||||||
|
};
|
||||||
|
registry[uri] = Promise.all(depsNames.map(
|
||||||
|
depName => specialDeps[depName] || require(depName)
|
||||||
|
)).then(deps => {
|
||||||
|
factory(...deps);
|
||||||
|
return exports;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
define(['./workbox-5357ef54'], (function (workbox) { 'use strict';
|
||||||
|
|
||||||
|
self.skipWaiting();
|
||||||
|
workbox.clientsClaim();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The precacheAndRoute() method efficiently caches and responds to
|
||||||
|
* requests for URLs in the manifest.
|
||||||
|
* See https://goo.gl/S9QRab
|
||||||
|
*/
|
||||||
|
workbox.precacheAndRoute([{
|
||||||
|
"url": "registerSW.js",
|
||||||
|
"revision": "3ca0b8505b4bec776b69afdba2768812"
|
||||||
|
}, {
|
||||||
|
"revision": null,
|
||||||
|
"url": "index.html"
|
||||||
|
}], {});
|
||||||
|
workbox.cleanupOutdatedCaches();
|
||||||
|
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), {
|
||||||
|
allowlist: [/^\/$/]
|
||||||
|
}));
|
||||||
|
|
||||||
|
}));
|
||||||
3394
dev-dist/workbox-5357ef54.js
Normal file
@@ -4,7 +4,7 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Vite + Vue</title>
|
<title>Segnapunti - Anto</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
|||||||
310
package-lock.json
generated
@@ -9,7 +9,8 @@
|
|||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nosleep.js": "^0.12.0",
|
"nosleep.js": "^0.12.0",
|
||||||
"vue": "^3.2.47"
|
"vue": "^3.2.47",
|
||||||
|
"wave-ui": "^3.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "^4.1.0",
|
"@vitejs/plugin-vue": "^4.1.0",
|
||||||
@@ -496,9 +497,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/plugin-proposal-private-property-in-object": {
|
"node_modules/@babel/plugin-proposal-private-property-in-object": {
|
||||||
"version": "7.21.0",
|
"version": "7.21.11",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz",
|
||||||
"integrity": "sha512-ha4zfehbJjc5MmXBlHec1igel5TJXXLDDRbuJ4+XT2TJcyD9/V1919BA8gMvsdHcNMBy4WBUBiRb3nw/EQUtBw==",
|
"integrity": "sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/helper-annotate-as-pure": "^7.18.6",
|
"@babel/helper-annotate-as-pure": "^7.18.6",
|
||||||
@@ -2163,6 +2164,94 @@
|
|||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@rollup/plugin-babel": {
|
||||||
|
"version": "5.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz",
|
||||||
|
"integrity": "sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/helper-module-imports": "^7.10.4",
|
||||||
|
"@rollup/pluginutils": "^3.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@babel/core": "^7.0.0",
|
||||||
|
"@types/babel__core": "^7.1.9",
|
||||||
|
"rollup": "^1.20.0||^2.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/babel__core": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/plugin-node-resolve": {
|
||||||
|
"version": "11.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.1.tgz",
|
||||||
|
"integrity": "sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@rollup/pluginutils": "^3.1.0",
|
||||||
|
"@types/resolve": "1.17.1",
|
||||||
|
"builtin-modules": "^3.1.0",
|
||||||
|
"deepmerge": "^4.2.2",
|
||||||
|
"is-module": "^1.0.0",
|
||||||
|
"resolve": "^1.19.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"rollup": "^1.20.0||^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/plugin-replace": {
|
||||||
|
"version": "2.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-2.4.2.tgz",
|
||||||
|
"integrity": "sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@rollup/pluginutils": "^3.1.0",
|
||||||
|
"magic-string": "^0.25.7"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"rollup": "^1.20.0 || ^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/plugin-replace/node_modules/magic-string": {
|
||||||
|
"version": "0.25.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
|
||||||
|
"integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"sourcemap-codec": "^1.4.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/pluginutils": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/estree": "0.0.39",
|
||||||
|
"estree-walker": "^1.0.1",
|
||||||
|
"picomatch": "^2.2.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"rollup": "^1.20.0||^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/pluginutils/node_modules/estree-walker": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/@surma/rollup-plugin-off-main-thread": {
|
"node_modules/@surma/rollup-plugin-off-main-thread": {
|
||||||
"version": "2.2.3",
|
"version": "2.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz",
|
||||||
@@ -2452,13 +2541,12 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/brace-expansion": {
|
"node_modules/brace-expansion": {
|
||||||
"version": "1.1.11",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
||||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"balanced-match": "^1.0.0",
|
"balanced-match": "^1.0.0"
|
||||||
"concat-map": "0.0.1"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/braces": {
|
"node_modules/braces": {
|
||||||
@@ -2537,9 +2625,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/caniuse-lite": {
|
"node_modules/caniuse-lite": {
|
||||||
"version": "1.0.30001492",
|
"version": "1.0.30001495",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001492.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001495.tgz",
|
||||||
"integrity": "sha512-2efF8SAZwgAX1FJr87KWhvuJxnGJKOnctQa8xLOskAXNXq8oiuqgl6u1kk3fFpsp3GgvzlRjiK1sl63hNtFADw==",
|
"integrity": "sha512-F6x5IEuigtUfU5ZMQK2jsy5JqUUlEFRVZq8bO2a+ysq5K7jD6PPc9YXZj78xDNS3uNchesp1Jw47YXEqr+Viyg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -2697,9 +2785,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/electron-to-chromium": {
|
"node_modules/electron-to-chromium": {
|
||||||
"version": "1.4.416",
|
"version": "1.4.423",
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.416.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.423.tgz",
|
||||||
"integrity": "sha512-AUYh0XDTb2vrj0rj82jb3P9hHSyzQNdTPYWZIhPdCOui7/vpme7+HTE07BE5jwuqg/34TZ8ktlRz6GImJ4IXjA==",
|
"integrity": "sha512-y4A7YfQcDGPAeSWM1IuoWzXpg9RY1nwHzHSwRtCSQFp9FgAVDgdWlFf0RbdWfLWQ2WUI+bddUgk5RgTjqRE6FQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/es-abstract": {
|
"node_modules/es-abstract": {
|
||||||
@@ -2896,15 +2984,6 @@
|
|||||||
"minimatch": "^5.0.1"
|
"minimatch": "^5.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/filelist/node_modules/brace-expansion": {
|
|
||||||
"version": "2.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
|
||||||
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"balanced-match": "^1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/filelist/node_modules/minimatch": {
|
"node_modules/filelist/node_modules/minimatch": {
|
||||||
"version": "5.1.6",
|
"version": "5.1.6",
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
|
||||||
@@ -3779,6 +3858,16 @@
|
|||||||
"node": "*"
|
"node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/minimatch/node_modules/brace-expansion": {
|
||||||
|
"version": "1.1.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||||
|
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"balanced-match": "^1.0.0",
|
||||||
|
"concat-map": "0.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ms": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
@@ -3810,7 +3899,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/nosleep.js": {
|
"node_modules/nosleep.js": {
|
||||||
"version": "0.12.0",
|
"version": "0.12.0",
|
||||||
"resolved": "https://registry.npmjs.org/nosleep.js/-/nosleep.js-0.12.0.tgz",
|
|
||||||
"integrity": "sha512-9d1HbpKLh3sdWlhXMhU6MMH+wQzKkrgfRkYV0EBdvt99YJfj0ilCJrWRDYG2130Tm4GXbEoTCx5b34JSaP+HhA=="
|
"integrity": "sha512-9d1HbpKLh3sdWlhXMhU6MMH+wQzKkrgfRkYV0EBdvt99YJfj0ilCJrWRDYG2130Tm4GXbEoTCx5b34JSaP+HhA=="
|
||||||
},
|
},
|
||||||
"node_modules/object-inspect": {
|
"node_modules/object-inspect": {
|
||||||
@@ -4092,21 +4180,36 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/rollup": {
|
"node_modules/rollup": {
|
||||||
"version": "3.23.0",
|
"version": "2.79.1",
|
||||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.23.0.tgz",
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz",
|
||||||
"integrity": "sha512-h31UlwEi7FHihLe1zbk+3Q7z1k/84rb9BSwmBSr/XjOCEaBJ2YyedQDuM0t/kfOS0IxM+vk1/zI9XxYj9V+NJQ==",
|
"integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"rollup": "dist/bin/rollup"
|
"rollup": "dist/bin/rollup"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14.18.0",
|
"node": ">=10.0.0"
|
||||||
"npm": ">=8.0.0"
|
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"fsevents": "~2.3.2"
|
"fsevents": "~2.3.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/rollup-plugin-terser": {
|
||||||
|
"version": "7.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz",
|
||||||
|
"integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==",
|
||||||
|
"deprecated": "This package has been deprecated and is no longer maintained. Please use @rollup/plugin-terser",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/code-frame": "^7.10.4",
|
||||||
|
"jest-worker": "^26.2.1",
|
||||||
|
"serialize-javascript": "^4.0.0",
|
||||||
|
"terser": "^5.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"rollup": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/run-parallel": {
|
"node_modules/run-parallel": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
|
||||||
@@ -4629,7 +4732,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/vite-plugin-pwa": {
|
"node_modules/vite-plugin-pwa": {
|
||||||
"version": "0.16.0",
|
"version": "0.16.0",
|
||||||
"resolved": "https://registry.npmjs.org/vite-plugin-pwa/-/vite-plugin-pwa-0.16.0.tgz",
|
|
||||||
"integrity": "sha512-E+AQRzHxqNU4ZhEeR8X37/foZB+ezJEhXauE/mcf1UITY6k2Pa1dtlFl+BQu57fTdiVlWim5S0Qy44Yap93Dkg==",
|
"integrity": "sha512-E+AQRzHxqNU4ZhEeR8X37/foZB+ezJEhXauE/mcf1UITY6k2Pa1dtlFl+BQu57fTdiVlWim5S0Qy44Yap93Dkg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -4651,6 +4753,22 @@
|
|||||||
"workbox-window": "^7.0.0"
|
"workbox-window": "^7.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/vite/node_modules/rollup": {
|
||||||
|
"version": "3.26.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.26.3.tgz",
|
||||||
|
"integrity": "sha512-7Tin0C8l86TkpcMtXvQu6saWH93nhG3dGQ1/+l5V2TDMceTxO7kDiK6GzbfLWNNxqJXm591PcEZUozZm51ogwQ==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"rollup": "dist/bin/rollup"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.18.0",
|
||||||
|
"npm": ">=8.0.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"fsevents": "~2.3.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/vue": {
|
"node_modules/vue": {
|
||||||
"version": "3.3.4",
|
"version": "3.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/vue/-/vue-3.3.4.tgz",
|
||||||
@@ -4663,6 +4781,17 @@
|
|||||||
"@vue/shared": "3.3.4"
|
"@vue/shared": "3.3.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/wave-ui": {
|
||||||
|
"version": "3.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/wave-ui/-/wave-ui-3.3.0.tgz",
|
||||||
|
"integrity": "sha512-z4hBt/tOFMwG3S+pNE1+is+6diSSgll7zeYOwr84v4+mdE1o+u1M4zTRwqYx1NvLE9DqeXD3iplCjhrxEjsziA==",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/antoniandre"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"vue": "^2.6.14 || ^3.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/webidl-conversions": {
|
"node_modules/webidl-conversions": {
|
||||||
"version": "4.0.2",
|
"version": "4.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
|
||||||
@@ -4783,94 +4912,6 @@
|
|||||||
"node": ">=16.0.0"
|
"node": ">=16.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/workbox-build/node_modules/@rollup/plugin-babel": {
|
|
||||||
"version": "5.3.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz",
|
|
||||||
"integrity": "sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/helper-module-imports": "^7.10.4",
|
|
||||||
"@rollup/pluginutils": "^3.1.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 10.0.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@babel/core": "^7.0.0",
|
|
||||||
"@types/babel__core": "^7.1.9",
|
|
||||||
"rollup": "^1.20.0||^2.0.0"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"@types/babel__core": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/workbox-build/node_modules/@rollup/plugin-node-resolve": {
|
|
||||||
"version": "11.2.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.1.tgz",
|
|
||||||
"integrity": "sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@rollup/pluginutils": "^3.1.0",
|
|
||||||
"@types/resolve": "1.17.1",
|
|
||||||
"builtin-modules": "^3.1.0",
|
|
||||||
"deepmerge": "^4.2.2",
|
|
||||||
"is-module": "^1.0.0",
|
|
||||||
"resolve": "^1.19.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 10.0.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"rollup": "^1.20.0||^2.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/workbox-build/node_modules/@rollup/plugin-replace": {
|
|
||||||
"version": "2.4.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-2.4.2.tgz",
|
|
||||||
"integrity": "sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@rollup/pluginutils": "^3.1.0",
|
|
||||||
"magic-string": "^0.25.7"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"rollup": "^1.20.0 || ^2.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/workbox-build/node_modules/@rollup/pluginutils": {
|
|
||||||
"version": "3.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz",
|
|
||||||
"integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@types/estree": "0.0.39",
|
|
||||||
"estree-walker": "^1.0.1",
|
|
||||||
"picomatch": "^2.2.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 8.0.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"rollup": "^1.20.0||^2.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/workbox-build/node_modules/estree-walker": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz",
|
|
||||||
"integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/workbox-build/node_modules/magic-string": {
|
|
||||||
"version": "0.25.9",
|
|
||||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
|
|
||||||
"integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"sourcemap-codec": "^1.4.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/workbox-build/node_modules/pretty-bytes": {
|
"node_modules/workbox-build/node_modules/pretty-bytes": {
|
||||||
"version": "5.6.0",
|
"version": "5.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz",
|
||||||
@@ -4883,37 +4924,6 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/workbox-build/node_modules/rollup": {
|
|
||||||
"version": "2.79.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz",
|
|
||||||
"integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==",
|
|
||||||
"dev": true,
|
|
||||||
"bin": {
|
|
||||||
"rollup": "dist/bin/rollup"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10.0.0"
|
|
||||||
},
|
|
||||||
"optionalDependencies": {
|
|
||||||
"fsevents": "~2.3.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/workbox-build/node_modules/rollup-plugin-terser": {
|
|
||||||
"version": "7.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz",
|
|
||||||
"integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==",
|
|
||||||
"deprecated": "This package has been deprecated and is no longer maintained. Please use @rollup/plugin-terser",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/code-frame": "^7.10.4",
|
|
||||||
"jest-worker": "^26.2.1",
|
|
||||||
"serialize-javascript": "^4.0.0",
|
|
||||||
"terser": "^5.0.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"rollup": "^2.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/workbox-cacheable-response": {
|
"node_modules/workbox-cacheable-response": {
|
||||||
"version": "7.0.0",
|
"version": "7.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-7.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-7.0.0.tgz",
|
||||||
|
|||||||
@@ -10,7 +10,8 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nosleep.js": "^0.12.0",
|
"nosleep.js": "^0.12.0",
|
||||||
"vue": "^3.2.47"
|
"vue": "^3.2.47",
|
||||||
|
"wave-ui": "^3.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "^4.1.0",
|
"@vitejs/plugin-vue": "^4.1.0",
|
||||||
|
|||||||
BIN
public/antoniana.jpg
Normal file
|
After Width: | Height: | Size: 132 KiB |
BIN
public/exit.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
public/gear.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
public/segnap-192x192.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
public/segnap-512x512.png
Normal file
|
After Width: | Height: | Size: 124 KiB |
BIN
public/serv.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
public/speaker.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
@@ -1,5 +1,5 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import HomePage from './components/HomePage.vue'
|
import HomePage from './components/HomePage/index.vue'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
BIN
src/assets/serve.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
@@ -1,40 +0,0 @@
|
|||||||
<script setup>
|
|
||||||
import { ref } from 'vue'
|
|
||||||
|
|
||||||
defineProps({
|
|
||||||
msg: String,
|
|
||||||
})
|
|
||||||
|
|
||||||
const count = ref(0)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<h1>{{ msg }}</h1>
|
|
||||||
|
|
||||||
<div class="card">
|
|
||||||
<button type="button" @click="count++">count is {{ count }}</button>
|
|
||||||
<p>
|
|
||||||
Edit
|
|
||||||
<code>components/HelloWorld.vue</code> to test HMR
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Check out
|
|
||||||
<a href="https://vuejs.org/guide/quick-start.html#local" target="_blank"
|
|
||||||
>create-vue</a
|
|
||||||
>, the official Vue + Vite starter
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Install
|
|
||||||
<a href="https://github.com/vuejs/language-tools" target="_blank">Volar</a>
|
|
||||||
in your IDE for a better DX
|
|
||||||
</p>
|
|
||||||
<p class="read-the-docs">Click on the Vite and Vue logos to learn more</p>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.read-the-docs {
|
|
||||||
color: #888;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -5,9 +5,19 @@ export default {
|
|||||||
components: {},
|
components: {},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
voices: null,
|
||||||
|
diaNomi: {
|
||||||
|
show: false,
|
||||||
|
home: "",
|
||||||
|
guest: "",
|
||||||
|
},
|
||||||
|
visuForm: false,
|
||||||
|
visuButt: true,
|
||||||
sp: {
|
sp: {
|
||||||
|
servHome: true,
|
||||||
punt: { home: 0, guest: 0 },
|
punt: { home: 0, guest: 0 },
|
||||||
set: { home: 0, guest: 0 },
|
set: { home: 0, guest: 0 },
|
||||||
|
nomi: { home: "Antoniana", guest: "Guest" },
|
||||||
form: {
|
form: {
|
||||||
home: ["1", "2", "3", "4", "5", "6"],
|
home: ["1", "2", "3", "4", "5", "6"],
|
||||||
guest: ["1", "2", "3", "4", "5", "6"],
|
guest: ["1", "2", "3", "4", "5", "6"],
|
||||||
@@ -15,13 +25,21 @@ export default {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted() {
|
||||||
|
this.voices = window.speechSynthesis.getVoices();
|
||||||
if (this.isMobile()) {
|
if (this.isMobile()) {
|
||||||
|
this.speak();
|
||||||
var noSleep = new NoSleep();
|
var noSleep = new NoSleep();
|
||||||
noSleep.enable();
|
noSleep.enable();
|
||||||
|
document.documentElement.requestFullscreen();
|
||||||
}
|
}
|
||||||
|
this.abilitaTastiSpeciali();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
closeApp() {
|
||||||
|
var win = window.open("", "_self");
|
||||||
|
win.close();
|
||||||
|
},
|
||||||
fullScreen() {
|
fullScreen() {
|
||||||
document.documentElement.requestFullscreen();
|
document.documentElement.requestFullscreen();
|
||||||
},
|
},
|
||||||
@@ -37,40 +55,166 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
resetta() {
|
resetta() {
|
||||||
if (confirm("Confermi il reset del punteggio ?")) {
|
this.$waveui.notify("Punteggio<br />RESETTATO", "success");
|
||||||
|
this.visuForm = false;
|
||||||
this.sp.punt.home = 0;
|
this.sp.punt.home = 0;
|
||||||
this.sp.punt.guest = 0;
|
this.sp.punt.guest = 0;
|
||||||
this.sp.form = {
|
this.sp.form = {
|
||||||
home: ["1", "2", "3", "4", "5", "6"],
|
home: ["1", "2", "3", "4", "5", "6"],
|
||||||
guest: ["1", "2", "3", "4", "5", "6"],
|
guest: ["1", "2", "3", "4", "5", "6"],
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
incSet(team) {
|
||||||
|
if (this.sp.set[team] == 2) {
|
||||||
|
this.sp.set[team] = 0;
|
||||||
|
} else {
|
||||||
|
this.sp.set[team]++;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
incPunt(team) {
|
incPunt(team) {
|
||||||
this.sp.punt[team]++;
|
this.sp.punt[team]++;
|
||||||
|
this.sp.servHome = (team == "home");
|
||||||
this.sp.form[team].push(this.sp.form[team].shift());
|
this.sp.form[team].push(this.sp.form[team].shift());
|
||||||
},
|
},
|
||||||
decPunt(team) {
|
decPunt(team) {
|
||||||
|
// decrementa il punteggio se è > 0.
|
||||||
if (this.sp.punt[team] > 0) {
|
if (this.sp.punt[team] > 0) {
|
||||||
this.sp.punt[team]--;
|
this.sp.punt[team]--;
|
||||||
this.sp.form[team].unshift(this.sp.form[team].pop());
|
this.sp.form[team].unshift(this.sp.form[team].pop());
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
speak() {
|
||||||
|
const msg = new SpeechSynthesisUtterance();
|
||||||
|
if (this.sp.punt.home + this.sp.punt.guest == 0) {
|
||||||
|
msg.text = "zero a zero";
|
||||||
|
} else if (this.sp.punt.home == this.sp.punt.guest) {
|
||||||
|
msg.text = this.sp.punt.home + " pari";
|
||||||
|
} else {
|
||||||
|
if (this.sp.servHome) {
|
||||||
|
msg.text = this.sp.punt.home + " a " + this.sp.punt.guest;
|
||||||
|
} else {
|
||||||
|
msg.text = this.sp.punt.guest + " a " + this.sp.punt.home;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// msg.volume = 1.0; // speech volume (default: 1.0)
|
||||||
|
// msg.pitch = 1.0; // speech pitch (default: 1.0)
|
||||||
|
// msg.rate = 1.0; // speech rate (default: 1.0)
|
||||||
|
msg.lang = 'it_IT'; // speech language (default: 'en-US')
|
||||||
|
const voices = window.speechSynthesis.getVoices();
|
||||||
|
msg.voice = voices.find(voice => voice.name === 'Google italiano'); // voice URI (default: platform-dependent)
|
||||||
|
// msg.onboundary = function (event) {
|
||||||
|
// console.log('Speech reached a boundary:', event.name);
|
||||||
|
// };
|
||||||
|
// msg.onpause = function (event) {
|
||||||
|
// console.log('Speech paused:', event.utterance.text.substring(event.charIndex));
|
||||||
|
// };
|
||||||
|
window.speechSynthesis.speak(msg);
|
||||||
|
},
|
||||||
|
apriDialogConfig() {
|
||||||
|
this.disabilitaTastiSpeciali();
|
||||||
|
this.diaNomi.show = true;
|
||||||
|
},
|
||||||
|
disabilitaTastiSpeciali() {
|
||||||
|
window.removeEventListener("keydown", this.funzioneTastiSpeciali);
|
||||||
|
},
|
||||||
|
abilitaTastiSpeciali() {
|
||||||
|
window.addEventListener("keydown", this.funzioneTastiSpeciali);
|
||||||
|
},
|
||||||
|
funzioneTastiSpeciali(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (e.ctrlKey && e.key == "m") {
|
||||||
|
this.diaNomi.show = true
|
||||||
|
} else if (e.ctrlKey && e.key == "b") {
|
||||||
|
this.visuButt = !this.visuButt
|
||||||
|
} else if (e.ctrlKey && e.key == "f") {
|
||||||
|
document.documentElement.requestFullscreen();
|
||||||
|
} else if (e.ctrlKey && e.key == "s") {
|
||||||
|
this.speak();
|
||||||
|
} else if (e.ctrlKey && e.key == "z") {
|
||||||
|
this.visuForm = !this.visuForm
|
||||||
|
} else if (e.ctrlKey && e.key == "ArrowUp") {
|
||||||
|
this.incPunt("home")
|
||||||
|
} else if (e.ctrlKey && e.key == "ArrowDown") {
|
||||||
|
this.decPunt("home")
|
||||||
|
} else if (e.ctrlKey && e.key == "ArrowRight") {
|
||||||
|
this.incSet("home")
|
||||||
|
} else if (e.shiftKey && e.key == "ArrowUp") {
|
||||||
|
this.incPunt("guest")
|
||||||
|
} else if (e.shiftKey && e.key == "ArrowDown") {
|
||||||
|
this.decPunt("guest")
|
||||||
|
} else if (e.shiftKey && e.key == "ArrowRight") {
|
||||||
|
this.incSet("guest")
|
||||||
|
} else if (e.ctrlKey && e.key == "ArrowLeft") {
|
||||||
|
this.sp.servHome = !this.sp.servHome
|
||||||
|
} else { return false }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<w-dialog v-model="diaNomi.show" :width="500" @close="abilitaTastiSpeciali()">
|
||||||
|
<w-input v-model="sp.nomi.home" type="text" class="pa3">Home</w-input>
|
||||||
|
<w-input v-model="sp.nomi.guest" type="text" class="pa3">Guest</w-input>
|
||||||
|
<w-button bg-color="success" @click="diaNomi.show = false">
|
||||||
|
Ok
|
||||||
|
</w-button>
|
||||||
|
</w-dialog>
|
||||||
<div class="campo">
|
<div class="campo">
|
||||||
<div class="hea home" @click="decPunt('home')">HOME</div>
|
<div class="hea home">
|
||||||
<div class="hea guest" @click="decPunt('guest')">GUEST</div>
|
<span @click="decPunt('home')" :style="{ 'float': 'left' }">
|
||||||
<div class="col home" @click="incPunt('home')">{{ sp.punt.home }}</div>
|
{{ sp.nomi.home }} <img v-if="sp.servHome" src="/serv.png" width="25" />
|
||||||
<div class="col guest" @click="incPunt('guest')">{{ sp.punt.guest }}</div>
|
<span v-if="visuForm">{{ sp.punt.home }}</span>
|
||||||
<div class="bot tal">
|
</span>
|
||||||
<button @click="fullScreen">Fullscreen</button>
|
<span @click="incSet('home')" class="mr3" :style="{ 'float': 'right' }">set {{ sp.set.home }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="bot tar">
|
<div class="hea guest">
|
||||||
<button @click="resetta">RESET</button>
|
<span @click="decPunt('guest')" :style="{ 'float': 'right' }">
|
||||||
|
<img v-if="!sp.servHome" src="/serv.png" width="25" /> {{ sp.nomi.guest }}
|
||||||
|
<span v-if="visuForm">{{ sp.punt.guest }}</span>
|
||||||
|
</span>
|
||||||
|
<span @click="incSet('guest')" class="ml3" :style="{ 'float': 'left' }">set {{ sp.set.guest }}</span>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<span v-if="visuForm">
|
||||||
|
<div class="col form home" @click="incPunt('home')">
|
||||||
|
<div class="formdiv" v-for="x in [3, 2, 1, 4, 5, 0]">
|
||||||
|
{{ sp.form.home[x] }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col form guest" @click="incPunt('guest')">
|
||||||
|
<div class="formdiv" v-for="x in [3, 2, 1, 4, 5, 0]">
|
||||||
|
{{ sp.form.guest[x] }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
<span v-else>
|
||||||
|
<div class="col punt home" @click="incPunt('home')">{{ sp.punt.home }}</div>
|
||||||
|
<div class="col punt guest" @click="incPunt('guest')">{{ sp.punt.guest }}</div>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<div class="bot" v-if="visuButt">
|
||||||
|
<w-flex justify-space-between class="pa2">
|
||||||
|
<w-confirm right align-bottom v-if="isMobile()" question="CHIUDO ?" cancel="NO" confirm="SI" @confirm="closeApp">
|
||||||
|
<img src="/exit.png" width="25" />
|
||||||
|
</w-confirm>
|
||||||
|
<w-button @click="apriDialogConfig()">
|
||||||
|
<img src="/gear.png" width="25" />
|
||||||
|
</w-button>
|
||||||
|
<w-button @click="sp.servHome = !sp.servHome">
|
||||||
|
<img src="/serv.png" width="25" />
|
||||||
|
</w-button>
|
||||||
|
<w-confirm top left question="Azzero punteggio ?" cancel="NO" confirm="SI" @confirm="resetta">
|
||||||
|
RESET
|
||||||
|
</w-confirm>
|
||||||
|
<w-button @click="visuForm = !visuForm">
|
||||||
|
<span v-if="visuForm">PUNTEGGIO</span>
|
||||||
|
<span v-if="!visuForm">FORMAZIONI</span>
|
||||||
|
</w-button>
|
||||||
|
<w-button @click="speak">
|
||||||
|
<img src="/speaker.png" width="25" />
|
||||||
|
</w-button>
|
||||||
|
</w-flex>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
240
src/components/HomePage/HomePage.html
Normal file
@@ -0,0 +1,240 @@
|
|||||||
|
<section class="homepage">
|
||||||
|
<w-dialog v-model="diaNomi.show" :width="600" @close="chiudiDialogConfig()">
|
||||||
|
<w-input v-model="sp.nomi.home" type="text" class="pa3" tabindex="1">Nome Home</w-input>
|
||||||
|
<w-input v-model="sp.nomi.guest" type="text" class="pa3" tabindex="2">Nome Guest</w-input>
|
||||||
|
|
||||||
|
<w-flex justify-center align-center class="pa3">
|
||||||
|
<span class="mr3">Modalità partita:</span>
|
||||||
|
<w-button
|
||||||
|
@click="modalitaPartita = '2/3'"
|
||||||
|
:bg-color="modalitaPartita === '2/3' ? 'success' : 'grey-light4'"
|
||||||
|
:dark="modalitaPartita === '2/3'"
|
||||||
|
class="ma1"
|
||||||
|
tabindex="-1">
|
||||||
|
2/3
|
||||||
|
</w-button>
|
||||||
|
<w-button
|
||||||
|
@click="modalitaPartita = '3/5'"
|
||||||
|
:bg-color="modalitaPartita === '3/5' ? 'success' : 'grey-light4'"
|
||||||
|
:dark="modalitaPartita === '3/5'"
|
||||||
|
class="ma1"
|
||||||
|
tabindex="-1">
|
||||||
|
3/5
|
||||||
|
</w-button>
|
||||||
|
</w-flex>
|
||||||
|
|
||||||
|
<w-flex justify-space-around class="pa3">
|
||||||
|
<div class="campo-config">
|
||||||
|
<div class="text-bold mb3 text-center">Formazione Home</div>
|
||||||
|
<div class="campo-pallavolo">
|
||||||
|
<!-- Fila anteriore - index [3, 2, 1] - VICINO ALLA RETE (prima fila visualizzata) -->
|
||||||
|
<w-flex justify-center class="fila-anteriore">
|
||||||
|
<w-input v-model="sp.form.home[3]" type="text" style="width: 50px; text-align: center;" class="ma1" tabindex="6"></w-input>
|
||||||
|
<w-input v-model="sp.form.home[2]" type="text" style="width: 50px; text-align: center;" class="ma1" tabindex="5"></w-input>
|
||||||
|
<w-input v-model="sp.form.home[1]" type="text" style="width: 50px; text-align: center;" class="ma1" tabindex="4"></w-input>
|
||||||
|
</w-flex>
|
||||||
|
<!-- Linea dei 3 metri -->
|
||||||
|
<div class="linea-tre-metri"></div>
|
||||||
|
<!-- Fila posteriore - index [4, 5, 0] - ZONA DIFESA (seconda fila visualizzata) -->
|
||||||
|
<w-flex justify-center class="fila-posteriore">
|
||||||
|
<w-input v-model="sp.form.home[4]" type="text" style="width: 50px; text-align: center;" class="ma1" tabindex="7"></w-input>
|
||||||
|
<w-input v-model="sp.form.home[5]" type="text" style="width: 50px; text-align: center;" class="ma1" tabindex="8"></w-input>
|
||||||
|
<w-input v-model="sp.form.home[0]" type="text" style="width: 50px; text-align: center;" class="ma1" tabindex="3"></w-input>
|
||||||
|
</w-flex>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="campo-config">
|
||||||
|
<div class="text-bold mb3 text-center">Formazione Guest</div>
|
||||||
|
<div class="campo-pallavolo">
|
||||||
|
<!-- Fila anteriore - index [3, 2, 1] - VICINO ALLA RETE (prima fila visualizzata) -->
|
||||||
|
<w-flex justify-center class="fila-anteriore">
|
||||||
|
<w-input v-model="sp.form.guest[3]" type="text" style="width: 50px; text-align: center;" class="ma1" tabindex="12"></w-input>
|
||||||
|
<w-input v-model="sp.form.guest[2]" type="text" style="width: 50px; text-align: center;" class="ma1" tabindex="11"></w-input>
|
||||||
|
<w-input v-model="sp.form.guest[1]" type="text" style="width: 50px; text-align: center;" class="ma1" tabindex="10"></w-input>
|
||||||
|
</w-flex>
|
||||||
|
<!-- Linea dei 3 metri -->
|
||||||
|
<div class="linea-tre-metri"></div>
|
||||||
|
<!-- Fila posteriore - index [4, 5, 0] - ZONA DIFESA (seconda fila visualizzata) -->
|
||||||
|
<w-flex justify-center class="fila-posteriore">
|
||||||
|
<w-input v-model="sp.form.guest[4]" type="text" style="width: 50px; text-align: center;" class="ma1" tabindex="13"></w-input>
|
||||||
|
<w-input v-model="sp.form.guest[5]" type="text" style="width: 50px; text-align: center;" class="ma1" tabindex="14"></w-input>
|
||||||
|
<w-input v-model="sp.form.guest[0]" type="text" style="width: 50px; text-align: center;" class="ma1" tabindex="9"></w-input>
|
||||||
|
</w-flex>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</w-flex>
|
||||||
|
|
||||||
|
<w-button @click="order = !order" class="ma2" tabindex="-1">Inverti ordine</w-button>
|
||||||
|
<w-button bg-color="success" @click="diaNomi.show = false" class="ma2" tabindex="-1">
|
||||||
|
Ok
|
||||||
|
</w-button>
|
||||||
|
</w-dialog>
|
||||||
|
<w-dialog v-model="diaCambiTeam.show" :width="420" @close="abilitaTastiSpeciali()">
|
||||||
|
<div class="text-bold text-center mb2">Scegli squadra</div>
|
||||||
|
<w-flex justify-center class="pa3">
|
||||||
|
<w-button class="ma2" @click="selezionaTeamCambi('home')">{{ sp.nomi.home }}</w-button>
|
||||||
|
<w-button class="ma2" @click="selezionaTeamCambi('guest')">{{ sp.nomi.guest }}</w-button>
|
||||||
|
</w-flex>
|
||||||
|
</w-dialog>
|
||||||
|
<w-dialog v-model="diaCambi.show" :width="360" @close="chiudiDialogCambi">
|
||||||
|
<div class="cambi-dialog">
|
||||||
|
<div class="cambi-title">{{ sp.nomi[diaCambi.team] }}: CAMBIO</div>
|
||||||
|
<div class="cambi-rows">
|
||||||
|
<div class="cambi-row">
|
||||||
|
<w-input v-model="diaCambi[diaCambi.team].cambi[0].in" type="text" class="cambi-input cambi-in"></w-input>
|
||||||
|
<span class="cambi-arrow">→</span>
|
||||||
|
<w-input v-model="diaCambi[diaCambi.team].cambi[0].out" type="text" class="cambi-input cambi-out"></w-input>
|
||||||
|
</div>
|
||||||
|
<div class="cambi-row">
|
||||||
|
<w-input v-model="diaCambi[diaCambi.team].cambi[1].in" type="text" class="cambi-input cambi-in"></w-input>
|
||||||
|
<span class="cambi-arrow">→</span>
|
||||||
|
<w-input v-model="diaCambi[diaCambi.team].cambi[1].out" type="text" class="cambi-input cambi-out"></w-input>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<w-flex justify-end class="pa3">
|
||||||
|
<w-button bg-color="success" :disabled="!cambiConfermabili" @click="confermaCambi">
|
||||||
|
CONFERMA
|
||||||
|
</w-button>
|
||||||
|
</w-flex>
|
||||||
|
</w-dialog>
|
||||||
|
<div class="campo">
|
||||||
|
|
||||||
|
<span v-if="order">
|
||||||
|
<!-- home guest -->
|
||||||
|
<div class="hea home">
|
||||||
|
<span @click="decPunt('home')" :style="{ 'float': 'left' }">
|
||||||
|
{{ sp.nomi.home }}
|
||||||
|
<span class="serv-slot">
|
||||||
|
<img v-show="sp.servHome" src="/serv.png" width="25" />
|
||||||
|
</span>
|
||||||
|
<span v-if="visuForm" class="score-inline">{{ sp.punt.home }}</span>
|
||||||
|
</span>
|
||||||
|
<span @click="incSet('home')" class="mr3" :style="{ 'float': 'right' }">set {{ sp.set.home }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="hea guest">
|
||||||
|
<span @click="decPunt('guest')" :style="{ 'float': 'right' }">
|
||||||
|
<span v-if="visuForm" class="score-inline">{{ sp.punt.guest }}</span>
|
||||||
|
<span class="serv-slot">
|
||||||
|
<img v-show="!sp.servHome" src="/serv.png" width="25" />
|
||||||
|
</span>
|
||||||
|
{{ sp.nomi.guest }}
|
||||||
|
</span>
|
||||||
|
<span @click="incSet('guest')" class="ml3" :style="{ 'float': 'left' }">set {{ sp.set.guest }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<span v-if="visuForm">
|
||||||
|
<div class="col form home" @click="incPunt('home')">
|
||||||
|
<div class="formdiv" v-for="x in [3, 2, 1, 4, 5, 0]">
|
||||||
|
{{ sp.form.home[x] }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col form guest" @click="incPunt('guest')">
|
||||||
|
<div class="formdiv" v-for="x in [3, 2, 1, 4, 5, 0]">
|
||||||
|
{{ sp.form.guest[x] }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
<span v-else>
|
||||||
|
<w-flex class="punteggio-container">
|
||||||
|
<w-flex justify-center align-center class="col punt home" @click="incPunt('home')">{{ sp.punt.home }}</w-flex>
|
||||||
|
<w-flex justify-center align-center class="col punt guest" @click="incPunt('guest')">{{ sp.punt.guest }}</w-flex>
|
||||||
|
</w-flex>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
</span>
|
||||||
|
<span v-else>
|
||||||
|
<!-- guest home -->
|
||||||
|
|
||||||
|
<div class="hea guest">
|
||||||
|
<span @click="decPunt('guest')" :style="{ 'float': 'left' }">
|
||||||
|
{{ sp.nomi.guest }}
|
||||||
|
<span class="serv-slot">
|
||||||
|
<img v-show="!sp.servHome" src="/serv.png" width="25" />
|
||||||
|
</span>
|
||||||
|
<span v-if="visuForm" class="score-inline">{{ sp.punt.guest }}</span>
|
||||||
|
</span>
|
||||||
|
<span @click="incSet('guest')" class="mr3" :style="{ 'float': 'right' }">set {{ sp.set.guest }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="hea home">
|
||||||
|
<span @click="decPunt('home')" :style="{ 'float': 'right' }">
|
||||||
|
<span v-if="visuForm" class="score-inline">{{ sp.punt.home }}</span>
|
||||||
|
<span class="serv-slot">
|
||||||
|
<img v-show="sp.servHome" src="/serv.png" width="25" />
|
||||||
|
</span>
|
||||||
|
{{ sp.nomi.home }}
|
||||||
|
</span>
|
||||||
|
<span @click="incSet('home')" class="ml3" :style="{ 'float': 'left' }">set {{ sp.set.home }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<span v-if="visuForm">
|
||||||
|
<div class="col form guest" @click="incPunt('guest')">
|
||||||
|
<div class="formdiv" v-for="x in [3, 2, 1, 4, 5, 0]">
|
||||||
|
{{ sp.form.guest[x] }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col form home" @click="incPunt('home')">
|
||||||
|
<div class="formdiv" v-for="x in [3, 2, 1, 4, 5, 0]">
|
||||||
|
{{ sp.form.home[x] }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
<span v-else>
|
||||||
|
<w-flex class="punteggio-container">
|
||||||
|
<w-flex justify-center align-center class="col punt guest" @click="incPunt('guest')">{{ sp.punt.guest }}</w-flex>
|
||||||
|
<w-flex justify-center align-center class="col punt home" @click="incPunt('home')">{{ sp.punt.home }}</w-flex>
|
||||||
|
</w-flex>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<div class="striscia" v-if="visuStriscia">
|
||||||
|
<div>
|
||||||
|
<span class="text-bold mr1">{{ sp.nomi.home }}</span>
|
||||||
|
<div v-for="h in sp.striscia.home" class="item">
|
||||||
|
{{String(h)}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="guest">
|
||||||
|
<span class="text-bold mr1">{{ sp.nomi.guest }}</span>
|
||||||
|
<div v-for="h in sp.striscia.guest" class="item">
|
||||||
|
{{String(h)}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bot" v-if="visuButt">
|
||||||
|
<w-flex justify-space-between class="pa2">
|
||||||
|
<w-confirm right align-bottom v-if="isMobile()" question="CHIUDO ?" cancel="NO" confirm="SI" @confirm="closeApp">
|
||||||
|
<img src="/exit.png" width="25" />
|
||||||
|
</w-confirm>
|
||||||
|
<w-button @click="apriDialogConfig()">
|
||||||
|
<img src="/gear.png" width="25" />
|
||||||
|
</w-button>
|
||||||
|
<w-button @click="cambiaPalla" :disabled="!isPunteggioZeroZero">
|
||||||
|
<img src="/serv.png" width="25" />
|
||||||
|
</w-button>
|
||||||
|
<w-confirm top left question="Azzero punteggio ?" cancel="NO" confirm="SI" @confirm="resetta">
|
||||||
|
RESET
|
||||||
|
</w-confirm>
|
||||||
|
<w-button @click="visuForm = !visuForm">
|
||||||
|
<span v-if="visuForm">PUNTEGGIO</span>
|
||||||
|
<span v-if="!visuForm">FORMAZIONI</span>
|
||||||
|
</w-button>
|
||||||
|
<w-button @click="apriDialogCambi">
|
||||||
|
CAMBI
|
||||||
|
</w-button>
|
||||||
|
<w-button @click="visuStriscia = !visuStriscia">
|
||||||
|
STRISCIA
|
||||||
|
</w-button>
|
||||||
|
<w-button @click="speak">
|
||||||
|
<img src="/speaker.png" width="25" />
|
||||||
|
</w-button>
|
||||||
|
</w-flex>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
479
src/components/HomePage/HomePage.js
Normal file
@@ -0,0 +1,479 @@
|
|||||||
|
import NoSleep from "nosleep.js";
|
||||||
|
export default {
|
||||||
|
name: "HomePage",
|
||||||
|
components: {},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
order: true,
|
||||||
|
voices: null,
|
||||||
|
diaNomi: {
|
||||||
|
show: false,
|
||||||
|
home: "",
|
||||||
|
guest: "",
|
||||||
|
},
|
||||||
|
diaCambi: {
|
||||||
|
show: false,
|
||||||
|
team: "home",
|
||||||
|
guest: { cambi: [{ in: "", out: "" }, { in: "", out: "" }] },
|
||||||
|
home: { cambi: [{ in: "", out: "" }, { in: "", out: "" }] },
|
||||||
|
},
|
||||||
|
diaCambiTeam: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
visuForm: false,
|
||||||
|
visuButt: true,
|
||||||
|
visuStriscia: true,
|
||||||
|
modalitaPartita: "3/5", // "2/3" o "3/5"
|
||||||
|
sp: {
|
||||||
|
striscia: { home: [0], guest: [0] },
|
||||||
|
servHome: true,
|
||||||
|
punt: { home: 0, guest: 0 },
|
||||||
|
set: { home: 0, guest: 0 },
|
||||||
|
nomi: { home: "Antoniana", guest: "Guest" },
|
||||||
|
form: {
|
||||||
|
home: ["1", "2", "3", "4", "5", "6"],
|
||||||
|
guest: ["1", "2", "3", "4", "5", "6"],
|
||||||
|
},
|
||||||
|
storicoServizio: [], // Stack per tracciare lo stato del servizio prima di ogni punto
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.voices = window.speechSynthesis.getVoices();
|
||||||
|
if (this.isMobile()) {
|
||||||
|
this.speak();
|
||||||
|
var noSleep = new NoSleep();
|
||||||
|
noSleep.enable();
|
||||||
|
document.documentElement.requestFullscreen();
|
||||||
|
}
|
||||||
|
this.abilitaTastiSpeciali();
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isPunteggioZeroZero() {
|
||||||
|
return this.sp.punt.home === 0 && this.sp.punt.guest === 0;
|
||||||
|
},
|
||||||
|
cambiConfermabili() {
|
||||||
|
const team = this.diaCambi.team;
|
||||||
|
const cambi = this.diaCambi[team].cambi || [];
|
||||||
|
let hasComplete = false;
|
||||||
|
let allValid = true;
|
||||||
|
|
||||||
|
cambi.forEach((cambio) => {
|
||||||
|
const teamIn = (cambio.in || "").trim();
|
||||||
|
const teamOut = (cambio.out || "").trim();
|
||||||
|
|
||||||
|
if (!teamIn && !teamOut) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!teamIn || !teamOut) {
|
||||||
|
allValid = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
hasComplete = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
return allValid && hasComplete;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
closeApp() {
|
||||||
|
var win = window.open("", "_self");
|
||||||
|
win.close();
|
||||||
|
},
|
||||||
|
fullScreen() {
|
||||||
|
document.documentElement.requestFullscreen();
|
||||||
|
},
|
||||||
|
isMobile() {
|
||||||
|
if (
|
||||||
|
/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
|
||||||
|
navigator.userAgent
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
resetta() {
|
||||||
|
this.$waveui.notify("Punteggio<br />RESETTATO", "success");
|
||||||
|
this.visuForm = false;
|
||||||
|
this.sp.punt.home = 0;
|
||||||
|
this.sp.punt.guest = 0;
|
||||||
|
this.sp.form = {
|
||||||
|
home: ["1", "2", "3", "4", "5", "6"],
|
||||||
|
guest: ["1", "2", "3", "4", "5", "6"],
|
||||||
|
}
|
||||||
|
this.sp.striscia = { home: [0], guest: [0] }
|
||||||
|
this.sp.storicoServizio = []
|
||||||
|
},
|
||||||
|
cambiaPalla() {
|
||||||
|
if (!this.isPunteggioZeroZero) {
|
||||||
|
this.$waveui.notify("Cambio palla consentito solo a inizio set (0-0)", "warning");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.sp.servHome = !this.sp.servHome;
|
||||||
|
},
|
||||||
|
incSet(team) {
|
||||||
|
if (this.sp.set[team] == 2) {
|
||||||
|
this.sp.set[team] = 0;
|
||||||
|
} else {
|
||||||
|
this.sp.set[team]++;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
incPunt(team) {
|
||||||
|
// Se il set è già terminato, evita ulteriori incrementi
|
||||||
|
if (this.checkVittoria()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Salva lo stato del servizio PRIMA di modificarlo
|
||||||
|
this.sp.storicoServizio.push({
|
||||||
|
servHome: this.sp.servHome,
|
||||||
|
cambioPalla: (team == "home" && !this.sp.servHome) || (team == "guest" && this.sp.servHome)
|
||||||
|
});
|
||||||
|
|
||||||
|
this.sp.punt[team]++;
|
||||||
|
if (team == 'home') {
|
||||||
|
this.sp.striscia.home.push(this.sp.punt.home)
|
||||||
|
this.sp.striscia.guest.push(' ')
|
||||||
|
} else {
|
||||||
|
this.sp.striscia.guest.push(this.sp.punt.guest)
|
||||||
|
this.sp.striscia.home.push(' ')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ruota la formazione solo se c'è cambio palla (conquista del servizio)
|
||||||
|
const cambioPalla = (team == "home" && !this.sp.servHome) || (team == "guest" && this.sp.servHome);
|
||||||
|
|
||||||
|
if (cambioPalla) {
|
||||||
|
this.sp.form[team].push(this.sp.form[team].shift());
|
||||||
|
}
|
||||||
|
|
||||||
|
this.sp.servHome = (team == "home");
|
||||||
|
},
|
||||||
|
checkVittoria() {
|
||||||
|
const puntHome = this.sp.punt.home;
|
||||||
|
const puntGuest = this.sp.punt.guest;
|
||||||
|
const setHome = this.sp.set.home;
|
||||||
|
const setGuest = this.sp.set.guest;
|
||||||
|
const totSet = setHome + setGuest;
|
||||||
|
|
||||||
|
// Determina se siamo nel set decisivo in base alla modalità partita
|
||||||
|
let isSetDecisivo = false;
|
||||||
|
if (this.modalitaPartita === "2/3") {
|
||||||
|
// Tie-break al 3° set (quando totSet >= 2)
|
||||||
|
isSetDecisivo = totSet >= 2;
|
||||||
|
} else {
|
||||||
|
// Tie-break al 5° set (quando totSet >= 4)
|
||||||
|
isSetDecisivo = totSet >= 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
const punteggioVittoria = isSetDecisivo ? 15 : 25;
|
||||||
|
|
||||||
|
// Vittoria con punteggio >= 25 (o 15 per set decisivo) e almeno 2 punti di vantaggio
|
||||||
|
if (puntHome >= punteggioVittoria && puntHome - puntGuest >= 2) {
|
||||||
|
return true; // Home ha vinto
|
||||||
|
}
|
||||||
|
if (puntGuest >= punteggioVittoria && puntGuest - puntHome >= 2) {
|
||||||
|
return true; // Guest ha vinto
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
decPunt() {
|
||||||
|
if (this.sp.striscia.home.length > 1 && this.sp.storicoServizio.length > 0) {
|
||||||
|
var tmpHome = this.sp.striscia.home.pop()
|
||||||
|
var tmpGuest = this.sp.striscia.guest.pop()
|
||||||
|
var statoServizio = this.sp.storicoServizio.pop() // Recupera lo stato completo del servizio
|
||||||
|
|
||||||
|
if (tmpHome == ' ') {
|
||||||
|
this.sp.punt.guest--
|
||||||
|
// Ruota indietro solo se c'era stato un cambio palla
|
||||||
|
if (statoServizio.cambioPalla) {
|
||||||
|
this.sp.form.guest.unshift(this.sp.form.guest.pop());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.sp.punt.home--
|
||||||
|
// Ruota indietro solo se c'era stato un cambio palla
|
||||||
|
if (statoServizio.cambioPalla) {
|
||||||
|
this.sp.form.home.unshift(this.sp.form.home.pop());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ripristina il servizio allo stato precedente
|
||||||
|
this.sp.servHome = statoServizio.servHome;
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// decPunt(team) {
|
||||||
|
// // decrementa il punteggio se è > 0.
|
||||||
|
// if (this.sp.punt[team] > 0) {
|
||||||
|
// this.sp.punt[team]--;
|
||||||
|
// this.sp.striscia.home.pop()
|
||||||
|
// this.sp.striscia.guest.pop()
|
||||||
|
// this.sp.form[team].unshift(this.sp.form[team].pop());
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
speak() {
|
||||||
|
const msg = new SpeechSynthesisUtterance();
|
||||||
|
if (this.sp.punt.home + this.sp.punt.guest == 0) {
|
||||||
|
msg.text = "zero a zero";
|
||||||
|
} else if (this.sp.punt.home == this.sp.punt.guest) {
|
||||||
|
msg.text = this.sp.punt.home + " pari";
|
||||||
|
} else {
|
||||||
|
if (this.sp.servHome) {
|
||||||
|
msg.text = this.sp.punt.home + " a " + this.sp.punt.guest;
|
||||||
|
} else {
|
||||||
|
msg.text = this.sp.punt.guest + " a " + this.sp.punt.home;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// msg.volume = 1.0; // speech volume (default: 1.0)
|
||||||
|
// msg.pitch = 1.0; // speech pitch (default: 1.0)
|
||||||
|
// msg.rate = 1.0; // speech rate (default: 1.0)
|
||||||
|
// msg.lang = 'it_IT'; // speech language (default: 'en-US')
|
||||||
|
const voices = window.speechSynthesis.getVoices();
|
||||||
|
msg.voice = voices.find(voice => voice.name === 'Google italiano');
|
||||||
|
// voice URI (default: platform-dependent)
|
||||||
|
// msg.onboundary = function (event) {
|
||||||
|
// console.log('Speech reached a boundary:', event.name);
|
||||||
|
// };
|
||||||
|
// msg.onpause = function (event) {
|
||||||
|
// console.log('Speech paused:', event.utterance.text.substring(event.charIndex));
|
||||||
|
// };
|
||||||
|
window.speechSynthesis.speak(msg);
|
||||||
|
},
|
||||||
|
apriDialogConfig() {
|
||||||
|
this.disabilitaTastiSpeciali();
|
||||||
|
this.diaNomi.show = true;
|
||||||
|
|
||||||
|
// Aggiungi gestore Tab per il dialog
|
||||||
|
this.dialogConfigTabHandler = (e) => {
|
||||||
|
if (e.key === 'Tab' && this.diaNomi.show) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
const dialog = document.querySelector('.w-dialog');
|
||||||
|
if (!dialog) return;
|
||||||
|
|
||||||
|
const allInputs = Array.from(dialog.querySelectorAll('input[type="text"]'))
|
||||||
|
.sort((a, b) => {
|
||||||
|
const tabA = parseInt(a.closest('[tabindex]')?.getAttribute('tabindex') || '0');
|
||||||
|
const tabB = parseInt(b.closest('[tabindex]')?.getAttribute('tabindex') || '0');
|
||||||
|
return tabA - tabB;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (allInputs.length === 0) return;
|
||||||
|
|
||||||
|
// Verifica se il focus è già dentro il dialog
|
||||||
|
const focusInDialog = dialog.contains(document.activeElement);
|
||||||
|
|
||||||
|
// Se non è nel dialog o non è in un input, vai al primo
|
||||||
|
if (!focusInDialog || !allInputs.includes(document.activeElement)) {
|
||||||
|
allInputs[0].focus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Navigazione normale tra i campi
|
||||||
|
const currentIndex = allInputs.indexOf(document.activeElement);
|
||||||
|
let nextIndex;
|
||||||
|
|
||||||
|
if (e.shiftKey) {
|
||||||
|
nextIndex = currentIndex <= 0 ? allInputs.length - 1 : currentIndex - 1;
|
||||||
|
} else {
|
||||||
|
nextIndex = currentIndex >= allInputs.length - 1 ? 0 : currentIndex + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allInputs[nextIndex]) {
|
||||||
|
allInputs[nextIndex].focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
window.addEventListener('keydown', this.dialogConfigTabHandler, true);
|
||||||
|
|
||||||
|
// Focus immediato + retry con timeout
|
||||||
|
this.$nextTick(() => {
|
||||||
|
const focusFirst = () => {
|
||||||
|
const dialog = document.querySelector('.w-dialog');
|
||||||
|
if (dialog) {
|
||||||
|
const firstInput = dialog.querySelector('input[type="text"]');
|
||||||
|
if (firstInput) {
|
||||||
|
firstInput.focus();
|
||||||
|
firstInput.select();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Prova immediatamente
|
||||||
|
if (!focusFirst()) {
|
||||||
|
// Se fallisce, riprova dopo un breve delay
|
||||||
|
setTimeout(focusFirst, 100);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
chiudiDialogConfig() {
|
||||||
|
if (this.dialogConfigTabHandler) {
|
||||||
|
window.removeEventListener('keydown', this.dialogConfigTabHandler, true);
|
||||||
|
this.dialogConfigTabHandler = null;
|
||||||
|
}
|
||||||
|
this.abilitaTastiSpeciali();
|
||||||
|
},
|
||||||
|
resettaCambi(team) {
|
||||||
|
const teams = team ? [team] : ["home", "guest"];
|
||||||
|
teams.forEach((t) => {
|
||||||
|
this.diaCambi[t].cambi.forEach((cambio) => {
|
||||||
|
cambio.in = "";
|
||||||
|
cambio.out = "";
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
apriDialogCambi() {
|
||||||
|
this.disabilitaTastiSpeciali();
|
||||||
|
this.diaCambiTeam.show = true;
|
||||||
|
},
|
||||||
|
apriDialogCambiTeam(team) {
|
||||||
|
this.disabilitaTastiSpeciali();
|
||||||
|
this.diaCambi.team = team;
|
||||||
|
this.resettaCambi(team);
|
||||||
|
this.diaCambi.show = true;
|
||||||
|
},
|
||||||
|
selezionaTeamCambi(team) {
|
||||||
|
this.diaCambiTeam.show = false;
|
||||||
|
this.apriDialogCambiTeam(team);
|
||||||
|
},
|
||||||
|
chiudiDialogCambi() {
|
||||||
|
this.diaCambi.show = false;
|
||||||
|
this.resettaCambi(this.diaCambi.team);
|
||||||
|
this.abilitaTastiSpeciali();
|
||||||
|
},
|
||||||
|
confermaCambi() {
|
||||||
|
if (!this.cambiConfermabili) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const team = this.diaCambi.team;
|
||||||
|
const cambi = (this.diaCambi[team].cambi || [])
|
||||||
|
.map((cambio) => ({
|
||||||
|
team,
|
||||||
|
in: (cambio.in || "").trim(),
|
||||||
|
out: (cambio.out || "").trim(),
|
||||||
|
}))
|
||||||
|
.filter((cambio) => cambio.in || cambio.out);
|
||||||
|
|
||||||
|
const form = this.sp.form[team].map((val) => String(val).trim());
|
||||||
|
const formAggiornata = [...form];
|
||||||
|
|
||||||
|
for (const cambio of cambi) {
|
||||||
|
if (!/^\d+$/.test(cambio.in) || !/^\d+$/.test(cambio.out)) {
|
||||||
|
this.$waveui.notify("Inserisci solo numeri nei campi", "warning");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (cambio.in === cambio.out) {
|
||||||
|
this.$waveui.notify(`Numero IN e OUT uguali per ${cambio.team}`, "warning");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (formAggiornata.includes(cambio.in)) {
|
||||||
|
this.$waveui.notify(`Numero ${cambio.in} già presente in formazione ${cambio.team}`, "warning");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!formAggiornata.includes(cambio.out)) {
|
||||||
|
this.$waveui.notify(`Numero ${cambio.out} non presente in formazione ${cambio.team}`, "warning");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const idx = formAggiornata.findIndex((val) => String(val).trim() === cambio.out);
|
||||||
|
if (idx !== -1) {
|
||||||
|
formAggiornata.splice(idx, 1, cambio.in);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.sp.form[team] = formAggiornata;
|
||||||
|
|
||||||
|
this.chiudiDialogCambi();
|
||||||
|
},
|
||||||
|
disabilitaTastiSpeciali() {
|
||||||
|
window.removeEventListener("keydown", this.funzioneTastiSpeciali);
|
||||||
|
},
|
||||||
|
abilitaTastiSpeciali() {
|
||||||
|
window.addEventListener("keydown", this.funzioneTastiSpeciali);
|
||||||
|
},
|
||||||
|
funzioneTastiSpeciali(e) {
|
||||||
|
if (this.diaNomi.show || this.diaCambi.show || this.diaCambiTeam.show) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const target = e.target;
|
||||||
|
const path = typeof e.composedPath === "function" ? e.composedPath() : [];
|
||||||
|
const elements = [target, ...path].filter(Boolean);
|
||||||
|
const isTypingField = elements.some((el) => {
|
||||||
|
if (!el || !el.tagName) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const tag = String(el.tagName).toLowerCase();
|
||||||
|
if (tag === "input" || tag === "textarea") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (el.isContentEditable) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (el.classList && (el.classList.contains("w-input") || el.classList.contains("w-textarea"))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const contentEditable = el.getAttribute && el.getAttribute("contenteditable");
|
||||||
|
return contentEditable === "true";
|
||||||
|
});
|
||||||
|
if (isTypingField) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let handled = false;
|
||||||
|
if (e.ctrlKey && e.key == "m") {
|
||||||
|
this.diaNomi.show = true
|
||||||
|
handled = true;
|
||||||
|
} else if (e.ctrlKey && e.key == "b") {
|
||||||
|
this.visuButt = !this.visuButt
|
||||||
|
handled = true;
|
||||||
|
} else if (e.ctrlKey && e.key == "f") {
|
||||||
|
document.documentElement.requestFullscreen();
|
||||||
|
handled = true;
|
||||||
|
} else if (e.ctrlKey && e.key == "s") {
|
||||||
|
this.speak();
|
||||||
|
handled = true;
|
||||||
|
} else if (e.ctrlKey && e.key == "z") {
|
||||||
|
this.visuForm = !this.visuForm
|
||||||
|
handled = true;
|
||||||
|
} else if (e.ctrlKey && e.key == "ArrowUp") {
|
||||||
|
this.incPunt("home")
|
||||||
|
handled = true;
|
||||||
|
} else if (e.ctrlKey && e.key == "ArrowDown") {
|
||||||
|
this.decPunt("home")
|
||||||
|
handled = true;
|
||||||
|
} else if (e.ctrlKey && e.key == "ArrowRight") {
|
||||||
|
this.incSet("home")
|
||||||
|
handled = true;
|
||||||
|
} else if (e.shiftKey && e.key == "ArrowUp") {
|
||||||
|
this.incPunt("guest")
|
||||||
|
handled = true;
|
||||||
|
} else if (e.shiftKey && e.key == "ArrowDown") {
|
||||||
|
this.decPunt("guest")
|
||||||
|
handled = true;
|
||||||
|
} else if (e.shiftKey && e.key == "ArrowRight") {
|
||||||
|
this.incSet("guest")
|
||||||
|
handled = true;
|
||||||
|
} else if (e.ctrlKey && e.key == "ArrowLeft") {
|
||||||
|
this.cambiaPalla()
|
||||||
|
handled = true;
|
||||||
|
} else if (e.ctrlKey && (e.key == "c" || e.key == "C")) {
|
||||||
|
this.apriDialogCambiTeam("home")
|
||||||
|
handled = true;
|
||||||
|
} else if (e.shiftKey && (e.key == "c" || e.key == "C")) {
|
||||||
|
this.apriDialogCambiTeam("guest")
|
||||||
|
handled = true;
|
||||||
|
} else { return false }
|
||||||
|
|
||||||
|
if (handled) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
112
src/components/HomePage/HomePage.scss
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
.homepage {
|
||||||
|
:root {
|
||||||
|
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
|
||||||
|
touch-action: pan-x pan-y;
|
||||||
|
height: 100%
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
overscroll-behavior-y: contain;
|
||||||
|
margin: 0;
|
||||||
|
place-items: center;
|
||||||
|
min-width: 320px;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 100vh;
|
||||||
|
background-color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
margin-left: 10px;
|
||||||
|
margin-right: 10px;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid #fff;
|
||||||
|
padding: 0.6em 1.2em;
|
||||||
|
font-size: 0.8em;
|
||||||
|
font-weight: 500;
|
||||||
|
font-family: inherit;
|
||||||
|
color: #fff;
|
||||||
|
background-color: #000;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: border-color 0.25s;
|
||||||
|
}
|
||||||
|
button:hover {
|
||||||
|
border-color: #646cff;
|
||||||
|
background-color: #333;
|
||||||
|
}
|
||||||
|
button:focus, button:focus-visible {
|
||||||
|
outline: 4px auto -webkit-focus-ring-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
#app {
|
||||||
|
margin: 0 auto;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.campo {
|
||||||
|
user-select: none;
|
||||||
|
width: 100%;
|
||||||
|
display: table;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
.hea {
|
||||||
|
float: left;
|
||||||
|
width: 50%;
|
||||||
|
font-size: xx-large;
|
||||||
|
}
|
||||||
|
.hea span {
|
||||||
|
/* border: 1px solid #f3fb00; */
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
.tal {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.tar {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.bot {
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 1px;
|
||||||
|
background-color: #111;
|
||||||
|
}
|
||||||
|
.col {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
text-align: center;
|
||||||
|
float: left;
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
.punt {
|
||||||
|
font-size: 60vh;
|
||||||
|
}
|
||||||
|
.form {
|
||||||
|
font-size: 5vh;
|
||||||
|
border-top: #fff dashed 25px;
|
||||||
|
padding-top: 50px;
|
||||||
|
}
|
||||||
|
.formtit {
|
||||||
|
font-size: 5vh;
|
||||||
|
margin-top: 30px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.formdiv {
|
||||||
|
font-size: 20vh;
|
||||||
|
float: left;
|
||||||
|
width: 32%;
|
||||||
|
}
|
||||||
|
.home {
|
||||||
|
background-color: black;
|
||||||
|
color: yellow;
|
||||||
|
}
|
||||||
|
.guest {
|
||||||
|
background-color: blue;
|
||||||
|
color: white
|
||||||
|
}
|
||||||
|
.item-stri {
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
3
src/components/HomePage/index.vue
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<template src="./HomePage.html"></template>
|
||||||
|
<script src="./HomePage.js"></script>
|
||||||
|
|
||||||
@@ -1,5 +1,10 @@
|
|||||||
import { createApp } from 'vue'
|
import { createApp } from 'vue'
|
||||||
import './style.css'
|
import './style.css'
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
|
import WaveUI from 'wave-ui'
|
||||||
|
import 'wave-ui/dist/wave-ui.css'
|
||||||
|
|
||||||
createApp(App).mount('#app')
|
const app = createApp(App)
|
||||||
|
|
||||||
|
app.use(WaveUI)
|
||||||
|
app.mount('#app')
|
||||||
|
|||||||
203
src/style.css
@@ -5,6 +5,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
|
overscroll-behavior-y: contain;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
place-items: center;
|
place-items: center;
|
||||||
min-width: 320px;
|
min-width: 320px;
|
||||||
@@ -19,7 +20,7 @@ button {
|
|||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
border: 1px solid #fff;
|
border: 1px solid #fff;
|
||||||
padding: 0.6em 1.2em;
|
padding: 0.6em 1.2em;
|
||||||
font-size: 1em;
|
font-size: 0.8em;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
@@ -40,7 +41,6 @@ button:focus-visible {
|
|||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.campo {
|
.campo {
|
||||||
user-select: none;
|
user-select: none;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -52,6 +52,25 @@ button:focus-visible {
|
|||||||
width: 50%;
|
width: 50%;
|
||||||
font-size: xx-large;
|
font-size: xx-large;
|
||||||
}
|
}
|
||||||
|
.hea span {
|
||||||
|
/* border: 1px solid #f3fb00; */
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
.score-inline {
|
||||||
|
display: inline-block;
|
||||||
|
min-width: 3ch;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.serv-slot {
|
||||||
|
display: inline-flex;
|
||||||
|
width: 25px;
|
||||||
|
height: 25px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
.tal {
|
.tal {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
@@ -59,19 +78,187 @@ button:focus-visible {
|
|||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
.bot {
|
.bot {
|
||||||
float: left;
|
position: fixed;
|
||||||
width: 50%;
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
background-color: #000;
|
margin-bottom: 1px;
|
||||||
|
background-color: #111;
|
||||||
}
|
}
|
||||||
.col {
|
.col {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
text-align: center;
|
||||||
float: left;
|
float: left;
|
||||||
font-size: 60vh;
|
|
||||||
width: 50%;
|
width: 50%;
|
||||||
}
|
}
|
||||||
|
.punteggio-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.punt {
|
||||||
|
font-size: 60vh;
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
min-height: 50vh;
|
||||||
|
min-width: 50vw;
|
||||||
|
max-width: 50vw;
|
||||||
|
overflow: hidden;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.form {
|
||||||
|
font-size: 5vh;
|
||||||
|
border-top: #fff dashed 25px;
|
||||||
|
padding-top: 50px;
|
||||||
|
}
|
||||||
|
.formtit {
|
||||||
|
font-size: 5vh;
|
||||||
|
margin-top: 30px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.formdiv {
|
||||||
|
font-size: 20vh;
|
||||||
|
float: left;
|
||||||
|
width: 32%;
|
||||||
|
}
|
||||||
.home {
|
.home {
|
||||||
background-color: #00f;
|
background-color: black;
|
||||||
|
color: yellow;
|
||||||
}
|
}
|
||||||
.guest {
|
.guest {
|
||||||
background-color: #f00;
|
background-color: blue;
|
||||||
|
color: white
|
||||||
|
}
|
||||||
|
.striscia {
|
||||||
|
position:fixed;
|
||||||
|
text-align: right;
|
||||||
|
bottom: 50px;
|
||||||
|
right: 10px;
|
||||||
|
margin-left: -10000px;
|
||||||
|
}
|
||||||
|
.striscia .item {
|
||||||
|
width: 25px;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: bold;
|
||||||
|
display: inline-block;
|
||||||
|
background-color: rgb(206, 247, 3);
|
||||||
|
color: blue;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.campo-config {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.campo-pallavolo {
|
||||||
|
border: 3px solid #999;
|
||||||
|
background-color: rgba(205, 133, 63, 0.25);
|
||||||
|
position: relative;
|
||||||
|
width: 220px;
|
||||||
|
height: 220px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fila-anteriore {
|
||||||
|
height: 33.33%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fila-posteriore {
|
||||||
|
height: 66.67%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.linea-tre-metri {
|
||||||
|
border-top: 2px solid #666;
|
||||||
|
width: 100%;
|
||||||
|
height: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cambi-rows {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cambi-dialog {
|
||||||
|
padding: 8px 6px 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cambi-title {
|
||||||
|
text-align: center;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
padding-bottom: 6px;
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cambi-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cambi-arrow {
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 1;
|
||||||
|
padding: 6px 8px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: rgba(255, 255, 255, 0.08);
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cambi-input {
|
||||||
|
min-width: 48px;
|
||||||
|
max-width: 64px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cambi-input input,
|
||||||
|
.cambi-input .w-input__input,
|
||||||
|
.cambi-input .w-input__field {
|
||||||
|
border: 2px solid rgba(255, 255, 255, 0.35);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 6px 10px;
|
||||||
|
color: #000;
|
||||||
|
text-align: center;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cambi-in input,
|
||||||
|
.cambi-in .w-input__input,
|
||||||
|
.cambi-in .w-input__field {
|
||||||
|
background: rgba(120, 200, 120, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cambi-out input,
|
||||||
|
.cambi-out .w-input__input,
|
||||||
|
.cambi-out .w-input__field {
|
||||||
|
background: rgba(200, 120, 120, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cambi-input input:focus,
|
||||||
|
.cambi-input .w-input__input:focus,
|
||||||
|
.cambi-input .w-input__field:focus {
|
||||||
|
border-color: rgba(255, 255, 255, 0.7);
|
||||||
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,28 @@ export default defineConfig({
|
|||||||
plugins: [
|
plugins: [
|
||||||
vue(),
|
vue(),
|
||||||
VitePWA({
|
VitePWA({
|
||||||
registerType: 'autoUpdate'
|
registerType: 'autoUpdate',
|
||||||
|
manifest: {
|
||||||
|
name: "app_segnap",
|
||||||
|
short_name: "segnap",
|
||||||
|
description: "Segnapunti standalone.",
|
||||||
|
background_color: "#eee",
|
||||||
|
theme_color: '#ffffff',
|
||||||
|
display: "fullscreen",
|
||||||
|
orientation: "landscape",
|
||||||
|
icons: [
|
||||||
|
{
|
||||||
|
src: 'segnap-192x192.png',
|
||||||
|
sizes: '192x192',
|
||||||
|
type: 'image/png'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: 'segnap-512x512.png',
|
||||||
|
sizes: '512x512',
|
||||||
|
type: 'image/png'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|||||||