feat(falukant): enhance child details with other parent information and birth context
All checks were successful
Deploy to production / deploy (push) Successful in 3m2s

- Updated FalukantService to include father and mother character IDs in child relationships.
- Added logic to retrieve and display other parent details in ChildDetailsDialog and FamilyView components.
- Introduced new translations for 'other parent' and 'birth context' in English, German, and Spanish localization files.
- Enhanced UI to show other parent information and birth context in child detail views.
This commit is contained in:
Torsten Schulz (local)
2026-03-31 10:29:22 +02:00
parent 9b3898e43c
commit db0e80a559
6 changed files with 145 additions and 4 deletions

View File

@@ -3439,7 +3439,7 @@ class FalukantService extends BaseService {
{ motherCharacterId: { [Op.in]: userCharacterIds } } { motherCharacterId: { [Op.in]: userCharacterIds } }
] ]
}, },
attributes: ['childCharacterId', 'nameSet', 'isHeir', 'legitimacy', 'birthContext', 'publicKnown', 'createdAt'] attributes: ['childCharacterId', 'nameSet', 'isHeir', 'legitimacy', 'birthContext', 'publicKnown', 'createdAt', 'fatherCharacterId', 'motherCharacterId']
}) })
: []; : [];
const childCharIds = [...new Set(childRels.map(r => r.childCharacterId))]; const childCharIds = [...new Set(childRels.map(r => r.childCharacterId))];
@@ -3451,7 +3451,57 @@ class FalukantService extends BaseService {
}) })
: []; : [];
const childCharMap = Object.fromEntries(childChars.map(c => [c.id, c])); const childCharMap = Object.fromEntries(childChars.map(c => [c.id, c]));
const children = childRels.map(rel => {
const otherParentIds = new Set();
const relMeta = childRels.map((rel) => {
const fatherId = rel.fatherCharacterId;
const motherId = rel.motherCharacterId;
const userIsFather = userCharacterIds.includes(fatherId);
const userIsMother = userCharacterIds.includes(motherId);
let otherParentId = null;
let playerRole = null;
if (userIsFather && userIsMother) {
otherParentId = null;
playerRole = null;
} else if (userIsFather) {
otherParentId = motherId;
playerRole = 'father';
} else if (userIsMother) {
otherParentId = fatherId;
playerRole = 'mother';
}
if (otherParentId != null) {
otherParentIds.add(otherParentId);
}
return { rel, otherParentId, playerRole };
});
const otherParentChars = otherParentIds.size
? await FalukantCharacter.findAll({
where: { id: { [Op.in]: [...otherParentIds] } },
attributes: ['id', 'gender', 'titleOfNobility'],
include: [
{ model: FalukantPredefineFirstname, as: 'definedFirstName', attributes: ['name'] },
{ model: TitleOfNobility, as: 'nobleTitle', attributes: ['labelTr'] }
]
})
: [];
const otherParentMap = Object.fromEntries(
otherParentChars.map((c) => {
const plain = c.get({ plain: true });
return [
plain.id,
{
characterId: plain.id,
firstName: plain.definedFirstName?.name || 'Unknown',
gender: plain.gender || 'male',
nobleTitle: plain.nobleTitle?.labelTr || 'noncivil',
}
];
})
);
const children = relMeta.map(({ rel, otherParentId, playerRole }) => {
const kid = childCharMap[rel.childCharacterId]; const kid = childCharMap[rel.childCharacterId];
return { return {
childCharacterId: rel.childCharacterId, childCharacterId: rel.childCharacterId,
@@ -3464,6 +3514,8 @@ class FalukantService extends BaseService {
birthContext: rel.birthContext || 'marriage', birthContext: rel.birthContext || 'marriage',
publicKnown: !!rel.publicKnown, publicKnown: !!rel.publicKnown,
_createdAt: rel.createdAt, _createdAt: rel.createdAt,
playerRole,
otherParent: otherParentId != null ? (otherParentMap[otherParentId] || null) : null,
}; };
}); });
// Sort children globally by relation createdAt ascending (older first) // Sort children globally by relation createdAt ascending (older first)

View File

@@ -27,6 +27,14 @@
<td>{{ $t('falukant.family.children.gender') }}</td> <td>{{ $t('falukant.family.children.gender') }}</td>
<td>{{ $t(`falukant.titles.${child.gender}.noncivil`) }}</td> <td>{{ $t(`falukant.titles.${child.gender}.noncivil`) }}</td>
</tr> </tr>
<tr v-if="child.birthContext">
<td>{{ $t('falukant.family.children.birthContextLabel') }}</td>
<td>{{ $t('falukant.family.children.birthContextLong.' + child.birthContext) }}</td>
</tr>
<tr>
<td>{{ $t('falukant.family.children.otherParent') }}</td>
<td>{{ otherParentDisplay }}</td>
</tr>
<tr> <tr>
<td>{{ $t('falukant.family.children.heir') }}</td> <td>{{ $t('falukant.family.children.heir') }}</td>
<td> <td>
@@ -58,6 +66,16 @@ export default {
child: null child: null
}; };
}, },
computed: {
otherParentDisplay() {
if (!this.child?.otherParent) {
return this.$t('falukant.family.children.otherParentUnknown');
}
const o = this.child.otherParent;
const title = this.$t(`falukant.titles.${o.gender}.${o.nobleTitle}`);
return `${title} ${o.firstName}`.trim();
}
},
methods: { methods: {
open(child) { open(child) {
this.child = child; this.child = child;

View File

@@ -659,6 +659,17 @@
"acknowledged_bastard": "Anerkannt unehelich", "acknowledged_bastard": "Anerkannt unehelich",
"hidden_bastard": "Unehelich" "hidden_bastard": "Unehelich"
}, },
"otherParent": "Anderes Elternteil",
"otherParentUnknown": "Unbekannt",
"birthContextLabel": "Herkunft",
"birthContextShort": {
"marriage": "Ehe",
"lover": "Liebschaft"
},
"birthContextLong": {
"marriage": "Aus der Ehe",
"lover": "Aus einer Liebschaft"
},
"details": { "details": {
"title": "Kind-Details" "title": "Kind-Details"
} }

View File

@@ -653,10 +653,21 @@
"acknowledged_bastard": "Acknowledged illegitimate", "acknowledged_bastard": "Acknowledged illegitimate",
"hidden_bastard": "Illegitimate" "hidden_bastard": "Illegitimate"
}, },
"otherParent": "Other parent",
"otherParentUnknown": "Unknown",
"birthContextLabel": "Origin",
"birthContextShort": {
"marriage": "Marriage",
"lover": "Affair"
},
"birthContextLong": {
"marriage": "From marriage",
"lover": "From an affair"
},
"details": { "details": {
"title": "Child Details" "title": "Child Details"
} }
, },
"taxes": { "taxes": {
"title": "Taxes", "title": "Taxes",
"loading": "Loading tax data...", "loading": "Loading tax data...",
@@ -665,7 +676,6 @@
"region": "Region", "region": "Region",
"taxPercent": "Tax %" "taxPercent": "Tax %"
} }
}
}, },
"spouse": { "spouse": {
"traitsToggle": "Character traits", "traitsToggle": "Character traits",

View File

@@ -640,6 +640,17 @@
"acknowledged_bastard": "Ilegítimo reconocido", "acknowledged_bastard": "Ilegítimo reconocido",
"hidden_bastard": "Ilegítimo" "hidden_bastard": "Ilegítimo"
}, },
"otherParent": "Otro progenitor",
"otherParentUnknown": "Desconocido",
"birthContextLabel": "Origen",
"birthContextShort": {
"marriage": "Matrimonio",
"lover": "Amorío"
},
"birthContextLong": {
"marriage": "Del matrimonio",
"lover": "De una relación amorosa"
},
"details": { "details": {
"title": "Detalles del hijo" "title": "Detalles del hijo"
} }

