Files
yourpart3/frontend/src/views/falukant/DirectorView.vue
Torsten Schulz 5029be81e9 Spiel erweitert
2025-06-02 11:26:45 +02:00

258 lines
7.9 KiB
Vue

<template>
<div class="director-view">
<StatusBar />
<div class="content-container">
<!-- Left: Director list -->
<div class="list-panel">
<h2>{{ $t('falukant.director.title') }}</h2>
<table class="director-table">
<thead>
<tr>
<th>{{ $t('falukant.director.name') }}</th>
<th>{{ $t('falukant.director.branch') }}</th>
<th>{{ $t('falukant.director.age') }}</th>
<th>{{ $t('falukant.director.satisfaction') }}</th>
</tr>
</thead>
<tbody>
<tr v-for="dir in directors" :key="dir.id" @click="selectDirector(dir)"
:class="{ selected: dir.id === selected?.id }" class="director-row">
<td>
{{ $t(`falukant.titles.${dir.character.gender}.${dir.character.nobleTitle.labelTr}`) }}
{{ dir.character.definedFirstName.name }} {{ dir.character.definedLastName.name }}
</td>
<td>{{ dir.region || '-' }}</td>
<td>{{ dir.age }}</td>
<td>{{ dir.satisfaction }} %</td>
</tr>
</tbody>
</table>
</div>
<!-- Right: Selected director detail -->
<div class="detail-panel" v-if="selected">
<h2>
{{ $t(`falukant.titles.${selected.character.gender}.${selected.character.nobleTitle.labelTr}`) }}
{{ selected.character.definedFirstName.name }} {{ selected.character.definedLastName.name }}
</h2>
<p>{{ $t('falukant.director.age') }}: {{ selected.age }}</p>
<h3>{{ $t('falukant.director.knowledge.title') }}</h3>
<div class="table-container">
<table class="knowledge-table">
<thead>
<tr>
<th>{{ $t('falukant.director.product') }}</th>
<th>{{ $t('falukant.director.knowledge.knowledge') }}</th>
</tr>
</thead>
<tbody>
<tr v-for="item in selected.character.knowledges" :key="item.productId">
<td>{{ $t(`falukant.product.${item.productType.labelTr}`) }}</td>
<td>{{ item.knowledge }} %</td>
</tr>
</tbody>
</table>
</div>
<div class="actions">
<div>
<label>
{{ $t('falukant.director.satisfaction') }}:
<span> {{ selected.satisfaction }} %</span>
</label>
</div>
<div>
<label>
{{ $t('falukant.director.income') }}:
<input type="text" v-model="selected.income" />
</label>
<span v-if="selected.satisfaction < 100" @click="setWishedIncome" class="link">({{ $t('falukant.director.wishedIncome') }}: {{ selected.wishedIncome }})</span>
</div>
<div>
<button @click="updateDirector">{{ $t('falukant.director.updateButton') }}</button>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import StatusBar from '@/components/falukant/StatusBar.vue';
import apiClient from '@/utils/axios.js';
import { mapState } from 'vuex';
export default {
name: 'DirectorView',
components: { StatusBar },
data() {
return {
directors: [],
selected: null,
editIncome: '',
editSatisfaction: 0
};
},
computed: {
...mapState(['daemonSocket'])
},
async mounted() {
await this.loadDirectors();
if (this.daemonSocket) {
this.daemonSocket.addEventListener('message', this.handleDaemonMessage);
}
},
beforeUnmount() {
if (this.daemonSocket) {
this.daemonSocket.removeEventListener('message', this.handleDaemonMessage);
}
},
methods: {
async loadDirectors() {
try {
const { data } = await apiClient.get('/api/falukant/directors');
this.directors = data.map(d => ({
...d,
branchName: d.branch?.regionName || null
}));
} catch (err) {
console.error('Error loading directors', err);
}
},
selectDirector(dir) {
this.selected = { ...dir };
this.editIncome = dir.income;
this.editSatisfaction = dir.satisfaction;
},
async updateDirector() {
try {
await apiClient.post('/api/falukant/directors', {
directorId: this.selected.id,
income: this.selected.income,
});
await this.loadDirectors();
this.selected = this.directors.find(d => d.id === this.selected.id);
} catch (err) {
console.error('Error updating director', err);
}
},
handleDaemonMessage(evt) {
try {
if (evt.data === 'ping') {
return;
}
const msg = JSON.parse(evt.data);
if (msg.event === 'directorchanged') {
this.loadDirectors();
if (this.selected) {
const updated = this.directors.find(d => d.id === this.selected.id);
if (updated) {
this.selected = { ...updated };
}
}
}
} catch (err) {
console.error('Error parsing daemon message', err, evt.data);
}
},
setWishedIncome() {
this.selected.income = this.selected.wishedIncome;
}
}
};
</script>
<style scoped>
.director-view .content-container {
display: flex;
gap: 20px;
}
.list-panel {
flex: 1;
overflow-y: auto;
}
.detail-panel {
flex: 1;
padding: 10px;
border-left: 1px solid #ccc;
}
.director-table,
.knowledge-table {
border-collapse: collapse;
}
.director-table th,
.director-table td,
.knowledge-table th,
.knowledge-table td {
border: 1px solid #ddd;
padding: 8px;
}
.selected {
background-color: #f0f8ff;
}
.actions {
margin-top: 10px;
display: flex;
gap: 10px;
align-items: center;
}
.actions label {
display: flex;
align-items: center;
gap: 5px;
}
button {
padding: 6px 12px;
cursor: pointer;
}
h2 {
padding-top: 20px;
}
.table-container {
max-height: 50vh;
/* maximal 50% der Viewport-Höhe */
min-height: 5em;
/* mindestens 5em hoch */
overflow-y: auto;
/* nur vertikales Scrollen */
}
.knowledge-table {
border-collapse: collapse;
table-layout: fixed;
/* Spalten fest verteilen */
}
/* Header-Zellen kleben oben im scrollenden Container */
.knowledge-table thead th {
position: sticky;
top: 0;
background: white;
/* Hintergrund, damit darunterliegender Inhalt nicht durchscheint */
z-index: 1;
/* sicherstellen, dass der Header immer oben liegt */
padding: 0.5em;
border: 1px solid #ccc;
}
/* Zellen-Styles für Körper und Kopf */
.knowledge-table th,
.knowledge-table td {
padding: 0.5em;
border: 1px solid #ccc;
text-align: left;
}
.director-row {
cursor: pointer;
}
</style>