feat(kaisertisch): implement Kaisertisch tournament functionality with model, routes, and controller
All checks were successful
Deploy tt-tagebuch / deploy (push) Successful in 57s

This commit is contained in:
Torsten Schulz (local)
2026-08-19 14:02:12 +02:00
parent d251d40868
commit 18ba120927
7 changed files with 191 additions and 27 deletions

View File

@@ -0,0 +1,59 @@
import { DiaryDate, KaisertischTournament } from '../models/index.js';
import HttpError from '../exceptions/HttpError.js';
const normalizeState = (state) => {
if (!state || typeof state !== 'object' || Array.isArray(state)) throw new HttpError('Ungültiger Kaisertisch-Stand', 400);
const tableCount = Math.min(30, Math.max(1, Number(state.tableCount) || 4));
const tables = Array.isArray(state.tables) ? state.tables.slice(0, tableCount).map((table) => ({ left: table?.left || null, right: table?.right || null })) : [];
return {
tableCount,
tables: tables.length === tableCount ? tables : Array.from({ length: tableCount }, () => ({ left: null, right: null })),
history: Array.isArray(state.history) ? state.history.slice(0, 500) : [],
round: Math.max(0, Number(state.round) || 0),
pendingWinners: state.pendingWinners && typeof state.pendingWinners === 'object' ? state.pendingWinners : {},
};
};
export const listKaisertischTournaments = async (req, res) => {
try {
const tournaments = await KaisertischTournament.findAll({
where: { clubId: req.params.clubId },
include: [{ model: DiaryDate, as: 'diaryDate', attributes: ['id', 'date'] }],
order: [[{ model: DiaryDate, as: 'diaryDate' }, 'date', 'DESC'], ['updatedAt', 'DESC']],
});
res.json(tournaments.map((tournament) => ({
diaryDateId: tournament.diaryDateId,
date: tournament.diaryDate?.date || null,
roundCount: Array.isArray(tournament.state?.history) ? tournament.state.history.length : 0,
updatedAt: tournament.updatedAt,
})));
} catch (error) {
console.error('[listKaisertischTournaments]', error);
res.status(error.statusCode || 500).json({ error: error.message || 'Kaisertisch-Turniere konnten nicht geladen werden.' });
}
};
export const getKaisertischTournament = async (req, res) => {
try {
const tournament = await KaisertischTournament.findOne({ where: { clubId: req.params.clubId, diaryDateId: req.params.dateId } });
if (!tournament) return res.status(404).json({ error: 'Kein Kaisertisch-Turnier für diesen Trainingstag gefunden.' });
res.json({ diaryDateId: tournament.diaryDateId, state: tournament.state, updatedAt: tournament.updatedAt });
} catch (error) {
console.error('[getKaisertischTournament]', error);
res.status(error.statusCode || 500).json({ error: error.message || 'Kaisertisch-Turnier konnte nicht geladen werden.' });
}
};
export const saveKaisertischTournament = async (req, res) => {
try {
const { clubId, dateId } = req.params;
const diaryDate = await DiaryDate.findOne({ where: { id: dateId, clubId } });
if (!diaryDate) throw new HttpError('Trainingstag nicht gefunden.', 404);
const state = normalizeState(req.body?.state);
const [tournament] = await KaisertischTournament.upsert({ clubId, diaryDateId: dateId, state });
res.status(200).json({ diaryDateId: Number(dateId), state: tournament?.state || state });
} catch (error) {
console.error('[saveKaisertischTournament]', error);
res.status(error.statusCode || 500).json({ error: error.message || 'Kaisertisch-Turnier konnte nicht gespeichert werden.' });
}
};