feat(Tournament): add official tournament participation feature
All checks were successful
Deploy tt-tagebuch / deploy (push) Successful in 43s

- Introduced functionality to load and display official tournament participation events in the CalendarView.
- Updated the API client to fetch official tournament data, enhancing the event management capabilities.
- Added new UI elements to represent official tournaments, including visual indicators and event details.
- Enhanced the ClubManager to support fetching and managing official tournament data.
- Updated the mobile app's TODO list to reflect progress on tournament-related features.
This commit is contained in:
Torsten Schulz (local)
2026-05-12 23:52:54 +02:00
parent bea5facb7d
commit 57468f1efb
15 changed files with 931 additions and 11 deletions

View File

@@ -138,6 +138,7 @@ export default {
activeTypes: {
training: true,
tournament: true,
officialTournament: true,
match: true,
holiday: true,
schoolHoliday: true,
@@ -160,6 +161,7 @@ export default {
return [
{ key: 'training', label: 'Training' },
{ key: 'tournament', label: 'Turnier' },
{ key: 'officialTournament', label: 'Teilnahme' },
{ key: 'match', label: 'Punktspiel' },
{ key: 'holiday', label: 'Feiertag' },
{ key: 'schoolHoliday', label: 'Ferien' },
@@ -188,7 +190,7 @@ export default {
return this.events.reduce((counts, event) => {
counts[event.type] = (counts[event.type] || 0) + 1;
return counts;
}, { training: 0, tournament: 0, match: 0, holiday: 0, schoolHoliday: 0, trainingCancellation: 0 });
}, { training: 0, tournament: 0, officialTournament: 0, match: 0, holiday: 0, schoolHoliday: 0, trainingCancellation: 0 });
},
visibleTrainingCancellations() {
const month = this.cursor.getMonth();
@@ -253,6 +255,7 @@ export default {
this.loadSource('Trainingszeiten', () => this.loadRecurringTrainingEvents()),
this.loadSource('Trainingsausfälle', () => this.loadTrainingCancellationEvents()),
this.loadSource('Turniere', () => this.loadTournamentEvents()),
this.loadSource('Turnierteilnahmen', () => this.loadOfficialTournamentEvents()),
this.loadSource('Punktspiele', () => this.loadMatchEvents()),
this.loadSource('Ferien/Feiertage', () => this.loadHolidayEvents())
]);
@@ -435,6 +438,31 @@ export default {
};
});
},
async loadOfficialTournamentEvents() {
const response = await apiClient.get(`/official-tournaments/${this.currentClub}/participations/summary`);
this.ensureSuccess(response, 'Turnierteilnahmen');
return (response.data || [])
.filter(tournament => Array.isArray(tournament.entries) && tournament.entries.length > 0)
.map(tournament => {
const date = this.parseDmyDate(tournament.startDate);
const endDate = this.parseDmyDate(tournament.endDate || tournament.startDate);
if (!date) return null;
const entries = tournament.entries || [];
const participantCount = new Set(entries.map(entry => entry.memberId).filter(Boolean)).size;
return {
id: `official-tournament-${tournament.tournamentId}`,
type: 'officialTournament',
date,
endDate: endDate || date,
startsAt: this.combineDateTime(date),
time: '',
title: tournament.tournamentName || tournament.title || 'Turnierteilnahme',
subtitle: participantCount > 0 ? `${participantCount} Teilnehmer` : `${entries.length} Starts`,
route: '/tournament-participations'
};
})
.filter(Boolean);
},
async loadHolidayEvents() {
const response = await apiClient.get(`/calendar/club/${this.currentClub}/holidays`, {
params: { year: this.displayedYear }
@@ -483,6 +511,12 @@ export default {
if (!year || !month || !day) return new Date(value);
return new Date(year, month - 1, day);
},
parseDmyDate(value) {
const match = String(value || '').match(/(\d{1,2})\.(\d{1,2})\.(\d{4})/);
if (!match) return null;
const date = new Date(Number(match[3]), Number(match[2]) - 1, Number(match[1]));
return Number.isNaN(date.getTime()) ? null : date;
},
combineDateTime(date, time) {
if (!date) return Number.POSITIVE_INFINITY;
const result = new Date(date);
@@ -659,6 +693,7 @@ export default {
.legend-training::before { background: #2f7a5f; }
.legend-tournament::before { background: #b7791f; }
.legend-officialTournament::before { background: #9333ea; }
.legend-match::before { background: #2563eb; }
.legend-holiday::before { background: #dc2626; }
.legend-schoolHoliday::before { background: #7c3aed; }
@@ -823,6 +858,7 @@ export default {
.event-training { background: #e8f4ef; border-left: 4px solid #2f7a5f; }
.event-tournament { background: #fff7e6; border-left: 4px solid #b7791f; }
.event-officialTournament { background: #f3e8ff; border-left: 4px solid #9333ea; }
.event-match { background: #eaf1ff; border-left: 4px solid #2563eb; }
.event-holiday { background: #fee2e2; border-left: 4px solid #dc2626; }
.event-schoolHoliday { background: #f3e8ff; border-left: 4px solid #7c3aed; }