feat: Add home/away game labels and participant count to friendly match schedule
Some checks failed
Deploy tt-tagebuch / deploy (push) Has been cancelled
Some checks failed
Deploy tt-tagebuch / deploy (push) Has been cancelled
This commit is contained in:
@@ -1484,6 +1484,16 @@
|
||||
"homeTeam": "Heimmannschaft",
|
||||
"guestTeam": "Gastmannschaft",
|
||||
"result": "Ergebnis",
|
||||
"homeGame": "Heimspiel",
|
||||
"participants": "Teilnehmer",
|
||||
"swapButton": "Tauschen",
|
||||
"away": "Auswärts",
|
||||
"homeLabel": "Heimspiel",
|
||||
"awayLabel": "Auswärtsspiel",
|
||||
"swapSuccess": "Heim/Auswärts getauscht",
|
||||
"swapFailed": "Heim/Auswärts konnte nicht getauscht werden.",
|
||||
"plannedShort": "geplant",
|
||||
"readyShort": "bereit",
|
||||
"ageClass": "Altersklasse",
|
||||
"code": "Code",
|
||||
"homePin": "Heim-PIN",
|
||||
|
||||
@@ -103,12 +103,14 @@
|
||||
<th v-if="!friendlyOnly">{{ $t('schedule.code') }}</th>
|
||||
<th>{{ friendlyOnly ? 'Aktionen' : $t('schedule.homePin') }}</th>
|
||||
<th v-if="!friendlyOnly">{{ $t('schedule.guestPin') }}</th>
|
||||
<th v-if="friendlyOnly"></th>
|
||||
<th v-if="friendlyOnly">{{ $t('schedule.homeGame') || 'Heimspiel' }}</th>
|
||||
<th v-if="friendlyOnly">{{ $t('schedule.participants') || 'Teilnehmer' }}</th>
|
||||
<th v-if="friendlyOnly"> </th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="match in matches" :key="match.id"
|
||||
@click="openPlayerSelectionDialog(match)"
|
||||
@click="friendlyOnly ? openFriendlyMatchDialog(match) : openPlayerSelectionDialog(match)"
|
||||
:class="getRowClass(match.date)"
|
||||
style="cursor: pointer;">
|
||||
<td class="location-info-cell">
|
||||
@@ -152,6 +154,25 @@
|
||||
:title="$t('schedule.copyCode') + ': ' + match.code">{{ match.code }}</span>
|
||||
<span v-else-if="!match.isFriendly" class="no-data">-</span>
|
||||
</td>
|
||||
<td v-if="friendlyOnly" class="participants-cell">
|
||||
<button type="button" class="btn-secondary" @click.stop="openPlayerSelectionDialog(match)">
|
||||
{{ (Array.isArray(match.playersPlanned) ? match.playersPlanned.length : (match.playersPlanned ? JSON.parse(match.playersPlanned).length : 0)) }} {{ $t('schedule.plannedShort') || 'geplant' }}
|
||||
·
|
||||
{{ (Array.isArray(match.playersReady) ? match.playersReady.length : (match.playersReady ? JSON.parse(match.playersReady).length : 0)) }} {{ $t('schedule.readyShort') || 'bereit' }}
|
||||
</button>
|
||||
</td>
|
||||
|
||||
<td v-if="friendlyOnly" class="home-away-cell">
|
||||
<div class="home-away-status">
|
||||
<span class="status-label">
|
||||
{{ isOurClubPlayingHome(match) ? $t('schedule.homeLabel') || $t('schedule.homeGame') : $t('schedule.awayLabel') || $t('schedule.away') }}
|
||||
</span>
|
||||
<button type="button" class="btn-small" @click.stop="toggleHomeAway(match)">
|
||||
{{ $t('schedule.swapButton') }}
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td class="pin-cell">
|
||||
<div v-if="match.isFriendly" class="friendly-actions-cell">
|
||||
<button type="button" class="btn-secondary" @click.stop="openFriendlyResultDialog(match)">Ergebnis</button>
|
||||
@@ -803,6 +824,20 @@ export default {
|
||||
return teamName.startsWith(this.currentClubName);
|
||||
},
|
||||
|
||||
isOurClubPlayingHome(match) {
|
||||
const club = (this.currentClubName || '').toString().trim().toLowerCase();
|
||||
if (!club || !match) return false;
|
||||
const homeName = (match.homeTeam?.name || '').toString().toLowerCase();
|
||||
const guestName = (match.guestTeam?.name || '').toString().toLowerCase();
|
||||
const homeHas = homeName.includes(club);
|
||||
const guestHas = guestName.includes(club);
|
||||
if (homeHas && guestHas) return true; // both teams from our club -> treat as home
|
||||
if (homeHas) return true;
|
||||
if (guestHas) return false;
|
||||
// Fallback: if neither includes club name, prefer original startsWith heuristic
|
||||
return homeName.startsWith(this.currentClubName || '') || false;
|
||||
},
|
||||
|
||||
// Dialog Helper Methods
|
||||
async showInfo(title, message, details = '', type = 'info') {
|
||||
this.infoDialog = {
|
||||
@@ -1426,6 +1461,40 @@ export default {
|
||||
await this.showInfo('Fehler', getSafeErrorMessage(error, 'Freundschaftsspiel konnte nicht gespeichert werden.'), '', 'error');
|
||||
}
|
||||
},
|
||||
async toggleHomeAway(match) {
|
||||
if (!match || !match.id) return;
|
||||
const originalHome = match.homeTeam ? { ...match.homeTeam } : { name: '' };
|
||||
const originalGuest = match.guestTeam ? { ...match.guestTeam } : { name: '' };
|
||||
// Optimistic UI update: swap locally
|
||||
try {
|
||||
if (match.homeTeam && match.guestTeam) {
|
||||
const tmp = match.homeTeam.name;
|
||||
match.homeTeam.name = match.guestTeam.name;
|
||||
match.guestTeam.name = tmp;
|
||||
} else {
|
||||
match.homeTeam = { name: originalGuest.name };
|
||||
match.guestTeam = { name: originalHome.name };
|
||||
}
|
||||
// Trigger reactivity: replace in matches array
|
||||
const idx = this.matches.findIndex(m => m.id === match.id);
|
||||
if (idx !== -1) this.matches.splice(idx, 1, { ...match });
|
||||
|
||||
const payload = {
|
||||
homeTeamName: match.homeTeam?.name || '',
|
||||
guestTeamName: match.guestTeam?.name || ''
|
||||
};
|
||||
await apiClient.put(`/friendly-matches/${this.currentClub}/${match.id}`, payload);
|
||||
} catch (error) {
|
||||
console.error('toggleHomeAway error:', error);
|
||||
// Revert optimistic change
|
||||
const idx = this.matches.findIndex(m => m.id === match.id);
|
||||
if (idx !== -1) {
|
||||
const reverted = { ...match, homeTeam: originalHome, guestTeam: originalGuest };
|
||||
this.matches.splice(idx, 1, reverted);
|
||||
}
|
||||
await this.showInfo(this.$t('messages.error'), getSafeErrorMessage(error, this.$t('schedule.swapFailed') || 'Heim/Auswärts konnte nicht getauscht werden.'), '', 'error');
|
||||
}
|
||||
},
|
||||
async deleteFriendlyMatch() {
|
||||
if (!this.friendlyMatchDialog.editingId) return;
|
||||
const confirmed = await this.showConfirm('Freundschaftsspiel löschen', 'Soll dieses Freundschaftsspiel gelöscht werden?', '', 'warning');
|
||||
|
||||
@@ -7586,6 +7586,20 @@ private fun DiaryPlanEditableCard(
|
||||
modifier = Modifier.width(DiaryPlanColStart),
|
||||
)
|
||||
Row(modifier = Modifier.weight(1f), verticalAlignment = Alignment.CenterVertically) {
|
||||
if (hasMainVisual) {
|
||||
DiaryPlanQuickIconAction(
|
||||
imageVector = Icons.Filled.Visibility,
|
||||
contentDescription = tr("diary.showImage", "Bild/Zeichnung anzeigen"),
|
||||
enabled = !planMutating,
|
||||
onClick = {
|
||||
if (drawingRaw != null) {
|
||||
onViewDrawing(drawingRaw, title)
|
||||
} else {
|
||||
mainImageUrl?.let(onOpenImage)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
Text(
|
||||
title,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
@@ -7602,22 +7616,6 @@ private fun DiaryPlanEditableCard(
|
||||
maxLines = 1,
|
||||
)
|
||||
}
|
||||
// Always show a quick icon action for viewing/creating a drawing.
|
||||
DiaryPlanQuickIconAction(
|
||||
imageVector = Icons.Filled.Visibility,
|
||||
contentDescription = tr("diary.showImage", "Bild/Zeichnung anzeigen"),
|
||||
enabled = !planMutating,
|
||||
onClick = {
|
||||
if (drawingRaw != null) {
|
||||
onViewDrawing(drawingRaw, title)
|
||||
} else if (mainImageUrl != null) {
|
||||
onOpenImage(mainImageUrl)
|
||||
} else {
|
||||
// no visual present yet -> open drawing editor to create one
|
||||
onOpenDrawing()
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
Text(
|
||||
groupLine ?: "—",
|
||||
@@ -7702,6 +7700,20 @@ private fun DiaryPlanEditableCard(
|
||||
modifier = Modifier.fillMaxWidth().padding(start = DiaryPlanColStart, top = 0.dp, bottom = 0.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
if (hasNestedVisual) {
|
||||
DiaryPlanQuickIconAction(
|
||||
imageVector = Icons.Filled.Visibility,
|
||||
contentDescription = tr("diary.showImage", "Bild/Zeichnung anzeigen"),
|
||||
enabled = !planMutating,
|
||||
onClick = {
|
||||
if (nestedDrawingRaw != null) {
|
||||
onViewNestedDrawing(nestedDrawingRaw, line)
|
||||
} else {
|
||||
nestedImageUrl?.let(onOpenImage)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
Text(
|
||||
"· $line",
|
||||
style = MaterialTheme.typography.caption,
|
||||
@@ -7720,20 +7732,6 @@ private fun DiaryPlanEditableCard(
|
||||
}
|
||||
val nid = ga.id
|
||||
if (nid != null) {
|
||||
if (hasNestedVisual) {
|
||||
DiaryPlanQuickIconAction(
|
||||
imageVector = Icons.Filled.Visibility,
|
||||
contentDescription = tr("diary.showImage", "Bild/Zeichnung anzeigen"),
|
||||
enabled = !planMutating,
|
||||
onClick = {
|
||||
if (nestedDrawingRaw != null) {
|
||||
onViewNestedDrawing(nestedDrawingRaw, line)
|
||||
} else {
|
||||
nestedImageUrl?.let(onOpenImage)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
DiaryPlanQuickIconAction(
|
||||
imageVector = Icons.Filled.Edit,
|
||||
contentDescription = tr("common.edit", "Bearbeiten"),
|
||||
|
||||
Reference in New Issue
Block a user