feat(ClubSettings): add member data quality requirements configuration
All checks were successful
Deploy tt-tagebuch / deploy (push) Successful in 38s

- Introduced new settings for member data quality requirements in club settings, allowing configuration of required fields such as street, postal code, city, phone, and email.
- Updated the backend to handle the new memberDataQualityRequirements field in club settings.
- Enhanced the frontend to display and manage these requirements in the ClubSettings view, improving user experience and data integrity.
- Added localization support for new terms related to member data quality across multiple languages.
This commit is contained in:
Torsten Schulz (local)
2026-04-15 22:15:04 +02:00
parent 4cfc82c7aa
commit 5fa34637ba
21 changed files with 237 additions and 24 deletions

View File

@@ -60,12 +60,19 @@ export const updateClubSettings = async (req, res) => {
try {
const { authcode: token } = req.headers;
const { clubid } = req.params;
const { greetingText, associationMemberNumber, myTischtennisFedNickname, autoFetchRankings } = req.body;
const {
greetingText,
associationMemberNumber,
myTischtennisFedNickname,
autoFetchRankings,
memberDataQualityRequirements
} = req.body;
const updated = await ClubService.updateClubSettings(token, clubid, {
greetingText,
associationMemberNumber,
myTischtennisFedNickname,
autoFetchRankings
autoFetchRankings,
memberDataQualityRequirements
});
res.status(200).json(updated);
} catch (error) {

View File

@@ -0,0 +1,6 @@
-- Migration: Add per-club member data quality requirements.
-- Controls which optional contact/address fields count as required on /members.
ALTER TABLE clubs
ADD COLUMN IF NOT EXISTS member_data_quality_requirements JSON NULL
COMMENT 'Configures which member fields are required for data quality checks';

View File

@@ -29,6 +29,12 @@ const Club = sequelize.define('Club', {
defaultValue: false,
field: 'auto_fetch_rankings',
comment: 'Enable automatic TTR/QTTR rankings fetch for this club'
},
memberDataQualityRequirements: {
type: DataTypes.JSON,
allowNull: true,
field: 'member_data_quality_requirements',
comment: 'Configures which member fields are required for data quality checks'
}
}, {
tableName: 'clubs',

View File

@@ -71,7 +71,8 @@ class ClubService {
greetingText,
associationMemberNumber,
myTischtennisFedNickname,
autoFetchRankings
autoFetchRankings,
memberDataQualityRequirements
}) {
await checkAccess(userToken, clubId);
const club = await Club.findByPk(clubId);
@@ -81,9 +82,33 @@ class ClubService {
const updates = { greetingText, associationMemberNumber };
if (myTischtennisFedNickname !== undefined) updates.myTischtennisFedNickname = myTischtennisFedNickname || null;
if (autoFetchRankings !== undefined) updates.autoFetchRankings = !!autoFetchRankings;
if (memberDataQualityRequirements !== undefined) {
updates.memberDataQualityRequirements = this.normalizeMemberDataQualityRequirements(memberDataQualityRequirements);
}
return await club.update(updates);
}
normalizeMemberDataQualityRequirements(settings) {
const defaults = {
requireStreet: true,
requirePostalCode: true,
requireCity: true,
requirePhone: true,
requireEmail: true
};
if (!settings || typeof settings !== 'object' || Array.isArray(settings)) {
return defaults;
}
return Object.fromEntries(
Object.entries(defaults).map(([key, defaultValue]) => [
key,
typeof settings[key] === 'boolean' ? settings[key] : defaultValue
])
);
}
async approveUserClubAccess(userToken, clubId, toApproveUserId) {
await checkAccess(userToken, clubId);
const toApproveUserClub = await UserClub.findOne({