Files
yourpart3/frontend/src/components/falukant/DirectorInfo.vue

564 lines
16 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="director-info">
<div v-if="!director || director === null">
<button @click="openNewDirectorDialog">
{{ $t('falukant.branch.director.actions.new') }}
</button>
</div>
<div v-else class="director-info-container">
<!-- Linke Seite: Stammdaten & Wissen (aus /falukant/directors) -->
<div class="director-main">
<h3 class="director-name">
{{ $t('falukant.titles.' + director.character.gender + '.' + director.character.nobleTitle.labelTr) }}
{{ director.character.definedFirstName.name }} {{ director.character.definedLastName.name }}
</h3>
<p class="director-meta">
{{ $t('falukant.director.age') }}:
{{ director.character.age }}
<span v-if="director.region"> {{ director.region }}</span>
</p>
<div
v-if="director.character.knowledges && director.character.knowledges.length"
class="knowledge-panel"
>
<h4>{{ $t('falukant.director.knowledge.title') }}</h4>
<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 director.character.knowledges"
:key="item.productId"
>
<td>{{ $t(`falukant.product.${item.productType.labelTr}`) }}</td>
<td>{{ item.knowledge }} %</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- Rechte Seite: Aktionen, Einkommen, Rechte -->
<div class="director-actions">
<div class="field">
<label>
{{ $t('falukant.branch.director.satisfaction') }}:
<span>{{ director.satisfaction }} %</span>
</label>
</div>
<div class="field">
<label>
{{ $t('falukant.branch.director.income') }}:
<input type="number" v-model.number="editIncome" />
</label>
<span
v-if="director.wishedIncome != null"
class="link"
@click="setWishedIncome"
>
({{ $t('falukant.director.wishedIncome') }}:
{{ director.wishedIncome }})
</span>
<div v-if="incomeUpdateSuccess" class="income-update-success">
{{ $t('falukant.branch.director.incomeUpdated') }}
</div>
</div>
<div class="field">
<button @click="updateDirector">
{{ $t('falukant.director.updateButton') }}
</button>
</div>
<div class="field toggles">
<label>
<input
type="checkbox"
v-model="director.mayProduce"
@change="saveSetting('mayProduce', director.mayProduce)"
/>
{{ $t('falukant.branch.director.produce') }}
</label>
<label>
<input
type="checkbox"
v-model="director.maySell"
@change="saveSetting('maySell', director.maySell)"
/>
{{ $t('falukant.branch.director.sell') }}
</label>
<label>
<input
type="checkbox"
v-model="director.mayStartTransport"
@change="saveSetting('mayStartTransport', director.mayStartTransport)"
/>
{{ $t('falukant.branch.director.starttransport') }}
</label>
</div>
<div class="field">
<button @click="fireDirector">
{{ $t('falukant.branch.director.fire') }}
</button>
</div>
<div class="field">
<button @click="teachDirector">
{{ $t('falukant.branch.director.teach') }}
</button>
</div>
</div>
</div>
<!-- Transport ohne Produkte (nur wenn Direktor vorhanden und mayStartTransport aktiviert) -->
<div v-if="director && director.mayStartTransport" class="director-transport-section">
<h4>{{ $t('falukant.branch.director.emptyTransport.title') }}</h4>
<p class="transport-description">
{{ $t('falukant.branch.director.emptyTransport.description') }}
</p>
<div class="transport-form">
<label>
{{ $t('falukant.branch.director.emptyTransport.vehicleType') }}
<select v-model.number="emptyTransportForm.vehicleTypeId" @change="loadEmptyTransportRoute">
<option :value="null" disabled>{{ $t('falukant.branch.director.emptyTransport.selectVehicle') }}</option>
<option v-for="vt in vehicleTypeOptions()" :key="vt.id" :value="vt.id">
{{ $t(`falukant.branch.vehicles.${vt.tr}`) }} ({{ vt.count }} × {{ vt.capacity }}) - {{ speedLabel(vt.speed) }}
</option>
</select>
</label>
<label>
{{ $t('falukant.branch.director.emptyTransport.targetBranch') }}
<select v-model.number="emptyTransportForm.targetBranchId" @change="loadEmptyTransportRoute">
<option :value="null" disabled>{{ $t('falukant.branch.director.emptyTransport.selectTarget') }}</option>
<option v-for="tb in targetBranchOptions" :key="tb.id" :value="tb.id">
{{ tb.label }}
</option>
</select>
</label>
<div v-if="emptyTransportForm.costLabel" class="transport-cost">
{{ $t('falukant.branch.director.emptyTransport.cost', { cost: emptyTransportForm.costLabel }) }}
</div>
<div class="transport-route" v-if="emptyTransportForm.durationLabel">
<div>
{{ $t('falukant.branch.director.emptyTransport.duration', { duration: emptyTransportForm.durationLabel }) }}
</div>
<div>
{{ $t('falukant.branch.director.emptyTransport.arrival', { datetime: emptyTransportForm.etaLabel }) }}
</div>
<div v-if="emptyTransportForm.routeNames && emptyTransportForm.routeNames.length">
{{ $t('falukant.branch.director.emptyTransport.route') }}:
{{ emptyTransportForm.routeNames.join(' ') }}
</div>
</div>
<button
@click="createEmptyTransport"
:disabled="!emptyTransportForm.vehicleTypeId || !emptyTransportForm.targetBranchId"
>
{{ $t('falukant.branch.director.emptyTransport.create') }}
</button>
</div>
</div>
</div>
<NewDirectorDialog ref="newDirectorDialog" />
</template>
<script>
import apiClient from '@/utils/axios.js';
import NewDirectorDialog from '@/dialogues/falukant/NewDirectorDialog.vue';
export default {
name: "DirectorInfo",
props: {
branchId: { type: Number, required: true },
vehicles: { type: Array, default: () => [] },
branches: { type: Array, default: () => [] },
},
components: {
NewDirectorDialog
},
data() {
return {
director: null,
showNewDirectorDialog: false,
editIncome: null,
incomeUpdateSuccess: false,
emptyTransportForm: {
vehicleTypeId: null,
targetBranchId: null,
distance: null,
durationHours: null,
eta: null,
durationLabel: '',
etaLabel: '',
routeNames: [],
cost: 0.1,
costLabel: '',
},
};
},
async mounted() {
await this.loadDirector();
},
methods: {
async refresh() {
await this.loadDirector();
},
async loadDirector() {
try {
const response = await apiClient.get(`/api/falukant/director/${this.branchId}`);
const data = response.data;
if (
!data ||
(Array.isArray(data) && data.length === 0) ||
typeof data.director === 'undefined' ||
data.director === null
) {
this.director = null;
this.editIncome = null;
} else {
this.director = data.director;
this.editIncome = this.director.income;
}
} catch (error) {
console.error('Error loading director:', error);
this.director = null;
this.editIncome = null;
}
},
async saveSetting(settingKey, value) {
if (!this.director) return;
try {
await apiClient.post(`/api/falukant/director/settings`, {
branchId: this.branchId,
directorId: this.director.id,
settingKey,
value,
});
} catch (error) {
console.error(`Error saving setting ${settingKey}:`, error);
}
},
speedLabel(value) {
const key = value == null ? 'unknown' : String(value);
const tKey = `falukant.branch.transport.speed.${key}`;
const translated = this.$t(tKey);
if (!translated || translated === tKey) return value;
return translated;
},
openNewDirectorDialog() {
console.log('openNewDirectorDialog');
this.$refs.newDirectorDialog.open(this.branchId);
},
async updateDirector() {
if (!this.director || this.editIncome == null) return;
try {
await apiClient.post(`/api/falukant/directors`, {
directorId: this.director.id,
income: this.editIncome,
});
await this.loadDirector();
// Erfolgsnachricht anzeigen
this.incomeUpdateSuccess = true;
// Nach 3 Sekunden automatisch ausblenden
setTimeout(() => {
this.incomeUpdateSuccess = false;
}, 3000);
} catch (error) {
console.error('Error updating director:', error);
}
},
setWishedIncome() {
if (!this.director || this.director.wishedIncome == null) return;
this.editIncome = this.director.wishedIncome;
},
fireDirector() {
alert(this.$t('falukant.branch.director.fireAlert'));
},
teachDirector() {
alert(this.$t('falukant.branch.director.teachAlert'));
},
vehicleTypeOptions() {
const groups = {};
for (const v of this.vehicles || []) {
if (v.status !== 'available' || !v.type || !v.type.id) continue;
const id = v.type.id;
if (!groups[id]) {
groups[id] = {
id,
tr: v.type.tr,
capacity: v.type.capacity,
speed: v.type.speed,
count: 0,
};
}
groups[id].count += 1;
}
return Object.values(groups);
},
targetBranchOptions() {
return (this.branches || [])
.filter(b => ['store', 'fullstack'].includes(b.branchTypeLabelTr))
.filter(b => b.id !== this.branchId)
.map(b => ({
id: b.id,
label: `${b.cityName} ${b.type}`,
}));
},
async loadEmptyTransportRoute() {
this.emptyTransportForm.distance = null;
this.emptyTransportForm.durationHours = null;
this.emptyTransportForm.eta = null;
this.emptyTransportForm.durationLabel = '';
this.emptyTransportForm.etaLabel = '';
this.emptyTransportForm.routeNames = [];
const vType = this.vehicleTypeOptions().find(v => v.id === this.emptyTransportForm.vehicleTypeId);
const targetBranch = (this.branches || []).find(b => b.id === this.emptyTransportForm.targetBranchId);
const sourceBranch = (this.branches || []).find(b => b.id === this.branchId);
if (!vType || !targetBranch || !sourceBranch) {
return;
}
try {
const { data } = await apiClient.get('/api/falukant/transports/route', {
params: {
sourceRegionId: sourceBranch.regionId,
targetRegionId: targetBranch.regionId,
vehicleTypeId: vType.id,
},
});
if (data && data.totalDistance != null) {
const distance = data.totalDistance;
const speed = vType.speed || 1;
const hours = distance / speed;
this.emptyTransportForm.distance = distance;
this.emptyTransportForm.durationHours = hours;
const now = new Date();
const etaMs = now.getTime() + hours * 60 * 60 * 1000;
const etaDate = new Date(etaMs);
const fullHours = Math.floor(hours);
const minutes = Math.round((hours - fullHours) * 60);
const parts = [];
if (fullHours > 0) parts.push(`${fullHours} h`);
if (minutes > 0) parts.push(`${minutes} min`);
this.emptyTransportForm.durationLabel = parts.length ? parts.join(' ') : '0 min';
this.emptyTransportForm.etaLabel = etaDate.toLocaleString();
this.emptyTransportForm.routeNames = (data.regions || []).map(r => r.name);
}
// Kosten für leeren Transport: 0.1
this.emptyTransportForm.cost = 0.1;
this.emptyTransportForm.costLabel = this.formatMoney(0.1);
} catch (error) {
console.error('Error loading transport route:', error);
this.emptyTransportForm.distance = null;
this.emptyTransportForm.durationHours = null;
this.emptyTransportForm.eta = null;
this.emptyTransportForm.durationLabel = '';
this.emptyTransportForm.etaLabel = '';
this.emptyTransportForm.routeNames = [];
}
},
formatMoney(amount) {
if (amount == null) return '';
try {
return amount.toLocaleString(undefined, {
minimumFractionDigits: 1,
maximumFractionDigits: 1,
});
} catch (e) {
return String(amount);
}
},
async createEmptyTransport() {
if (!this.emptyTransportForm.vehicleTypeId || !this.emptyTransportForm.targetBranchId) {
return;
}
try {
await apiClient.post('/api/falukant/transports', {
branchId: this.branchId,
vehicleTypeId: this.emptyTransportForm.vehicleTypeId,
productId: null,
quantity: 0,
targetBranchId: this.emptyTransportForm.targetBranchId,
});
// Formular zurücksetzen
this.emptyTransportForm = {
vehicleTypeId: null,
targetBranchId: null,
distance: null,
durationHours: null,
eta: null,
durationLabel: '',
etaLabel: '',
routeNames: [],
cost: 0.1,
costLabel: '',
};
alert(this.$t('falukant.branch.director.emptyTransport.success'));
this.$emit('transportCreated');
} catch (error) {
console.error('Error creating empty transport:', error);
alert(this.$t('falukant.branch.director.emptyTransport.error'));
}
},
},
};
</script>
<style scoped>
.director-info {
border: 1px solid #ccc;
margin: 10px 0;
border-radius: 4px;
padding: 10px;
}
.director-info-container {
display: flex;
gap: 1rem;
}
.director-main {
flex: 2;
}
.director-actions {
flex: 1;
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.director-name {
margin: 0 0 0.25rem 0;
}
.director-meta {
margin: 0 0 0.75rem 0;
font-style: italic;
}
.field {
margin-bottom: 0.5rem;
}
.field label {
display: flex;
align-items: center;
gap: 0.5rem;
}
.income-update-success {
margin-top: 0.5rem;
padding: 0.5rem;
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
border-radius: 4px;
font-size: 0.9em;
}
.toggles label {
display: block;
}
.link {
color: #007bff;
cursor: pointer;
margin-left: 0.5rem;
}
.table-container {
max-height: 40vh;
overflow-y: auto;
}
.knowledge-table {
width: 100%;
border-collapse: collapse;
}
.knowledge-table th,
.knowledge-table td {
border: 1px solid #ddd;
padding: 4px 6px;
}
.director-transport-section {
margin-top: 2rem;
padding-top: 1rem;
border-top: 1px solid #ddd;
}
.transport-description {
margin-bottom: 1rem;
color: #666;
font-style: italic;
}
.transport-form {
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.transport-form label {
display: flex;
flex-direction: column;
gap: 0.25rem;
}
.transport-form select {
padding: 0.5rem;
border: 1px solid #ccc;
border-radius: 4px;
}
.transport-cost {
font-weight: bold;
color: #333;
}
.transport-route {
padding: 0.75rem;
background-color: #f5f5f5;
border-radius: 4px;
font-size: 0.9em;
}
.transport-route > div {
margin-bottom: 0.25rem;
}
</style>