View File

@@ -278,6 +278,7 @@
<thead> <thead>
<tr> <tr>
<th>{{ $t('falukant.family.children.name') }}</th> <th>{{ $t('falukant.family.children.name') }}</th>
<th>{{ $t('falukant.family.children.otherParent') }}</th>
<th>{{ $t('falukant.family.children.age') }}</th> <th>{{ $t('falukant.family.children.age') }}</th>
<th>{{ $t('falukant.family.children.heir') }}</th> <th>{{ $t('falukant.family.children.heir') }}</th>
<th>{{ $t('falukant.family.children.actions') }}</th> <th>{{ $t('falukant.family.children.actions') }}</th>
@@ -295,6 +296,15 @@
<button @click="jumpToChurchForm">{{ $t('falukant.family.children.baptism') <button @click="jumpToChurchForm">{{ $t('falukant.family.children.baptism')
}}</button> }}</button>
</td> </td>
<td class="child-other-parent-cell">
<span class="child-other-parent-name">{{ formatOtherParentLine(child) }}</span>
<span
v-if="child.birthContext"
class="child-origin-badge child-origin-badge--context"
>
{{ birthContextShortLabel(child.birthContext) }}
</span>
</td>
<td>{{ child.age }}</td> <td>{{ child.age }}</td>
<td> <td>
<span v-if="child.isHeir" class="heir-badge">{{ $t('falukant.family.children.isHeir') }}</span> <span v-if="child.isHeir" class="heir-badge">{{ $t('falukant.family.children.isHeir') }}</span>
@@ -720,6 +730,21 @@ export default {
this.$refs.childDetailsDialog?.open(child); this.$refs.childDetailsDialog?.open(child);
}, },
formatOtherParentLine(child) {
if (!child?.otherParent) {
return this.$t('falukant.family.children.otherParentUnknown');
}
const o = child.otherParent;
const title = this.$t(`falukant.titles.${o.gender}.${o.nobleTitle}`);
return `${title} ${o.firstName}`.trim();
},
birthContextShortLabel(context) {
const key = `falukant.family.children.birthContextShort.${context}`;
const t = this.$t(key);
return t === key ? context : t;
},
async setAsHeir(child) { async setAsHeir(child) {
if (!child.childCharacterId) { if (!child.childCharacterId) {
showError(this, 'tr:falukant.family.children.heirSetError'); showError(this, 'tr:falukant.family.children.heirSetError');
@@ -1218,6 +1243,20 @@ export default {
font-weight: 700; font-weight: 700;
} }
.child-origin-badge--context {
margin-left: 6px;
margin-top: 4px;
}
.child-other-parent-cell {
max-width: 14rem;
vertical-align: top;
}
.child-other-parent-name {
display: block;
}
.lovers-grid { .lovers-grid {
display: grid; display: grid;
grid-template-columns: repeat(auto-fill, minmax(min(100%, 300px), 1fr)); grid-template-columns: repeat(auto-fill, minmax(min(100%, 300px), 1fr));