Add heir selection functionality in Falukant module

- Implemented getPotentialHeirs and selectHeir methods in FalukantService to allow users to retrieve and select potential heirs based on specific criteria.
- Updated FalukantController to wrap new methods with user authentication and added corresponding routes in FalukantRouter.
- Enhanced OverviewView component to display heir selection UI when no character is present, including loading states and error handling.
- Added translations for heir selection messages in both German and English locales to improve user experience.
This commit is contained in:
Torsten Schulz (local)
2026-01-07 10:29:16 +01:00
parent c17af04cbf
commit c90b7785c0
6 changed files with 280 additions and 11 deletions

View File

@@ -93,6 +93,8 @@ class FalukantController {
return result;
});
this.setHeir = this._wrapWithUser((userId, req) => this.service.setHeir(userId, req.body.childCharacterId));
this.getPotentialHeirs = this._wrapWithUser((userId) => this.service.getPotentialHeirs(userId));
this.selectHeir = this._wrapWithUser((userId, req) => this.service.selectHeir(userId, req.body.heirId));
this.acceptMarriageProposal = this._wrapWithUser((userId, req) => this.service.acceptMarriageProposal(userId, req.body.proposalId));
this.getGifts = this._wrapWithUser((userId) => {
console.log('🔍 getGifts called with userId:', userId);

View File

@@ -39,6 +39,8 @@ router.get('/directors', falukantController.getAllDirectors);
router.post('/directors', falukantController.updateDirector);
router.post('/family/acceptmarriageproposal', falukantController.acceptMarriageProposal);
router.post('/family/set-heir', falukantController.setHeir);
router.get('/heirs/potential', falukantController.getPotentialHeirs);
router.post('/heirs/select', falukantController.selectHeir);
router.get('/family/gifts', falukantController.getGifts);
router.get('/family/children', falukantController.getChildren);
router.post('/family/gift', falukantController.sendGift);

View File

@@ -2925,6 +2925,107 @@ class FalukantService extends BaseService {
return { success: true, childCharacterId };
}
async getPotentialHeirs(hashedUserId) {
const user = await getFalukantUserOrFail(hashedUserId);
// Prüfe, ob der User bereits einen Charakter hat
const existingCharacter = await FalukantCharacter.findOne({ where: { userId: user.id } });
if (existingCharacter) {
throw new Error('User already has a character');
}
if (!user.mainBranchRegionId) {
throw new Error('User has no main branch region');
}
// Hole den noncivil Titel
const noncivilTitle = await TitleOfNobility.findOne({ where: { labelTr: 'noncivil' } });
if (!noncivilTitle) {
throw new Error('Noncivil title not found');
}
// Berechne das Datum für 10-14 Jahre alt (in Tagen)
const now = new Date();
now.setHours(0, 0, 0, 0);
const minDate = new Date(now);
minDate.setDate(minDate.getDate() - 14); // 14 Tage = 14 Jahre
const maxDate = new Date(now);
maxDate.setDate(maxDate.getDate() - 10); // 10 Tage = 10 Jahre
// Hole zufällige Charaktere aus der Hauptregion, die 10-14 Jahre alt sind
// und keinen userId haben (also noch keinem User zugeordnet sind)
// und den noncivil Titel haben
const potentialHeirs = await FalukantCharacter.findAll({
where: {
regionId: user.mainBranchRegionId,
userId: null,
titleOfNobility: noncivilTitle.id,
birthdate: {
[Op.between]: [minDate, maxDate]
}
},
include: [
{ model: FalukantPredefineFirstname, as: 'definedFirstName', attributes: ['name'] },
{ model: FalukantPredefineLastname, as: 'definedLastName', attributes: ['name'] }
],
order: Sequelize.fn('RANDOM'),
limit: 5
});
// Berechne das Alter für jeden Charakter
return potentialHeirs.map(heir => {
const age = calcAge(heir.birthdate);
return {
id: heir.id,
definedFirstName: heir.definedFirstName,
definedLastName: heir.definedLastName,
gender: heir.gender,
age: age
};
});
}
async selectHeir(hashedUserId, heirId) {
const user = await getFalukantUserOrFail(hashedUserId);
// Prüfe, ob der User bereits einen Charakter hat
const existingCharacter = await FalukantCharacter.findOne({ where: { userId: user.id } });
if (existingCharacter) {
throw new Error('User already has a character');
}
// Hole den Erben-Charakter
const heir = await FalukantCharacter.findOne({
where: {
id: heirId,
userId: null // Stelle sicher, dass er noch keinem User zugeordnet ist
}
});
if (!heir) {
throw new Error('Heir not found or already assigned');
}
// Prüfe, ob der Erbe in der Hauptregion ist
if (heir.regionId !== user.mainBranchRegionId) {
throw new Error('Heir is not in main branch region');
}
// Prüfe das Alter (10-14 Jahre)
const age = calcAge(heir.birthdate);
if (age < 10 || age > 14) {
throw new Error('Heir age is not between 10 and 14');
}
// Weise den Charakter dem User zu
await heir.update({ userId: user.id });
// Benachrichtige den User
notifyUser(hashedUserId, 'falukantUserUpdated', {});
return { success: true, characterId: heir.id };
}
async getPossiblePartners(requestingCharacterId) {
const proposals = await MarriageProposal.findAll({
where: {