- Added detailed logging for partner search criteria and results in FalukantService to improve debugging and traceability. - Refactored partner search logic to use a dynamic where clause for better readability and maintainability. - Implemented error handling in FamilyView for gift loading, ensuring an empty array is set on failure to load gifts, enhancing user experience.
516 lines
19 KiB
Vue
516 lines
19 KiB
Vue
<template>
|
|
<div class="contenthidden">
|
|
<StatusBar />
|
|
<div class="contentscroll">
|
|
|
|
<h2>{{ $t('falukant.family.title') }}</h2>
|
|
|
|
<div class="spouse-section">
|
|
<h3>{{ $t('falukant.family.spouse.title') }}</h3>
|
|
<div v-if="relationships.length > 0">
|
|
<div class="relationship">
|
|
<table>
|
|
<tr>
|
|
<td>{{ $t('falukant.family.relationships.name') }}</td>
|
|
<td>
|
|
{{ $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>{{ $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].relationshipType === 'wooing'">
|
|
<td>{{ $t('falukant.family.spouse.progress') }}</td>
|
|
<td>
|
|
<div class="progress">
|
|
<div class="progress-inner" :style="{
|
|
width: 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>
|
|
</div>
|
|
<div v-if="relationships[0].relationshipType === 'wooing'">
|
|
<h3>{{ $t('falukant.family.spouse.wooing.gifts') }}</h3>
|
|
<table class="spouse-table">
|
|
<thead>
|
|
<tr>
|
|
<th></th>
|
|
<th>{{ $t('falukant.family.spouse.wooing.gift') }}</th>
|
|
<th>{{ $t('falukant.family.spouse.wooing.effect') }}</th>
|
|
<th>{{ $t('falukant.family.spouse.wooing.value') }}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr v-for="gift in gifts" :key="gift.id">
|
|
<td>
|
|
<input type="radio" name="gift" :value="gift.id" v-model="selectedGiftId" />
|
|
</td>
|
|
<td>{{ $t(`falukant.gifts.${gift.name}`) }}</td>
|
|
<td>{{ getEffect(gift) }}</td>
|
|
<td>{{ formatCost(gift.cost) }}</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<div>
|
|
<button @click="sendGift" class="button">{{ $t('falukant.family.spouse.wooing.sendGift')
|
|
}}</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div v-else-if="proposals && proposals.length > 0">
|
|
<table class="spouse-table">
|
|
<thead>
|
|
<tr>
|
|
<th>{{ $t('falukant.family.spouse.select') }}</th>
|
|
<th>{{ $t('falukant.family.spouse.name') }}</th>
|
|
<th>{{ $t('falukant.family.spouse.age') }}</th>
|
|
<th>{{ $t('falukant.family.spouse.marriagecost') }}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr v-for="proposal in proposals" :key="proposal.id">
|
|
<td><input type="radio" name="spouse" :value="proposal.proposedCharacterId"
|
|
v-model="selectedProposalId"></td>
|
|
<td>{{
|
|
$t(`falukant.titles.${proposal.proposedCharacterGender}.${proposal.proposedCharacterNobleTitle}`)
|
|
}} {{ proposal.proposedCharacterName }}</td>
|
|
<td>{{ proposal.proposedCharacterAge }}</td>
|
|
<td>{{ formatCost(proposal.cost) }}</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<div>{{ $t('falukant.family.spouse.notice') }}</div>
|
|
<div v-if="selectedProposalId">
|
|
<button @click="acceptProposal">{{ $t('falukant.family.spouse.accept') }}</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="children-section">
|
|
<h3>{{ $t('falukant.family.children.title') }}</h3>
|
|
<div v-if="children && children.length > 0">
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>{{ $t('falukant.family.children.name') }}</th>
|
|
<th>{{ $t('falukant.family.children.age') }}</th>
|
|
<th>{{ $t('falukant.family.children.heir') }}</th>
|
|
<th>{{ $t('falukant.family.children.actions') }}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr v-for="(child, index) in children" :key="index">
|
|
<td v-if="child.hasName">
|
|
{{ child.name }}
|
|
</td>
|
|
<td v-else>
|
|
<button @click="jumpToChurchForm">{{ $t('falukant.family.children.baptism')
|
|
}}</button>
|
|
</td>
|
|
<td>{{ child.age }}</td>
|
|
<td>
|
|
<span v-if="child.isHeir" class="heir-badge">{{ $t('falukant.family.children.isHeir') }}</span>
|
|
<button v-else-if="child.hasName" @click="setAsHeir(child)" class="set-heir-button">
|
|
{{ $t('falukant.family.children.setAsHeir') }}
|
|
</button>
|
|
</td>
|
|
<td>
|
|
<button @click="showChildDetails(child)">
|
|
{{ $t('falukant.family.children.detailButton') }}
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<div v-else>
|
|
<p>{{ $t('falukant.family.children.none') }}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Liebhaber / Geliebte -->
|
|
<div class="lovers-section">
|
|
<h3>{{ $t('falukant.family.lovers.title') }}</h3>
|
|
<div v-if="lovers && lovers.length > 0">
|
|
<ul>
|
|
<li v-for="(lover, idx) in lovers" :key="idx">
|
|
{{ $t('falukant.titles.' + lover.gender + '.' + lover.title) }} {{ lover.name }}
|
|
({{ $t('falukant.family.lovers.affection') }}: {{ lover.affection }})
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<div v-else>
|
|
<p>{{ $t('falukant.family.lovers.none') }}</p>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
<ChildDetailsDialog ref="childDetailsDialog" />
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import StatusBar from '@/components/falukant/StatusBar.vue'
|
|
import MessageDialog from '@/dialogues/standard/MessageDialog.vue'
|
|
import ErrorDialog from '@/dialogues/standard/ErrorDialog.vue'
|
|
import ChildDetailsDialog from '@/dialogues/falukant/ChildDetailsDialog.vue'
|
|
|
|
import apiClient from '@/utils/axios.js'
|
|
import { mapState } from 'vuex'
|
|
|
|
export default {
|
|
name: 'FamilyView',
|
|
components: {
|
|
StatusBar,
|
|
MessageDialog,
|
|
ErrorDialog,
|
|
ChildDetailsDialog
|
|
},
|
|
data() {
|
|
return {
|
|
relationships: [],
|
|
children: [],
|
|
lovers: [],
|
|
deathPartners: [],
|
|
proposals: [],
|
|
selectedProposalId: null,
|
|
gifts: [],
|
|
selectedGiftId: null,
|
|
moodAffects: [],
|
|
characterAffects: []
|
|
}
|
|
},
|
|
computed: {
|
|
...mapState(['socket'])
|
|
},
|
|
async mounted() {
|
|
await this.loadFamilyData();
|
|
await this.loadGifts();
|
|
await this.loadMoodAffects();
|
|
await this.loadCharacterAffects();
|
|
this.setupSocketEvents();
|
|
},
|
|
methods: {
|
|
setupSocketEvents() {
|
|
if (this.socket) {
|
|
this.socket.on('falukantUpdateStatus', (data) => {
|
|
this.handleEvent({ event: 'falukantUpdateStatus', ...data });
|
|
});
|
|
this.socket.on('familychanged', (data) => {
|
|
this.handleEvent({ event: 'familychanged', ...data });
|
|
});
|
|
} else {
|
|
setTimeout(() => this.setupSocketEvents(), 1000);
|
|
}
|
|
},
|
|
handleEvent(eventData) {
|
|
switch (eventData.event) {
|
|
case 'falukantUpdateStatus':
|
|
case 'familychanged':
|
|
this.loadFamilyData();
|
|
break;
|
|
}
|
|
},
|
|
async loadFamilyData() {
|
|
try {
|
|
const response = await apiClient.get('/api/falukant/family');
|
|
this.relationships = response.data.relationships;
|
|
this.children = response.data.children;
|
|
this.lovers = response.data.lovers;
|
|
this.proposals = response.data.possiblePartners;
|
|
this.deathPartners = response.data.deathPartners;
|
|
} catch (error) {
|
|
console.error('Error loading family data:', error);
|
|
}
|
|
},
|
|
|
|
showChildDetails(child) {
|
|
this.$refs.childDetailsDialog?.open(child);
|
|
},
|
|
|
|
async setAsHeir(child) {
|
|
if (!child.childCharacterId) {
|
|
console.error('Child character ID missing');
|
|
return;
|
|
}
|
|
try {
|
|
await apiClient.post('/api/falukant/family/set-heir', {
|
|
childCharacterId: child.childCharacterId
|
|
});
|
|
await this.loadFamilyData();
|
|
this.$root.$refs.messageDialog?.open('tr:falukant.family.children.heirSetSuccess');
|
|
} catch (error) {
|
|
console.error('Error setting heir:', error);
|
|
this.$root.$refs.errorDialog?.open('tr:falukant.family.children.heirSetError');
|
|
}
|
|
},
|
|
|
|
formatCost(value) {
|
|
return new Intl.NumberFormat(navigator.language, { minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(value);
|
|
},
|
|
|
|
getEffect(gift) {
|
|
const relationship = this.relationships[0];
|
|
const partner = relationship.character2;
|
|
const currentMoodId = partner.mood?.id ?? partner.mood_id;
|
|
const moodEntry = gift.moodsAffects.find(ma => ma.mood_id === currentMoodId);
|
|
const moodValue = moodEntry ? moodEntry.suitability : 0;
|
|
let highestCharacterValue = 0;
|
|
for (const trait of partner.characterTrait) {
|
|
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 });
|
|
this.loadFamilyData();
|
|
},
|
|
|
|
async loadGifts() {
|
|
try {
|
|
const response = await apiClient.get('/api/falukant/family/gifts');
|
|
this.gifts = response.data || [];
|
|
} catch (error) {
|
|
console.error('Error loading gifts:', error);
|
|
this.gifts = []; // Leeres Array bei Fehler
|
|
}
|
|
},
|
|
|
|
async sendGift() {
|
|
if (!this.selectedGiftId) {
|
|
this.$root.$refs.errorDialog.open(`tr:falukant.family.sendgift.error.nogiftselected`);
|
|
return;
|
|
}
|
|
try {
|
|
await apiClient.post('/api/falukant/family/gift'
|
|
, { giftId: this.selectedGiftId });
|
|
this.loadFamilyData();
|
|
this.$root.$refs.messageDialog.open('tr:falukant.family.sendgift.success');
|
|
} catch (error) {
|
|
console.log(error.response);
|
|
if (error.response.status === 412) {
|
|
this.$root.$refs.errorDialog.open(`tr:falukant.family.sendgift.error.${error.response.data.error}`);
|
|
} else {
|
|
this.$root.$refs.errorDialog.open(`tr:falukant.family.sendgift.error.generic`);
|
|
}
|
|
}
|
|
},
|
|
|
|
async loadMoodAffects() {
|
|
try {
|
|
const response = await apiClient.get('/api/falukant/mood/affect');
|
|
this.moodAffects = response.data;
|
|
} catch (error) {
|
|
console.error(error);
|
|
}
|
|
},
|
|
|
|
async loadCharacterAffects() {
|
|
try {
|
|
const response = await apiClient.get('/api/falukant/character/affect');
|
|
this.characterAffects = response.data;
|
|
} catch (error) {
|
|
console.error(error);
|
|
}
|
|
},
|
|
|
|
progressColor(p) {
|
|
const pct = Math.max(0, Math.min(100, p)) / 100;
|
|
const red = Math.round(255 * (1 - pct));
|
|
const green = Math.round(255 * pct);
|
|
return `rgb(${red}, ${green}, 0)`;
|
|
},
|
|
|
|
jumpToPartyForm() {
|
|
this.$router.push({
|
|
name: 'ReputationView',
|
|
query: { tab: 'party' }
|
|
});
|
|
},
|
|
|
|
jumpToChurchForm() {
|
|
this.$router.push({
|
|
name: 'ChurchView',
|
|
});
|
|
},
|
|
|
|
handleDaemonMessage(event) {
|
|
if (event.data === 'ping') {
|
|
return;
|
|
}
|
|
const message = JSON.parse(event.data);
|
|
if (message.event === 'children_update') {
|
|
this.loadFamilyData();
|
|
}
|
|
},
|
|
|
|
getEffect(gift) {
|
|
// aktueller Partner
|
|
const partner = this.relationships[0].character2;
|
|
// seine aktuelle Mood-ID
|
|
const moodId = partner.mood?.id ?? partner.mood_id;
|
|
|
|
// 1) Mood-Eintrag finden
|
|
const moodEntry = gift.moodsAffects.find(ma => ma.mood_id === moodId);
|
|
const moodValue = moodEntry ? moodEntry.suitability : 0;
|
|
|
|
// 2) Trait-Einträge matchen
|
|
let highestTraitValue = 0;
|
|
for (const trait of partner.traits) {
|
|
const charEntry = gift.charactersAffects.find(ca => ca.trait_id === trait.id);
|
|
if (charEntry && charEntry.suitability > highestTraitValue) {
|
|
highestTraitValue = charEntry.suitability;
|
|
}
|
|
}
|
|
|
|
// Durchschnitt, gerundet
|
|
return Math.round((moodValue + highestTraitValue) / 2);
|
|
},
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped lang="scss">
|
|
.spouse-section,
|
|
.children-section,
|
|
.lovers-section {
|
|
border: 1px solid #ccc;
|
|
margin: 10px 0;
|
|
border-radius: 4px;
|
|
padding: 10px;
|
|
}
|
|
|
|
.spouse-section table,
|
|
.children-section table {
|
|
margin-top: 10px;
|
|
border-collapse: collapse;
|
|
}
|
|
|
|
.spouse-section th,
|
|
.spouse-section td,
|
|
.children-section th,
|
|
.children-section td {
|
|
border: 1px solid #ddd;
|
|
padding: 8px;
|
|
text-align: left;
|
|
}
|
|
|
|
.spouse-section th {
|
|
background-color: #f2f2f2;
|
|
}
|
|
|
|
.children-section th {
|
|
background-color: #f9f9f9;
|
|
}
|
|
|
|
.lovers-section {
|
|
ul {
|
|
list-style: none;
|
|
padding-left: 0;
|
|
}
|
|
|
|
li {
|
|
padding: 4px 0;
|
|
}
|
|
}
|
|
|
|
.spouse-table th,
|
|
.spouse-table td {
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.spouse-table th:first-child,
|
|
.spouse-table td:first-child {
|
|
width: 20px;
|
|
}
|
|
|
|
.spouse-table th:nth-child(3),
|
|
.spouse-table td:nth-child(3) {
|
|
width: 30px;
|
|
}
|
|
|
|
.spouse-table th:nth-child(4),
|
|
.spouse-table td:nth-child(4) {
|
|
width: 50px;
|
|
}
|
|
|
|
h2 {
|
|
padding-top: 20px;
|
|
}
|
|
|
|
.relationship>table,
|
|
.relationship>ul {
|
|
display: inline-block;
|
|
margin-right: 1em;
|
|
vertical-align: top;
|
|
}
|
|
|
|
.relationship>ul {
|
|
list-style: none;
|
|
}
|
|
|
|
.progress {
|
|
width: 100%;
|
|
background-color: #e5e7eb;
|
|
border-radius: 0.25rem;
|
|
overflow: hidden;
|
|
height: 1rem;
|
|
}
|
|
|
|
.progress-inner {
|
|
height: 100%;
|
|
transition: width 0.3s ease, background-color 0.3s ease;
|
|
}
|
|
|
|
.heir-badge {
|
|
display: inline-block;
|
|
padding: 4px 8px;
|
|
background-color: #4CAF50;
|
|
color: white;
|
|
border-radius: 4px;
|
|
font-size: 0.9em;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.set-heir-button {
|
|
padding: 4px 8px;
|
|
background-color: #28a745;
|
|
color: white;
|
|
border: none;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
font-size: 0.9em;
|
|
}
|
|
|
|
.set-heir-button:hover {
|
|
background-color: #218838;
|
|
}
|
|
</style> |