feat(tournament): add mini championship functionality and enhance tournament class handling
- Introduced addMiniChampionship method in tournamentService to create tournaments with predefined classes for mini championships. - Updated getTournaments method to filter tournaments based on type, including support for mini championships. - Enhanced TournamentClass model to include maxBirthYear for age class restrictions. - Modified tournamentController and tournamentRoutes to support new mini championship endpoint. - Updated frontend components to manage mini championship creation and display, including localization for new terms.
This commit is contained in:
@@ -678,13 +678,19 @@ class TournamentService {
|
||||
};
|
||||
}
|
||||
|
||||
// 1. Turniere listen
|
||||
async getTournaments(userToken, clubId) {
|
||||
// 1. Turniere listen (type: 'internal' | 'external' | 'mini' optional – bei 'mini' nur Minimeisterschaften)
|
||||
async getTournaments(userToken, clubId, type = null) {
|
||||
await checkAccess(userToken, clubId);
|
||||
const where = { clubId };
|
||||
if (type === 'mini') {
|
||||
where.miniChampionshipYear = { [Op.ne]: null };
|
||||
} else if (type === 'internal' || type === 'external') {
|
||||
where.miniChampionshipYear = { [Op.is]: null };
|
||||
}
|
||||
const tournaments = await Tournament.findAll({
|
||||
where: { clubId },
|
||||
where,
|
||||
order: [['date', 'DESC']],
|
||||
attributes: ['id', 'name', 'date', 'allowsExternal']
|
||||
attributes: ['id', 'name', 'date', 'allowsExternal', 'miniChampionshipYear']
|
||||
});
|
||||
return JSON.parse(JSON.stringify(tournaments));
|
||||
}
|
||||
@@ -708,6 +714,54 @@ class TournamentService {
|
||||
return JSON.parse(JSON.stringify(t));
|
||||
}
|
||||
|
||||
/**
|
||||
* Minimeisterschaft anlegen: Turnier + 6 vorkonfigurierte Klassen (Jungen/Mädchen 12, 10, 8).
|
||||
* Jahr Y: 12 = in Y 11 oder 12 Jahre (Geburtsjahr Y-12 oder Y-11), 10 = 9/10 (Y-10, Y-9), 8 = 8 oder jünger (≥ Y-8).
|
||||
*/
|
||||
async addMiniChampionship(userToken, clubId, tournamentName, date, year, winningSets = 3) {
|
||||
await checkAccess(userToken, clubId);
|
||||
const existing = await Tournament.findOne({ where: { clubId, date } });
|
||||
if (existing) {
|
||||
throw new Error('Ein Turnier mit diesem Datum existiert bereits');
|
||||
}
|
||||
const Y = Number(year);
|
||||
if (!Number.isFinite(Y) || Y < 2000 || Y > 2100) {
|
||||
throw new Error('Ungültiges Jahr für die Minimeisterschaft');
|
||||
}
|
||||
const t = await Tournament.create({
|
||||
name: tournamentName,
|
||||
date,
|
||||
clubId: +clubId,
|
||||
bestOfEndroundSize: 0,
|
||||
type: '',
|
||||
winningSets: winningSets || 3,
|
||||
allowsExternal: false,
|
||||
miniChampionshipYear: Y
|
||||
});
|
||||
const classes = [
|
||||
{ name: 'Jungen 12', gender: 'male', minBirthYear: Y - 12, maxBirthYear: Y - 11 },
|
||||
{ name: 'Jungen 10', gender: 'male', minBirthYear: Y - 10, maxBirthYear: Y - 9 },
|
||||
{ name: 'Jungen 8', gender: 'male', minBirthYear: Y - 8, maxBirthYear: null },
|
||||
{ name: 'Mädchen 12', gender: 'female', minBirthYear: Y - 12, maxBirthYear: Y - 11 },
|
||||
{ name: 'Mädchen 10', gender: 'female', minBirthYear: Y - 10, maxBirthYear: Y - 9 },
|
||||
{ name: 'Mädchen 8', gender: 'female', minBirthYear: Y - 8, maxBirthYear: null },
|
||||
];
|
||||
for (let i = 0; i < classes.length; i++) {
|
||||
await TournamentClass.create({
|
||||
tournamentId: t.id,
|
||||
name: classes[i].name,
|
||||
sortOrder: i + 1,
|
||||
isDoubles: false,
|
||||
gender: classes[i].gender,
|
||||
minBirthYear: classes[i].minBirthYear,
|
||||
maxBirthYear: classes[i].maxBirthYear
|
||||
});
|
||||
}
|
||||
return JSON.parse(JSON.stringify(await Tournament.findByPk(t.id, {
|
||||
attributes: ['id', 'name', 'date', 'allowsExternal', 'miniChampionshipYear']
|
||||
})));
|
||||
}
|
||||
|
||||
// 3. Teilnehmer hinzufügen (kein Duplikat) - klassengebunden
|
||||
async addParticipant(userToken, clubId, classId, participantId, tournamentId = null) {
|
||||
await checkAccess(userToken, clubId);
|
||||
@@ -756,9 +810,8 @@ class TournamentService {
|
||||
}
|
||||
}
|
||||
|
||||
// Validierung: Geburtsjahr muss zur Klasse passen (geboren im Jahr X oder später, also >=)
|
||||
if (tournamentClass.minBirthYear && member.birthDate) {
|
||||
// Parse das Geburtsdatum (Format: YYYY-MM-DD oder DD.MM.YYYY)
|
||||
// Validierung: Geburtsjahr muss zur Klasse passen (minBirthYear <= birthYear <= maxBirthYear)
|
||||
if (member.birthDate) {
|
||||
let birthYear = null;
|
||||
if (member.birthDate.includes('-')) {
|
||||
birthYear = parseInt(member.birthDate.split('-')[0]);
|
||||
@@ -768,10 +821,13 @@ class TournamentService {
|
||||
birthYear = parseInt(parts[2]);
|
||||
}
|
||||
}
|
||||
if (birthYear && !isNaN(birthYear)) {
|
||||
if (birthYear < tournamentClass.minBirthYear) {
|
||||
if (birthYear != null && !isNaN(birthYear)) {
|
||||
if (tournamentClass.minBirthYear != null && birthYear < tournamentClass.minBirthYear) {
|
||||
throw new Error(`Dieser Teilnehmer ist zu alt für diese Klasse. Erlaubt: geboren ${tournamentClass.minBirthYear} oder später`);
|
||||
}
|
||||
if (tournamentClass.maxBirthYear != null && birthYear > tournamentClass.maxBirthYear) {
|
||||
throw new Error(`Dieser Teilnehmer ist zu jung für diese Klasse. Erlaubt: geboren ${tournamentClass.maxBirthYear} oder früher`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3258,11 +3314,13 @@ class TournamentService {
|
||||
}
|
||||
}
|
||||
|
||||
if (birthYear && !isNaN(birthYear)) {
|
||||
// Geboren im Jahr X oder später bedeutet: birthYear >= minBirthYear
|
||||
if (birthYear < tournamentClass.minBirthYear) {
|
||||
if (birthYear != null && !isNaN(birthYear)) {
|
||||
if (tournamentClass.minBirthYear != null && birthYear < tournamentClass.minBirthYear) {
|
||||
throw new Error(`Dieser Teilnehmer ist zu alt für diese Klasse. Erlaubt: geboren ${tournamentClass.minBirthYear} oder später`);
|
||||
}
|
||||
if (tournamentClass.maxBirthYear != null && birthYear > tournamentClass.maxBirthYear) {
|
||||
throw new Error(`Dieser Teilnehmer ist zu jung für diese Klasse. Erlaubt: geboren ${tournamentClass.maxBirthYear} oder früher`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3337,13 +3395,12 @@ class TournamentService {
|
||||
});
|
||||
}
|
||||
|
||||
async addTournamentClass(userToken, clubId, tournamentId, name, isDoubles = false, gender = null, minBirthYear = null) {
|
||||
async addTournamentClass(userToken, clubId, tournamentId, name, isDoubles = false, gender = null, minBirthYear = null, maxBirthYear = null) {
|
||||
await checkAccess(userToken, clubId);
|
||||
const tournament = await Tournament.findByPk(tournamentId);
|
||||
if (!tournament || tournament.clubId != clubId) {
|
||||
throw new Error('Turnier nicht gefunden');
|
||||
}
|
||||
// Finde die höchste sortOrder
|
||||
const maxSortOrder = await TournamentClass.max('sortOrder', {
|
||||
where: { tournamentId }
|
||||
}) || 0;
|
||||
@@ -3353,11 +3410,12 @@ class TournamentService {
|
||||
sortOrder: maxSortOrder + 1,
|
||||
isDoubles: isDoubles || false,
|
||||
gender: gender || null,
|
||||
minBirthYear: minBirthYear || null
|
||||
minBirthYear: minBirthYear ?? null,
|
||||
maxBirthYear: maxBirthYear ?? null
|
||||
});
|
||||
}
|
||||
|
||||
async updateTournamentClass(userToken, clubId, tournamentId, classId, name, sortOrder, isDoubles, gender, minBirthYear) {
|
||||
async updateTournamentClass(userToken, clubId, tournamentId, classId, name, sortOrder, isDoubles, gender, minBirthYear, maxBirthYear) {
|
||||
await checkAccess(userToken, clubId);
|
||||
const tournament = await Tournament.findByPk(tournamentId);
|
||||
if (!tournament || tournament.clubId != clubId) {
|
||||
@@ -3369,37 +3427,15 @@ class TournamentService {
|
||||
if (!tournamentClass) {
|
||||
throw new Error('Klasse nicht gefunden');
|
||||
}
|
||||
console.log('[updateTournamentClass] Before update:', {
|
||||
id: tournamentClass.id,
|
||||
name: tournamentClass.name,
|
||||
isDoubles: tournamentClass.isDoubles,
|
||||
gender: tournamentClass.gender,
|
||||
minBirthYear: tournamentClass.minBirthYear
|
||||
});
|
||||
console.log('[updateTournamentClass] New values:', { name, sortOrder, isDoubles, gender, minBirthYear });
|
||||
|
||||
// Verwende update() statt direkter Zuweisung für bessere Kontrolle
|
||||
const updateData = {};
|
||||
if (name !== undefined) updateData.name = name;
|
||||
if (sortOrder !== undefined) updateData.sortOrder = sortOrder;
|
||||
if (isDoubles !== undefined) updateData.isDoubles = isDoubles;
|
||||
if (gender !== undefined) updateData.gender = gender;
|
||||
if (minBirthYear !== undefined) updateData.minBirthYear = minBirthYear;
|
||||
|
||||
console.log('[updateTournamentClass] Update data:', updateData);
|
||||
|
||||
if (maxBirthYear !== undefined) updateData.maxBirthYear = maxBirthYear;
|
||||
await tournamentClass.update(updateData);
|
||||
|
||||
// Lade die aktualisierte Instanz neu, um sicherzustellen, dass wir die aktuellen DB-Werte haben
|
||||
await tournamentClass.reload();
|
||||
|
||||
console.log('[updateTournamentClass] After update and reload:', {
|
||||
id: tournamentClass.id,
|
||||
name: tournamentClass.name,
|
||||
isDoubles: tournamentClass.isDoubles,
|
||||
gender: tournamentClass.gender,
|
||||
minBirthYear: tournamentClass.minBirthYear
|
||||
});
|
||||
return tournamentClass;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user