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-flex>
</w-dialog>
<w-dialog v-model="diaCambi.show" :width="520" @close="chiudiDialogCambi">
<div class="text-bold text-center mb2">CAMBI</div>
<table class="cambi-table">
<thead>
<tr>
<th></th>
<th>IN</th>
<th>OUT</th>
</tr>
</thead>
<tbody>
<tr v-if="diaCambi.team === 'home'">
<td class="row-label">{{ sp.nomi.home }}</td>
<td><w-input v-model="diaCambi.home.in" type="text" class="cambi-input"></w-input></td>
<td><w-input v-model="diaCambi.home.out" type="text" class="cambi-input"></w-input></td>
</tr>
<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-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

View File

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

View File

@@ -191,27 +191,74 @@ button:focus-visible {
margin: 0;
}
.cambi-table {
width: 100%;
border-collapse: collapse;
.cambi-rows {
display: flex;
flex-direction: column;
gap: 12px;
padding: 8px 0;
}
.cambi-table th,
.cambi-table td {
padding: 8px;
.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-table th {
font-weight: bold;
.cambi-row {
display: flex;
align-items: center;
justify-content: center;
gap: 12px;
}
.cambi-table .row-label {
text-align: left;
font-weight: bold;
width: 90px;
.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: 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;
}