Implement age validation for political applications in FalukantService: Added MIN_AGE_POLITICS_DAYS constant and logic to check user age before allowing applications. Updated PoliticsView to reflect age eligibility and display appropriate hints. Enhanced error handling for age-related restrictions.

This commit is contained in:
Torsten Schulz (local)
2026-02-06 00:00:11 +01:00
parent 23b0c45518
commit 6b96ee9856
4 changed files with 33 additions and 14 deletions

View File

@@ -4408,9 +4408,15 @@ class FalukantService extends BaseService {
});
}
/** Mindestalter für Bewerbung auf politische Ämter (in Tagen; 16 Tage Realzeit = 16 Spieljahre) */
static MIN_AGE_POLITICS_DAYS = 16;
async getOpenPolitics(hashedUserId) {
const user = await this.getFalukantUserByHashedId(hashedUserId);
const characterId = user.character.id;
const character = user.character;
const characterId = character.id;
const ageDays = character.birthdate ? calcAge(character.birthdate) : 0;
const canApplyByAge = ageDays >= FalukantService.MIN_AGE_POLITICS_DAYS;
const rows = await sequelize.query(
FalukantService.RECURSIVE_REGION_SEARCH,
{
@@ -4481,7 +4487,8 @@ class FalukantService extends BaseService {
return {
...e,
history: matchingHistory,
alreadyApplied
alreadyApplied,
canApplyByAge
};
})
.filter(election => !election.alreadyApplied); // Nur Positionen ohne bestehende Bewerbung
@@ -4499,24 +4506,30 @@ class FalukantService extends BaseService {
throw new Error('Kein Charakter zum User gefunden');
}
// 2) NoncivilTitel aussperren
// 2) Mindestalter 16 (Spieljahre = 16 Tage Realzeit)
const ageDays = character.birthdate ? calcAge(character.birthdate) : 0;
if (ageDays < FalukantService.MIN_AGE_POLITICS_DAYS) {
throw new Error('too_young');
}
// 3) NoncivilTitel aussperren
if (character.nobleTitle.labelTr === 'noncivil') {
return { applied: [], skipped: electionIds };
}
// 3) Ermittle die offenen Wahlen, auf die er zugreifen darf
// 4) Ermittle die offenen Wahlen, auf die er zugreifen darf
// Verwende getOpenPolitics statt getElections, da getOpenPolitics die gleichen Wahlen
// zurückgibt, die im Frontend angezeigt werden
const openPolitics = await this.getOpenPolitics(hashedUserId);
const allowedIds = new Set(openPolitics.map(e => e.id));
// 4) Filter alle electionIds auf gültige/erlaubte
// 5) Filter alle electionIds auf gültige/erlaubte
const toTry = electionIds.filter(id => allowedIds.has(id));
if (toTry.length === 0) {
return { applied: [], skipped: electionIds };
}
// 5) Prüfe, auf welche dieser Wahlen der Character bereits als Candidate eingetragen ist
// 6) Prüfe, auf welche dieser Wahlen der Character bereits als Candidate eingetragen ist
const existing = await Candidate.findAll({
where: {
electionId: { [Op.in]: toTry },
@@ -4526,13 +4539,11 @@ class FalukantService extends BaseService {
});
const alreadyIds = new Set(existing.map(c => c.electionId));
// 6) Erstelle Liste der Wahlen, für die er sich noch nicht beworben hat
// 7) Erstelle Liste der Wahlen, für die er sich noch nicht beworben hat
const newApplications = toTry.filter(id => !alreadyIds.has(id));
const skipped = electionIds.filter(id => !newApplications.includes(id));
console.log(newApplications, skipped);
// 7) Bulk-Insert aller neuen Bewerbungen
// 8) Bulk-Insert aller neuen Bewerbungen
if (newApplications.length > 0) {
const toInsert = newApplications.map(eid => ({
electionId: eid,

View File

@@ -1030,8 +1030,10 @@
"date": "Datum",
"candidacy": "Kandidatur",
"none": "Keine offenen Positionen.",
"apply": "Für ausgewählte Positionen kandidieren"
"apply": "Für ausgewählte Positionen kandidieren",
"minAgeHint": "Kandidatur erst ab 16 Jahren möglich."
},
"too_young": "Dein Charakter ist noch zu jung. Eine Bewerbung ist erst ab 16 Jahren möglich.",
"upcoming": {
"office": "Amt",
"region": "Region",

View File

@@ -364,8 +364,10 @@
"date": "Date",
"candidacy": "Candidacy",
"none": "No open positions.",
"apply": "Apply for selected positions"
"apply": "Apply for selected positions",
"minAgeHint": "Candidacy is only possible from age 16."
},
"too_young": "Your character is too young. Applications are only possible from age 16.",
"upcoming": {
"office": "Office",
"region": "Region",

View File

@@ -74,13 +74,13 @@
<td>{{ e.region.name }}</td>
<td>{{ formatDate(e.date) }}</td>
<!-- Checkbox ganz am Ende -->
<td>
<td :title="e.canApplyByAge === false ? $t('falukant.politics.open.minAgeHint') : null">
<input
type="checkbox"
:id="`apply-${e.id}`"
v-model="selectedApplications"
:value="e.id"
:disabled="e.alreadyApplied"
:disabled="e.alreadyApplied || e.canApplyByAge === false"
/>
</td>
</tr>
@@ -389,6 +389,10 @@ export default {
.map(e => e.id);
} catch (err) {
console.error('Error submitting applications', err);
const msg = err?.response?.data?.error === 'too_young'
? this.$t('falukant.politics.too_young')
: (err?.response?.data?.error || err?.message || this.$t('falukant.politics.applyError'));
this.$root.$refs?.messageDialog?.open?.(msg, this.$t('falukant.politics.title'));
}
}
}