Add servant management features: Implement endpoints for hiring, dismissing, and setting pay levels for servants in the FalukantController. Update UserHouse model to include servant-related attributes. Enhance frontend components to manage servant details, including staffing state and household order, with corresponding localization updates in multiple languages.
This commit is contained in:
@@ -34,6 +34,65 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section v-if="userHouse" class="servants-panel surface-card">
|
||||
<div class="servants-panel__header">
|
||||
<div>
|
||||
<h3>{{ $t('falukant.house.servants.title') }}</h3>
|
||||
<p>{{ $t('falukant.house.servants.description') }}</p>
|
||||
</div>
|
||||
<div class="servants-panel__actions">
|
||||
<button @click="hireServant">
|
||||
{{ $t('falukant.house.servants.actions.hire') }}
|
||||
</button>
|
||||
<button class="button-secondary" :disabled="(userHouse.servantCount || 0) <= 0" @click="dismissServant">
|
||||
{{ $t('falukant.house.servants.actions.dismiss') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="servants-grid">
|
||||
<article class="servant-card">
|
||||
<span class="servant-card__label">{{ $t('falukant.house.servants.count') }}</span>
|
||||
<strong>{{ userHouse.servantCount || 0 }}</strong>
|
||||
</article>
|
||||
<article class="servant-card">
|
||||
<span class="servant-card__label">{{ $t('falukant.house.servants.expectedRange') }}</span>
|
||||
<strong>{{ servantSummary.expectedMin }} - {{ servantSummary.expectedMax }}</strong>
|
||||
</article>
|
||||
<article class="servant-card">
|
||||
<span class="servant-card__label">{{ $t('falukant.house.servants.monthlyCost') }}</span>
|
||||
<strong>{{ formatPrice(servantSummary.monthlyCost || 0) }} {{ currency }}</strong>
|
||||
</article>
|
||||
<article class="servant-card">
|
||||
<span class="servant-card__label">{{ $t('falukant.house.servants.quality') }}</span>
|
||||
<strong>{{ userHouse.servantQuality || 0 }}</strong>
|
||||
</article>
|
||||
<article class="servant-card">
|
||||
<span class="servant-card__label">{{ $t('falukant.house.servants.householdOrder') }}</span>
|
||||
<strong>{{ userHouse.householdOrder || 0 }}</strong>
|
||||
</article>
|
||||
<article class="servant-card">
|
||||
<span class="servant-card__label">{{ $t('falukant.house.servants.staffingState.label') }}</span>
|
||||
<strong>{{ $t(`falukant.house.servants.staffingState.${servantSummary.staffingState || 'fitting'}`) }}</strong>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<div class="servants-settings">
|
||||
<label class="servants-settings__label">
|
||||
{{ $t('falukant.house.servants.payLevel') }}
|
||||
<select v-model="servantPayLevel" @change="updateServantPayLevel" class="servants-settings__select">
|
||||
<option v-for="option in servantPayOptions" :key="option" :value="option">
|
||||
{{ $t(`falukant.house.servants.payLevels.${option}`) }}
|
||||
</option>
|
||||
</select>
|
||||
</label>
|
||||
<div class="servants-settings__state">
|
||||
{{ $t('falukant.house.servants.orderState.label') }}:
|
||||
<strong>{{ $t(`falukant.house.servants.orderState.${servantSummary.orderState || 'stable'}`) }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="buyable-houses">
|
||||
<h3>{{ $t('falukant.house.buyablehouses') }}</h3>
|
||||
<div class="houses-list">
|
||||
@@ -67,6 +126,7 @@
|
||||
import StatusBar from '@/components/falukant/StatusBar.vue';
|
||||
import apiClient from '@/utils/axios.js';
|
||||
import { mapState } from 'vuex';
|
||||
import { showError, showSuccess, confirmAction } from '@/utils/feedback.js';
|
||||
|
||||
export default {
|
||||
name: 'HouseView',
|
||||
@@ -76,6 +136,15 @@ export default {
|
||||
userHouse: null,
|
||||
houseType: {},
|
||||
status: {},
|
||||
servantSummary: {
|
||||
expectedMin: 0,
|
||||
expectedMax: 0,
|
||||
monthlyCost: 0,
|
||||
staffingState: 'fitting',
|
||||
orderState: 'stable'
|
||||
},
|
||||
servantPayLevel: 'normal',
|
||||
servantPayOptions: ['low', 'normal', 'high'],
|
||||
buyableHouses: [],
|
||||
currency: '€'
|
||||
};
|
||||
@@ -94,6 +163,8 @@ export default {
|
||||
this.houseType = this.userHouse.houseType;
|
||||
const { roofCondition, wallCondition, floorCondition, windowCondition } = this.userHouse;
|
||||
this.status = { roofCondition, wallCondition, floorCondition, windowCondition };
|
||||
this.servantSummary = this.userHouse.servantSummary || this.servantSummary;
|
||||
this.servantPayLevel = this.userHouse.servantPayLevel || 'normal';
|
||||
|
||||
const buyRes = await apiClient.get('/api/falukant/houses/buyable');
|
||||
this.buyableHouses = buyRes.data;
|
||||
@@ -172,19 +243,60 @@ export default {
|
||||
}
|
||||
},
|
||||
async sellHouse() {
|
||||
const confirmed = await confirmAction(this, {
|
||||
title: this.$t('falukant.house.sell'),
|
||||
text: this.$t('falukant.house.sellConfirm')
|
||||
});
|
||||
if (!confirmed) return;
|
||||
try {
|
||||
await apiClient.post('/api/falukant/houses/sell');
|
||||
await this.loadData();
|
||||
showSuccess(this, this.$t('falukant.house.sellSuccess'));
|
||||
} catch (err) {
|
||||
console.error('Error selling house', err);
|
||||
showError(this, this.$t('falukant.house.sellError'));
|
||||
}
|
||||
},
|
||||
async buyHouse(id) {
|
||||
try {
|
||||
await apiClient.post('/api/falukant/houses', { houseId: id });
|
||||
await this.loadData();
|
||||
showSuccess(this, this.$t('falukant.house.buySuccess'));
|
||||
} catch (err) {
|
||||
console.error('Error buying house', err);
|
||||
showError(this, this.$t('falukant.house.buyError'));
|
||||
}
|
||||
},
|
||||
async hireServant() {
|
||||
try {
|
||||
await apiClient.post('/api/falukant/houses/servants/hire', { amount: 1 });
|
||||
await this.loadData();
|
||||
showSuccess(this, this.$t('falukant.house.servants.actions.hireSuccess'));
|
||||
} catch (err) {
|
||||
console.error('Error hiring servant', err);
|
||||
showError(this, this.$t('falukant.house.servants.actions.hireError'));
|
||||
}
|
||||
},
|
||||
async dismissServant() {
|
||||
try {
|
||||
await apiClient.post('/api/falukant/houses/servants/dismiss', { amount: 1 });
|
||||
await this.loadData();
|
||||
showSuccess(this, this.$t('falukant.house.servants.actions.dismissSuccess'));
|
||||
} catch (err) {
|
||||
console.error('Error dismissing servant', err);
|
||||
showError(this, this.$t('falukant.house.servants.actions.dismissError'));
|
||||
}
|
||||
},
|
||||
async updateServantPayLevel() {
|
||||
try {
|
||||
await apiClient.post('/api/falukant/houses/servants/pay-level', {
|
||||
payLevel: this.servantPayLevel
|
||||
});
|
||||
await this.loadData();
|
||||
showSuccess(this, this.$t('falukant.house.servants.actions.payLevelSuccess'));
|
||||
} catch (err) {
|
||||
console.error('Error updating servant pay level', err);
|
||||
showError(this, this.$t('falukant.house.servants.actions.payLevelError'));
|
||||
}
|
||||
},
|
||||
handleDaemonMessage(evt) {
|
||||
@@ -258,6 +370,78 @@ h2 {
|
||||
padding: 18px;
|
||||
}
|
||||
|
||||
.servants-panel {
|
||||
padding: 18px;
|
||||
}
|
||||
|
||||
.servants-panel__header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.servants-panel__header h3 {
|
||||
margin: 0 0 6px;
|
||||
}
|
||||
|
||||
.servants-panel__header p {
|
||||
margin: 0;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.servants-panel__actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.servants-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||
gap: 12px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.servant-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
padding: 14px 16px;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-lg);
|
||||
background: rgba(255, 255, 255, 0.68);
|
||||
}
|
||||
|
||||
.servant-card__label {
|
||||
color: var(--color-text-secondary);
|
||||
font-size: 0.88rem;
|
||||
}
|
||||
|
||||
.servants-settings {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.servants-settings__label {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.servants-settings__select {
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
.servants-settings__state {
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.buyable-houses {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -356,6 +540,10 @@ button {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.servants-panel__header {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.house {
|
||||
width: min(341px, 100%);
|
||||
margin: 0 auto;
|
||||
|
||||
Reference in New Issue
Block a user