diff --git a/backend/services/falukantService.js b/backend/services/falukantService.js index 74041c8..af3c885 100644 --- a/backend/services/falukantService.js +++ b/backend/services/falukantService.js @@ -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) Noncivil‐Titel 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) Noncivil‐Titel 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, diff --git a/frontend/src/i18n/locales/de/falukant.json b/frontend/src/i18n/locales/de/falukant.json index fd8aad9..4ae1639 100644 --- a/frontend/src/i18n/locales/de/falukant.json +++ b/frontend/src/i18n/locales/de/falukant.json @@ -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", diff --git a/frontend/src/i18n/locales/en/falukant.json b/frontend/src/i18n/locales/en/falukant.json index cf70ead..cde5ff4 100644 --- a/frontend/src/i18n/locales/en/falukant.json +++ b/frontend/src/i18n/locales/en/falukant.json @@ -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", diff --git a/frontend/src/views/falukant/PoliticsView.vue b/frontend/src/views/falukant/PoliticsView.vue index d23e90b..4360b7f 100644 --- a/frontend/src/views/falukant/PoliticsView.vue +++ b/frontend/src/views/falukant/PoliticsView.vue @@ -74,13 +74,13 @@ {{ e.region.name }} {{ formatDate(e.date) }} - + @@ -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')); } } }