258 lines
7.9 KiB
Vue
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> |