feat(ClubTeam): enhance club team management with lineup features and member eligibility

- Added teamGender and teamAgeGroup fields to ClubTeam model for better categorization.
- Updated create and update club team endpoints to handle new fields and default values.
- Implemented getClubTeamLineup and updateClubTeamLineup functions for managing team lineups.
- Enhanced member management with adultReleaseApproved and adultReserveApproved fields in Member model.
- Updated frontend views to support new lineup features and member eligibility flags.
- Improved localization for new terms related to team management and member eligibility across multiple languages.
This commit is contained in:
Torsten Schulz (local)
2026-03-31 13:44:28 +02:00
parent cb7830571b
commit 5eff1d63aa
28 changed files with 1325 additions and 72 deletions

View File

@@ -1,10 +1,16 @@
import ClubTeam from '../models/ClubTeam.js';
import ClubTeamMember from '../models/ClubTeamMember.js';
import League from '../models/League.js';
import Member from '../models/Member.js';
import Season from '../models/Season.js';
import SeasonService from './seasonService.js';
import { devLog } from '../utils/logger.js';
class ClubTeamService {
static isMissingTeamLineupTable(error) {
return error?.original?.code === 'ER_NO_SUCH_TABLE'
&& String(error?.original?.sqlMessage || '').includes('club_team_member');
}
/**
* Holt alle ClubTeams für einen Verein, optional gefiltert nach Saison.
* Wenn keine Saison-ID angegeben ist, wird die aktuelle Saison verwendet.
@@ -36,6 +42,8 @@ class ClubTeamService {
leagueId: clubTeam.leagueId,
seasonId: clubTeam.seasonId,
myTischtennisTeamId: clubTeam.myTischtennisTeamId,
teamGender: clubTeam.teamGender,
teamAgeGroup: clubTeam.teamAgeGroup,
createdAt: clubTeam.createdAt,
updatedAt: clubTeam.updatedAt,
league: { name: 'Unbekannt' },
@@ -178,6 +186,63 @@ class ClubTeamService {
throw error;
}
}
static async getTeamLineup(clubTeamId, lineupHalf = 'first_half') {
try {
return await ClubTeamMember.findAll({
where: { clubTeamId, lineupHalf },
include: [
{
model: Member,
as: 'member'
}
],
order: [['position', 'ASC']]
});
} catch (error) {
if (this.isMissingTeamLineupTable(error)) {
return [];
}
console.error('[ClubTeamService.getTeamLineup] - Error:', error);
throw error;
}
}
static async replaceTeamLineup(clubTeamId, assignments, lineupHalf = 'first_half') {
try {
await ClubTeamMember.destroy({
where: { clubTeamId, lineupHalf }
});
if (!Array.isArray(assignments) || assignments.length === 0) {
return [];
}
const normalizedAssignments = assignments
.filter((entry) => entry && entry.memberId)
.map((entry, index) => ({
clubTeamId,
lineupHalf,
memberId: Number(entry.memberId),
position: Number(entry.position) || (index + 1)
}));
if (normalizedAssignments.length === 0) {
return [];
}
await ClubTeamMember.bulkCreate(normalizedAssignments);
return await this.getTeamLineup(clubTeamId, lineupHalf);
} catch (error) {
if (this.isMissingTeamLineupTable(error)) {
const tableMissingError = new Error('teamlineuptablemissing');
tableMissingError.code = 'TEAM_LINEUP_TABLE_MISSING';
throw tableMissingError;
}
console.error('[ClubTeamService.replaceTeamLineup] - Error:', error);
throw error;
}
}
}
export default ClubTeamService;

View File

@@ -106,7 +106,7 @@ class MemberService {
}
async setClubMember(userToken, clubId, memberId, firstName, lastName, street, city, postalCode, birthdate, phone, email, active = true, testMembership = false,
picsInInternetAllowed = false, gender = 'unknown', ttr = null, qttr = null, memberFormHandedOver = false, contacts = []) {
picsInInternetAllowed = false, gender = 'unknown', ttr = null, qttr = null, memberFormHandedOver = false, adultReleaseApproved = false, adultReserveApproved = false, contacts = []) {
try {
await checkAccess(userToken, clubId);
let member = null;
@@ -130,6 +130,8 @@ class MemberService {
if (ttr !== undefined) member.ttr = ttr;
if (qttr !== undefined) member.qttr = qttr;
member.memberFormHandedOver = !!memberFormHandedOver;
member.adultReleaseApproved = !!adultReleaseApproved;
member.adultReserveApproved = !!adultReserveApproved;
await member.save();
// Update contacts if provided
@@ -173,6 +175,8 @@ class MemberService {
ttr: ttr,
qttr: qttr,
memberFormHandedOver: !!memberFormHandedOver,
adultReleaseApproved: !!adultReleaseApproved,
adultReserveApproved: !!adultReserveApproved,
});
// Create contacts if provided