feat(family): enhance family view and character pregnancy handling
All checks were successful
Deploy to production / deploy (push) Successful in 2m48s
All checks were successful
Deploy to production / deploy (push) Successful in 2m48s
- Updated the FalukantCharacter model to include a default scope that excludes pregnancy-related fields for compatibility with older databases. - Implemented a new method in FalukantService to conditionally retrieve pregnancy information based on database schema. - Enhanced the FamilyView component to display a summary navigation for family relationships, including partners, children, and lovers. - Updated internationalization files to include new translations for family-related terms and summaries.
This commit is contained in:
@@ -536,6 +536,20 @@
|
||||
},
|
||||
"family": {
|
||||
"title": "Familie",
|
||||
"heroIntro": "Beziehungen, Kinder und Entwicklung — unten nach Bereichen geordnet.",
|
||||
"summary": {
|
||||
"partnerChip": "Partner",
|
||||
"childrenChip": "Kinder",
|
||||
"loversChip": "Liebschaften",
|
||||
"proposalsAvailable": "Verlobung möglich",
|
||||
"noPartner": "Kein Partner"
|
||||
},
|
||||
"tabs": {
|
||||
"partner": "Partner & Ehe",
|
||||
"children": "Kinder",
|
||||
"lovers": "Liebschaften"
|
||||
},
|
||||
"tabsAria": "Familienbereiche",
|
||||
"debtorsPrison": {
|
||||
"familyWarning": "Anhaltender Kreditverzug belastet Ehe, Haushalt und Liebschaften.",
|
||||
"familyImpact": "Der Schuldturm schadet Ehe, Hausfrieden und der Stabilität von Liebschaften."
|
||||
@@ -546,6 +560,7 @@
|
||||
},
|
||||
"spouse": {
|
||||
"title": "Beziehung",
|
||||
"traitsToggle": "Charaktereigenschaften",
|
||||
"name": "Name",
|
||||
"age": "Alter",
|
||||
"status": "Status",
|
||||
|
||||
@@ -607,6 +607,21 @@
|
||||
}
|
||||
},
|
||||
"family": {
|
||||
"title": "Family",
|
||||
"heroIntro": "Relationships, children and development — organized by section below.",
|
||||
"summary": {
|
||||
"partnerChip": "Partner",
|
||||
"childrenChip": "Children",
|
||||
"loversChip": "Affairs",
|
||||
"proposalsAvailable": "Betrothal available",
|
||||
"noPartner": "No partner"
|
||||
},
|
||||
"tabs": {
|
||||
"partner": "Partner & marriage",
|
||||
"children": "Children",
|
||||
"lovers": "Affairs"
|
||||
},
|
||||
"tabsAria": "Family sections",
|
||||
"debtorsPrison": {
|
||||
"familyWarning": "Ongoing debt delinquency puts strain on marriage, household and affairs.",
|
||||
"familyImpact": "Debtors' prison damages marriage, household peace and the stability of affairs."
|
||||
@@ -653,6 +668,7 @@
|
||||
}
|
||||
},
|
||||
"spouse": {
|
||||
"traitsToggle": "Character traits",
|
||||
"marriageSatisfaction": "Marriage Satisfaction",
|
||||
"marriageState": "Marriage State",
|
||||
"wooing": {
|
||||
|
||||
@@ -517,6 +517,20 @@
|
||||
},
|
||||
"family": {
|
||||
"title": "Familia",
|
||||
"heroIntro": "Relaciones, hijos y desarrollo — ordenado por secciones abajo.",
|
||||
"summary": {
|
||||
"partnerChip": "Pareja",
|
||||
"childrenChip": "Hijos",
|
||||
"loversChip": "Relaciones",
|
||||
"proposalsAvailable": "Compromiso posible",
|
||||
"noPartner": "Sin pareja"
|
||||
},
|
||||
"tabs": {
|
||||
"partner": "Pareja y matrimonio",
|
||||
"children": "Hijos",
|
||||
"lovers": "Relaciones"
|
||||
},
|
||||
"tabsAria": "Secciones de familia",
|
||||
"debtorsPrison": {
|
||||
"familyWarning": "La mora continuada perjudica el matrimonio, el hogar y las relaciones.",
|
||||
"familyImpact": "La prisión por deudas daña el matrimonio, la paz del hogar y la estabilidad de las relaciones."
|
||||
@@ -527,6 +541,7 @@
|
||||
},
|
||||
"spouse": {
|
||||
"title": "Relación",
|
||||
"traitsToggle": "Rasgos de carácter",
|
||||
"name": "Nombre",
|
||||
"age": "Edad",
|
||||
"status": "Estado",
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<div>
|
||||
<span class="family-kicker">Familie</span>
|
||||
<h2>{{ $t('falukant.family.title') }}</h2>
|
||||
<p>Beziehungen, Kinder und familiäre Entwicklung in einer eigenen Spielweltansicht.</p>
|
||||
<p>{{ $t('falukant.family.heroIntro') }}</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -36,62 +36,116 @@
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<nav
|
||||
class="family-summary-strip surface-card"
|
||||
role="tablist"
|
||||
:aria-label="$t('falukant.family.tabsAria')"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
role="tab"
|
||||
:aria-selected="familyTab === 'partner'"
|
||||
class="family-summary-chip"
|
||||
:class="{ 'is-active': familyTab === 'partner' }"
|
||||
:title="$t('falukant.family.tabs.partner')"
|
||||
@click="familyTab = 'partner'"
|
||||
>
|
||||
<span class="family-summary-chip__label">{{ $t('falukant.family.summary.partnerChip') }}</span>
|
||||
<span class="family-summary-chip__value">{{ partnerSummaryLine }}</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
role="tab"
|
||||
:aria-selected="familyTab === 'children'"
|
||||
class="family-summary-chip"
|
||||
:class="{ 'is-active': familyTab === 'children' }"
|
||||
:title="$t('falukant.family.tabs.children')"
|
||||
@click="familyTab = 'children'"
|
||||
>
|
||||
<span class="family-summary-chip__label">{{ $t('falukant.family.summary.childrenChip') }}</span>
|
||||
<span class="family-summary-chip__value">{{ children.length }}</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
role="tab"
|
||||
:aria-selected="familyTab === 'lovers'"
|
||||
class="family-summary-chip"
|
||||
:class="{ 'is-active': familyTab === 'lovers' }"
|
||||
:title="$t('falukant.family.tabs.lovers')"
|
||||
@click="familyTab = 'lovers'"
|
||||
>
|
||||
<span class="family-summary-chip__label">{{ $t('falukant.family.summary.loversChip') }}</span>
|
||||
<span class="family-summary-chip__value">{{ lovers.length }}</span>
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
<div
|
||||
v-show="familyTab === 'partner'"
|
||||
class="family-tab-panel"
|
||||
role="tabpanel"
|
||||
>
|
||||
<div class="spouse-section">
|
||||
<h3>{{ $t('falukant.family.spouse.title') }}</h3>
|
||||
<div v-if="relationships.length > 0" class="relationship-container">
|
||||
<div class="relationship-row">
|
||||
<div class="relationship">
|
||||
<table>
|
||||
<tr>
|
||||
<td>{{ $t('falukant.family.relationships.name') }}</td>
|
||||
<td>
|
||||
<dl class="spouse-card__dl">
|
||||
<div>
|
||||
<dt>{{ $t('falukant.family.relationships.name') }}</dt>
|
||||
<dd>
|
||||
{{ $t('falukant.titles.' + relationships[0].character2.gender + '.' +
|
||||
relationships[0].character2.nobleTitle) }}
|
||||
{{ relationships[0].character2.firstName }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('falukant.family.spouse.age') }}</td>
|
||||
<td>{{ relationships[0].character2.age }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('falukant.family.spouse.mood') }}</td>
|
||||
<td>{{ relationships[0].character2.mood?.tr ? $t(`falukant.mood.${relationships[0].character2.mood.tr}`) : '—' }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('falukant.family.spouse.status') }}</td>
|
||||
<td>{{ $t('falukant.family.statuses.' + relationships[0].relationshipType) }}</td>
|
||||
</tr>
|
||||
<tr v-if="relationships[0].marriageSatisfaction != null">
|
||||
<td>{{ $t('falukant.family.spouse.marriageSatisfaction') }}</td>
|
||||
<td>
|
||||
</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>{{ $t('falukant.family.spouse.age') }}</dt>
|
||||
<dd>{{ relationships[0].character2.age }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>{{ $t('falukant.family.spouse.mood') }}</dt>
|
||||
<dd>{{ relationships[0].character2.mood?.tr ? $t(`falukant.mood.${relationships[0].character2.mood.tr}`) : '—' }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>{{ $t('falukant.family.spouse.status') }}</dt>
|
||||
<dd>{{ $t('falukant.family.statuses.' + relationships[0].relationshipType) }}</dd>
|
||||
</div>
|
||||
<div v-if="relationships[0].marriageSatisfaction != null">
|
||||
<dt>{{ $t('falukant.family.spouse.marriageSatisfaction') }}</dt>
|
||||
<dd>
|
||||
{{ relationships[0].marriageSatisfaction }}
|
||||
<span class="inline-status-pill" :class="`inline-status-pill--${relationships[0].marriageState || 'stable'}`">
|
||||
{{ $t('falukant.family.marriageState.' + (relationships[0].marriageState || 'stable')) }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="relationships[0].relationshipType === 'wooing'">
|
||||
<td>{{ $t('falukant.family.spouse.progress') }}</td>
|
||||
<td>
|
||||
</dd>
|
||||
</div>
|
||||
<div v-if="relationships[0].relationshipType === 'wooing'">
|
||||
<dt>{{ $t('falukant.family.spouse.progress') }}</dt>
|
||||
<dd>
|
||||
<div class="progress">
|
||||
<div class="progress-inner" :style="{
|
||||
width: normalizeWooingProgress(relationships[0].progress) + '%',
|
||||
backgroundColor: progressColor(relationships[0].progress)
|
||||
}"></div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="relationships[0].relationshipType === 'engaged'" colspan="2">
|
||||
<button @click="jumpToPartyForm">{{ $t('falukant.family.spouse.jumpToPartyForm')
|
||||
}}</button>
|
||||
</tr>
|
||||
</table>
|
||||
<ul>
|
||||
<li v-for="trait in relationships[0].character2.traits" :key="trait.id">
|
||||
{{ $t(`falukant.character.${trait.tr}`) }}
|
||||
</li>
|
||||
</ul>
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
<div v-if="relationships[0].relationshipType === 'engaged'" class="spouse-card__cta">
|
||||
<button type="button" class="button" @click="jumpToPartyForm">{{ $t('falukant.family.spouse.jumpToPartyForm')
|
||||
}}</button>
|
||||
</div>
|
||||
<details
|
||||
v-if="relationships[0].character2.traits?.length"
|
||||
class="spouse-traits"
|
||||
>
|
||||
<summary>{{ $t('falukant.family.spouse.traitsToggle') }}</summary>
|
||||
<ul>
|
||||
<li v-for="trait in relationships[0].character2.traits" :key="trait.id">
|
||||
{{ $t(`falukant.character.${trait.tr}`) }}
|
||||
</li>
|
||||
</ul>
|
||||
</details>
|
||||
</div>
|
||||
<div class="partner-character-3d">
|
||||
<Character3D
|
||||
@@ -208,7 +262,13 @@
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-show="familyTab === 'children'"
|
||||
class="family-tab-panel"
|
||||
role="tabpanel"
|
||||
>
|
||||
<div class="children-section">
|
||||
<h3>{{ $t('falukant.family.children.title') }}</h3>
|
||||
<div v-if="children && children.length > 0" class="children-container">
|
||||
@@ -262,7 +322,13 @@
|
||||
<p>{{ $t('falukant.family.children.none') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-show="familyTab === 'lovers'"
|
||||
class="family-tab-panel"
|
||||
role="tabpanel"
|
||||
>
|
||||
<!-- Liebhaber / Geliebte -->
|
||||
<div class="lovers-section">
|
||||
<h3>{{ $t('falukant.family.lovers.title') }}</h3>
|
||||
@@ -367,6 +433,7 @@
|
||||
<p v-else>{{ $t('falukant.family.lovers.candidates.none') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ChildDetailsDialog ref="childDetailsDialog" />
|
||||
@@ -417,11 +484,27 @@ export default {
|
||||
},
|
||||
pregnancy: null,
|
||||
selectedChild: null,
|
||||
pendingFamilyRefresh: null
|
||||
pendingFamilyRefresh: null,
|
||||
familyTab: 'partner',
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(['socket', 'daemonSocket', 'user'])
|
||||
...mapState(['socket', 'daemonSocket', 'user']),
|
||||
partnerSummaryLine() {
|
||||
if (this.relationships?.length > 0) {
|
||||
const r = this.relationships[0];
|
||||
const c2 = r.character2;
|
||||
const name = c2
|
||||
? `${this.$t(`falukant.titles.${c2.gender}.${c2.nobleTitle}`)} ${c2.firstName}`.trim()
|
||||
: '';
|
||||
const status = this.$t(`falukant.family.statuses.${r.relationshipType}`);
|
||||
return name ? `${name} · ${status}` : status;
|
||||
}
|
||||
if (this.proposals?.length > 0) {
|
||||
return this.$t('falukant.family.summary.proposalsAvailable');
|
||||
}
|
||||
return this.$t('falukant.family.summary.noPartner');
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
socket(newVal, oldVal) {
|
||||
@@ -725,26 +808,6 @@ export default {
|
||||
return new Intl.NumberFormat(navigator.language, { minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(value);
|
||||
},
|
||||
|
||||
getEffect(gift) {
|
||||
const relationship = this.relationships[0];
|
||||
if (!relationship || !relationship.character2) {
|
||||
return 0;
|
||||
}
|
||||
const partner = relationship.character2;
|
||||
const currentMoodId = partner.moodId;
|
||||
const moodEntry = gift.moodsAffects.find(ma => ma.mood_id === currentMoodId);
|
||||
const moodValue = moodEntry ? moodEntry.suitability : 0;
|
||||
let highestCharacterValue = 0;
|
||||
// traits ist ein Array von Trait-Objekten mit id und tr
|
||||
for (const trait of partner.traits || []) {
|
||||
const charEntry = gift.charactersAffects.find(ca => ca.trait_id === trait.id);
|
||||
if (charEntry && charEntry.suitability > highestCharacterValue) {
|
||||
highestCharacterValue = charEntry.suitability;
|
||||
}
|
||||
}
|
||||
return Math.round((moodValue + highestCharacterValue) / 2);
|
||||
},
|
||||
|
||||
async acceptProposal() {
|
||||
const response = await apiClient.post('/api/falukant/family/acceptmarriageproposal'
|
||||
, { proposalId: this.selectedProposalId });
|
||||
@@ -955,6 +1018,116 @@ export default {
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.family-summary-strip {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
margin-bottom: 12px;
|
||||
padding: 12px 14px;
|
||||
}
|
||||
|
||||
.family-summary-chip {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 2px;
|
||||
padding: 10px 14px;
|
||||
border-radius: var(--radius-md);
|
||||
border: 1px solid var(--color-border);
|
||||
background: rgba(255, 252, 247, 0.95);
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
font: inherit;
|
||||
color: inherit;
|
||||
min-width: 0;
|
||||
flex: 1 1 140px;
|
||||
max-width: 100%;
|
||||
transition: border-color 0.15s ease, box-shadow 0.15s ease;
|
||||
}
|
||||
|
||||
.family-summary-chip:hover {
|
||||
border-color: rgba(248, 162, 43, 0.45);
|
||||
}
|
||||
|
||||
.family-summary-chip.is-active {
|
||||
border-color: rgba(248, 162, 43, 0.65);
|
||||
box-shadow: 0 0 0 1px rgba(248, 162, 43, 0.2);
|
||||
}
|
||||
|
||||
.family-summary-chip__label {
|
||||
font-size: 0.72rem;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.family-summary-chip__value {
|
||||
font-size: 0.92rem;
|
||||
font-weight: 600;
|
||||
line-height: 1.35;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.family-tab-panel {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.spouse-card__dl {
|
||||
margin: 0;
|
||||
display: grid;
|
||||
gap: 10px 16px;
|
||||
}
|
||||
|
||||
.spouse-card__dl > div {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(7rem, 34%) 1fr;
|
||||
gap: 8px 12px;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.spouse-card__dl dt {
|
||||
margin: 0;
|
||||
color: var(--color-text-secondary);
|
||||
font-size: 0.82rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.spouse-card__dl dd {
|
||||
margin: 0;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.spouse-card__cta {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.spouse-traits {
|
||||
margin-top: 14px;
|
||||
padding: 10px 12px;
|
||||
border: 1px dashed var(--color-border);
|
||||
border-radius: var(--radius-md);
|
||||
background: rgba(255, 250, 243, 0.6);
|
||||
}
|
||||
|
||||
.spouse-traits summary {
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
font-size: 0.88rem;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.spouse-traits ul {
|
||||
margin: 10px 0 0;
|
||||
padding-left: 1.1rem;
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.spouse-card__dl > div {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.marriage-overview {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
||||
@@ -1335,17 +1508,6 @@ export default {
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
.relationship>table,
|
||||
.relationship>ul {
|
||||
display: inline-block;
|
||||
margin-right: 1em;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.relationship>ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.progress {
|
||||
width: 100%;
|
||||
background-color: #e5e7eb;
|
||||
|
||||
Reference in New Issue
Block a user