339 lines
12 KiB
Vue
339 lines
12 KiB
Vue
<template>
|
|
<div>
|
|
<StatusBar />
|
|
<h2>{{ $t('falukant.overview.title') }}</h2>
|
|
<div class="overviewcontainer">
|
|
<div>
|
|
<h3>{{ $t('falukant.overview.metadata.title') }}</h3>
|
|
<table>
|
|
<tr>
|
|
<td>{{ $t('falukant.overview.metadata.name') }}</td>
|
|
<td>{{ falukantUser?.character.definedFirstName.name }} {{
|
|
falukantUser?.character.definedLastName.name }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td>{{ $t('falukant.overview.metadata.nobleTitle') }}</td>
|
|
<td>{{ $t('falukant.titles.' + falukantUser?.character.gender + '.' +
|
|
falukantUser?.character.nobleTitle.labelTr) }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td>{{ $t('falukant.overview.metadata.money') }}</td>
|
|
<td>
|
|
{{ moneyValue != null
|
|
? moneyValue.toLocaleString(locale, { style: 'currency', currency: 'EUR' })
|
|
: '---' }}
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td>{{ $t('falukant.overview.metadata.age') }}</td>
|
|
<td>{{ falukantUser?.character.age }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td>{{ $t('falukant.overview.metadata.mainbranch') }}</td>
|
|
<td>{{ falukantUser?.mainBranchRegion.name }}</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div>
|
|
<h3>{{ $t('falukant.overview.productions.title') }}</h3>
|
|
<table v-if="productions.length > 0">
|
|
<thead>
|
|
<tr>
|
|
<th>{{ $t('falukant.branch.sale.region') }}</th>
|
|
<th>{{ $t('falukant.branch.production.product') }}</th>
|
|
<th>{{ $t('falukant.branch.production.quantity') }}</th>
|
|
<th>{{ $t('falukant.branch.production.ending') }}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr v-for="(production, index) in productions" :key="index">
|
|
<td>{{ production.cityName }}</td>
|
|
<td>{{ $t(`falukant.product.${production.productName}`) }}</td>
|
|
<td>{{ production.quantity }}</td>
|
|
<td>{{ formatDate(production.endTimestamp) }}</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<p v-else>{{ $t('falukant.branch.production.noProductions') }}</p>
|
|
</div>
|
|
<div>
|
|
<h3>{{ $t('falukant.overview.stock.title') }}</h3>
|
|
<table v-if="allStock.length > 0">
|
|
<thead>
|
|
<tr>
|
|
<th>{{ $t('falukant.branch.sale.region') }}</th>
|
|
<th>{{ $t('falukant.branch.sale.product') }}</th>
|
|
<th>{{ $t('falukant.branch.sale.quantity') }}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr v-for="(item, index) in allStock" :key="index">
|
|
<td>{{ item.regionName }}</td>
|
|
<td>{{ $t(`falukant.product.${item.productLabelTr}`) }}</td>
|
|
<td>{{ item.quantity }}</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<p v-else>{{ $t('falukant.branch.sale.noInventory') }}</p>
|
|
</div>
|
|
<div>
|
|
<h3>{{ $t('falukant.overview.branches.title') }}</h3>
|
|
<table>
|
|
<tr v-for="branch in falukantUser?.branches" :key="branch.id">
|
|
<td>
|
|
<span @click="openBranch(branch.id)" class="link">{{ branch.region.name }}</span>
|
|
</td>
|
|
<td>
|
|
{{ $t(`falukant.overview.branches.level.${branch.branchType.labelTr}`) }}
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<div class="imagecontainer">
|
|
<div :style="getAvatarStyle" class="avatar"></div>
|
|
<div :style="getHouseStyle" class="house"></div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import StatusBar from '@/components/falukant/StatusBar.vue';
|
|
import apiClient from '@/utils/axios.js';
|
|
import { mapState } from 'vuex';
|
|
|
|
const AVATAR_POSITIONS = {
|
|
male: {
|
|
width: 195,
|
|
height: 300,
|
|
positions: {
|
|
"0-1": { x: 161, y: 28 },
|
|
"2-3": { x: 802, y: 28 },
|
|
"4-6": { x: 1014, y: 28 },
|
|
"7-10": { x: 800, y: 368 },
|
|
"11-13": { x: 373, y: 368 },
|
|
"14-16": { x: 1441, y: 28 },
|
|
"17-20": { x: 1441, y: 368 },
|
|
"21-30": { x: 1014, y: 368 },
|
|
"31-45": { x: 1227, y: 368 },
|
|
"45-55": { x: 803, y: 687 },
|
|
"55+": { x: 1441, y: 687 },
|
|
},
|
|
},
|
|
female: {
|
|
width: 223,
|
|
height: 298,
|
|
positions: {
|
|
"0-1": { x: 302, y: 66 },
|
|
"2-3": { x: 792, y: 66 },
|
|
"4-6": { x: 62, y: 66 },
|
|
"7-10": { x: 1034, y: 66 },
|
|
"11-13": { x: 1278, y: 66 },
|
|
"14-16": { x: 303, y: 392 },
|
|
"17-20": { x: 1525, y: 392 },
|
|
"21-30": { x: 1278, y: 392 },
|
|
"31-45": { x: 547, y: 718 },
|
|
"45-55": { x: 1034, y: 718 },
|
|
"55+": { x: 1525, y: 718 },
|
|
},
|
|
},
|
|
};
|
|
|
|
export default {
|
|
name: 'FalukantOverviewView',
|
|
components: {
|
|
StatusBar,
|
|
},
|
|
data() {
|
|
return {
|
|
falukantUser: null,
|
|
allStock: [],
|
|
productions: [],
|
|
};
|
|
},
|
|
computed: {
|
|
...mapState(['daemonSocket']),
|
|
getAvatarStyle() {
|
|
if (!this.falukantUser) return {};
|
|
const { gender, age } = this.falukantUser.character;
|
|
const imageUrl = `/images/falukant/avatar/${gender}.png`;
|
|
const ageGroup = this.getAgeGroup(age);
|
|
const genderData = AVATAR_POSITIONS[gender] || {};
|
|
const position = genderData.positions?.[ageGroup] || { x: 0, y: 0 };
|
|
const width = genderData.width || 100;
|
|
const height = genderData.height || 100;
|
|
return {
|
|
backgroundImage: `url(${imageUrl})`,
|
|
backgroundPosition: `-${position.x}px -${position.y}px`,
|
|
backgroundSize: "1792px 1024px",
|
|
width: `${width}px`,
|
|
height: `${height}px`,
|
|
};
|
|
},
|
|
getHouseStyle() {
|
|
console.log(this.falukantUser);
|
|
if (!this.falukantUser || !this.falukantUser.userHouse?.houseType) return {};
|
|
const imageUrl = '/images/falukant/houses.png';
|
|
const pos = this.falukantUser.userHouse.houseType.position;
|
|
const index = pos - 1;
|
|
const columns = 3;
|
|
const spriteSize = 300;
|
|
const x = (index % columns) * spriteSize;
|
|
const y = Math.floor(index / columns) * spriteSize;
|
|
return {
|
|
backgroundImage: `url(${imageUrl})`,
|
|
backgroundPosition: `-${x}px -${y}px`,
|
|
backgroundSize: `${columns * spriteSize}px auto`,
|
|
width: `300px`,
|
|
height: `300px`,
|
|
border: '1px solid #ccc',
|
|
borderRadius: '4px',
|
|
imageRendering: 'crisp-edges',
|
|
};
|
|
},
|
|
getAgeColor(age) {
|
|
const ageGroup = this.getAgeGroup(age);
|
|
return ageGroup === 'child' ? 'blue' : ageGroup === 'teen' ? 'green' : ageGroup === 'adult' ? 'red' : 'gray';
|
|
},
|
|
moneyValue() {
|
|
const m = this.falukantUser?.money;
|
|
return typeof m === 'string' ? parseFloat(m) : m;
|
|
},
|
|
locale() {
|
|
return window.navigator.language || 'en-US';
|
|
},
|
|
},
|
|
async mounted() {
|
|
await this.fetchFalukantUser();
|
|
await this.fetchAllStock();
|
|
await this.fetchProductions();
|
|
if (this.socket) {
|
|
this.socket.on("falukantUserUpdated", this.fetchFalukantUser);
|
|
this.socket.on("production_ready", this.handleProductionReadyEvent);
|
|
}
|
|
if (this.daemonSocket) {
|
|
this.daemonSocket.addEventListener('message', (event) => {
|
|
try {
|
|
if (event.data === "ping") return;
|
|
const message = JSON.parse(event.data);
|
|
if (message.event === 'production_ready') {
|
|
this.handleProductionReadyEvent(message);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error processing WebSocket message in FalukantOverviewView:', error);
|
|
}
|
|
});
|
|
} else {
|
|
console.log('no daemon socket');
|
|
}
|
|
},
|
|
beforeUnmount() {
|
|
if (this.socket) {
|
|
this.socket.off("falukantUserUpdated", this.fetchFalukantUser);
|
|
this.socket.off("production_ready", this.handleProductionReadyEvent);
|
|
}
|
|
if (this.daemonSocket) {
|
|
this.daemonSocket.onmessage = null;
|
|
}
|
|
},
|
|
methods: {
|
|
getAgeGroup(age) {
|
|
if (age <= 1) return "0-1";
|
|
if (age <= 3) return "2-3";
|
|
if (age <= 6) return "4-6";
|
|
if (age <= 10) return "7-10";
|
|
if (age <= 13) return "11-13";
|
|
if (age <= 16) return "14-16";
|
|
if (age <= 20) return "17-20";
|
|
if (age <= 30) return "21-30";
|
|
if (age <= 45) return "31-45";
|
|
if (age <= 55) return "45-55";
|
|
return "55+";
|
|
},
|
|
async fetchFalukantUser() {
|
|
const falukantUser = await apiClient.get('/api/falukant/user');
|
|
if (!falukantUser.data) {
|
|
this.$router.push({ name: 'FalukantCreate' });
|
|
return;
|
|
}
|
|
this.falukantUser = falukantUser.data;
|
|
},
|
|
async fetchAllStock() {
|
|
const response = await apiClient.get('/api/falukant/stockoverview');
|
|
const rawData = response.data;
|
|
const aggregated = {};
|
|
for (const item of rawData) {
|
|
const key = `${item.regionName}__${item.productLabelTr}`;
|
|
if (!aggregated[key]) {
|
|
aggregated[key] = {
|
|
regionName: item.regionName,
|
|
productLabelTr: item.productLabelTr,
|
|
quantity: 0,
|
|
};
|
|
}
|
|
aggregated[key].quantity += item.quantity;
|
|
}
|
|
this.allStock = Object.values(aggregated);
|
|
},
|
|
handleProductionReadyEvent() {
|
|
this.fetchAllStock();
|
|
this.fetchProductions();
|
|
},
|
|
openBranch(branchId) {
|
|
this.$router.push({ name: 'BranchView', params: { branchId } });
|
|
},
|
|
async fetchProductions() {
|
|
try {
|
|
const response = await apiClient.get('/api/falukant/productions');
|
|
this.productions = response.data;
|
|
} catch (error) {
|
|
console.error('Error fetching productions:', error);
|
|
}
|
|
},
|
|
formatDate(timestamp) {
|
|
return new Date(timestamp).toLocaleString();
|
|
},
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<style scoped lang="scss">
|
|
.overviewcontainer {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr 1fr 1fr;
|
|
grid-gap: 5px;
|
|
}
|
|
|
|
.overviewcontainer>div {
|
|
border: 1px solid #ccc;
|
|
padding: 5px;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.imagecontainer {
|
|
margin-top: 20px;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
}
|
|
|
|
.avatar {
|
|
border: 1px solid #ccc;
|
|
border-radius: 4px;
|
|
background-repeat: no-repeat;
|
|
background-size: cover;
|
|
image-rendering: crisp-edges;
|
|
}
|
|
|
|
.house {
|
|
border: 1px solid #ccc;
|
|
border-radius: 4px;
|
|
background-repeat: no-repeat;
|
|
image-rendering: crisp-edges;
|
|
}
|
|
|
|
h2 {
|
|
padding-top: 20px;
|
|
}
|
|
</style>
|