Feat(cambi): supporto cambi multipli con UI migliorata

Consente di effettuare fino a 2 cambi simultanei per squadra con una nuova
interfaccia utente più compatta e visuale. Gli input IN sono colorati di
verde, gli OUT di rosso, e una freccia indica la direzione del cambio.

La validazione permette cambi parziali (campi vuoti) ma richiede che ogni
cambio inserito sia completo (sia IN che OUT) e che almeno un cambio sia
presente per confermare
This commit is contained in:
2026-01-29 11:08:25 +01:00
parent 9df74a760f
commit f190db2161
3 changed files with 128 additions and 64 deletions

View File

@@ -75,29 +75,22 @@
<w-button class="ma2" @click="selezionaTeamCambi('guest')">{{ sp.nomi.guest }}</w-button> <w-button class="ma2" @click="selezionaTeamCambi('guest')">{{ sp.nomi.guest }}</w-button>
</w-flex> </w-flex>
</w-dialog> </w-dialog>
<w-dialog v-model="diaCambi.show" :width="520" @close="chiudiDialogCambi"> <w-dialog v-model="diaCambi.show" :width="360" @close="chiudiDialogCambi">
<div class="text-bold text-center mb2">CAMBI</div> <div class="cambi-dialog">
<table class="cambi-table"> <div class="cambi-title">{{ sp.nomi[diaCambi.team] }}: CAMBIO</div>
<thead> <div class="cambi-rows">
<tr> <div class="cambi-row">
<th></th> <w-input v-model="diaCambi[diaCambi.team].cambi[0].in" type="text" class="cambi-input cambi-in"></w-input>
<th>IN</th> <span class="cambi-arrow"></span>
<th>OUT</th> <w-input v-model="diaCambi[diaCambi.team].cambi[0].out" type="text" class="cambi-input cambi-out"></w-input>
</tr> </div>
</thead> <div class="cambi-row">
<tbody> <w-input v-model="diaCambi[diaCambi.team].cambi[1].in" type="text" class="cambi-input cambi-in"></w-input>
<tr v-if="diaCambi.team === 'home'"> <span class="cambi-arrow"></span>
<td class="row-label">{{ sp.nomi.home }}</td> <w-input v-model="diaCambi[diaCambi.team].cambi[1].out" type="text" class="cambi-input cambi-out"></w-input>
<td><w-input v-model="diaCambi.home.in" type="text" class="cambi-input"></w-input></td> </div>
<td><w-input v-model="diaCambi.home.out" type="text" class="cambi-input"></w-input></td> </div>
</tr> </div>
<tr v-if="diaCambi.team === 'guest'">
<td class="row-label">{{ sp.nomi.guest }}</td>
<td><w-input v-model="diaCambi.guest.in" type="text" class="cambi-input"></w-input></td>
<td><w-input v-model="diaCambi.guest.out" type="text" class="cambi-input"></w-input></td>
</tr>
</tbody>
</table>
<w-flex justify-end class="pa3"> <w-flex justify-end class="pa3">
<w-button bg-color="success" :disabled="!cambiConfermabili" @click="confermaCambi"> <w-button bg-color="success" :disabled="!cambiConfermabili" @click="confermaCambi">
CONFERMA CONFERMA

View File

@@ -14,8 +14,8 @@ export default {
diaCambi: { diaCambi: {
show: false, show: false,
team: "home", team: "home",
guest: { in: "", out: "" }, guest: { cambi: [{ in: "", out: "" }, { in: "", out: "" }] },
home: { in: "", out: "" }, home: { cambi: [{ in: "", out: "" }, { in: "", out: "" }] },
}, },
diaCambiTeam: { diaCambiTeam: {
show: false, show: false,
@@ -54,10 +54,25 @@ export default {
}, },
cambiConfermabili() { cambiConfermabili() {
const team = this.diaCambi.team; const team = this.diaCambi.team;
const teamIn = (this.diaCambi[team].in || "").trim(); const cambi = this.diaCambi[team].cambi || [];
const teamOut = (this.diaCambi[team].out || "").trim(); let hasComplete = false;
let allValid = true;
return !!teamIn && !!teamOut; 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: { methods: {
@@ -233,8 +248,10 @@ export default {
resettaCambi(team) { resettaCambi(team) {
const teams = team ? [team] : ["home", "guest"]; const teams = team ? [team] : ["home", "guest"];
teams.forEach((t) => { teams.forEach((t) => {
this.diaCambi[t].in = ""; this.diaCambi[t].cambi.forEach((cambio) => {
this.diaCambi[t].out = ""; cambio.in = "";
cambio.out = "";
});
}); });
}, },
apriDialogCambi() { apriDialogCambi() {
@@ -262,31 +279,38 @@ export default {
} }
const team = this.diaCambi.team; const team = this.diaCambi.team;
const cambio = { const cambi = (this.diaCambi[team].cambi || [])
team, .map((cambio) => ({
in: (this.diaCambi[team].in || "").trim(), team,
out: (this.diaCambi[team].out || "").trim(), in: (cambio.in || "").trim(),
}; out: (cambio.out || "").trim(),
}))
.filter((cambio) => cambio.in || cambio.out);
const form = this.sp.form[cambio.team].map((val) => String(val).trim()); const form = this.sp.form[team].map((val) => String(val).trim());
const formAggiornata = [...form];
if (form.includes(cambio.in)) { for (const cambio of cambi) {
this.$waveui.notify(`Numero ${cambio.in} già presente in formazione ${cambio.team}`, "warning"); if (cambio.in === cambio.out) {
return; this.$waveui.notify(`Numero IN e OUT uguali per ${cambio.team}`, "warning");
} return;
if (!form.includes(cambio.out)) { }
this.$waveui.notify(`Numero ${cambio.out} non presente in formazione ${cambio.team}`, "warning"); if (formAggiornata.includes(cambio.in)) {
return; this.$waveui.notify(`Numero ${cambio.in} già presente in formazione ${cambio.team}`, "warning");
} return;
if (cambio.in === cambio.out) { }
this.$waveui.notify(`Numero IN e OUT uguali per ${cambio.team}`, "warning"); if (!formAggiornata.includes(cambio.out)) {
return; 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);
}
} }
const idx = this.sp.form[cambio.team].findIndex((val) => String(val).trim() === cambio.out); this.sp.form[team] = formAggiornata;
if (idx !== -1) {
this.sp.form[cambio.team].splice(idx, 1, cambio.in);
}
this.chiudiDialogCambi(); this.chiudiDialogCambi();
}, },

View File

@@ -191,27 +191,74 @@ button:focus-visible {
margin: 0; margin: 0;
} }
.cambi-table { .cambi-rows {
width: 100%; display: flex;
border-collapse: collapse; flex-direction: column;
gap: 12px;
padding: 8px 0;
} }
.cambi-table th, .cambi-dialog {
.cambi-table td { padding: 8px 6px 2px;
padding: 8px; }
.cambi-title {
text-align: center; 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-table th { .cambi-row {
font-weight: bold; display: flex;
align-items: center;
justify-content: center;
gap: 12px;
} }
.cambi-table .row-label { .cambi-arrow {
text-align: left; font-weight: 700;
font-weight: bold; font-size: 18px;
width: 90px; line-height: 1;
padding: 6px 8px;
border-radius: 999px;
background: rgba(255, 255, 255, 0.08);
white-space: nowrap;
} }
.cambi-input { .cambi-input {
min-width: 110px; 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;
} }