feat(MatchReportApiDialog, ScheduleLayoutShell, ScheduleView): enhance lineup management and layout improvements

- Updated MatchReportApiDialog.vue to improve lineup tab accessibility based on team status.
- Enhanced player selection filtering to ensure only valid players are displayed in lineups.
- Modified ScheduleLayoutShell.vue to adjust overflow properties for better layout handling.
- Improved ScheduleView.vue with new methods for sorting matches by date and combining league matches, enhancing match display and user experience.
- Added sticky header styling for better visibility in match lists.
This commit is contained in:
Torsten Schulz (local)
2026-03-25 10:12:50 +01:00
parent 02f1bed452
commit 64090d9ff0
3 changed files with 376 additions and 34 deletions

View File

@@ -27,15 +27,15 @@
<span>{{ $t('matchReportApi.general') }}</span>
</button>
<button class="section-btn" :class="{ active: activeSection === 'homeLineup', certified: isHomeLineupCertified, disabled: teamNotAppeared === 'home' }"
@click="setActiveSection('homeLineup')" :disabled="teamNotAppeared === 'home'">
<button class="section-btn" :class="{ active: activeSection === 'homeLineup', certified: isHomeLineupCertified, disabled: !canOpenLineupTab('home') }"
@click="setActiveSection('homeLineup')" :disabled="!canOpenLineupTab('home')">
<div class="section-icon">👥</div>
<span>{{ $t('matchReportApi.homeLineup') }}</span>
<span v-if="isHomeLineupCertified" class="certified-badge"></span>
</button>
<button class="section-btn" :class="{ active: activeSection === 'guestLineup', certified: isGuestLineupCertified, disabled: teamNotAppeared === 'guest' }"
@click="setActiveSection('guestLineup')" :disabled="teamNotAppeared === 'guest'">
<button class="section-btn" :class="{ active: activeSection === 'guestLineup', certified: isGuestLineupCertified, disabled: !canOpenLineupTab('guest') }"
@click="setActiveSection('guestLineup')" :disabled="!canOpenLineupTab('guest')">
<div class="section-icon">👥</div>
<span>{{ $t('matchReportApi.guestLineup') }}</span>
<span v-if="isGuestLineupCertified" class="certified-badge"></span>
@@ -160,7 +160,7 @@
<div v-if="meetingDetails && meetingDetails.teamLineupHomePlayers" class="lineup-section">
<h4>{{ $t('matchReportApi.selectedPlayers') }}</h4>
<div class="players-grid">
<div v-for="player in meetingDetails.teamLineupHomePlayers.filter(p => p.isSelected)"
<div v-for="player in meetingDetails.teamLineupHomePlayers.filter(p => p.isSelected && p.nuLigaPersonId > 0)"
:key="player.nuLigaPersonId" class="player-card selected">
<div class="player-main" @click="togglePlayerSelection(player, 'home')">
<div class="player-name">{{ player.firstname }} {{ player.lastname }}</div>
@@ -254,7 +254,7 @@
<div v-if="meetingDetails && meetingDetails.teamLineupGuestPlayers" class="lineup-section">
<h4>{{ $t('matchReportApi.selectedPlayers') }}</h4>
<div class="players-grid">
<div v-for="player in meetingDetails.teamLineupGuestPlayers.filter(p => p.isSelected)"
<div v-for="player in meetingDetails.teamLineupGuestPlayers.filter(p => p.isSelected && p.nuLigaPersonId > 0)"
:key="player.nuLigaPersonId" class="player-card selected">
<div class="player-main" @click="togglePlayerSelection(player, 'guest')">
<div class="player-name">{{ player.firstname }} {{ player.lastname }}</div>
@@ -849,7 +849,7 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
if (this.teamNotAppeared !== null) {
return false;
}
return this.canOpenNextStages;
return this.areBothLineupsCertified();
},
canOpenCompletionStage() {
if (this.isMatchCompleted) {
@@ -869,6 +869,12 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
// Spezielle Behandlung für Braunschweiger System
if (matrixConfig && typeof matrixConfig === 'object' && matrixConfig.matrices) {
const serverMatrix = this.getBraunschweigerServerMatrix();
if (Array.isArray(serverMatrix) && serverMatrix.length > 0) {
console.log('🎯 Braunschweiger System: verwende Server-Matrix', serverMatrix);
return serverMatrix;
}
const homePlayerCount = this.getEffectivePlayerCount('home');
const guestPlayerCount = this.getEffectivePlayerCount('guest');
@@ -876,6 +882,7 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
const playerCountKey = `${homePlayerCount}-${guestPlayerCount}`;
console.log(`🎯 Braunschweiger System: ${homePlayerCount} vs ${guestPlayerCount} → Key: ${playerCountKey}`);
this.logBraunschweigerDebugSnapshot(`Matrix-Auswahl ${playerCountKey}`);
// Verwende spezifische Matrix oder Fallback
return matrixConfig.matrices[playerCountKey] || matrixConfig.default || [];
@@ -950,7 +957,7 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
}
if (newValue === 'home') {
this.isHomeLineupCertified = false;
this.setLineupCertificationState('home', false);
this.homePin = '';
this.finalHomePin = '';
this.activeSection = 'completion';
@@ -958,7 +965,7 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
}
if (newValue === 'guest') {
this.isGuestLineupCertified = false;
this.setLineupCertificationState('guest', false);
this.guestPin = '';
this.finalGuestPin = '';
this.activeSection = 'completion';
@@ -971,6 +978,41 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
}
},
methods: {
logBraunschweigerDebugSnapshot(context, payload = this.meetingDetails) {
if (!payload || typeof payload !== 'object') {
console.log(`[Braunschweiger Debug] ${context}: keine Daten`);
return;
}
const summarizeTeam = (teamKey) => {
const players = Array.isArray(payload[teamKey]) ? payload[teamKey] : [];
return players.map(player => ({
id: player?.nuLigaPersonId ?? null,
name: [player?.firstname || player?.firstName || '', player?.lastname || player?.lastName || ''].filter(Boolean).join(' ').trim(),
selected: Boolean(player?.isSelected),
positionSingle: player?.positionSingle ?? null,
positionDouble: player?.positionDouble ?? null,
positions: Array.isArray(player?.positions) ? [...player.positions] : []
}));
};
const matches = Array.isArray(payload.matches)
? payload.matches.slice(0, 10).map((match, index) => ({
index: index + 1,
homePlayer: match?.homePlayer ?? '',
guestPlayer: match?.guestPlayer ?? '',
matchNumber: match?.matchNumber ?? null
}))
: [];
console.log(`[Braunschweiger Debug] ${context}`, {
playMode: payload.playMode || this.meetingData?.playMode || this.meetingData?.matchSystem || this.meetingData?.system || null,
homePlayers: summarizeTeam('teamLineupHomePlayers'),
guestPlayers: summarizeTeam('teamLineupGuestPlayers'),
matches
});
},
getFriendlySubmitErrorMessage(message) {
const normalized = (message || '').toString();
@@ -1696,7 +1738,9 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
const result = await response.json();
if (result?.object) {
this.meetingDetails = JSON.parse(JSON.stringify(result.object));
const hydratedResult = JSON.parse(JSON.stringify(result.object));
this.applyCurrentLineupStateToPayload(hydratedResult);
this.meetingDetails = hydratedResult;
this.meetingData = {
...(this.meetingData || {}),
...JSON.parse(JSON.stringify(result.object))
@@ -1773,6 +1817,12 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
matchData.guestPin = this.finalGuestPin.trim();
}
const signature = this.ensureSignatureContainer(matchData);
if (signature) {
signature.lineupSignatureHome = this.isHomeLineupCertified ? 'confirmed' : '';
signature.lineupSignatureGuest = this.isGuestLineupCertified ? 'confirmed' : '';
}
// Zwischenstände dürfen nie als bereits freigegeben an nuscore zurückgeschickt werden.
matchData.isCompleted = finalizeReport;
@@ -1786,7 +1836,10 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
matchData.playerCountHome = this.getSelectedPlayerCount('home');
matchData.playerCountGuest = this.getSelectedPlayerCount('guest');
// 1.1 Player-Positionen aktualisieren (Einzel + Doppel Positionen erfassen)
// 1.1 Lokalen Aufstellungszustand in den Payload übernehmen
this.applyCurrentLineupStateToPayload(matchData);
// 1.2 Player-Positionen aktualisieren (Einzel + Doppel Positionen erfassen)
this.updatePlayerPositions(matchData);
// 2. Zeitangaben aktualisieren
@@ -1920,8 +1973,11 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
// Verwende positionSingle falls verfügbar, sonst rank für Einzel-Position
const singlePos = player.positionSingle || player.rank;
if (singlePos) {
player.positionSingle = singlePos;
positions.push(`E${singlePos}`);
}
} else {
player.positionSingle = null;
}
// Doppel-Position NUR hinzufügen wenn Spieler Doppel-Position hat
@@ -1944,8 +2000,11 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
// Verwende positionSingle falls verfügbar, sonst rank für Einzel-Position
const singlePos = player.positionSingle || player.rank;
if (singlePos) {
player.positionSingle = singlePos;
positions.push(`E${singlePos}`);
}
} else {
player.positionSingle = null;
}
// Doppel-Position NUR hinzufügen wenn Spieler Doppel-Position hat
@@ -1961,16 +2020,62 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
console.log('✅ Player-Positionen aktualisiert');
},
applyCurrentLineupStateToPayload(target) {
if (!target || typeof target !== 'object' || !this.meetingDetails) {
return;
}
const copyTeamState = (teamKey) => {
if (!Array.isArray(this.meetingDetails[teamKey]) || !Array.isArray(target[teamKey])) {
return;
}
const localByPersonId = new Map(
this.meetingDetails[teamKey]
.filter(player => player && player.nuLigaPersonId != null)
.map(player => [String(player.nuLigaPersonId), player])
);
target[teamKey] = target[teamKey].map((player) => {
if (!player || player.nuLigaPersonId == null) {
return player;
}
const localPlayer = localByPersonId.get(String(player.nuLigaPersonId));
if (!localPlayer) {
return player;
}
return {
...player,
isSelected: Boolean(localPlayer.isSelected),
positionSingle: localPlayer.isSelected ? (localPlayer.positionSingle || null) : null,
positionDouble: localPlayer.isSelected ? (localPlayer.positionDouble || null) : null,
positions: Array.isArray(localPlayer.positions) ? [...localPlayer.positions] : []
};
});
};
copyTeamState('teamLineupHomePlayers');
copyTeamState('teamLineupGuestPlayers');
const signature = this.ensureSignatureContainer(target);
if (signature) {
signature.lineupSignatureHome = this.isHomeLineupCertified ? 'confirmed' : '';
signature.lineupSignatureGuest = this.isGuestLineupCertified ? 'confirmed' : '';
}
},
// Zähle die ausgewählten Spieler für ein Team
getSelectedPlayerCount(team) {
try {
if (team === 'home') {
const homePlayers = this.meetingDetails?.teamLineupHomePlayers || this.teamLineupHomePlayers || [];
return homePlayers.filter(p => p.isSelected === true).length;
return homePlayers.filter(p => p.isSelected === true && p.nuLigaPersonId > 0).length;
} else if (team === 'guest') {
const guestPlayers = this.meetingDetails?.teamLineupGuestPlayers || this.teamLineupGuestPlayers || [];
return guestPlayers.filter(p => p.isSelected === true).length;
return guestPlayers.filter(p => p.isSelected === true && p.nuLigaPersonId > 0).length;
}
return 0;
} catch (error) {
@@ -2006,6 +2111,32 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
}
},
formatMatchPlayerName(player) {
if (!player || typeof player !== 'object') {
return '';
}
return [player.firstname || player.firstName || '', player.lastname || player.lastName || '']
.filter(Boolean)
.join(' ')
.trim();
},
formatMeetingMatchSide(match, side) {
if (!match || typeof match !== 'object') {
return '';
}
const firstPlayer = side === 'home' ? match.playerA1 : match.playerB1;
const secondPlayer = side === 'home' ? match.playerA2 : match.playerB2;
const firstName = this.formatMatchPlayerName(firstPlayer);
const secondName = this.formatMatchPlayerName(secondPlayer);
if (firstName && secondName) {
return `${firstName} / ${secondName}`;
}
return firstName || secondName || '';
},
getMatchCountForPlayMode(playMode) {
// Bestimme Anzahl der Spiele basierend auf Spielsystem
switch (playMode) {
@@ -2083,6 +2214,7 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
if (detailsResponse.ok) {
this.meetingDetails = await detailsResponse.json();
console.log('[nuscore] /meetingdetails response:', this.meetingDetails);
this.logBraunschweigerDebugSnapshot('Server /meetingdetails');
}
} catch (detailsError) {
console.warn('⚠️ Detaillierte Meeting-Daten konnten nicht geladen werden:', detailsError);
@@ -2166,6 +2298,21 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
const src = sourceMatches[i] || {};
const dst = this.results[i];
const resolvedHomeName = this.formatMeetingMatchSide(src, 'home');
const resolvedGuestName = this.formatMeetingMatchSide(src, 'guest');
const fallbackHomeName = typeof src.homePlayer === 'string' ? src.homePlayer.trim() : '';
const fallbackGuestName = typeof src.guestPlayer === 'string' ? src.guestPlayer.trim() : '';
if (typeof src.name === 'string' && src.name.trim() !== '') {
dst.label = src.name.trim();
}
if (resolvedHomeName || fallbackHomeName) {
dst.homeName = resolvedHomeName || fallbackHomeName;
}
if (resolvedGuestName || fallbackGuestName) {
dst.guestName = resolvedGuestName || fallbackGuestName;
}
if (!dst.sets || !Array.isArray(dst.sets)) {
this.$set(dst, 'sets', ['', '', '', '', '']);
}
@@ -2233,6 +2380,36 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
}
},
ensureSignatureContainer(target) {
if (!target || typeof target !== 'object') {
return null;
}
if (!target.signature || typeof target.signature !== 'object') {
target.signature = {};
}
return target.signature;
},
setLineupCertificationState(team, certified) {
const teamSuffix = team === 'home' ? 'Home' : 'Guest';
const signatureField = `lineupSignature${teamSuffix}`;
const signatureValue = certified ? 'confirmed' : '';
if (team === 'home') {
this.isHomeLineupCertified = certified;
} else {
this.isGuestLineupCertified = certified;
}
[this.meetingDetails, this.meetingData].forEach((source) => {
const signature = this.ensureSignatureContainer(source);
if (!signature) {
return;
}
signature[signatureField] = signatureValue;
});
},
normalizeCertificationValue(value) {
if (typeof value === 'boolean') return value;
if (typeof value === 'number') {
@@ -2351,6 +2528,13 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
});
return {
matches,
signature: this.meetingDetails.signature ? { ...this.meetingDetails.signature } : undefined,
teamLineupHomePlayers: Array.isArray(this.meetingDetails.teamLineupHomePlayers)
? JSON.parse(JSON.stringify(this.meetingDetails.teamLineupHomePlayers))
: undefined,
teamLineupGuestPlayers: Array.isArray(this.meetingDetails.teamLineupGuestPlayers)
? JSON.parse(JSON.stringify(this.meetingDetails.teamLineupGuestPlayers))
: undefined,
homePin: this.meetingDetails.homePin,
guestPin: this.meetingDetails.guestPin,
startDate: this.meetingData?.startDate ?? this.match?.startDate,
@@ -2385,6 +2569,15 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
if (Array.isArray(matchData.matches)) {
this.meetingDetails.matches = matchData.matches;
}
if (matchData.signature && typeof matchData.signature === 'object') {
this.meetingDetails.signature = { ...(this.meetingDetails.signature || {}), ...matchData.signature };
}
if (Array.isArray(matchData.teamLineupHomePlayers)) {
this.meetingDetails.teamLineupHomePlayers = matchData.teamLineupHomePlayers;
}
if (Array.isArray(matchData.teamLineupGuestPlayers)) {
this.meetingDetails.teamLineupGuestPlayers = matchData.teamLineupGuestPlayers;
}
if (matchData.homePin != null) this.meetingDetails.homePin = matchData.homePin;
if (matchData.guestPin != null) this.meetingDetails.guestPin = matchData.guestPin;
if (matchData.startDate != null && this.meetingData) this.meetingData.startDate = matchData.startDate;
@@ -2404,7 +2597,7 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
const pos = parseInt(token.replace(/[^0-9]/g, '')); // 1 oder 2
const key = side === 'home' ? 'teamLineupHomePlayers' : 'teamLineupGuestPlayers';
const list = (this.meetingDetails && this.meetingDetails[key]) ? this.meetingDetails[key] : [];
const pair = list.filter(p => p.positionDouble === pos);
const pair = list.filter(p => p.positionDouble === pos && p.nuLigaPersonId > 0);
const n = (p) => [p.firstname || p.firstName || '', p.lastname || p.lastName || ''].filter(Boolean).join(' ').trim();
// Wenn ein Doppel-Paar gefunden wurde, verwende es
@@ -2419,7 +2612,7 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
// Falls kein Doppel gefunden, verwende die entsprechenden Spieler basierend auf der Position
if (pair.length === 0) {
const selectedPlayers = list.filter(p => p.isSelected);
const selectedPlayers = list.filter(p => p.isSelected && p.nuLigaPersonId > 0);
// Versuche verschiedene Sortierungen
@@ -2476,7 +2669,7 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
const list = (this.meetingDetails && this.meetingDetails[key]) ? this.meetingDetails[key] : [];
// Prüfe zuerst, ob überhaupt genügend Spieler für diese Position vorhanden sind
const selectedPlayers = list.filter(p => p.isSelected);
const selectedPlayers = list.filter(p => p.isSelected && p.nuLigaPersonId > 0);
// Für Braunschweiger System: 2er-Mannschaften werden als 3er-Mannschaften gewertet
let effectivePlayerCount = selectedPlayers.length;
@@ -3290,6 +3483,7 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
this.meetingDetails[teamKey].forEach(player => {
if (player.nuLigaPersonId > 0) {
player.positions = [];
player.positionSingle = null;
}
});
@@ -3297,6 +3491,7 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
sortedByRank.forEach((player, index) => {
const playerIndex = this.meetingDetails[teamKey].findIndex(p => p.nuLigaPersonId === player.nuLigaPersonId);
if (playerIndex !== -1) {
this.meetingDetails[teamKey][playerIndex].positionSingle = index + 1;
this.meetingDetails[teamKey][playerIndex].positions = [`E${index + 1}`];
}
});
@@ -3425,10 +3620,10 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
if (isValid) {
// Aufstellung zertifizieren
if (team === 'home') {
this.isHomeLineupCertified = true;
this.setLineupCertificationState('home', true);
this.originalHomePin = pin; // Ursprüngliche PIN speichern
} else {
this.isGuestLineupCertified = true;
this.setLineupCertificationState('guest', true);
this.originalGuestPin = pin; // Ursprüngliche PIN speichern
}
@@ -3707,6 +3902,69 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
}
},
formatLineupPositionLabel(position, side) {
if (typeof position !== 'string') {
return '';
}
const trimmedPosition = position.trim().toUpperCase();
if (!trimmedPosition) {
return '';
}
if (trimmedPosition.startsWith('D')) {
return `${side === 'home' ? 'DA' : 'DB'}${trimmedPosition.slice(1)}`;
}
if (trimmedPosition.startsWith('E')) {
return `${side === 'home' ? 'A' : 'B'}${trimmedPosition.slice(1)}`;
}
return trimmedPosition;
},
getBraunschweigerServerMatrix() {
const mapFormationsToLabels = (formations) => {
if (!Array.isArray(formations) || formations.length === 0) {
return null;
}
return formations
.map((formation) => {
const homeLabel = this.formatLineupPositionLabel(formation?.homeLineupPosition, 'home');
const guestLabel = this.formatLineupPositionLabel(formation?.guestLineupPosition, 'guest');
if (homeLabel && guestLabel) {
return `${homeLabel} ${guestLabel}`;
}
if (typeof formation?.displayString === 'string' && formation.displayString.trim() !== '') {
return formation.displayString.trim();
}
return '';
})
.filter(Boolean);
};
const meetingPlayModeFormations = mapFormationsToLabels(this.meetingDetails?.meetingPlayMode?.matchFormations);
if (meetingPlayModeFormations && meetingPlayModeFormations.length > 0) {
return meetingPlayModeFormations;
}
const homePlayerCount = this.getEffectivePlayerCount('home');
const guestPlayerCount = this.getEffectivePlayerCount('guest');
const optionalMode = Array.isArray(this.meetingDetails?.optionalPlayModes)
? this.meetingDetails.optionalPlayModes.find(mode =>
Number(mode?.homePlayersCount) === Number(homePlayerCount) &&
Number(mode?.guestPlayersCount) === Number(guestPlayerCount) &&
Array.isArray(mode?.matchFormations) &&
mode.matchFormations.length > 0
)
: null;
return mapFormationsToLabels(optionalMode?.matchFormations);
},
// Hole aktuellen Spielmodus (für Validierungen)
getPlayThrough() {
@@ -3886,10 +4144,10 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
console.log('✅ PIN erfolgreich - entsperre Aufstellung und wechsle Tab');
if (requiresHomePin) {
this.isHomeLineupCertified = false;
this.setLineupCertificationState('home', false);
}
if (requiresGuestPin) {
this.isGuestLineupCertified = false;
this.setLineupCertificationState('guest', false);
}
const targetSection = requiresHomePin ? 'homeLineup' : 'guestLineup';
@@ -3907,8 +4165,23 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
return team === 'home' ? this.isHomeLineupCertified : this.isGuestLineupCertified;
},
areBothLineupsCertified() {
return this.isHomeLineupCertified && this.isGuestLineupCertified;
},
// Prüfe ob Aufstellung-Tab geöffnet werden kann
canOpenLineupTab(team) {
if ((team === 'home' && this.teamNotAppeared === 'home') ||
(team === 'guest' && this.teamNotAppeared === 'guest')) {
return false;
}
const isTeamCertified = team === 'home' ? this.isHomeLineupCertified : this.isGuestLineupCertified;
if (isTeamCertified) {
return this.areBothLineupsCertified();
}
if (team === 'home') {
return !this.isHomeLineupCertified;
} else {
@@ -3927,10 +4200,12 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
return;
}
if ((section === 'homeLineup' && this.isHomeLineupCertified) ||
(section === 'guestLineup' && this.isGuestLineupCertified)) {
this.openPinModal(section);
return;
if (section === 'homeLineup' || section === 'guestLineup') {
const team = section === 'homeLineup' ? 'home' : 'guest';
if (!this.canOpenLineupTab(team)) {
return;
}
}
this.activeSection = section;

View File

@@ -213,7 +213,7 @@ export default {
gap: 1rem;
min-height: 0;
height: 100%;
overflow: hidden;
overflow: visible;
}
.schedule-static-chrome {
@@ -278,13 +278,15 @@ export default {
align-items: stretch;
flex: 1 1 auto;
min-height: 0;
overflow: hidden;
overflow: visible;
}
.schedule-sidebar {
width: 280px;
flex: 0 0 280px;
min-height: 0;
display: flex;
flex-direction: column;
}
.schedule-sidebar-card {
@@ -300,6 +302,7 @@ export default {
overflow-y: auto;
margin-top: 0.75rem;
padding-right: 0.25rem;
max-height: calc(100vh - 300px);
}
.schedule-team-list {
@@ -408,6 +411,7 @@ export default {
min-height: auto;
height: auto;
overflow: visible;
max-height: none;
}
}

View File

@@ -428,7 +428,7 @@ export default {
},
leagueTeamOptions() {
const ownTeamName = this.selectedTeam?.name;
return Array.from(new Set(this.allLeagueMatches.flatMap(match => [
return Array.from(new Set(this.getCombinedLeagueMatches().flatMap(match => [
match.homeTeam?.name,
match.guestTeam?.name
].filter(Boolean))))
@@ -507,6 +507,47 @@ export default {
};
},
methods: {
sortMatchesByDateTime(matches) {
if (!Array.isArray(matches)) {
return [];
}
const toTimestamp = (match) => {
if (!match?.date) {
return Number.POSITIVE_INFINITY;
}
const dateValue = new Date(match.date);
if (isNaN(dateValue.getTime())) {
return Number.POSITIVE_INFINITY;
}
const timeString = typeof match.time === 'string' ? match.time : '';
const [hours, minutes] = timeString.split(':').map((value) => parseInt(value, 10));
if (!Number.isNaN(hours)) {
dateValue.setHours(hours, Number.isNaN(minutes) ? 0 : minutes, 0, 0);
} else {
dateValue.setHours(0, 0, 0, 0);
}
return dateValue.getTime();
};
return [...matches].sort((a, b) => {
const diff = toTimestamp(a) - toTimestamp(b);
if (diff !== 0) {
return diff;
}
const homeA = a?.homeTeam?.name || '';
const homeB = b?.homeTeam?.name || '';
const guestA = a?.guestTeam?.name || '';
const guestB = b?.guestTeam?.name || '';
return `${homeA} ${guestA}`.localeCompare(`${homeB} ${guestB}`);
});
},
getResultClass(match) {
if (!match.isCompleted) {
return '';
@@ -972,13 +1013,29 @@ export default {
const response = await apiClient.get(`/matches/leagues/${this.currentClub}/matches/${leagueId}?scope=all`);
this.allLeagueMatches = response.data;
},
getCombinedLeagueMatches() {
const combined = [...(this.allLeagueMatches || []), ...(this.ownLeagueMatches || [])];
const seenIds = new Set();
const uniqueMatches = combined.filter((match) => {
const key = match?.id ?? `${match?.date || ''}-${match?.time || ''}-${match?.homeTeam?.name || ''}-${match?.guestTeam?.name || ''}`;
if (seenIds.has(key)) {
return false;
}
seenIds.add(key);
return true;
});
return this.sortMatchesByDateTime(uniqueMatches);
},
applyLeagueMatchScope() {
if (!this.selectedTeam) {
return;
}
const ownTeamName = this.selectedTeam.name;
const combinedLeagueMatches = this.getCombinedLeagueMatches();
if (this.leagueMatchScope === 'all') {
this.matches = this.allLeagueMatches;
this.matches = combinedLeagueMatches;
return;
}
if (this.leagueMatchScope === 'other') {
@@ -986,7 +1043,7 @@ export default {
this.selectedComparisonTeamName = this.leagueTeamOptions[0];
}
this.matches = this.selectedComparisonTeamName
? this.allLeagueMatches.filter(match =>
? combinedLeagueMatches.filter(match =>
match.homeTeam?.name === this.selectedComparisonTeamName ||
match.guestTeam?.name === this.selectedComparisonTeamName
)
@@ -994,8 +1051,8 @@ export default {
return;
}
this.matches = this.ownLeagueMatches.length > 0
? this.ownLeagueMatches
: this.allLeagueMatches.filter(match =>
? this.sortMatchesByDateTime(this.ownLeagueMatches)
: combinedLeagueMatches.filter(match =>
match.homeTeam?.name === ownTeamName || match.guestTeam?.name === ownTeamName
);
},
@@ -1016,7 +1073,7 @@ export default {
try {
const seasonParam = this.selectedSeasonId ? `?seasonid=${this.selectedSeasonId}` : '';
const response = await apiClient.get(`/matches/leagues/${this.currentClub}/matches${seasonParam}`);
this.matches = response.data;
this.matches = this.sortMatchesByDateTime(response.data);
} catch (error) {
this.showInfo(this.$t('messages.error'), this.$t('schedule.errorLoadingOverallSchedule'), '', 'error');
this.matches = [];
@@ -1034,12 +1091,12 @@ export default {
const response = await apiClient.get(`/matches/leagues/${this.currentClub}/matches${seasonParam}`);
// Filtere nur Erwachsenenligen (keine Jugendligen)
const allMatches = response.data;
this.matches = allMatches.filter(match => {
this.matches = this.sortMatchesByDateTime(allMatches.filter(match => {
const leagueName = match.leagueDetails?.name || '';
// Prüfe, ob es eine Jugendliga ist (J, M, Jugend im Namen)
const isYouth = /[JM]\d|jugend/i.test(leagueName);
return !isYouth;
});
}));
} catch (error) {
this.showInfo(this.$t('messages.error'), this.$t('schedule.errorLoadingAdultSchedule'), '', 'error');
this.matches = [];
@@ -1295,6 +1352,7 @@ export default {
const idx = this.matches.findIndex(m => m.id === payload.matchId);
if (idx !== -1) {
this.matches.splice(idx, 1, payload.match);
this.matches = this.sortMatchesByDateTime(this.matches);
return;
}
}
@@ -1314,6 +1372,7 @@ export default {
if (d.startDate) m.startDate = d.startDate;
if (d.endDate) m.endDate = d.endDate;
this.matches.splice(idx, 1, m);
this.matches = this.sortMatchesByDateTime(this.matches);
return;
}
}
@@ -2110,6 +2169,10 @@ li {
background: linear-gradient(180deg, rgba(47, 122, 95, 0.08), rgba(47, 122, 95, 0.03));
padding: 14px;
margin-bottom: 12px;
position: sticky;
top: 12px;
z-index: 5;
backdrop-filter: blur(6px);
}
.league-match-scope-header {