feat(admin): add potential fathers retrieval for character management
All checks were successful
Deploy to production / deploy (push) Successful in 2m47s

- Implemented a new method in AdminService to fetch potential fathers for a given character based on existing relationships.
- Updated AdminController to expose this functionality via a new API endpoint.
- Enhanced adminRouter to include the route for retrieving potential fathers.
- Modified frontend components to allow selection of potential fathers during pregnancy and birth management.
- Updated internationalization files to include new translation keys related to father selection.
This commit is contained in:
Torsten Schulz (local)
2026-03-31 08:50:56 +02:00
parent ee11a989a0
commit 9a78bc7c4b
30 changed files with 3907 additions and 45 deletions

View File

@@ -54,13 +54,28 @@
<template v-else>{{ $t('admin.falukant.edituser.pregnancy.statusNone') }}</template>
</p>
<label class="form-field">
{{ $t('admin.falukant.edituser.pregnancy.fatherId') }}
<input type="number" v-model.number="adminPregnancyFatherId" min="1" placeholder="—" />
{{ $t('admin.falukant.edituser.pregnancy.fatherSelect') }}
<select v-if="potentialFathers.length" v-model="adminPregnancyFatherSelect">
<option value="">{{ $t('admin.falukant.edituser.pregnancy.fatherNone') }}</option>
<option v-for="p in potentialFathers" :key="p.characterId" :value="String(p.characterId)">
{{ p.displayName }} {{ fatherRelationshipLabel(p.relationshipType) }}
</option>
</select>
<input
v-else
type="number"
v-model.number="adminPregnancyFatherManualId"
min="1"
:placeholder="$t('admin.falukant.edituser.pregnancy.fatherManualPlaceholder')"
/>
</label>
<p v-if="potentialFathers.length" class="admin-family-tools__hint">{{ $t('admin.falukant.edituser.pregnancy.fatherHintList') }}</p>
<p v-else class="admin-family-tools__hint">{{ $t('admin.falukant.edituser.pregnancy.fatherHintManual') }}</p>
<label class="form-field">
{{ $t('admin.falukant.edituser.pregnancy.dueDays') }}
<input type="number" v-model.number="adminDueInDays" min="1" max="365" />
<input type="number" v-model.number="adminDueInDays" min="0" max="365" />
</label>
<p class="admin-family-tools__hint">{{ $t('admin.falukant.edituser.pregnancy.dueDaysHint') }}</p>
<div class="action-buttons">
<button type="button" @click="adminForcePregnancy">{{ $t('admin.falukant.edituser.pregnancy.force') }}</button>
<button type="button" class="button-secondary" @click="adminClearPregnancy">{{ $t('admin.falukant.edituser.pregnancy.clear') }}</button>
@@ -69,9 +84,24 @@
<h4>{{ $t('admin.falukant.edituser.birth.title') }}</h4>
<p class="admin-family-tools__hint">{{ $t('admin.falukant.edituser.birth.motherHint') }}</p>
<label class="form-field">
{{ $t('admin.falukant.edituser.birth.fatherId') }} *
<input type="number" v-model.number="adminBirthFatherId" min="1" required />
{{ $t('admin.falukant.edituser.birth.fatherSelect') }} *
<select v-if="potentialFathers.length" v-model="adminBirthFatherSelect">
<option value="">{{ $t('admin.falukant.edituser.birth.fatherChoose') }}</option>
<option v-for="p in potentialFathers" :key="'b-' + p.characterId" :value="String(p.characterId)">
{{ p.displayName }} {{ fatherRelationshipLabel(p.relationshipType) }}
</option>
</select>
<input
v-else
type="number"
v-model.number="adminBirthFatherManualId"
min="1"
required
:placeholder="$t('admin.falukant.edituser.pregnancy.fatherManualPlaceholder')"
/>
</label>
<p v-if="potentialFathers.length" class="admin-family-tools__hint">{{ $t('admin.falukant.edituser.birth.fatherHintList') }}</p>
<p v-else class="admin-family-tools__hint">{{ $t('admin.falukant.edituser.birth.fatherHintManual') }}</p>
<label class="form-field">
{{ $t('admin.falukant.edituser.birth.context') }}
<select v-model="adminBirthContext">
@@ -220,9 +250,12 @@ export default {
branches: false,
stockTypes: false
},
adminPregnancyFatherId: null,
potentialFathers: [],
adminPregnancyFatherSelect: '',
adminPregnancyFatherManualId: null,
adminDueInDays: 21,
adminBirthFatherId: null,
adminBirthFatherSelect: '',
adminBirthFatherManualId: null,
adminBirthContext: 'marriage',
adminBirthLegitimacy: 'legitimate',
adminBirthGender: ''
@@ -300,6 +333,12 @@ export default {
this.originalAge = this.age;
this.users = [];
this.activeTab = 'userdata';
this.adminPregnancyFatherSelect = '';
this.adminPregnancyFatherManualId = null;
this.adminBirthFatherSelect = '';
this.adminBirthFatherManualId = null;
await this.loadPotentialFathers();
this.syncFatherSelectionsFromCharacter();
},
async saveUser() {
const dataToChange = {
@@ -432,11 +471,61 @@ export default {
this.originalUser = JSON.parse(JSON.stringify(this.editableUser));
this.age = Math.floor((Date.now() - new Date(this.editableUser.falukantData[0].character.birthdate)) / (24 * 60 * 60 * 1000));
this.originalAge = this.age;
await this.loadPotentialFathers();
this.syncFatherSelectionsFromCharacter();
},
fatherRelationshipLabel(relationshipType) {
const key = `admin.falukant.edituser.pregnancy.relationship.${relationshipType}`;
const t = this.$t(key);
return t === key ? relationshipType : t;
},
async loadPotentialFathers() {
const cid = this.editableUser?.falukantData?.[0]?.character?.id;
if (!cid) {
this.potentialFathers = [];
return;
}
try {
const r = await apiClient.get(`/api/admin/falukant/character/${cid}/potential-fathers`);
this.potentialFathers = Array.isArray(r.data?.options) ? r.data.options : [];
} catch (e) {
console.error('loadPotentialFathers', e);
this.potentialFathers = [];
}
},
syncFatherSelectionsFromCharacter() {
const c = this.editableUser?.falukantData?.[0]?.character;
if (!c) return;
const pf = c.pregnancyFatherCharacterId ?? c.pregnancy_father_character_id;
if (pf != null && this.potentialFathers.some((x) => x.characterId === Number(pf))) {
this.adminPregnancyFatherSelect = String(pf);
}
},
resolvePregnancyFatherCharacterId() {
if (this.potentialFathers.length) {
return this.adminPregnancyFatherSelect ? Number(this.adminPregnancyFatherSelect) : null;
}
return this.adminPregnancyFatherManualId ? Number(this.adminPregnancyFatherManualId) : null;
},
resolveBirthFatherCharacterId() {
if (this.potentialFathers.length) {
return this.adminBirthFatherSelect ? Number(this.adminBirthFatherSelect) : null;
}
return this.adminBirthFatherManualId ? Number(this.adminBirthFatherManualId) : null;
},
resolveDueInDays() {
const raw = this.adminDueInDays;
if (raw === '' || raw === null || raw === undefined) {
return 21;
}
const n = Number(raw);
return Number.isFinite(n) ? n : 21;
},
async adminForcePregnancy() {
const characterId = this.editableUser.falukantData[0].character.id;
const payload = { characterId, dueInDays: Number(this.adminDueInDays) || 21 };
if (this.adminPregnancyFatherId) payload.fatherCharacterId = Number(this.adminPregnancyFatherId);
const payload = { characterId, dueInDays: this.resolveDueInDays() };
const fatherId = this.resolvePregnancyFatherCharacterId();
if (fatherId != null) payload.fatherCharacterId = fatherId;
try {
await apiClient.post('/api/admin/falukant/character/force-pregnancy', payload);
showSuccess(this, 'tr:admin.falukant.edituser.pregnancy.successForce');
@@ -457,13 +546,14 @@ export default {
},
async adminForceBirth() {
const motherCharacterId = this.editableUser.falukantData[0].character.id;
if (!this.adminBirthFatherId) {
showError(this, this.$t('admin.falukant.edituser.birth.fatherId'));
const fatherCharacterId = this.resolveBirthFatherCharacterId();
if (fatherCharacterId == null || Number.isNaN(fatherCharacterId)) {
showError(this, this.$t('admin.falukant.edituser.birth.fatherRequired'));
return;
}
const body = {
motherCharacterId,
fatherCharacterId: Number(this.adminBirthFatherId),
fatherCharacterId,
birthContext: this.adminBirthContext,
legitimacy: this.adminBirthLegitimacy
};