From c771dae5a46b8d7c5c9ec5e6b8d7d77704100375 Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Wed, 19 Aug 2026 13:38:29 +0200 Subject: [PATCH] feat(politics): enhance office application feedback with required title details --- backend/services/falukantService.js | 24 ++++++++++++++++++++ frontend/src/i18n/locales/de/falukant.json | 3 ++- frontend/src/i18n/locales/en/falukant.json | 3 ++- frontend/src/views/falukant/PoliticsView.vue | 10 +++++++- 4 files changed, 37 insertions(+), 3 deletions(-) diff --git a/backend/services/falukantService.js b/backend/services/falukantService.js index 11bc91f..4b7aee5 100755 --- a/backend/services/falukantService.js +++ b/backend/services/falukantService.js @@ -7197,6 +7197,27 @@ class FalukantService extends BaseService { const nameToId = new Map(allTypes.map((t) => [t.name, t.id])); const titleId = character.titleOfNobility ?? character.nobleTitle?.id; const allowedOfficeNames = await getAllowedOfficeTypeNamesByTitle(titleId); + const [titles, officeEligibilityBenefits] = await Promise.all([ + TitleOfNobility.findAll({ attributes: ['id', 'labelTr', 'level'] }), + TitleBenefit.findAll({ + where: { benefitType: 'office_eligibility' }, + attributes: ['titleId', 'parameters'] + }) + ]); + const titleById = new Map(titles.map((title) => [Number(title.id), title])); + const minimumTitleByOfficeName = new Map(); + for (const benefit of officeEligibilityBenefits) { + const officeNames = benefit.parameters?.officeTypeNames; + const title = titleById.get(Number(benefit.titleId)); + if (!Array.isArray(officeNames) || !title) continue; + + for (const officeName of officeNames) { + const currentMinimum = minimumTitleByOfficeName.get(officeName); + if (!currentMinimum || title.level < currentMinimum.level) { + minimumTitleByOfficeName.set(officeName, title); + } + } + } return allTypes.map((typeRow) => { const type = typeRow.get({ plain: true }); @@ -7213,6 +7234,7 @@ class FalukantService extends BaseService { const prerequisitesMet = requiredOfficeNames.length === 0 || matchedOfficeNames.length > 0; const titleEligible = allowedOfficeNames.size === 0 || allowedOfficeNames.has(type.name); const canApplyNow = canApplyByAge && titleEligible && prerequisitesMet; + const requiredTitle = minimumTitleByOfficeName.get(type.name); return { officeTypeId: type.id, @@ -7225,6 +7247,8 @@ class FalukantService extends BaseService { matchedOfficeNames, prerequisitesMet, titleEligible, + requiredTitleLabelTr: requiredTitle?.labelTr ?? null, + characterGender: character.gender, canApplyByAge, canApplyNow }; diff --git a/frontend/src/i18n/locales/de/falukant.json b/frontend/src/i18n/locales/de/falukant.json index d6b6c5c..9a277f4 100755 --- a/frontend/src/i18n/locales/de/falukant.json +++ b/frontend/src/i18n/locales/de/falukant.json @@ -1611,7 +1611,8 @@ "yes": "Ja", "no": "Nein", "blockedByAge": "Mindestalter nicht erreicht", - "blockedByTitle": "Standesvoraussetzung nicht erfüllt", + "blockedByTitle": "Benötigter Stand: mindestens {title}", + "unknownRequiredTitle": "unbekannt", "blockedByPrerequisites": "Vorherige Ämter fehlen" }, "too_young": "Dein Charakter ist noch zu jung. Eine Bewerbung ist erst ab 16 Jahren möglich.", diff --git a/frontend/src/i18n/locales/en/falukant.json b/frontend/src/i18n/locales/en/falukant.json index eb8bf8e..43260ae 100755 --- a/frontend/src/i18n/locales/en/falukant.json +++ b/frontend/src/i18n/locales/en/falukant.json @@ -797,7 +797,8 @@ "yes": "Yes", "no": "No", "blockedByAge": "Minimum age not met", - "blockedByTitle": "Title requirement not met", + "blockedByTitle": "Required title: at least {title}", + "unknownRequiredTitle": "unknown", "blockedByPrerequisites": "Required prior offices missing" }, "too_young": "Your character is too young. Applications are only possible from age 16.", diff --git a/frontend/src/views/falukant/PoliticsView.vue b/frontend/src/views/falukant/PoliticsView.vue index 62f9a49..04bd01d 100755 --- a/frontend/src/views/falukant/PoliticsView.vue +++ b/frontend/src/views/falukant/PoliticsView.vue @@ -180,7 +180,11 @@ : $t('falukant.politics.officesTab.no') }} @@ -364,6 +368,10 @@ export default { await this.loadCurrentPositions(); }, methods: { + formatRequiredTitle(titleLabelTr, gender) { + if (!titleLabelTr) return this.$t('falukant.politics.officesTab.unknownRequiredTitle'); + return this.$t(`falukant.titles.${gender === 'female' ? 'female' : 'male'}.${titleLabelTr}`); + }, formatPoliticalCharacterName(character) { const firstName = character?.definedFirstName?.name || ''; const lastName = character?.definedLastName?.name || '';