diff --git a/backend/services/matchService.js b/backend/services/matchService.js index 30f108f5..36bee54a 100644 --- a/backend/services/matchService.js +++ b/backend/services/matchService.js @@ -12,6 +12,7 @@ import Club from '../models/Club.js'; import SeasonService from './seasonService.js'; import { checkAccess } from '../utils/userUtils.js'; import { Op } from 'sequelize'; +import HttpError from '../exceptions/HttpError.js'; import { devLog } from '../utils/logger.js'; class MatchService { @@ -470,6 +471,9 @@ class MatchService { // Aktualisiertes Match nochmals laden und für WebSocket-Broadcast anreichern (gleiche Struktur wie getMatchesForLeague) const updated = await Match.findByPk(matchId); + if (!updated) { + throw new HttpError('Match not found after update', 404); + } const enriched = { id: updated.id, date: updated.date, diff --git a/frontend/src/views/DiaryView.vue b/frontend/src/views/DiaryView.vue index 4b466165..2ccc7da8 100644 --- a/frontend/src/views/DiaryView.vue +++ b/frontend/src/views/DiaryView.vue @@ -2068,12 +2068,34 @@ export default { }, sortedMembers() { // Erstelle eine Kopie des Arrays, um Mutation zu vermeiden + // Sortierung: zuerst nach Vorname, dann nach Nachname (beides case-insensitive, deutsch) + // Fallback: ID, damit die Reihenfolge bei komplett gleichen Namen stabil ist + const locale = 'de-DE'; + const options = { sensitivity: 'base' }; + + const safe = (value) => (value ?? '').toString().trim(); + return [...this.members].sort((a, b) => { - const firstNameComparison = a.firstName.localeCompare(b.firstName); - if (firstNameComparison === 0) { - return a.lastName.localeCompare(b.lastName); + const firstNameA = safe(a.firstName); + const firstNameB = safe(b.firstName); + const firstNameComparison = firstNameA.localeCompare(firstNameB, locale, options); + + if (firstNameComparison !== 0) { + return firstNameComparison; } - return firstNameComparison; + + const lastNameA = safe(a.lastName); + const lastNameB = safe(b.lastName); + const lastNameComparison = lastNameA.localeCompare(lastNameB, locale, options); + + if (lastNameComparison !== 0) { + return lastNameComparison; + } + + // Stabiler Fallback, falls sowohl Vor- als auch Nachname identisch sind + const idA = Number.isFinite(a.id) ? a.id : parseInt(a.id, 10) || 0; + const idB = Number.isFinite(b.id) ? b.id : parseInt(b.id, 10) || 0; + return idA - idB; }); }, async showPic(member) { diff --git a/frontend/src/views/ScheduleView.vue b/frontend/src/views/ScheduleView.vue index 6f7cc47c..805e0546 100644 --- a/frontend/src/views/ScheduleView.vue +++ b/frontend/src/views/ScheduleView.vue @@ -325,14 +325,17 @@ export default { ...mapGetters(['isAuthenticated', 'currentClub', 'clubs', 'currentClubName']), }, watch: { - currentClub(newVal) { - offScheduleMatchUpdated(this.handleScheduleMatchUpdated); - offMatchReportSubmitted(this.handleMatchReportSubmitted); - disconnectSocket(); - if (newVal) { - connectSocket(newVal); - onScheduleMatchUpdated(this.handleScheduleMatchUpdated); - onMatchReportSubmitted(this.handleMatchReportSubmitted); + currentClub: { + immediate: true, + handler(newVal) { + offScheduleMatchUpdated(this.handleScheduleMatchUpdated); + offMatchReportSubmitted(this.handleMatchReportSubmitted); + disconnectSocket(); + if (newVal) { + connectSocket(newVal); + onScheduleMatchUpdated(this.handleScheduleMatchUpdated); + onMatchReportSubmitted(this.handleMatchReportSubmitted); + } } }, }, @@ -1084,13 +1087,6 @@ export default { // und ruft anschließend onSeasonChange auf, was loadTeams() ausführt this.loadTeams(); }, - mounted() { - if (this.currentClub) { - connectSocket(this.currentClub); - onScheduleMatchUpdated(this.handleScheduleMatchUpdated); - onMatchReportSubmitted(this.handleMatchReportSubmitted); - } - }, beforeUnmount() { offScheduleMatchUpdated(this.handleScheduleMatchUpdated); offMatchReportSubmitted(this.handleMatchReportSubmitted);