Spiel erweitert
This commit is contained in:
175
frontend/src/views/falukant/BankView.vue
Normal file
175
frontend/src/views/falukant/BankView.vue
Normal file
@@ -0,0 +1,175 @@
|
||||
<template>
|
||||
<div class="contenthidden">
|
||||
<StatusBar />
|
||||
<div class="contentscroll">
|
||||
|
||||
<h2>{{ $t('falukant.bank.title') }}</h2>
|
||||
<SimpleTabs v-model="activeTab" :tabs="tabs" />
|
||||
|
||||
<!-- OVERVIEW -->
|
||||
<div v-if="activeTab === 'account'">
|
||||
<div class="account-section">
|
||||
<table>
|
||||
<tr>
|
||||
<td>{{ $t('falukant.bank.account.balance') }}</td>
|
||||
<td>{{ formatCost(bankOverview.money) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('falukant.bank.account.totalDebt') }}</td>
|
||||
<td>{{ formatCost(bankOverview.totalDebt) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('falukant.bank.account.maxCredit') }}</td>
|
||||
<td>{{ formatCost(bankOverview.maxCredit) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('falukant.bank.account.availableCredit') }}</td>
|
||||
<td>{{ formatCost(bankOverview.availableCredit) }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ACTIVE CREDITS -->
|
||||
<div v-else-if="activeTab === 'credits'">
|
||||
<div class="credits-section">
|
||||
<div v-if="bankOverview.activeCredits?.length">
|
||||
<table class="credits-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ $t('falukant.bank.credits.amount') }}</th>
|
||||
<th>{{ $t('falukant.bank.credits.remaining') }}</th>
|
||||
<th>{{ $t('falukant.bank.credits.interestRate') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="credit in bankOverview.activeCredits" :key="credit.id">
|
||||
<td>{{ formatCost(credit.amount) }}</td>
|
||||
<td>{{ formatCost(credit.remainingAmount) }}</td>
|
||||
<td>{{ credit.interestRate }}%</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div v-else>
|
||||
<p>{{ $t('falukant.bank.credits.none') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- PAYOFF INLINE -->
|
||||
<div v-else-if="activeTab === 'payoff'">
|
||||
<div class="payoff-section">
|
||||
<label>
|
||||
{{ $t('falukant.bank.credits.payoff.height') }}:
|
||||
<input
|
||||
type="number"
|
||||
v-model="selectedCredit"
|
||||
:min="0"
|
||||
:max="bankOverview.availableCredit"
|
||||
value="0"
|
||||
/>
|
||||
</label>
|
||||
<div v-if="selectedCredit">
|
||||
<p>{{ $t('falukant.bank.credits.payoff.remaining') }}: {{ formatCost(bankOverview.availableCredit - selectedCredit) }}</p>
|
||||
<p>{{ $t('falukant.bank.credits.payoff.fee') }}: {{ formatCost(bankOverview.fee) }}</p>
|
||||
<p>{{ $t('falukant.bank.credits.payoff.feeHeight') }}: {{ formatCost(feeRate()) }}</p>
|
||||
<p>
|
||||
<strong>{{ $t('falukant.bank.credits.payoff.total') }}: {{ formatCost(creditCost()) }}</strong>
|
||||
</p>
|
||||
<button @click="confirmPayoff" class="button" :disabled="!selectedCredit">
|
||||
{{ $t('falukant.bank.credits.payoff.confirm') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import StatusBar from '@/components/falukant/StatusBar.vue';
|
||||
import SimpleTabs from '@/components/SimpleTabs.vue';
|
||||
import apiClient from '@/utils/axios.js';
|
||||
import { mapState } from 'vuex';
|
||||
|
||||
export default {
|
||||
name: 'BankView',
|
||||
components: { StatusBar, SimpleTabs },
|
||||
data() {
|
||||
return {
|
||||
activeTab: 'account',
|
||||
tabs: [
|
||||
{ value: 'account', label: 'falukant.bank.account.title' },
|
||||
{ value: 'credits', label: 'falukant.bank.credits.title' },
|
||||
{ value: 'payoff', label: 'falukant.bank.credits.payoff.title' }
|
||||
],
|
||||
bankOverview: {
|
||||
money: 0,
|
||||
totalDebt: 0,
|
||||
maxCredit: 0,
|
||||
availableCredit: 0,
|
||||
activeCredits: []
|
||||
},
|
||||
selectedCreditId: null,
|
||||
selectedCredit: null,
|
||||
earlyPayoffFee: 0
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState(['daemonSocket'])
|
||||
},
|
||||
async mounted() {
|
||||
await this.loadBankOverview();
|
||||
if (this.daemonSocket) this.daemonSocket.addEventListener('message', this.handleDaemonMessage);
|
||||
},
|
||||
beforeUnmount() {
|
||||
if (this.daemonSocket) this.daemonSocket.removeEventListener('message', this.handleDaemonMessage);
|
||||
},
|
||||
methods: {
|
||||
async loadBankOverview() {
|
||||
try {
|
||||
const { data } = await apiClient.get('/api/falukant/bank/overview');
|
||||
this.bankOverview = data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
},
|
||||
async confirmPayoff() {
|
||||
try {
|
||||
await apiClient.post('/api/falukant/bank/credits', {
|
||||
height: this.selectedCredit
|
||||
});
|
||||
await this.loadBankOverview();
|
||||
this.selectedCredit = null;
|
||||
this.activeTab = 'credits';
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
},
|
||||
handleDaemonMessage(msg) {
|
||||
try {
|
||||
if (['falukantUpdateStatus', 'moneyChange', 'creditChange'].includes(msg.event)) {
|
||||
this.loadBankOverview();
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(evt, err);
|
||||
}
|
||||
},
|
||||
feeRate() {
|
||||
return this.bankOverview.fee * this.selectedCredit / 100 + this.selectedCredit / 10;
|
||||
},
|
||||
creditCost() {
|
||||
return this.selectedCredit + (this.bankOverview.fee * 10 * this.selectedCredit / 100);
|
||||
},
|
||||
formatCost(val) {
|
||||
return new Intl.NumberFormat(navigator.language, { minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(val);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
h2 { padding-top: 20px; }
|
||||
</style>
|
||||
Reference in New Issue
Block a user