feat(ClubSettings): add country and state code fields for regional calendar data
All checks were successful
Deploy tt-tagebuch / deploy (push) Successful in 43s

- Introduced `countryCode` and `stateCode` fields in the Club model to support regional calendar data.
- Updated ClubSettings component to allow users to select their country and state, enhancing the configuration options for clubs.
- Enhanced the ClubService to handle normalization of country and state codes during updates.
- Added new routes and middleware to support the training cancellation feature and calendar integration in the backend.
- Updated frontend navigation to include a calendar link, improving user access to scheduling features.
This commit is contained in:
Torsten Schulz (local)
2026-05-12 23:46:07 +02:00
parent 1e23171370
commit bea5facb7d
46 changed files with 4286 additions and 12 deletions

View File

@@ -0,0 +1,20 @@
import calendarHolidayService from '../services/calendarHolidayService.js';
export const getClubCalendarDays = async (req, res) => {
try {
const { authcode: token } = req.headers;
const { clubId } = req.params;
const { year } = req.query;
const result = await calendarHolidayService.getClubCalendarDays(token, clubId, year);
res.status(200).json(result);
} catch (error) {
if (error.message === 'clubnotfound') {
res.status(404).json({ error: 'clubnotfound' });
} else if (error.message === 'noaccess') {
res.status(403).json({ error: 'noaccess' });
} else {
console.error('[getClubCalendarDays] - error:', error);
res.status(502).json({ error: 'calendarproviderfailed' });
}
}
};

View File

@@ -65,6 +65,8 @@ export const updateClubSettings = async (req, res) => {
associationMemberNumber,
myTischtennisFedNickname,
autoFetchRankings,
countryCode,
stateCode,
memberDataQualityRequirements
} = req.body;
const updated = await ClubService.updateClubSettings(token, clubid, {
@@ -72,6 +74,8 @@ export const updateClubSettings = async (req, res) => {
associationMemberNumber,
myTischtennisFedNickname,
autoFetchRankings,
countryCode,
stateCode,
memberDataQualityRequirements
});
res.status(200).json(updated);

View File

@@ -0,0 +1,49 @@
import trainingCancellationService from '../services/trainingCancellationService.js';
import { getSafeErrorMessage } from '../utils/errorUtils.js';
export const getTrainingCancellations = async (req, res) => {
try {
const { authcode: userToken } = req.headers;
const { clubId } = req.params;
const { year } = req.query;
const result = await trainingCancellationService.getTrainingCancellations(userToken, clubId, year);
res.status(200).json(result);
} catch (error) {
console.error('[getTrainingCancellations] - Error:', error);
const message = getSafeErrorMessage(error, 'Fehler beim Laden der Trainingsausfälle');
res.status(error.statusCode || 500).json({ error: message });
}
};
export const upsertTrainingCancellation = async (req, res) => {
try {
const { authcode: userToken } = req.headers;
const { clubId } = req.params;
const { date, startDate, endDate, reason } = req.body;
const result = await trainingCancellationService.upsertTrainingCancellation(
userToken,
clubId,
startDate || date,
reason,
endDate || date || startDate
);
res.status(200).json(result);
} catch (error) {
console.error('[upsertTrainingCancellation] - Error:', error);
const message = getSafeErrorMessage(error, 'Fehler beim Speichern des Trainingsausfalls');
res.status(error.statusCode || 500).json({ error: message });
}
};
export const deleteTrainingCancellation = async (req, res) => {
try {
const { authcode: userToken } = req.headers;
const { clubId, cancellationId } = req.params;
const result = await trainingCancellationService.deleteTrainingCancellation(userToken, clubId, cancellationId);
res.status(200).json(result);
} catch (error) {
console.error('[deleteTrainingCancellation] - Error:', error);
const message = getSafeErrorMessage(error, 'Fehler beim Löschen des Trainingsausfalls');
res.status(error.statusCode || 500).json({ error: message });
}
};