feat: prototipo generazione referto PDF a fine partita
Aggiunge un bottone "REFERTO" nel modal PARTITA FINITA che apre una nuova scheda con il referto di gara in HTML e avvia il dialogo di stampa del browser (Salva come PDF). Il referto include: punteggio per set, formazione di partenza per ogni set (salvata automaticamente al primo punto), andamento punto per punto con colori per squadra, data e ora di generazione. Nota: funzionalità prototipale — layout, contenuti e dettagli sono ancora da perfezionare sulla base dell'utilizzo reale.
This commit is contained in:
@@ -167,6 +167,7 @@
|
|||||||
<div class="dialog-subtitle" v-if="!isPartitaFinita">Configura le formazioni per il prossimo set</div>
|
<div class="dialog-subtitle" v-if="!isPartitaFinita">Configura le formazioni per il prossimo set</div>
|
||||||
<div class="dialog-buttons">
|
<div class="dialog-buttons">
|
||||||
<button class="btn btn-cancel" @click="undoUltimoPoint()">INDIETRO</button>
|
<button class="btn btn-cancel" @click="undoUltimoPoint()">INDIETRO</button>
|
||||||
|
<button v-if="isPartitaFinita" class="btn btn-secondary" @click="generaReferto(state)">REFERTO</button>
|
||||||
<button v-if="isPartitaFinita" class="btn btn-confirm" @click="showSetVinto = false">CHIUDI</button>
|
<button v-if="isPartitaFinita" class="btn btn-confirm" @click="showSetVinto = false">CHIUDI</button>
|
||||||
<button v-else class="btn btn-confirm" @click="doNuovoSet()">VAI AL SET SUCCESSIVO</button>
|
<button v-else class="btn btn-confirm" @click="doNuovoSet()">VAI AL SET SUCCESSIVO</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -274,6 +275,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { createWsMixin } from '../wsMixin.js'
|
import { createWsMixin } from '../wsMixin.js'
|
||||||
|
import { generaReferto } from '../referto.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "ControllerPage",
|
name: "ControllerPage",
|
||||||
@@ -348,6 +350,7 @@ export default {
|
|||||||
window.removeEventListener('orientationchange', this._resizeHandler)
|
window.removeEventListener('orientationchange', this._resizeHandler)
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
generaReferto,
|
||||||
onWsMessage(msg) {
|
onWsMessage(msg) {
|
||||||
if (msg.type === 'error') this.showErrorFeedback(msg.message)
|
if (msg.type === 'error') this.showErrorFeedback(msg.message)
|
||||||
},
|
},
|
||||||
@@ -891,6 +894,19 @@ export default {
|
|||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn-secondary {
|
||||||
|
flex: 1;
|
||||||
|
background: rgba(255,255,255,0.14);
|
||||||
|
color: #ccc;
|
||||||
|
padding: 12px;
|
||||||
|
border-radius: 12px;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
border: 1px solid rgba(255,255,255,0.2);
|
||||||
|
cursor: pointer;
|
||||||
|
font-family: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
.btn-confirm {
|
.btn-confirm {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
background: #2e7d32;
|
background: #2e7d32;
|
||||||
|
|||||||
@@ -62,6 +62,13 @@ export function applyAction(state, action) {
|
|||||||
|
|
||||||
const servHome = servizio(s.sp.striscia)
|
const servHome = servizio(s.sp.striscia)
|
||||||
const cambioPalla = (team === "home") !== servHome
|
const cambioPalla = (team === "home") !== servHome
|
||||||
|
const setCorrente = s.sp.striscia.at(-1)
|
||||||
|
if (setCorrente.ris === '') {
|
||||||
|
setCorrente.formInizio = {
|
||||||
|
home: [...s.sp.form.home],
|
||||||
|
guest: [...s.sp.form.guest],
|
||||||
|
}
|
||||||
|
}
|
||||||
s.sp.striscia.at(-1).ris += team === 'home' ? 'h' : 'g'
|
s.sp.striscia.at(-1).ris += team === 'home' ? 'h' : 'g'
|
||||||
|
|
||||||
if (cambioPalla) {
|
if (cambioPalla) {
|
||||||
@@ -82,6 +89,7 @@ export function applyAction(state, action) {
|
|||||||
const wasCambioPalla = lastScorerShort !== prevServerShort
|
const wasCambioPalla = lastScorerShort !== prevServerShort
|
||||||
|
|
||||||
currentSet.ris = currentSet.ris.slice(0, -1)
|
currentSet.ris = currentSet.ris.slice(0, -1)
|
||||||
|
if (currentSet.ris === '') delete currentSet.formInizio
|
||||||
|
|
||||||
const lastScorer = lastScorerShort === 'h' ? 'home' : 'guest'
|
const lastScorer = lastScorerShort === 'h' ? 'home' : 'guest'
|
||||||
|
|
||||||
|
|||||||
+132
@@ -0,0 +1,132 @@
|
|||||||
|
function vincitoreSet(s) {
|
||||||
|
if (s.vinc === 'h' || s.vinc === 'g') return s.vinc
|
||||||
|
let h = 0, g = 0
|
||||||
|
for (const c of s.ris) c === 'h' ? h++ : g++
|
||||||
|
if (h > g) return 'h'
|
||||||
|
if (g > h) return 'g'
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generaReferto(state) {
|
||||||
|
const { sp, modalitaPartita } = state
|
||||||
|
const { nomi, striscia, form } = sp
|
||||||
|
|
||||||
|
const setReali = striscia.filter(s => !s._phantom)
|
||||||
|
|
||||||
|
const setVinti = { home: 0, guest: 0 }
|
||||||
|
for (const s of setReali) {
|
||||||
|
const v = vincitoreSet(s)
|
||||||
|
if (v === 'h') setVinti.home++
|
||||||
|
else if (v === 'g') setVinti.guest++
|
||||||
|
}
|
||||||
|
|
||||||
|
const dataOra = new Date().toLocaleString('it-IT', {
|
||||||
|
day: '2-digit', month: '2-digit', year: 'numeric',
|
||||||
|
hour: '2-digit', minute: '2-digit',
|
||||||
|
})
|
||||||
|
|
||||||
|
const giocatoreHtml = (n) => `<div class="giocatore">${n}</div>`
|
||||||
|
|
||||||
|
const formazioneHtml = (formazioneSet) => {
|
||||||
|
if (!formazioneSet) return '<em style="color:#999;font-size:11px">non disponibile</em>'
|
||||||
|
return `
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-team">
|
||||||
|
<div class="form-team-name">${nomi.home}</div>
|
||||||
|
<div class="giocatori">${formazioneSet.home.map(giocatoreHtml).join('')}</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-team">
|
||||||
|
<div class="form-team-name">${nomi.guest}</div>
|
||||||
|
<div class="giocatori">${formazioneSet.guest.map(giocatoreHtml).join('')}</div>
|
||||||
|
</div>
|
||||||
|
</div>`
|
||||||
|
}
|
||||||
|
|
||||||
|
const setsHtml = setReali.map((s, i) => {
|
||||||
|
let h = 0, g = 0
|
||||||
|
const punti = []
|
||||||
|
for (const c of s.ris) {
|
||||||
|
c === 'h' ? h++ : g++
|
||||||
|
punti.push({ chi: c, h, g })
|
||||||
|
}
|
||||||
|
|
||||||
|
const vinc = vincitoreSet(s)
|
||||||
|
const nomeVinc = vinc === 'h' ? nomi.home : vinc === 'g' ? nomi.guest : ''
|
||||||
|
const etichettaVinc = nomeVinc ? ` · vinto da <strong>${nomeVinc}</strong>` : ''
|
||||||
|
|
||||||
|
const puntiHtml = punti.map(p =>
|
||||||
|
`<span class="punto punto-${p.chi}">${p.h}-${p.g}</span>`
|
||||||
|
).join('')
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div class="set-section">
|
||||||
|
<div class="set-header">
|
||||||
|
Set ${i + 1} — ${nomi.home} <strong>${h}</strong> · <strong>${g}</strong> ${nomi.guest}${etichettaVinc}
|
||||||
|
</div>
|
||||||
|
<div class="form-inizio">
|
||||||
|
<div class="form-inizio-label">Formazione di partenza</div>
|
||||||
|
${formazioneHtml(s.formInizio)}
|
||||||
|
</div>
|
||||||
|
<div class="punti-grid">${puntiHtml || '<em style="color:#999;font-size:11px">Nessun punto registrato</em>'}</div>
|
||||||
|
</div>`
|
||||||
|
}).join('')
|
||||||
|
|
||||||
|
const html = `<!DOCTYPE html>
|
||||||
|
<html lang="it">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Referto — ${nomi.home} vs ${nomi.guest}</title>
|
||||||
|
<style>
|
||||||
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
body { font-family: Arial, Helvetica, sans-serif; color: #111; background: #fff; padding: 28px; font-size: 14px; }
|
||||||
|
@media print { body { padding: 12px; } }
|
||||||
|
|
||||||
|
.header { text-align: center; border-bottom: 2px solid #111; padding-bottom: 12px; margin-bottom: 20px; }
|
||||||
|
.titolo { font-size: 18px; font-weight: bold; letter-spacing: 3px; text-transform: uppercase; }
|
||||||
|
.meta { font-size: 12px; color: #666; margin-top: 5px; }
|
||||||
|
|
||||||
|
.squadre-row { display: flex; justify-content: space-around; align-items: center; margin: 18px 0 6px; }
|
||||||
|
.nome-sq { font-size: 22px; font-weight: bold; text-align: center; max-width: 40%; }
|
||||||
|
.vs { font-size: 16px; color: #999; }
|
||||||
|
.risultato { text-align: center; font-size: 36px; font-weight: bold; letter-spacing: 6px; margin-bottom: 4px; }
|
||||||
|
.modalita-label { text-align: center; font-size: 12px; color: #888; margin-bottom: 22px; }
|
||||||
|
|
||||||
|
.set-section { margin-bottom: 12px; border: 1px solid #ddd; border-radius: 4px; overflow: hidden; }
|
||||||
|
.set-header { background: #f5f5f5; padding: 7px 12px; font-size: 13px; border-bottom: 1px solid #ddd; }
|
||||||
|
.punti-grid { display: flex; flex-wrap: wrap; gap: 3px; padding: 8px 10px; }
|
||||||
|
.punto { display: inline-block; padding: 2px 5px; border-radius: 3px; font-size: 11px; font-family: 'Courier New', monospace; white-space: nowrap; }
|
||||||
|
.punto-h { background: #d0e8ff; color: #003a6e; }
|
||||||
|
.punto-g { background: #ffddc0; color: #6e2700; }
|
||||||
|
|
||||||
|
.form-inizio { padding: 8px 10px; border-bottom: 1px solid #eee; background: #fafafa; }
|
||||||
|
.form-inizio-label { font-size: 11px; font-weight: bold; letter-spacing: 0.5px; text-transform: uppercase; color: #888; margin-bottom: 7px; }
|
||||||
|
.form-row { display: flex; gap: 40px; }
|
||||||
|
.form-team { flex: 1; }
|
||||||
|
.form-team-name { font-weight: bold; font-size: 13px; margin-bottom: 6px; }
|
||||||
|
.giocatori { display: flex; gap: 5px; flex-wrap: wrap; }
|
||||||
|
.giocatore { background: #f0f0f0; border-radius: 50%; width: 30px; height: 30px; display: flex; align-items: center; justify-content: center; font-weight: bold; font-size: 12px; border: 1px solid #ccc; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="header">
|
||||||
|
<div class="titolo">Referto di Gara</div>
|
||||||
|
<div class="meta">${dataOra} · Modalità: ${modalitaPartita}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="squadre-row">
|
||||||
|
<div class="nome-sq">${nomi.home}</div>
|
||||||
|
<div class="vs">vs</div>
|
||||||
|
<div class="nome-sq">${nomi.guest}</div>
|
||||||
|
</div>
|
||||||
|
<div class="risultato">${setVinti.home} – ${setVinti.guest}</div>
|
||||||
|
<div class="modalita-label">set vinti</div>
|
||||||
|
|
||||||
|
${setsHtml}
|
||||||
|
</body>
|
||||||
|
</html>`
|
||||||
|
|
||||||
|
const w = window.open('', '_blank')
|
||||||
|
w.document.write(html)
|
||||||
|
w.document.close()
|
||||||
|
w.print()
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user