Merge branch 'issue#19'

Migliora il referto di gara: nomi squadra al posto di A/B, icona
servizio a inizio set, allineamento "vinto da" e colori squadra
coerenti col segnapunti live.
This commit is contained in:
2026-07-15 10:26:50 +02:00
3 changed files with 29 additions and 13 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

+23 -12
View File
@@ -34,11 +34,11 @@ export function buildRefertoHtml(state, now = new Date()) {
return ` return `
<div class="form-row"> <div class="form-row">
<div class="form-team"> <div class="form-team">
<div class="form-team-name">${nomi.home}</div> <div class="form-team-name form-team-name-h">${nomi.home}</div>
<div class="giocatori">${formazioneSet.home.map(giocatoreHtml).join('')}</div> <div class="giocatori">${formazioneSet.home.map(giocatoreHtml).join('')}</div>
</div> </div>
<div class="form-team"> <div class="form-team">
<div class="form-team-name">${nomi.guest}</div> <div class="form-team-name form-team-name-g">${nomi.guest}</div>
<div class="giocatori">${formazioneSet.guest.map(giocatoreHtml).join('')}</div> <div class="giocatori">${formazioneSet.guest.map(giocatoreHtml).join('')}</div>
</div> </div>
</div>` </div>`
@@ -79,7 +79,11 @@ export function buildRefertoHtml(state, now = new Date()) {
const vinc = vincitoreSet(s) const vinc = vincitoreSet(s)
const nomeVinc = vinc === 'h' ? nomi.home : vinc === 'g' ? nomi.guest : '' const nomeVinc = vinc === 'h' ? nomi.home : vinc === 'g' ? nomi.guest : ''
const etichettaVinc = nomeVinc ? ` &nbsp;·&nbsp; vinto da <strong>${nomeVinc}</strong>` : '' const etichettaVinc = nomeVinc ? `<span class="set-vinc">vinto da <strong>${nomeVinc}</strong></span>` : ''
const servIcon = '<img src="/serv-nero.png" class="serv-icon" alt="Servizio">'
const servHomeIcon = s.serv === 'h' ? servIcon : ''
const servGuestIcon = s.serv === 'g' ? servIcon : ''
const puntiHtml = punti.map(p => const puntiHtml = punti.map(p =>
`<span class="punto punto-${p.chi}">${p.h}-${p.g}</span>` `<span class="punto punto-${p.chi}">${p.h}-${p.g}</span>`
@@ -90,7 +94,8 @@ export function buildRefertoHtml(state, now = new Date()) {
<div class="set-barra"><span>SET</span><span class="set-numero">${i + 1}</span></div> <div class="set-barra"><span>SET</span><span class="set-numero">${i + 1}</span></div>
<div class="set-corpo"> <div class="set-corpo">
<div class="set-header"> <div class="set-header">
${nomi.home} <strong>${h}</strong> · <strong>${g}</strong> ${nomi.guest}${etichettaVinc} <span>${servHomeIcon}${nomi.home} <strong>${h}</strong> · <strong>${g}</strong> ${nomi.guest}${servGuestIcon}</span>
${etichettaVinc}
</div> </div>
<div class="form-inizio"> <div class="form-inizio">
<div class="form-inizio-label">Formazione di partenza</div> <div class="form-inizio-label">Formazione di partenza</div>
@@ -130,7 +135,7 @@ export function buildRefertoHtml(state, now = new Date()) {
.meta { font-size: 11px; color: #333; margin-top: 3px; text-transform: uppercase; letter-spacing: 0.5px; } .meta { font-size: 11px; color: #333; margin-top: 3px; text-transform: uppercase; letter-spacing: 0.5px; }
.squadre-row { display: flex; align-items: center; gap: 14px; } .squadre-row { display: flex; align-items: center; gap: 14px; }
.badge-ab { display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px; border-radius: 50%; border: 2px solid #111; font-weight: bold; font-size: 12px; flex-shrink: 0; } .badge-ab { display: inline-flex; align-items: center; justify-content: center; height: 22px; padding: 0 8px; border-radius: 11px; border: 2px solid #111; font-weight: bold; font-size: 12px; flex-shrink: 0; }
.nome-sq { font-size: 18px; font-weight: bold; } .nome-sq { font-size: 18px; font-weight: bold; }
.vs { font-size: 12px; color: #666; font-weight: bold; } .vs { font-size: 12px; color: #666; font-weight: bold; }
@@ -143,12 +148,14 @@ export function buildRefertoHtml(state, now = new Date()) {
.set-barra span:first-child { writing-mode: vertical-rl; transform: rotate(180deg); font-weight: bold; font-size: 11px; letter-spacing: 2px; } .set-barra span:first-child { writing-mode: vertical-rl; transform: rotate(180deg); font-weight: bold; font-size: 11px; letter-spacing: 2px; }
.set-numero { font-weight: bold; font-size: 15px; } .set-numero { font-weight: bold; font-size: 15px; }
.set-corpo { flex: 1; min-width: 0; } .set-corpo { flex: 1; min-width: 0; }
.set-header { background: #eaf7fb; padding: 6px 12px; font-size: 13px; border-bottom: 1px solid #111; } .set-header { background: #eaf7fb; padding: 6px 12px; font-size: 13px; border-bottom: 1px solid #111; display: flex; justify-content: space-between; align-items: center; gap: 10px; }
.set-vinc { flex-shrink: 0; }
.serv-icon { width: 12px; height: 12px; vertical-align: middle; margin: 0 3px; }
.punti-grid { display: flex; flex-wrap: wrap; gap: 0; padding: 0; border-top: 1px solid #111; } .punti-grid { display: flex; flex-wrap: wrap; gap: 0; padding: 0; border-top: 1px solid #111; }
.punto { display: inline-flex; align-items: center; justify-content: center; min-width: 34px; padding: 3px 4px; font-size: 11px; font-family: 'Courier New', monospace; white-space: nowrap; border-right: 1px solid #ccc; border-bottom: 1px solid #ccc; } .punto { display: inline-flex; align-items: center; justify-content: center; min-width: 34px; padding: 3px 4px; font-size: 11px; font-family: 'Courier New', monospace; white-space: nowrap; border-right: 1px solid #ccc; border-bottom: 1px solid #ccc; }
.punto-h { background: #d0e8ff; color: #003a6e; } .punto-h { background: #f5c518; color: #111; }
.punto-g { background: #ffddc0; color: #6e2700; } .punto-g { background: #2196f3; color: #fff; }
.eventi-set { padding: 6px 12px; border-bottom: 1px solid #111; } .eventi-set { padding: 6px 12px; border-bottom: 1px solid #111; }
.eventi-label { font-size: 10px; font-weight: bold; letter-spacing: 0.5px; text-transform: uppercase; color: #0f8faf; margin-bottom: 4px; } .eventi-label { font-size: 10px; font-weight: bold; letter-spacing: 0.5px; text-transform: uppercase; color: #0f8faf; margin-bottom: 4px; }
@@ -160,6 +167,8 @@ export function buildRefertoHtml(state, now = new Date()) {
.form-row { display: flex; gap: 40px; } .form-row { display: flex; gap: 40px; }
.form-team { flex: 1; } .form-team { flex: 1; }
.form-team-name { font-weight: bold; font-size: 12px; margin-bottom: 5px; } .form-team-name { font-weight: bold; font-size: 12px; margin-bottom: 5px; }
.form-team-name-h { color: #f5c518; }
.form-team-name-g { color: #2196f3; }
.giocatori { display: flex; gap: 5px; flex-wrap: wrap; } .giocatori { display: flex; gap: 5px; flex-wrap: wrap; }
.giocatore { background: #fff; border-radius: 50%; width: 26px; height: 26px; display: flex; align-items: center; justify-content: center; font-weight: bold; font-size: 11px; border: 2px solid #111; } .giocatore { background: #fff; border-radius: 50%; width: 26px; height: 26px; display: flex; align-items: center; justify-content: center; font-weight: bold; font-size: 11px; border: 2px solid #111; }
@@ -181,11 +190,11 @@ export function buildRefertoHtml(state, now = new Date()) {
<div class="meta">${dataOra} &nbsp;·&nbsp; Modalità: ${modalitaPartita}</div> <div class="meta">${dataOra} &nbsp;·&nbsp; Modalità: ${modalitaPartita}</div>
</div> </div>
<div class="squadre-row"> <div class="squadre-row">
<span class="badge-ab">A</span> <span class="badge-ab">Casa</span>
<span class="nome-sq">${nomi.home}</span> <span class="nome-sq">${nomi.home}</span>
<span class="vs">vs</span> <span class="vs">vs</span>
<span class="nome-sq">${nomi.guest}</span> <span class="nome-sq">${nomi.guest}</span>
<span class="badge-ab">B</span> <span class="badge-ab">Ospiti</span>
</div> </div>
</div> </div>
@@ -195,7 +204,7 @@ export function buildRefertoHtml(state, now = new Date()) {
<div class="finale-box"> <div class="finale-box">
<div class="finale-box-header">Risultato finale</div> <div class="finale-box-header">Risultato finale</div>
<table class="finale-tabella"> <table class="finale-tabella">
<thead><tr><th>Set</th><th>A</th><th>B</th></tr></thead> <thead><tr><th>Set</th><th>${nomi.home}</th><th>${nomi.guest}</th></tr></thead>
<tbody>${risultatoSetHtml}</tbody> <tbody>${risultatoSetHtml}</tbody>
</table> </table>
<div class="finale-vince">Vince ${setVinti.home >= setVinti.guest ? nomi.home : nomi.guest} &nbsp;${setVinti.home} ${setVinti.guest}</div> <div class="finale-vince">Vince ${setVinti.home >= setVinti.guest ? nomi.home : nomi.guest} &nbsp;${setVinti.home} ${setVinti.guest}</div>
@@ -219,5 +228,7 @@ export function generaReferto(state) {
const w = window.open('', '_blank') const w = window.open('', '_blank')
w.document.write(html) w.document.write(html)
w.document.close() w.document.close()
w.print() // Aspetta il caricamento delle immagini (icona servizio) prima di stampare,
// altrimenti la stampa parte con risorse ancora in caricamento e appaiono vuote.
w.addEventListener('load', () => w.print())
} }
@@ -11,10 +11,12 @@ describe('generaReferto (referto.js)', () => {
vi.restoreAllMocks() vi.restoreAllMocks()
}) })
it('apre una nuova scheda, scrive il referto e avvia la stampa', () => { it('apre una nuova scheda, scrive il referto e avvia la stampa dopo il caricamento', () => {
const listeners = {}
const popup = { const popup = {
document: { write: vi.fn(), close: vi.fn() }, document: { write: vi.fn(), close: vi.fn() },
print: vi.fn(), print: vi.fn(),
addEventListener: vi.fn((evt, cb) => { listeners[evt] = cb }),
} }
const openSpy = vi.spyOn(window, 'open').mockReturnValue(popup) const openSpy = vi.spyOn(window, 'open').mockReturnValue(popup)
@@ -26,6 +28,9 @@ describe('generaReferto (referto.js)', () => {
expect(popup.document.write).toHaveBeenCalledTimes(1) expect(popup.document.write).toHaveBeenCalledTimes(1)
expect(popup.document.write.mock.calls[0][0]).toContain('Antoniana') expect(popup.document.write.mock.calls[0][0]).toContain('Antoniana')
expect(popup.document.close).toHaveBeenCalledTimes(1) expect(popup.document.close).toHaveBeenCalledTimes(1)
expect(popup.print).not.toHaveBeenCalled()
listeners.load()
expect(popup.print).toHaveBeenCalledTimes(1) expect(popup.print).toHaveBeenCalledTimes(1)
}) })
}) })