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:
@@ -826,8 +826,51 @@
|
||||
"price": "Kaufpreis",
|
||||
"worth": "Restwert",
|
||||
"sell": "Verkaufen",
|
||||
"sellConfirm": "Möchtest du dein Haus wirklich verkaufen?",
|
||||
"sellSuccess": "Das Haus wurde verkauft.",
|
||||
"sellError": "Das Haus konnte nicht verkauft werden.",
|
||||
"buySuccess": "Das Haus wurde gekauft.",
|
||||
"buyError": "Das Haus konnte nicht gekauft werden.",
|
||||
"renovate": "Renovieren",
|
||||
"renovateAll": "Komplett renovieren",
|
||||
"servants": {
|
||||
"title": "Dienerschaft",
|
||||
"description": "Verwalte Hauspersonal, Ordnung und laufende Kosten deines Haushalts.",
|
||||
"count": "Dienerzahl",
|
||||
"expectedRange": "Erwarteter Bereich",
|
||||
"monthlyCost": "Monatskosten",
|
||||
"quality": "Qualität",
|
||||
"householdOrder": "Haushaltsordnung",
|
||||
"payLevel": "Bezahlung",
|
||||
"payLevels": {
|
||||
"low": "Niedrig",
|
||||
"normal": "Normal",
|
||||
"high": "Großzügig"
|
||||
},
|
||||
"staffingState": {
|
||||
"label": "Besetzung",
|
||||
"understaffed": "Unterbesetzt",
|
||||
"fitting": "Passend",
|
||||
"overstaffed": "Überbesetzt"
|
||||
},
|
||||
"orderState": {
|
||||
"label": "Ordnungszustand",
|
||||
"chaotic": "Chaotisch",
|
||||
"strained": "Angespannt",
|
||||
"stable": "Stabil",
|
||||
"excellent": "Vorbildlich"
|
||||
},
|
||||
"actions": {
|
||||
"hire": "1 Diener einstellen",
|
||||
"dismiss": "1 Diener entlassen",
|
||||
"hireSuccess": "Die Dienerschaft wurde erweitert.",
|
||||
"hireError": "Die Dienerschaft konnte nicht erweitert werden.",
|
||||
"dismissSuccess": "Ein Diener wurde entlassen.",
|
||||
"dismissError": "Der Diener konnte nicht entlassen werden.",
|
||||
"payLevelSuccess": "Die Bezahlung der Dienerschaft wurde angepasst.",
|
||||
"payLevelError": "Die Bezahlung konnte nicht angepasst werden."
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"roofCondition": "Dach",
|
||||
"wallCondition": "Wände",
|
||||
|
||||
@@ -149,7 +149,7 @@
|
||||
"all": "All history"
|
||||
}
|
||||
},
|
||||
"activities": {
|
||||
"activities": {
|
||||
"Product sale": "Product sale",
|
||||
"Production cost": "Production cost",
|
||||
"Sell all products": "Sell all products",
|
||||
@@ -181,6 +181,75 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"house": {
|
||||
"title": "House",
|
||||
"statusreport": "House condition",
|
||||
"element": "Element",
|
||||
"state": "Condition",
|
||||
"buyablehouses": "Buy a house",
|
||||
"buy": "Buy",
|
||||
"price": "Purchase price",
|
||||
"worth": "Residual value",
|
||||
"sell": "Sell",
|
||||
"sellConfirm": "Do you really want to sell your house?",
|
||||
"sellSuccess": "The house has been sold.",
|
||||
"sellError": "The house could not be sold.",
|
||||
"buySuccess": "The house has been bought.",
|
||||
"buyError": "The house could not be bought.",
|
||||
"renovate": "Renovate",
|
||||
"renovateAll": "Renovate completely",
|
||||
"servants": {
|
||||
"title": "Servants",
|
||||
"description": "Manage household staff, order and recurring costs in your home.",
|
||||
"count": "Servant count",
|
||||
"expectedRange": "Expected range",
|
||||
"monthlyCost": "Monthly cost",
|
||||
"quality": "Quality",
|
||||
"householdOrder": "Household order",
|
||||
"payLevel": "Pay level",
|
||||
"payLevels": {
|
||||
"low": "Low",
|
||||
"normal": "Normal",
|
||||
"high": "Generous"
|
||||
},
|
||||
"staffingState": {
|
||||
"label": "Staffing",
|
||||
"understaffed": "Understaffed",
|
||||
"fitting": "Fitting",
|
||||
"overstaffed": "Overstaffed"
|
||||
},
|
||||
"orderState": {
|
||||
"label": "Order state",
|
||||
"chaotic": "Chaotic",
|
||||
"strained": "Strained",
|
||||
"stable": "Stable",
|
||||
"excellent": "Excellent"
|
||||
},
|
||||
"actions": {
|
||||
"hire": "Hire 1 servant",
|
||||
"dismiss": "Dismiss 1 servant",
|
||||
"hireSuccess": "The household staff has been expanded.",
|
||||
"hireError": "The staff could not be expanded.",
|
||||
"dismissSuccess": "A servant has been dismissed.",
|
||||
"dismissError": "The servant could not be dismissed.",
|
||||
"payLevelSuccess": "Servant pay has been updated.",
|
||||
"payLevelError": "Servant pay could not be updated."
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"roofCondition": "Roof",
|
||||
"wallCondition": "Walls",
|
||||
"floorCondition": "Floors",
|
||||
"windowCondition": "Windows"
|
||||
},
|
||||
"type": {
|
||||
"backyard_room": "Backyard room",
|
||||
"wooden_house": "Wooden house",
|
||||
"straw_hut": "Straw hut",
|
||||
"family_house": "Family house",
|
||||
"townhouse": "Townhouse"
|
||||
}
|
||||
},
|
||||
"newdirector": {
|
||||
"title": "New Director",
|
||||
"age": "Age",
|
||||
|
||||
@@ -792,8 +792,51 @@
|
||||
"price": "Precio de compra",
|
||||
"worth": "Valor restante",
|
||||
"sell": "Vender",
|
||||
"sellConfirm": "¿De verdad quieres vender tu casa?",
|
||||
"sellSuccess": "La casa ha sido vendida.",
|
||||
"sellError": "No se pudo vender la casa.",
|
||||
"buySuccess": "La casa ha sido comprada.",
|
||||
"buyError": "No se pudo comprar la casa.",
|
||||
"renovate": "Renovar",
|
||||
"renovateAll": "Renovar por completo",
|
||||
"servants": {
|
||||
"title": "Servicio doméstico",
|
||||
"description": "Administra el personal, el orden y los costes periódicos de tu casa.",
|
||||
"count": "Número de sirvientes",
|
||||
"expectedRange": "Rango esperado",
|
||||
"monthlyCost": "Coste mensual",
|
||||
"quality": "Calidad",
|
||||
"householdOrder": "Orden del hogar",
|
||||
"payLevel": "Pago",
|
||||
"payLevels": {
|
||||
"low": "Bajo",
|
||||
"normal": "Normal",
|
||||
"high": "Generoso"
|
||||
},
|
||||
"staffingState": {
|
||||
"label": "Dotación",
|
||||
"understaffed": "Insuficiente",
|
||||
"fitting": "Adecuada",
|
||||
"overstaffed": "Excesiva"
|
||||
},
|
||||
"orderState": {
|
||||
"label": "Estado del orden",
|
||||
"chaotic": "Caótico",
|
||||
"strained": "Tenso",
|
||||
"stable": "Estable",
|
||||
"excellent": "Excelente"
|
||||
},
|
||||
"actions": {
|
||||
"hire": "Contratar 1 sirviente",
|
||||
"dismiss": "Despedir 1 sirviente",
|
||||
"hireSuccess": "Se ha ampliado el servicio doméstico.",
|
||||
"hireError": "No se pudo ampliar el servicio doméstico.",
|
||||
"dismissSuccess": "Se ha despedido a un sirviente.",
|
||||
"dismissError": "No se pudo despedir al sirviente.",
|
||||
"payLevelSuccess": "Se ha ajustado el pago del servicio.",
|
||||
"payLevelError": "No se pudo ajustar el pago."
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"roofCondition": "Techo",
|
||||
"wallCondition": "Paredes",
|
||||
|
||||
@@ -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