Falukant production, family and administration enhancements
This commit is contained in:
182
frontend/src/components/falukant/ProductionSection.vue
Normal file
182
frontend/src/components/falukant/ProductionSection.vue
Normal file
@@ -0,0 +1,182 @@
|
||||
<template>
|
||||
<div class="production-section">
|
||||
<h3>{{ $t('falukant.branch.production.title') }}</h3>
|
||||
<div v-if="productions && productions.length > 0">
|
||||
<h4>{{ $t('falukant.branch.production.current') }}</h4>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ $t('falukant.branch.production.product') }}</th>
|
||||
<th>{{ $t('falukant.branch.production.quantity') }}</th>
|
||||
<th>{{ $t('falukant.branch.production.ending') }}</th>
|
||||
<th>{{ $t('falukant.branch.production.remainingTime') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="production in productions" :key="production.id">
|
||||
<td>{{ $t(`falukant.product.${production.productType.labelTr}`) }}</td>
|
||||
<td>{{ production.quantity }}</td>
|
||||
<td>{{ calculateEndDateTime(production.startTimestamp, production.productType.productionTime) }}</td>
|
||||
<td>{{ calculateRemainingTime(production.startTimestamp, production.productType.productionTime) }} s</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div v-if="(!productions || productions.length < 2)">
|
||||
<div>
|
||||
<label for="product">{{ $t('falukant.branch.production.selectProduct') }}</label>
|
||||
<select name="product" id="product" v-model="selectedProduct">
|
||||
<option v-for="product in products" :key="product.id" :value="product.id">
|
||||
{{ $t(`falukant.product.${product.labelTr}`) }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label for="quantity">{{ $t('falukant.branch.production.quantity') }}</label>
|
||||
<input type="number" id="quantity" v-model.number="productionQuantity" min="1" max="200"/>
|
||||
</div>
|
||||
<div>
|
||||
<p>{{ $t('falukant.branch.production.cost') }}:
|
||||
<strong>{{ calculateProductionCost() }}</strong>
|
||||
</p>
|
||||
<p>{{ $t('falukant.branch.production.duration') }}:
|
||||
<strong>{{ calculateProductionDuration(selectedProduct) }}</strong>
|
||||
</p>
|
||||
<p>{{ $t('falukant.branch.production.revenue') }}:
|
||||
<strong>{{ calculateProductionRevenue() }}</strong>
|
||||
</p>
|
||||
</div>
|
||||
<button @click="startProduction" :disabled="!selectedProduct || productionQuantity < 1">
|
||||
{{ $t('falukant.branch.production.start') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import apiClient from '@/utils/axios.js';
|
||||
export default {
|
||||
name: "ProductionSection",
|
||||
props: {
|
||||
branchId: { type: Number, required: true },
|
||||
products: { type: Array, required: true },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
productions: [],
|
||||
selectedProduct: null,
|
||||
productionQuantity: 1,
|
||||
currentTime: Date.now(),
|
||||
timer: null,
|
||||
};
|
||||
},
|
||||
async mounted() {
|
||||
await this.loadProductions();
|
||||
this.timer = setInterval(() => {
|
||||
this.currentTime = Date.now();
|
||||
}, 1000);
|
||||
},
|
||||
beforeUnmount() {
|
||||
if (this.timer) {
|
||||
clearInterval(this.timer);
|
||||
this.timer = null;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async loadProductions() {
|
||||
try {
|
||||
const response = await apiClient.get(`/api/falukant/branches/${this.branchId}`);
|
||||
this.productions = response.data.productions.sort((a, b) => {
|
||||
const endTimeA = new Date(a.startTimestamp).getTime() + a.productType.productionTime * 60 * 1000;
|
||||
const endTimeB = new Date(b.startTimestamp).getTime() + b.productType.productionTime * 60 * 1000;
|
||||
return endTimeA - endTimeB;
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error loading productions:', error);
|
||||
}
|
||||
},
|
||||
calculateProductionCost() {
|
||||
if (!this.products) return 0;
|
||||
const product = this.products.find(p => p.id === this.selectedProduct);
|
||||
return product ? 6 * product.category * this.productionQuantity : 0;
|
||||
},
|
||||
calculateProductionDuration(productId) {
|
||||
if (!this.products || !productId) return 0;
|
||||
const product = this.products.find(p => p.id === productId);
|
||||
if (!product) return 0;
|
||||
const totalMinutes = product.productionTime * 60;
|
||||
return (totalMinutes / 60).toFixed(2).replace('.', ':');
|
||||
},
|
||||
calculateProductionRevenue() {
|
||||
if (!this.selectedProduct || !this.products) return 0;
|
||||
const product = this.products.find(p => p.id === this.selectedProduct);
|
||||
if (!product) return 0;
|
||||
const revenue = this.calculateProductRevenue(product).absolute * this.productionQuantity;
|
||||
return revenue.toFixed(2).toLocaleString();
|
||||
},
|
||||
calculateEndDateTime(startTimestamp, productionTime) {
|
||||
const start = new Date(startTimestamp);
|
||||
const end = new Date(start.getTime() + productionTime * 60 * 1000);
|
||||
return end.toLocaleString();
|
||||
},
|
||||
calculateRemainingTime(startTimestamp, productionTime) {
|
||||
const start = new Date(startTimestamp).getTime();
|
||||
const end = start + productionTime * 60 * 1000;
|
||||
const secondsLeft = Math.max(Math.floor((end - this.currentTime) / 1000), 0);
|
||||
return secondsLeft;
|
||||
},
|
||||
async startProduction() {
|
||||
if (this.selectedProduct && this.productionQuantity > 0) {
|
||||
this.productionQuantity = Math.min(this.productionQuantity, 200);
|
||||
let productionQuantity = this.productionQuantity;
|
||||
while (productionQuantity > 0) {
|
||||
const sendQuantitiy = Math.min(productionQuantity, 100);
|
||||
productionQuantity -= sendQuantitiy;
|
||||
try {
|
||||
await apiClient.post(`/api/falukant/production`, {
|
||||
branchId: this.branchId,
|
||||
productId: this.selectedProduct,
|
||||
quantity: sendQuantitiy,
|
||||
});
|
||||
this.loadProductions();
|
||||
} catch (error) {
|
||||
alert(this.$t(`falukant.branch.production.error${error.response.data.error}`));
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
calculateProductRevenue(product) {
|
||||
if (!product.knowledges || product.knowledges.length === 0) {
|
||||
return { absolute: 0, perMinute: 0 };
|
||||
}
|
||||
const knowledgeFactor = product.knowledges[0].knowledge || 0;
|
||||
const maxPrice = product.sellCost;
|
||||
const minPrice = maxPrice * 0.6;
|
||||
const revenuePerUnit = minPrice + (maxPrice - minPrice) * (knowledgeFactor / 100);
|
||||
const perMinute = product.productionTime > 0 ? revenuePerUnit / product.productionTime : 0;
|
||||
return {
|
||||
absolute: revenuePerUnit.toFixed(2),
|
||||
perMinute: perMinute.toFixed(2),
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.production-section {
|
||||
border: 1px solid #ccc;
|
||||
margin: 10px 0;
|
||||
border-radius: 4px;
|
||||
padding: 10px;
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
th, td {
|
||||
padding: 8px;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user