Spiel erweitert
This commit is contained in:
301
frontend/src/views/falukant/ReputationView.vue
Normal file
301
frontend/src/views/falukant/ReputationView.vue
Normal file
@@ -0,0 +1,301 @@
|
||||
<template>
|
||||
<div class="reputation-view">
|
||||
<StatusBar />
|
||||
<h2>{{ $t('falukant.reputation.title') }}</h2>
|
||||
|
||||
<div class="simple-tabs">
|
||||
<button v-for="tab in tabs" :key="tab.value" :class="['simple-tab', { active: activeTab === tab.value }]"
|
||||
@click="activeTab = tab.value">
|
||||
{{ $t(tab.label) }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="tab-content">
|
||||
<div v-if="activeTab === 'overview'">
|
||||
<p>Deine aktuelle Reputation: …</p>
|
||||
</div>
|
||||
|
||||
<div v-else-if="activeTab === 'party'">
|
||||
<button @click="toggleNewPartyView">
|
||||
{{ $t('falukant.reputation.party.newpartyview.' + (newPartyView ? 'close' : 'open')) }}
|
||||
</button>
|
||||
|
||||
<div v-if="newPartyView" class="new-party-form">
|
||||
<label>
|
||||
{{ $t('falukant.reputation.party.newpartyview.type') }}:
|
||||
<select v-model.number="newPartyTypeId">
|
||||
<option v-for="type in partyTypes" :key="type.id" :value="type.id">
|
||||
{{ $t('falukant.party.type.' + type.tr) }}
|
||||
</option>
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<div v-if="newPartyTypeId" class="party-options">
|
||||
<label>
|
||||
{{ $t('falukant.reputation.party.music.label') }}:
|
||||
<select v-model.number="musicId">
|
||||
<option v-for="m in musicTypes" :key="m.id" :value="m.id">
|
||||
{{ $t(`falukant.reputation.party.music.${m.tr}`) }}
|
||||
</option>
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
{{ $t('falukant.reputation.party.banquette.label') }}:
|
||||
<select v-model.number="banquetteId">
|
||||
<option v-for="b in banquetteTypes" :key="b.id" :value="b.id">
|
||||
{{ $t(`falukant.reputation.party.banquette.${b.tr}`) }}
|
||||
</option>
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
{{ $t('falukant.reputation.party.servants.label') }}:
|
||||
<input type="number" v-model.number="servantRatio" min="1" max="50" />
|
||||
{{ $t('falukant.reputation.party.servants.perPersons') }}
|
||||
</label>
|
||||
|
||||
<label>
|
||||
{{ $t('falukant.reputation.party.esteemedInvites.label') }}:
|
||||
<multiselect v-model="selectedNobilityIds" :options="nobilityTitles" :multiple="true"
|
||||
track-by="id" label="labelTr" :close-on-select="false" :preserve-search="true"
|
||||
placeholder="">
|
||||
<template #option="{ option }">
|
||||
{{ $t('falukant.titles.male.' + option.labelTr) }}
|
||||
</template>
|
||||
<template #tag="{ option, remove }">
|
||||
<span class="multiselect__tag">
|
||||
{{ $t('falukant.titles.male.' + option.labelTr) }}
|
||||
<i @click="remove(option.id)" class="multiselect__tag-icon"></i>
|
||||
</span>
|
||||
</template>
|
||||
</multiselect>
|
||||
</label>
|
||||
|
||||
<p class="total-cost">
|
||||
{{ $t('falukant.reputation.party.totalCost') }}:
|
||||
{{ formattedCost }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button @click="orderParty()">
|
||||
{{ $t('falukant.reputation.party.order') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- In-Progress Parties -->
|
||||
<div class="separator-class">
|
||||
<h3>{{ $t('falukant.reputation.party.inProgress') }}</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ $t('falukant.reputation.party.type') }}</th>
|
||||
<th>{{ $t('falukant.reputation.party.music.label') }}</th>
|
||||
<th>{{ $t('falukant.reputation.party.banquette.label') }}</th>
|
||||
<th>{{ $t('falukant.reputation.party.servants.label') }}</th>
|
||||
<th>{{ $t('falukant.reputation.party.cost') }}</th>
|
||||
<th>{{ $t('falukant.reputation.party.date') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="party in inProgressParties" :key="party.id">
|
||||
<td>{{ $t('falukant.party.type.' + party.partyType.tr) }}</td>
|
||||
<td>{{ $t('falukant.reputation.party.music.' + party.musicType.tr) }}</td>
|
||||
<td>{{ $t('falukant.reputation.party.banquette.' + party.banquetteType.tr) }}</td>
|
||||
<td>{{ party.servantRatio }}</td>
|
||||
<td>{{ party.cost.toLocaleString($i18n.locale, { minimumFractionDigits: 2, maximumFractionDigits: 2 }) }}</td>
|
||||
<td>{{ new Date(party.createdAt).toLocaleString() }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Completed Parties -->
|
||||
<div class="separator-class">
|
||||
<h3>{{ $t('falukant.reputation.party.completed') }}</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ $t('falukant.reputation.party.type') }}</th>
|
||||
<th>{{ $t('falukant.reputation.party.music.label') }}</th>
|
||||
<th>{{ $t('falukant.reputation.party.banquette.label') }}</th>
|
||||
<th>{{ $t('falukant.reputation.party.servants.label') }}</th>
|
||||
<th>{{ $t('falukant.reputation.party.cost') }}</th>
|
||||
<th>{{ $t('falukant.reputation.party.date') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="party in completedParties" :key="party.id">
|
||||
<td>{{ $t('falukant.party.type.' + party.partyType.tr) }}</td>
|
||||
<td>{{ $t('falukant.reputation.party.music.' + party.musicType.tr) }}</td>
|
||||
<td>{{ $t('falukant.reputation.party.banquette.' + party.banquetteType.tr) }}</td>
|
||||
<td>{{ party.servantRatio }}</td>
|
||||
<td>{{ party.cost.toLocaleString($i18n.locale, { minimumFractionDigits: 2, maximumFractionDigits: 2 }) }}</td>
|
||||
<td>{{ new Date(party.createdAt).toLocaleString() }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import StatusBar from '@/components/falukant/StatusBar.vue'
|
||||
import apiClient from '@/utils/axios.js'
|
||||
import Multiselect from 'vue-multiselect'
|
||||
|
||||
export default {
|
||||
name: 'ReputationView',
|
||||
components: { StatusBar, Multiselect },
|
||||
data() {
|
||||
return {
|
||||
activeTab: 'overview',
|
||||
tabs: [
|
||||
{ value: 'overview', label: 'falukant.reputation.overview.title' },
|
||||
{ value: 'party', label: 'falukant.reputation.party.title' }
|
||||
],
|
||||
newPartyView: false,
|
||||
newPartyTypeId: null,
|
||||
partyTypes: [],
|
||||
musicId: null,
|
||||
musicTypes: [],
|
||||
banquetteId: null,
|
||||
banquetteTypes: [],
|
||||
nobilityTitles: [],
|
||||
selectedNobilityIds: [],
|
||||
servantRatio: 50,
|
||||
inProgressParties: [],
|
||||
completedParties: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleNewPartyView() {
|
||||
this.newPartyView = !this.newPartyView
|
||||
},
|
||||
async loadPartyTypes() {
|
||||
const { data } = await apiClient.get('/api/falukant/party/types');
|
||||
this.partyTypes = data.partyTypes;
|
||||
this.musicTypes = data.musicTypes;
|
||||
this.banquetteTypes = data.banquetteTypes;
|
||||
this.musicId = this.musicTypes[0]?.id;
|
||||
this.banquetteId = this.banquetteTypes[0]?.id;
|
||||
},
|
||||
async loadParties() {
|
||||
const { data } = await apiClient.get('/api/falukant/party');
|
||||
const yesterday = new Date();
|
||||
yesterday.setDate(yesterday.getDate() - 1);
|
||||
this.inProgressParties = data.filter(party => {
|
||||
const partyDate = new Date(party.createdAt);
|
||||
return partyDate > yesterday;
|
||||
});
|
||||
this.completedParties = data.filter(party => {
|
||||
const partyDate = new Date(party.createdAt);
|
||||
return partyDate <= yesterday;
|
||||
});
|
||||
},
|
||||
async loadNobilityTitles() {
|
||||
this.nobilityTitles = await apiClient.get('/api/falukant/nobility/titels').then(r => r.data)
|
||||
},
|
||||
async orderParty() {
|
||||
await apiClient.post('/api/falukant/party', {
|
||||
partyTypeId: this.newPartyTypeId,
|
||||
musicId: this.musicId,
|
||||
banquetteId: this.banquetteId,
|
||||
nobilityIds: this.selectedNobilityIds.map(n => n.id ?? n),
|
||||
servantRatio: this.servantRatio
|
||||
});
|
||||
this.toggleNewPartyView();
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
formattedCost() {
|
||||
const type = this.partyTypes.find(t => t.id === this.newPartyTypeId) || {};
|
||||
const music = this.musicTypes.find(m => m.id === this.musicId) || {};
|
||||
const banquette = this.banquetteTypes.find(b => b.id === this.banquetteId) || {};
|
||||
let cost = (type.cost || 0) + (music.cost || 0) + (banquette.cost || 0);
|
||||
cost += (50 / this.servantRatio - 1) * 1000;
|
||||
let nobilityCost = this.selectedNobilityIds.reduce((sum, id) => {
|
||||
const nob = this.nobilityTitles.find(n => n.id === id)
|
||||
return sum + ((nob?.id ^ 5) * 1000)
|
||||
}, 0);
|
||||
cost += nobilityCost;
|
||||
const locale = this.$i18n?.locale || 'de-DE';
|
||||
return cost.toLocaleString(locale, {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
});
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
const tabFromQuery = this.$route?.query?.tab;
|
||||
if (['overview','party'].includes(tabFromQuery)) {
|
||||
this.activeTab = tabFromQuery;
|
||||
}
|
||||
await this.loadPartyTypes();
|
||||
await this.loadNobilityTitles();
|
||||
await this.loadParties();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
h2 {
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
.simple-tabs {
|
||||
display: flex;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.simple-tab {
|
||||
padding: 0.5rem 1rem;
|
||||
background: #fff;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.simple-tab.active {
|
||||
background: #F9A22C;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.tab-content {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.new-party-form {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.party-options {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.total-cost {
|
||||
font-weight: bold;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.multiselect {
|
||||
display: inline-block !important;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
table th {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.separator-class {
|
||||
border-top: 1px solid #ccc;
|
||||
margin-top: 1em;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user