Implement tournament pairing functionality and enhance participant management

- Introduced new endpoints for managing tournament pairings, including creating, updating, and deleting pairings.
- Updated the tournament service to handle pairing logic, ensuring validation for participants and preventing duplicate pairings.
- Enhanced participant management by adding class-based checks for gender and age restrictions when adding participants.
- Updated the tournament controller and routes to support the new pairing features and improved participant handling.
- Added localization support for new UI elements related to pairings in the frontend, enhancing user experience.
This commit is contained in:
Torsten Schulz (local)
2025-11-29 00:15:01 +01:00
parent bdbbb88be9
commit dc2c60cefe
23 changed files with 4613 additions and 1100 deletions

View File

@@ -70,6 +70,11 @@ const ExternalTournamentParticipant = sequelize.define('ExternalTournamentPartic
return decryptData(encryptedValue);
}
},
gender: {
type: DataTypes.ENUM('male', 'female', 'diverse', 'unknown'),
allowNull: true,
defaultValue: 'unknown'
},
seeded: {
type: DataTypes.BOOLEAN,
allowNull: false,

View File

@@ -27,6 +27,23 @@ const TournamentClass = sequelize.define('TournamentClass', {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 0
},
isDoubles: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: false
},
gender: {
type: DataTypes.ENUM('male', 'female', 'mixed'),
allowNull: true,
defaultValue: null
},
minBirthYear: {
type: DataTypes.INTEGER,
allowNull: true,
defaultValue: null,
field: 'min_birth_year',
comment: 'Geboren im Jahr X oder später (>=)'
}
}, {
underscored: true,

View File

@@ -0,0 +1,71 @@
import { DataTypes } from 'sequelize';
import sequelize from '../database.js';
import Tournament from './Tournament.js';
import TournamentClass from './TournamentClass.js';
const TournamentPairing = sequelize.define('TournamentPairing', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false
},
tournamentId: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: Tournament,
key: 'id'
},
onDelete: 'CASCADE',
onUpdate: 'CASCADE'
},
classId: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: TournamentClass,
key: 'id'
},
onDelete: 'CASCADE',
onUpdate: 'CASCADE'
},
groupId: {
type: DataTypes.INTEGER,
allowNull: true
},
// Player 1: entweder Mitglied oder externer Teilnehmer
member1Id: {
type: DataTypes.INTEGER,
allowNull: true
},
external1Id: {
type: DataTypes.INTEGER,
allowNull: true
},
// Player 2: entweder Mitglied oder externer Teilnehmer
member2Id: {
type: DataTypes.INTEGER,
allowNull: true
},
external2Id: {
type: DataTypes.INTEGER,
allowNull: true
},
seeded: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: false
}
}, {
underscored: true,
tableName: 'tournament_pairing',
timestamps: true
});
export default TournamentPairing;

View File

@@ -32,6 +32,7 @@ import TournamentMember from './TournamentMember.js';
import TournamentMatch from './TournamentMatch.js';
import TournamentResult from './TournamentResult.js';
import ExternalTournamentParticipant from './ExternalTournamentParticipant.js';
import TournamentPairing from './TournamentPairing.js';
import Accident from './Accident.js';
import UserToken from './UserToken.js';
import OfficialTournament from './OfficialTournament.js';
@@ -269,6 +270,41 @@ TournamentClass.hasMany(ExternalTournamentParticipant, {
as: 'externalParticipants'
});
// Tournament Pairings
TournamentPairing.belongsTo(Tournament, { foreignKey: 'tournamentId', as: 'tournament' });
Tournament.hasMany(TournamentPairing, { foreignKey: 'tournamentId', as: 'pairings' });
TournamentPairing.belongsTo(TournamentClass, { foreignKey: 'classId', as: 'class' });
TournamentClass.hasMany(TournamentPairing, { foreignKey: 'classId', as: 'pairings' });
TournamentPairing.belongsTo(TournamentGroup, {
foreignKey: 'groupId',
as: 'group',
constraints: false
});
TournamentGroup.hasMany(TournamentPairing, {
foreignKey: 'groupId',
as: 'pairings'
});
TournamentPairing.belongsTo(TournamentMember, {
foreignKey: 'member1Id',
as: 'member1',
constraints: false
});
TournamentPairing.belongsTo(TournamentMember, {
foreignKey: 'member2Id',
as: 'member2',
constraints: false
});
TournamentPairing.belongsTo(ExternalTournamentParticipant, {
foreignKey: 'external1Id',
as: 'external1',
constraints: false
});
TournamentPairing.belongsTo(ExternalTournamentParticipant, {
foreignKey: 'external2Id',
as: 'external2',
constraints: false
});
Accident.belongsTo(Member, { foreignKey: 'memberId', as: 'members' });
Member.hasMany(Accident, { foreignKey: 'memberId', as: 'accidents' });
@@ -355,6 +391,7 @@ export {
TournamentMatch,
TournamentResult,
ExternalTournamentParticipant,
TournamentPairing,
Accident,
UserToken,
OfficialTournament,