mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-02-25 18:45:27 -06:00
Update frontend.
This commit is contained in:
parent
45f918963e
commit
70aaa67cfc
@ -118,6 +118,7 @@ REDIS_CACHE_DB="1"
|
||||
COOKIE_PATH="/"
|
||||
COOKIE_DOMAIN=
|
||||
COOKIE_SECURE=false
|
||||
COOKIE_SAMESITE=lax
|
||||
|
||||
# If you want Firefly III to mail you, update these settings
|
||||
# For instructions, see: https://docs.firefly-iii.org/advanced-installation/email
|
||||
|
@ -36,5 +36,5 @@ return [
|
||||
'domain' => env('COOKIE_DOMAIN', null),
|
||||
'secure' => env('COOKIE_SECURE', null),
|
||||
'http_only' => true,
|
||||
'same_site' => null,
|
||||
'same_site' => env('COOKIE_SAMESITE','lax'),
|
||||
];
|
||||
|
@ -1,22 +1,12 @@
|
||||
{
|
||||
"/public/js/manifest.js": "/public/js/manifest.js",
|
||||
"/public/js/manifest.js.map": "/public/js/manifest.js.map",
|
||||
"/public/js/vendor.js": "/public/js/vendor.js",
|
||||
"/public/js/vendor.js.map": "/public/js/vendor.js.map",
|
||||
"/public/js/accounts/index.js": "/public/js/accounts/index.js",
|
||||
"/public/js/accounts/index.js.map": "/public/js/accounts/index.js.map",
|
||||
"/public/js/accounts/show.js": "/public/js/accounts/show.js",
|
||||
"/public/js/accounts/show.js.map": "/public/js/accounts/show.js.map",
|
||||
"/public/js/dashboard.js": "/public/js/dashboard.js",
|
||||
"/public/css/app.css": "/public/css/app.css",
|
||||
"/public/js/dashboard.js.map": "/public/js/dashboard.js.map",
|
||||
"/public/css/app.css.map": "/public/css/app.css.map",
|
||||
"/public/js/empty.js": "/public/js/empty.js",
|
||||
"/public/js/empty.js.map": "/public/js/empty.js.map",
|
||||
"/public/js/manifest.js": "/public/js/manifest.js",
|
||||
"/public/js/new-user/index.js": "/public/js/new-user/index.js",
|
||||
"/public/js/new-user/index.js.map": "/public/js/new-user/index.js.map",
|
||||
"/public/js/register.js": "/public/js/register.js",
|
||||
"/public/js/register.js.map": "/public/js/register.js.map",
|
||||
"/public/js/transactions/create.js": "/public/js/transactions/create.js",
|
||||
"/public/js/transactions/create.js.map": "/public/js/transactions/create.js.map"
|
||||
"/public/js/vendor.js": "/public/js/vendor.js"
|
||||
}
|
||||
|
@ -50,7 +50,7 @@
|
||||
|
||||
<div class="progress-bar bg-danger progress-bar-striped" role="progressbar"
|
||||
:aria-valuenow="budgetLimit.pctRed" aria-valuemin="0" aria-valuemax="100" :style="'width: '+ budgetLimit.pctRed + '%;'">
|
||||
<span v-if="budgetLimit.pctOrange <= 50 && budgetLimit.pctRed > 35">
|
||||
<span v-if="budgetLimit.pctOrange <= 50 && budgetLimit.pctRed > 35" class="text-muted">
|
||||
Spent
|
||||
{{ Intl.NumberFormat(locale, {style: 'currency', currency: budgetLimit.currency_code}).format(budgetLimit.spent) }}
|
||||
of
|
||||
@ -59,7 +59,7 @@
|
||||
</div>
|
||||
|
||||
<!-- amount if bar is very small -->
|
||||
<span v-if="budgetLimit.pctGreen <= 35 && 0 === budgetLimit.pctOrange && 0 === budgetLimit.pctRed">
|
||||
<span v-if="budgetLimit.pctGreen <= 35 && 0 === budgetLimit.pctOrange && 0 === budgetLimit.pctRed && 0 !== budgetLimit.pctGreen">
|
||||
|
||||
Spent
|
||||
{{ Intl.NumberFormat(locale, {style: 'currency', currency: budgetLimit.currency_code}).format(budgetLimit.spent) }}
|
||||
|
@ -18,6 +18,7 @@
|
||||
- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
|
||||
<template>
|
||||
<tr>
|
||||
<td style="width:25%;">
|
||||
|
@ -31,7 +31,7 @@
|
||||
<date-picker
|
||||
v-model="range"
|
||||
mode="date"
|
||||
rows="2"
|
||||
:rows="2"
|
||||
is-range
|
||||
>
|
||||
<template v-slot="{ inputValue, inputEvents, isDragging, togglePopover }">
|
||||
@ -40,7 +40,7 @@
|
||||
<div class="btn-group btn-group-sm d-flex">
|
||||
<button
|
||||
class="btn btn-secondary btn-sm" :title="$t('firefly.custom_period')"
|
||||
@click="togglePopover({ placement: 'auto-start', positionFixed:true })"
|
||||
@click="togglePopover({ placement: 'auto-start', positionFixed: true })"
|
||||
><i class="fas fa-calendar-alt"></i></button>
|
||||
<button
|
||||
class="btn btn-secondary"
|
||||
@ -78,6 +78,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import {createNamespacedHelpers} from "vuex";
|
||||
|
||||
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('dashboard/index')
|
||||
@ -88,7 +89,14 @@ export default {
|
||||
this.ready = true;
|
||||
this.locale = localStorage.locale ?? 'en-US';
|
||||
},
|
||||
|
||||
methods: {
|
||||
...mapMutations(
|
||||
[
|
||||
'setEnd',
|
||||
'setStart',
|
||||
],
|
||||
),
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'viewRange',
|
||||
@ -106,6 +114,11 @@ export default {
|
||||
this.range.end = new Date(this.end);
|
||||
}
|
||||
},
|
||||
range: function(value) {
|
||||
console.log('User updated range');
|
||||
this.setStart(value.start);
|
||||
this.setEnd(value.end);
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -60,31 +60,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import {createNamespacedHelpers} from "vuex";
|
||||
|
||||
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('dashboard/index')
|
||||
|
||||
export default {
|
||||
name: "Dashboard",
|
||||
created() {
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'viewRange',
|
||||
'start',
|
||||
'end'
|
||||
])
|
||||
},
|
||||
methods: {},
|
||||
watch: {
|
||||
start: function (value) {
|
||||
// console.log('Value of start is now ' + value);
|
||||
},
|
||||
end: function (value) {
|
||||
// console.log('Value of end is now ' + value);
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -25,7 +25,7 @@
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div v-if="!loading">
|
||||
<MainAccountChart :chart-data="dataCollection" :options="chartOptions" v-if="!loading && !error" />
|
||||
<MainAccountChart :chart-data="dataCollection" :options="chartOptions" v-if="!loading && !error"/>
|
||||
</div>
|
||||
<div v-if="loading && !error" class="text-center">
|
||||
<i class="fas fa-spinner fa-spin"></i>
|
||||
@ -41,11 +41,13 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import DataConverter from "../charts/DataConverter";
|
||||
import DefaultLineOptions from "../charts/DefaultLineOptions";
|
||||
|
||||
import {createNamespacedHelpers} from "vuex";
|
||||
import MainAccountChart from "./MainAccountChart";
|
||||
|
||||
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('dashboard/index')
|
||||
|
||||
export default {
|
||||
@ -61,7 +63,7 @@ export default {
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.ready= true;
|
||||
this.ready = true;
|
||||
this.chartOptions = DefaultLineOptions.methods.getDefaultOptions();
|
||||
},
|
||||
computed: {
|
||||
@ -79,10 +81,17 @@ export default {
|
||||
// console.log(this.chartOptions);
|
||||
this.initialiseChart();
|
||||
}
|
||||
}
|
||||
},
|
||||
start: function () {
|
||||
this.initialiseChart();
|
||||
},
|
||||
end: function () {
|
||||
this.initialiseChart();
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
initialiseChart: function () {
|
||||
this.loading = true;
|
||||
let startStr = this.start.toISOString().split('T')[0];
|
||||
let endStr = this.end.toISOString().split('T')[0];
|
||||
let url = './api/v1/chart/account/overview?start=' + startStr + '&end=' + endStr;
|
||||
|
@ -20,6 +20,7 @@
|
||||
-->
|
||||
|
||||
<script>
|
||||
|
||||
import { Line, mixins } from 'vue-chartjs'
|
||||
const { reactiveProp } = mixins
|
||||
|
||||
|
@ -19,29 +19,53 @@
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="row">
|
||||
<div v-bind:class="{ 'col-lg-12': 1 === accounts.length, 'col-lg-6': 2 === accounts.length, 'col-lg-4': accounts.length > 2 }"
|
||||
v-for="account in accounts">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><a :href="account.url">{{ account.title }}</a></h3>
|
||||
<div class="card-tools">
|
||||
<div>
|
||||
<!-- row if loading -->
|
||||
<div class="row" v-if="loading && !error">
|
||||
<div class="col">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="text-center">
|
||||
<i class="fas fa-spinner fa-spin"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- row if error -->
|
||||
<div class="row" v-if="error">
|
||||
<div class="col">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="text-center">
|
||||
<i class="fas fa-exclamation-triangle text-danger"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- row if normal -->
|
||||
<div class="row" v-if="!loading && !error">
|
||||
<div
|
||||
v-bind:class="{ 'col-lg-12': 1 === accounts.length, 'col-lg-6': 2 === accounts.length, 'col-lg-4': accounts.length > 2 }"
|
||||
v-for="account in accounts">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><a :href="account.url">{{ account.title }}</a></h3>
|
||||
<div class="card-tools">
|
||||
<span :class="parseFloat(account.current_balance) < 0 ? 'text-danger' : 'text-success'">
|
||||
{{ Intl.NumberFormat(locale, {style: 'currency', currency: account.currency_code}).format(parseFloat(account.current_balance)) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body table-responsive p-0">
|
||||
<div v-if="!loading && !error">
|
||||
<transaction-list-large :transactions="account.transactions" v-if="1===accounts.length" :account_id="account.id"/>
|
||||
<transaction-list-medium :transactions="account.transactions" v-if="2===accounts.length" :account_id="account.id"/>
|
||||
<transaction-list-small :transactions="account.transactions" v-if="accounts.length > 2" :account_id="account.id"/>
|
||||
</div>
|
||||
<div v-if="loading && !error" class="text-center">
|
||||
<i class="fas fa-spinner fa-spin"></i>
|
||||
</div>
|
||||
<div v-if="error" class="text-center">
|
||||
<i class="fas fa-exclamation-triangle text-danger"></i>
|
||||
<div class="card-body table-responsive p-0">
|
||||
<div>
|
||||
<transaction-list-large :transactions="account.transactions" v-if="1===accounts.length" :account_id="account.id"/>
|
||||
<transaction-list-medium :transactions="account.transactions" v-if="2===accounts.length" :account_id="account.id"/>
|
||||
<transaction-list-small :transactions="account.transactions" v-if="accounts.length > 2" :account_id="account.id"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -50,6 +74,10 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {createNamespacedHelpers} from "vuex";
|
||||
|
||||
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('dashboard/index')
|
||||
|
||||
export default {
|
||||
name: "MainAccountList",
|
||||
data() {
|
||||
@ -63,52 +91,72 @@ export default {
|
||||
},
|
||||
created() {
|
||||
this.locale = localStorage.locale ?? 'en-US';
|
||||
axios.get('./api/v1/preferences/frontpageAccounts')
|
||||
.then(response => {
|
||||
this.loadAccounts(response);
|
||||
}
|
||||
);
|
||||
this.ready = true;
|
||||
},
|
||||
methods:
|
||||
{
|
||||
loadAccounts(response) {
|
||||
let accountIds = response.data.data.attributes.data;
|
||||
for (let key in accountIds) {
|
||||
if (accountIds.hasOwnProperty(key) && /^0$|^[1-9]\d*$/.test(key) && key <= 4294967294) {
|
||||
this.accounts.push({
|
||||
id: accountIds[key],
|
||||
title: '',
|
||||
url: '',
|
||||
current_balance: '',
|
||||
currency_code: '',
|
||||
transactions: []
|
||||
});
|
||||
this.loadSingleAccount(key, accountIds[key]);
|
||||
}
|
||||
}
|
||||
},
|
||||
loadSingleAccount(key, accountId) {
|
||||
axios.get('./api/v1/accounts/' + accountId)
|
||||
.then(response => {
|
||||
this.accounts[key].title = response.data.data.attributes.name;
|
||||
this.accounts[key].url = './accounts/show/' + response.data.data.id;
|
||||
this.accounts[key].current_balance = response.data.data.attributes.current_balance;
|
||||
this.accounts[key].currency_code = response.data.data.attributes.currency_code;
|
||||
|
||||
this.loadTransactions(key, accountId);
|
||||
}
|
||||
);
|
||||
},
|
||||
loadTransactions(key, accountId) {
|
||||
axios.get('./api/v1/accounts/' + accountId + '/transactions?page=1&limit=10')
|
||||
.then(response => {
|
||||
this.accounts[key].transactions = response.data.data;
|
||||
this.loading = false;
|
||||
this.error = false;
|
||||
}
|
||||
);
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'start',
|
||||
'end'
|
||||
]),
|
||||
'datesReady': function () {
|
||||
return null !== this.start && null !== this.end && this.ready;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
datesReady: function (value) {
|
||||
if (true === value) {
|
||||
this.initialiseList();
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
initialiseList: function () {
|
||||
axios.get('./api/v1/preferences/frontpageAccounts')
|
||||
.then(response => {
|
||||
this.loadAccounts(response);
|
||||
}
|
||||
);
|
||||
},
|
||||
loadAccounts(response) {
|
||||
let accountIds = response.data.data.attributes.data;
|
||||
for (let key in accountIds) {
|
||||
if (accountIds.hasOwnProperty(key) && /^0$|^[1-9]\d*$/.test(key) && key <= 4294967294) {
|
||||
this.accounts.push({
|
||||
id: accountIds[key],
|
||||
title: '',
|
||||
url: '',
|
||||
current_balance: '',
|
||||
currency_code: 'EUR',
|
||||
transactions: []
|
||||
});
|
||||
this.loadSingleAccount(key, accountIds[key]);
|
||||
}
|
||||
}
|
||||
},
|
||||
loadSingleAccount(key, accountId) {
|
||||
axios.get('./api/v1/accounts/' + accountId)
|
||||
.then(response => {
|
||||
this.accounts[key].title = response.data.data.attributes.name;
|
||||
this.accounts[key].url = './accounts/show/' + response.data.data.id;
|
||||
this.accounts[key].current_balance = response.data.data.attributes.current_balance;
|
||||
this.accounts[key].currency_code = response.data.data.attributes.currency_code;
|
||||
|
||||
this.loadTransactions(key, accountId);
|
||||
}
|
||||
);
|
||||
},
|
||||
loadTransactions(key, accountId) {
|
||||
let startStr = this.start.toISOString().split('T')[0];
|
||||
let endStr = this.end.toISOString().split('T')[0];
|
||||
axios.get('./api/v1/accounts/' + accountId + '/transactions?page=1&limit=10&start=' + startStr + '&end=' + endStr)
|
||||
.then(response => {
|
||||
this.accounts[key].transactions = response.data.data;
|
||||
this.loading = false;
|
||||
this.error = false;
|
||||
}
|
||||
);
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -23,7 +23,20 @@
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">{{ $t('firefly.bills') }}</h3>
|
||||
</div>
|
||||
<div class="card-body table-responsive p-0">
|
||||
<!-- body if loading -->
|
||||
<div class="card-body" v-if="loading && !error">
|
||||
<div class="text-center">
|
||||
<i class="fas fa-spinner fa-spin"></i>
|
||||
</div>
|
||||
</div>
|
||||
<!-- body if error -->
|
||||
<div class="card-body" v-if="error">
|
||||
<div class="text-center">
|
||||
<i class="fas fa-exclamation-triangle text-danger"></i>
|
||||
</div>
|
||||
</div>
|
||||
<!-- body if normal -->
|
||||
<div class="card-body table-responsive p-0" v-if="!loading && !error">
|
||||
<table class="table table-striped">
|
||||
<caption style="display:none;">{{ $t('firefly.bills') }}</caption>
|
||||
<thead>
|
||||
@ -67,6 +80,15 @@ import {createNamespacedHelpers} from "vuex";
|
||||
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('dashboard/index')
|
||||
export default {
|
||||
name: "MainBillsList",
|
||||
data() {
|
||||
return {
|
||||
bills: [],
|
||||
locale: 'en-US',
|
||||
ready: false,
|
||||
loading: true,
|
||||
error: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'start',
|
||||
@ -79,7 +101,6 @@ export default {
|
||||
watch: {
|
||||
datesReady: function (value) {
|
||||
if (true === value) {
|
||||
// console.log(this.chartOptions);
|
||||
this.initialiseBills();
|
||||
}
|
||||
}
|
||||
@ -98,7 +119,10 @@ export default {
|
||||
.then(response => {
|
||||
this.loadBills(response.data.data);
|
||||
}
|
||||
);
|
||||
).catch(error => {
|
||||
this.error = true;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
renderPaidDate: function (obj) {
|
||||
let dateStr = new Intl.DateTimeFormat(this.locale, {year: 'numeric', month: 'long', day: 'numeric'}).format(new Date(obj.date));
|
||||
@ -116,14 +140,9 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
this.error = false;
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
bills: [],
|
||||
locale: 'en-US',
|
||||
ready: false
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -1,60 +0,0 @@
|
||||
<!--
|
||||
- MainBudgetChart.vue
|
||||
- Copyright (c) 2020 james@firefly-iii.org
|
||||
-
|
||||
- This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
-
|
||||
- This program is free software: you can redistribute it and/or modify
|
||||
- it under the terms of the GNU Affero General Public License as
|
||||
- published by the Free Software Foundation, either version 3 of the
|
||||
- License, or (at your option) any later version.
|
||||
-
|
||||
- This program is distributed in the hope that it will be useful,
|
||||
- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
- GNU Affero General Public License for more details.
|
||||
-
|
||||
- You should have received a copy of the GNU Affero General Public License
|
||||
- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">{{ $t('firefly.budgets') }}</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div style="position: relative;">
|
||||
<canvas id="mainBudgetChart" style="min-height: 400px; height: 400px; max-height: 400px; max-width: 100%;"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<a href="./budgets" class="btn btn-default button-sm"><i class="far fa-money-bill-alt"></i> {{ $t('firefly.go_to_budgets') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DefaultBarOptions from "../charts/DefaultBarOptions";
|
||||
import DataConverter from "../charts/DataConverter";
|
||||
|
||||
export default {
|
||||
name: "MainBudget",
|
||||
created() {
|
||||
axios.get('./api/v1/chart/budget/overview?start=' + window.sessionXStart + '&end=' + window.sessionXEnd)
|
||||
.then(response => {
|
||||
let chartData = DataConverter.methods.convertChart(response.data);
|
||||
let stackedBarChartCanvas = $('#mainBudgetChart').get(0).getContext('2d')
|
||||
new Chart(stackedBarChartCanvas, {
|
||||
type: 'bar',
|
||||
data: chartData,
|
||||
options: DefaultBarOptions.methods.getDefaultOptions()
|
||||
});
|
||||
});
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@ -21,7 +21,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- daily budgets (will be the exception, I expect) -->
|
||||
<div class="row">
|
||||
<div class="row" v-if="!loading">
|
||||
<div class="col-xl-6 col-lg-12 col-md-12 col-sm-12 col-xs-12" v-if="budgetLimits.daily.length > 0">
|
||||
<BudgetListGroup :title="$t('firefly.daily_budgets')" :budgetLimits=budgetLimits.daily
|
||||
/>
|
||||
@ -51,13 +51,24 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" v-if="loading && !error">
|
||||
<div class="col">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="text-center">
|
||||
<i class="fas fa-spinner fa-spin"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BudgetListGroup from "./BudgetListGroup";
|
||||
import {createNamespacedHelpers} from "vuex";
|
||||
|
||||
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('dashboard/index')
|
||||
|
||||
export default {
|
||||
@ -78,7 +89,9 @@ export default {
|
||||
budgets: {},
|
||||
rawBudgets: [],
|
||||
locale: 'en-US',
|
||||
ready: false
|
||||
ready: false,
|
||||
loading: true,
|
||||
error: false
|
||||
}
|
||||
},
|
||||
created() {
|
||||
@ -112,6 +125,7 @@ export default {
|
||||
axios.get('./api/v1/budgets?start=' + startStr + '&end=' + endStr)
|
||||
.then(response => {
|
||||
this.parseBudgets(response.data);
|
||||
this.loading = false;
|
||||
}
|
||||
);
|
||||
},
|
||||
@ -197,53 +211,8 @@ export default {
|
||||
|
||||
let period = data.data[key].attributes.period ?? 'other';
|
||||
this.budgetLimits[period].push(obj);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// // loop budgets (and do what?)
|
||||
// for (let key in data.included) {
|
||||
// if (data.included.hasOwnProperty(key) && /^0$|^[1-9]\d*$/.test(key) && key <= 4294967294) {
|
||||
// let obj = {
|
||||
// name: data.included[key].attributes.name,
|
||||
// id: data.included[key].id,
|
||||
// };
|
||||
// this.budgets[data.included[key].id] = obj;
|
||||
// }
|
||||
// }
|
||||
|
||||
// loop budget limits:
|
||||
// for (let key in data.data) {
|
||||
// if (data.data.hasOwnProperty(key) && /^0$|^[1-9]\d*$/.test(key) && key <= 4294967294) {
|
||||
// let pctGreen = 0;
|
||||
// let pctOrange = 0;
|
||||
// let pctRed = 0;
|
||||
//
|
||||
|
||||
//
|
||||
// let obj = {
|
||||
// id: data.data[key].id,
|
||||
// amount: data.data[key].attributes.amount,
|
||||
// budget_id: data.data[key].attributes.budget_id,
|
||||
// currency_id: data.data[key].attributes.currency_id,
|
||||
// currency_code: data.data[key].attributes.currency_code,
|
||||
// period: data.data[key].attributes.period,
|
||||
// start: new Date(data.data[key].attributes.start),
|
||||
// end: new Date(data.data[key].attributes.end),
|
||||
// spent: data.data[key].attributes.spent,
|
||||
// pctGreen: pctGreen,
|
||||
// pctOrange: pctOrange,
|
||||
// pctRed: pctRed,
|
||||
// };
|
||||
//
|
||||
//
|
||||
//
|
||||
// let period = data.data[key].attributes.period ?? 'other';
|
||||
// this.budgetLimits[period].push(obj);
|
||||
// }
|
||||
// }
|
||||
|
||||
},
|
||||
filterBudgets(budgetId, currencyId) {
|
||||
for (let key in this.rawBudgets) {
|
||||
|
@ -1,60 +0,0 @@
|
||||
<!--
|
||||
- MainCategoryChart.vue
|
||||
- Copyright (c) 2020 james@firefly-iii.org
|
||||
-
|
||||
- This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
-
|
||||
- This program is free software: you can redistribute it and/or modify
|
||||
- it under the terms of the GNU Affero General Public License as
|
||||
- published by the Free Software Foundation, either version 3 of the
|
||||
- License, or (at your option) any later version.
|
||||
-
|
||||
- This program is distributed in the hope that it will be useful,
|
||||
- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
- GNU Affero General Public License for more details.
|
||||
-
|
||||
- You should have received a copy of the GNU Affero General Public License
|
||||
- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">{{ $t('firefly.categories') }}</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div>
|
||||
<canvas id="mainCategoryChart" style="min-height: 400px; height: 400px; max-height: 400px; max-width: 100%;"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<a href="./categories" class="btn btn-default button-sm"><i class="far fa-money-bill-alt"></i> {{ $t('firefly.go_to_categories') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DefaultBarOptions from "../charts/DefaultBarOptions";
|
||||
import DataConverter from "../charts/DataConverter";
|
||||
export default {
|
||||
name: "MainCategory",
|
||||
created() {
|
||||
axios.get('./api/v1/chart/category/overview?start=' + window.sessionStart + '&end=' + window.sessionEnd)
|
||||
.then(response => {
|
||||
let chartData = DataConverter.methods.convertChart(response.data);
|
||||
chartData = DataConverter.methods.colorizeLineData(chartData);
|
||||
let stackedBarChartCanvas = $('#mainCategoryChart').get(0).getContext('2d')
|
||||
new Chart(stackedBarChartCanvas, {
|
||||
type: 'bar',
|
||||
data: chartData,
|
||||
options: DefaultBarOptions.methods.getDefaultOptions()
|
||||
});
|
||||
});
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@ -23,15 +23,25 @@
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">{{ $t('firefly.categories') }}</h3>
|
||||
</div>
|
||||
<div class="card-body table-responsive p-0">
|
||||
<!-- body if loading -->
|
||||
<div class="card-body" v-if="loading && !error">
|
||||
<div class="text-center">
|
||||
<i class="fas fa-spinner fa-spin"></i>
|
||||
</div>
|
||||
</div>
|
||||
<!-- body if error -->
|
||||
<div class="card-body" v-if="error">
|
||||
<div class="text-center">
|
||||
<i class="fas fa-exclamation-triangle text-danger"></i>
|
||||
</div>
|
||||
</div>
|
||||
<!-- body if normal -->
|
||||
<div class="card-body table-responsive p-0" v-if="!loading && !error">
|
||||
<table class="table table-sm">
|
||||
<tbody>
|
||||
<tr v-for="category in sortedList">
|
||||
<td style="width:20%;">
|
||||
<a :href="'./categories/show/' + category.id">{{ category.name }}</a>
|
||||
<!--<p>Spent: {{ category.spentPct }}</p>
|
||||
<p>earned: {{ category.earnedPct }}</p>
|
||||
-->
|
||||
</td>
|
||||
<td class="align-middle">
|
||||
<!-- SPENT -->
|
||||
@ -53,7 +63,8 @@
|
||||
<span v-if="category.earnedPct <= 20">
|
||||
{{ Intl.NumberFormat(locale, {style: 'currency', currency: category.currency_code}).format(category.earned) }}
|
||||
</span>
|
||||
<div class="progress-bar progress-bar-striped bg-success" role="progressbar" :aria-valuenow="category.earnedPct" :style="{ width: category.earnedPct + '%'}" aria-valuemin="0"
|
||||
<div class="progress-bar progress-bar-striped bg-success" role="progressbar" :aria-valuenow="category.earnedPct"
|
||||
:style="{ width: category.earnedPct + '%'}" aria-valuemin="0"
|
||||
aria-valuemax="100" title="hello">
|
||||
<span v-if="category.earnedPct > 20">
|
||||
{{ Intl.NumberFormat(locale, {style: 'currency', currency: category.currency_code}).format(category.earned) }}
|
||||
@ -70,12 +81,16 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {createNamespacedHelpers} from "vuex";
|
||||
|
||||
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('dashboard/index')
|
||||
|
||||
export default {
|
||||
name: "MainCategoryList",
|
||||
|
||||
created() {
|
||||
this.locale = localStorage.locale ?? 'en-US';
|
||||
this.getCategories();
|
||||
this.ready = true;
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@ -83,17 +98,40 @@ export default {
|
||||
categories: [],
|
||||
sortedList: [],
|
||||
spent: 0,
|
||||
earned: 0
|
||||
earned: 0,
|
||||
loading: true,
|
||||
error: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'start',
|
||||
'end'
|
||||
]),
|
||||
'datesReady': function () {
|
||||
return null !== this.start && null !== this.end && this.ready;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
datesReady: function (value) {
|
||||
if (true === value) {
|
||||
this.getCategories();
|
||||
}
|
||||
}
|
||||
},
|
||||
methods:
|
||||
{
|
||||
getCategories() {
|
||||
axios.get('./api/v1/categories?start=' + window.sessionStart + '&end=' + window.sessionEnd)
|
||||
let startStr = this.start.toISOString().split('T')[0];
|
||||
let endStr = this.end.toISOString().split('T')[0];
|
||||
axios.get('./api/v1/categories?start=' + startStr + '&end=' + endStr)
|
||||
.then(response => {
|
||||
this.parseCategories(response.data);
|
||||
this.loading = false;
|
||||
}
|
||||
);
|
||||
).catch(error => {
|
||||
this.error = true;
|
||||
});
|
||||
},
|
||||
parseCategories(data) {
|
||||
for (let key in data.data) {
|
||||
|
@ -1,56 +0,0 @@
|
||||
<!--
|
||||
- MainCrebitChart.vue
|
||||
- Copyright (c) 2020 james@firefly-iii.org
|
||||
-
|
||||
- This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
-
|
||||
- This program is free software: you can redistribute it and/or modify
|
||||
- it under the terms of the GNU Affero General Public License as
|
||||
- published by the Free Software Foundation, either version 3 of the
|
||||
- License, or (at your option) any later version.
|
||||
-
|
||||
- This program is distributed in the hope that it will be useful,
|
||||
- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
- GNU Affero General Public License for more details.
|
||||
-
|
||||
- You should have received a copy of the GNU Affero General Public License
|
||||
- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<!-- debit = expense -->
|
||||
<h3 class="card-title">{{ $t('firefly.income') }}</h3>
|
||||
</div>
|
||||
<div class="card-body table-responsive p-0">
|
||||
<transaction-list-small :transactions="this.transactions" />
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<a href="./accounts/revenue" class="btn btn-default button-sm"><i class="far fa-money-bill-alt"></i> {{ $t('firefly.go_to_deposits') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "MainCredit",
|
||||
components: {},
|
||||
data() {
|
||||
return {
|
||||
transactions: []
|
||||
}
|
||||
},
|
||||
created() {
|
||||
axios.get('./api/v1/transactions?type=deposit&limit=10&start=' + window.sessionStart + '&end=' + window.sessionEnd)
|
||||
.then(response => {
|
||||
this.transactions = response.data.data;
|
||||
}
|
||||
);
|
||||
},
|
||||
methods: {
|
||||
},
|
||||
computed: {},
|
||||
}
|
||||
</script>
|
@ -23,7 +23,20 @@
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">{{ $t('firefly.revenue_accounts') }}</h3>
|
||||
</div>
|
||||
<div class="card-body table-responsive p-0">
|
||||
<!-- body if loading -->
|
||||
<div class="card-body" v-if="loading && !error">
|
||||
<div class="text-center">
|
||||
<i class="fas fa-spinner fa-spin"></i>
|
||||
</div>
|
||||
</div>
|
||||
<!-- body if error -->
|
||||
<div class="card-body" v-if="error">
|
||||
<div class="text-center">
|
||||
<i class="fas fa-exclamation-triangle text-danger"></i>
|
||||
</div>
|
||||
</div>
|
||||
<!-- body if normal -->
|
||||
<div class="card-body table-responsive p-0" v-if="!loading && !error">
|
||||
<table class="table table-sm">
|
||||
<tbody>
|
||||
<tr v-for="entry in income">
|
||||
@ -53,28 +66,57 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import {createNamespacedHelpers} from "vuex";
|
||||
|
||||
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('dashboard/index')
|
||||
|
||||
|
||||
export default {
|
||||
name: "MainCreditList",
|
||||
data() {
|
||||
return {
|
||||
locale: 'en-US',
|
||||
income: [],
|
||||
max: 0
|
||||
max: 0,
|
||||
loading: true,
|
||||
error: false
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.locale = localStorage.locale ?? 'en-US';
|
||||
this.getExpenses();
|
||||
this.ready = true;
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'start',
|
||||
'end'
|
||||
]),
|
||||
'datesReady': function () {
|
||||
return null !== this.start && null !== this.end && this.ready;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
datesReady: function (value) {
|
||||
if (true === value) {
|
||||
this.getIncome();
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getExpenses() {
|
||||
axios.get('./api/v1/insight/income/date/basic?start=' + window.sessionStart + '&end=' + window.sessionEnd)
|
||||
getIncome() {
|
||||
let startStr = this.start.toISOString().split('T')[0];
|
||||
let endStr = this.end.toISOString().split('T')[0];
|
||||
axios.get('./api/v1/insight/income/date/basic?start=' + startStr + '&end=' + endStr)
|
||||
.then(response => {
|
||||
// do something with response.
|
||||
this.parseExpenses(response.data);
|
||||
});
|
||||
this.parseIncome(response.data);
|
||||
this.loading = false;
|
||||
}).catch(error => {
|
||||
this.error = true
|
||||
});
|
||||
},
|
||||
parseExpenses(data) {
|
||||
parseIncome(data) {
|
||||
for (let mainKey in data) {
|
||||
if (data.hasOwnProperty(mainKey) && /^0$|^[1-9]\d*$/.test(mainKey) && mainKey <= 4294967294) {
|
||||
// contains currency info and entries.
|
||||
@ -83,12 +125,11 @@ export default {
|
||||
this.max = data[mainKey].difference_float;
|
||||
current.pct = 100;
|
||||
}
|
||||
if(0 !== parseInt(mainKey)) {
|
||||
if (0 !== parseInt(mainKey)) {
|
||||
// calc percentage:
|
||||
current.pct = (data[mainKey].difference_float / this.max) * 100;
|
||||
}
|
||||
this.income.push(current);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,62 +0,0 @@
|
||||
<!--
|
||||
- MainDebit.vue
|
||||
- Copyright (c) 2020 james@firefly-iii.org
|
||||
-
|
||||
- This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
-
|
||||
- This program is free software: you can redistribute it and/or modify
|
||||
- it under the terms of the GNU Affero General Public License as
|
||||
- published by the Free Software Foundation, either version 3 of the
|
||||
- License, or (at your option) any later version.
|
||||
-
|
||||
- This program is distributed in the hope that it will be useful,
|
||||
- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
- GNU Affero General Public License for more details.
|
||||
-
|
||||
- You should have received a copy of the GNU Affero General Public License
|
||||
- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<!-- debit = expense -->
|
||||
<h3 class="card-title">{{ $t('firefly.expense_accounts') }}</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div>
|
||||
<canvas id="mainDebitChart" style="min-height: 400px; height: 400px; max-height: 400px; max-width: 100%;"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<a href="./transactions/withdrawals" class="btn btn-default button-sm"><i class="far fa-money-bill-alt"></i> {{ $t('firefly.go_to_expenses') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DefaultBarOptions from "../charts/DefaultBarOptions";
|
||||
import DataConverter from "../charts/DataConverter";
|
||||
|
||||
export default {
|
||||
name: "MainDebit",
|
||||
created() {
|
||||
axios.get('./api/v1/chart/account/expense?start=' + window.sessionStart + '&end=' + window.sessionEnd)
|
||||
.then(response => {
|
||||
let chartData = DataConverter.methods.convertChart(response.data);
|
||||
chartData = DataConverter.methods.colorizeLineData(chartData);
|
||||
let stackedBarChartCanvas = $('#mainDebitChart').get(0).getContext('2d')
|
||||
new Chart(stackedBarChartCanvas, {
|
||||
type: 'bar',
|
||||
data: chartData,
|
||||
options: DefaultBarOptions.methods.getDefaultOptions()
|
||||
});
|
||||
});
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@ -23,7 +23,20 @@
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">{{ $t('firefly.expense_accounts') }}</h3>
|
||||
</div>
|
||||
<div class="card-body table-responsive p-0">
|
||||
<!-- body if loading -->
|
||||
<div class="card-body" v-if="loading && !error">
|
||||
<div class="text-center">
|
||||
<i class="fas fa-spinner fa-spin"></i>
|
||||
</div>
|
||||
</div>
|
||||
<!-- body if error -->
|
||||
<div class="card-body" v-if="error">
|
||||
<div class="text-center">
|
||||
<i class="fas fa-exclamation-triangle text-danger"></i>
|
||||
</div>
|
||||
</div>
|
||||
<!-- body if normal -->
|
||||
<div class="card-body table-responsive p-0" v-if="!loading && !error">
|
||||
<table class="table table-sm">
|
||||
<tbody>
|
||||
<tr v-for="entry in expenses">
|
||||
@ -53,26 +66,55 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import {createNamespacedHelpers} from "vuex";
|
||||
|
||||
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('dashboard/index')
|
||||
|
||||
|
||||
export default {
|
||||
name: "MainDebitList",
|
||||
data() {
|
||||
return {
|
||||
locale: 'en-US',
|
||||
expenses: [],
|
||||
max: 0
|
||||
max: 0,
|
||||
loading: true,
|
||||
error: false
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.locale = localStorage.locale ?? 'en-US';
|
||||
this.getExpenses();
|
||||
this.ready = true;
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'start',
|
||||
'end'
|
||||
]),
|
||||
'datesReady': function () {
|
||||
return null !== this.start && null !== this.end && this.ready;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
datesReady: function (value) {
|
||||
if (true === value) {
|
||||
this.getExpenses();
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getExpenses() {
|
||||
axios.get('./api/v1/insight/expense/date/basic?start=' + window.sessionStart + '&end=' + window.sessionEnd)
|
||||
let startStr = this.start.toISOString().split('T')[0];
|
||||
let endStr = this.end.toISOString().split('T')[0];
|
||||
axios.get('./api/v1/insight/expense/date/basic?start=' + startStr + '&end=' + endStr)
|
||||
.then(response => {
|
||||
// do something with response.
|
||||
this.parseExpenses(response.data);
|
||||
});
|
||||
this.loading = false
|
||||
}).catch(error => {
|
||||
this.error = true
|
||||
});
|
||||
},
|
||||
parseExpenses(data) {
|
||||
for (let mainKey in data) {
|
||||
@ -83,7 +125,7 @@ export default {
|
||||
this.max = data[mainKey].difference_float;
|
||||
current.pct = 100;
|
||||
}
|
||||
if(0 !== parseInt(mainKey)) {
|
||||
if (0 !== parseInt(mainKey)) {
|
||||
// calc percentage:
|
||||
current.pct = (data[mainKey].difference_float / this.max) * 100;
|
||||
}
|
||||
|
@ -23,7 +23,21 @@
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">{{ $t('firefly.piggy_banks') }}</h3>
|
||||
</div>
|
||||
<div class="card-body table-responsive p-0">
|
||||
|
||||
<!-- body if loading -->
|
||||
<div class="card-body" v-if="loading && !error">
|
||||
<div class="text-center">
|
||||
<i class="fas fa-spinner fa-spin"></i>
|
||||
</div>
|
||||
</div>
|
||||
<!-- body if error -->
|
||||
<div class="card-body" v-if="error">
|
||||
<div class="text-center">
|
||||
<i class="fas fa-exclamation-triangle text-danger"></i>
|
||||
</div>
|
||||
</div>
|
||||
<!-- body if normal -->
|
||||
<div class="card-body table-responsive p-0" v-if="!loading && !error">
|
||||
<table class="table table-striped">
|
||||
<caption style="display:none;">{{ $t('firefly.piggy_banks') }}</caption>
|
||||
<thead>
|
||||
@ -45,7 +59,8 @@
|
||||
<div class="progress-group">
|
||||
<div class="progress progress-sm">
|
||||
<div class="progress-bar progress-bar-striped primary" v-if="piggy.attributes.pct < 100" :style="{'width': piggy.attributes.pct + '%'}"></div>
|
||||
<div class="progress-bar progress-bar-striped bg-success" v-if="100 === piggy.attributes.pct" :style="{'width': piggy.attributes.pct + '%'}"></div>
|
||||
<div class="progress-bar progress-bar-striped bg-success" v-if="100 === piggy.attributes.pct"
|
||||
:style="{'width': piggy.attributes.pct + '%'}"></div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="text-success">
|
||||
@ -74,17 +89,24 @@
|
||||
<script>
|
||||
export default {
|
||||
name: "MainPiggyList",
|
||||
data() {
|
||||
return {
|
||||
piggy_banks: [],
|
||||
loading: true,
|
||||
error: false,
|
||||
locale: 'en-US'
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.locale = localStorage.locale ?? 'en-US';
|
||||
axios.get('./api/v1/piggy_banks')
|
||||
.then(response => {
|
||||
this.loadPiggyBanks(response.data.data);
|
||||
this.loading = false;
|
||||
}
|
||||
);
|
||||
},
|
||||
computed: {
|
||||
locale() {
|
||||
return this.$store.getters.locale;
|
||||
}
|
||||
).catch(error => {
|
||||
this.error = true
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
loadPiggyBanks(data) {
|
||||
@ -101,11 +123,6 @@ export default {
|
||||
return b.attributes.pct - a.attributes.pct;
|
||||
});
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
piggy_banks: []
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -25,7 +25,9 @@
|
||||
<span class="info-box-icon"><i class="far fa-bookmark text-info"></i></span>
|
||||
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-text">{{ $t("firefly.balance") }}</span>
|
||||
<span class="info-box-text" v-if="!loading && !error">{{ $t("firefly.balance") }}</span>
|
||||
<span class="info-box-text" v-if="loading && !error"><i class="fas fa-spinner fa-spin"></i></span>
|
||||
<span class="info-box-text" v-if="error"><i class="fas fa-exclamation-triangle text-danger"></i></span>
|
||||
<!-- balance in preferred currency -->
|
||||
<span class="info-box-number" v-for="balance in prefCurrencyBalances" :title="balance.sub_title">{{ balance.value_parsed }}</span>
|
||||
|
||||
@ -48,8 +50,9 @@
|
||||
<span class="info-box-icon"><i class="far fa-calendar-alt text-teal"></i></span>
|
||||
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-text">{{ $t('firefly.bills_to_pay') }}</span>
|
||||
|
||||
<span class="info-box-text" v-if="!loading && !error">{{ $t('firefly.bills_to_pay') }}</span>
|
||||
<span class="info-box-text" v-if="loading && !error"><i class="fas fa-spinner fa-spin"></i></span>
|
||||
<span class="info-box-text" v-if="error"><i class="fas fa-exclamation-triangle text-danger"></i></span>
|
||||
<!-- bills unpaid, in preferred currency. -->
|
||||
<span class="info-box-number" v-for="balance in prefBillsUnpaid">{{ balance.value_parsed }}</span>
|
||||
|
||||
@ -72,8 +75,9 @@
|
||||
<span class="info-box-icon"><i class="fas fa-money-bill text-success"></i></span>
|
||||
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-text">{{ $t('firefly.left_to_spend') }}</span>
|
||||
|
||||
<span class="info-box-text" v-if="!loading && !error">{{ $t('firefly.left_to_spend') }}</span>
|
||||
<span class="info-box-text" v-if="loading && !error"><i class="fas fa-spinner fa-spin"></i></span>
|
||||
<span class="info-box-text" v-if="error"><i class="fas fa-exclamation-triangle text-danger"></i></span>
|
||||
<!-- left to spend in preferred currency -->
|
||||
<span class="info-box-number" v-for="left in prefLeftToSpend" :title="left.sub_title">{{ left.value_parsed }}</span>
|
||||
|
||||
@ -97,7 +101,9 @@
|
||||
<span class="info-box-icon"><i class="fas fa-money-bill text-success"></i></span>
|
||||
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-text"><span>{{ $t('firefly.net_worth') }}</span></span>
|
||||
<span class="info-box-text" v-if="!loading && !error">{{ $t('firefly.net_worth') }}</span>
|
||||
<span class="info-box-text" v-if="loading && !error"><i class="fas fa-spinner fa-spin"></i></span>
|
||||
<span class="info-box-text" v-if="error"><i class="fas fa-exclamation-triangle text-danger"></i></span>
|
||||
<span class="info-box-number" v-for="nw in prefNetWorth" :title="nw.sub_title">{{ nw.value_parsed }}</span>
|
||||
|
||||
<div class="progress bg-success">
|
||||
@ -117,6 +123,9 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {createNamespacedHelpers} from "vuex";
|
||||
|
||||
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('dashboard/index')
|
||||
export default {
|
||||
name: "TopBoxes",
|
||||
props: {},
|
||||
@ -128,9 +137,19 @@ export default {
|
||||
billsUnpaid: [],
|
||||
leftToSpend: [],
|
||||
netWorth: [],
|
||||
loading: true,
|
||||
error: false,
|
||||
ready: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'start',
|
||||
'end'
|
||||
]),
|
||||
'datesReady': function () {
|
||||
return null !== this.start && null !== this.end && this.ready;
|
||||
},
|
||||
|
||||
// contains only balances with preferred currency.
|
||||
prefCurrencyBalances: function () {
|
||||
@ -170,8 +189,15 @@ export default {
|
||||
return this.$store.getters.currencyId;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
datesReady: function (value) {
|
||||
if (true === value) {
|
||||
this.prepareComponent();
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.prepareComponent();
|
||||
this.ready = true;
|
||||
},
|
||||
methods: {
|
||||
filterOnCurrency(array) {
|
||||
@ -205,11 +231,16 @@ export default {
|
||||
* Prepare the component.
|
||||
*/
|
||||
prepareComponent() {
|
||||
axios.get('./api/v1/summary/basic?start=' + window.sessionStart + '&end=' + window.sessionEnd)
|
||||
let startStr = this.start.toISOString().split('T')[0];
|
||||
let endStr = this.end.toISOString().split('T')[0];
|
||||
axios.get('./api/v1/summary/basic?start=' + startStr + '&end=' + endStr)
|
||||
.then(response => {
|
||||
this.summary = response.data;
|
||||
this.buildComponent();
|
||||
});
|
||||
this.loading = false
|
||||
}).catch(error => {
|
||||
this.error = true
|
||||
});
|
||||
},
|
||||
buildComponent() {
|
||||
this.getBalanceEntries();
|
||||
|
@ -76,6 +76,9 @@
|
||||
"first_split_overrules_source": "The first split may overrule the source account",
|
||||
"first_split_overrules_destination": "The first split may overrule the destination account",
|
||||
"transaction_stored_link": "<a href=\"transactions\/show\/{ID}\">\u0422\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u044f #{ID}(\"{title}\")<\/a> \u0431\u0435\u0448\u0435 \u0437\u0430\u043f\u0438\u0441\u0430\u043d\u0430.",
|
||||
"custom_period": "Custom period",
|
||||
"reset_to_current": "Reset to current period",
|
||||
"select_period": "Select a period",
|
||||
"other_budgets": "\u0412\u0440\u0435\u043c\u0435\u0432\u043e \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u043b\u0438\u0437\u0438\u0440\u0430\u043d\u0438 \u0431\u044e\u0434\u0436\u0435\u0442\u0438",
|
||||
"journal_links": "\u0412\u0440\u044a\u0437\u043a\u0438 \u043d\u0430 \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u044f",
|
||||
"go_to_withdrawals": "\u0412\u0438\u0436\u0442\u0435 \u0442\u0435\u0433\u043b\u0435\u043d\u0438\u044f\u0442\u0430 \u0441\u0438",
|
||||
|
@ -76,6 +76,9 @@
|
||||
"first_split_overrules_source": "The first split may overrule the source account",
|
||||
"first_split_overrules_destination": "The first split may overrule the destination account",
|
||||
"transaction_stored_link": "<a href=\"transactions\/show\/{ID}\">Transaction #{ID} (\"{title}\")<\/a> has been stored.",
|
||||
"custom_period": "Custom period",
|
||||
"reset_to_current": "Reset to current period",
|
||||
"select_period": "Select a period",
|
||||
"other_budgets": "Custom timed budgets",
|
||||
"journal_links": "Transaction links",
|
||||
"go_to_withdrawals": "Go to your withdrawals",
|
||||
|
@ -76,6 +76,9 @@
|
||||
"first_split_overrules_source": "The first split may overrule the source account",
|
||||
"first_split_overrules_destination": "The first split may overrule the destination account",
|
||||
"transaction_stored_link": "<a href=\"transactions\/show\/{ID}\">Buchung #{ID} (\"{title}\")<\/a> wurde gespeichert.",
|
||||
"custom_period": "Custom period",
|
||||
"reset_to_current": "Reset to current period",
|
||||
"select_period": "Select a period",
|
||||
"other_budgets": "Zeitlich befristete Budgets",
|
||||
"journal_links": "Buchungsverkn\u00fcpfungen",
|
||||
"go_to_withdrawals": "Ausgaben anzeigen",
|
||||
|
@ -76,6 +76,9 @@
|
||||
"first_split_overrules_source": "The first split may overrule the source account",
|
||||
"first_split_overrules_destination": "The first split may overrule the destination account",
|
||||
"transaction_stored_link": "<a href=\"transactions\/show\/{ID}\">\u0397 \u03c3\u03c5\u03bd\u03b1\u03bb\u03bb\u03b1\u03b3\u03ae #{ID} (\"{title}\")<\/a> \u03ad\u03c7\u03b5\u03b9 \u03b1\u03c0\u03bf\u03b8\u03b7\u03ba\u03b5\u03c5\u03c4\u03b5\u03af.",
|
||||
"custom_period": "Custom period",
|
||||
"reset_to_current": "Reset to current period",
|
||||
"select_period": "Select a period",
|
||||
"other_budgets": "\u03a0\u03c1\u03bf\u03cb\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03bc\u03bf\u03af \u03bc\u03b5 \u03c7\u03c1\u03bf\u03bd\u03b9\u03ba\u03ae \u03c0\u03c1\u03bf\u03c3\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae",
|
||||
"journal_links": "\u03a3\u03c5\u03bd\u03b4\u03ad\u03c3\u03b5\u03b9\u03c2 \u03c3\u03c5\u03bd\u03b1\u03bb\u03bb\u03b1\u03b3\u03ce\u03bd",
|
||||
"go_to_withdrawals": "\u03a0\u03b7\u03b3\u03b1\u03af\u03bd\u03b5\u03c4\u03b5 \u03c3\u03c4\u03b9\u03c2 \u03b1\u03bd\u03b1\u03bb\u03ae\u03c8\u03b5\u03b9\u03c2 \u03c3\u03b1\u03c2",
|
||||
|
@ -76,6 +76,9 @@
|
||||
"first_split_overrules_source": "The first split may overrule the source account",
|
||||
"first_split_overrules_destination": "The first split may overrule the destination account",
|
||||
"transaction_stored_link": "<a href=\"transactions\/show\/{ID}\">Transaction #{ID} (\"{title}\")<\/a> has been stored.",
|
||||
"custom_period": "Custom period",
|
||||
"reset_to_current": "Reset to current period",
|
||||
"select_period": "Select a period",
|
||||
"other_budgets": "Custom timed budgets",
|
||||
"journal_links": "Transaction links",
|
||||
"go_to_withdrawals": "Go to your withdrawals",
|
||||
|
@ -76,6 +76,9 @@
|
||||
"first_split_overrules_source": "The first split may overrule the source account",
|
||||
"first_split_overrules_destination": "The first split may overrule the destination account",
|
||||
"transaction_stored_link": "<a href=\"transactions\/show\/{ID}\">Transaction #{ID} (\"{title}\")<\/a> has been stored.",
|
||||
"custom_period": "Custom period",
|
||||
"reset_to_current": "Reset to current period",
|
||||
"select_period": "Select a period",
|
||||
"other_budgets": "Custom timed budgets",
|
||||
"journal_links": "Transaction links",
|
||||
"go_to_withdrawals": "Go to your withdrawals",
|
||||
|
@ -76,6 +76,9 @@
|
||||
"first_split_overrules_source": "The first split may overrule the source account",
|
||||
"first_split_overrules_destination": "The first split may overrule the destination account",
|
||||
"transaction_stored_link": "<a href=\"transactions\/show\/{ID}\">La transacci\u00f3n #{ID} (\"{title}\")<\/a> ha sido almacenada.",
|
||||
"custom_period": "Custom period",
|
||||
"reset_to_current": "Reset to current period",
|
||||
"select_period": "Select a period",
|
||||
"other_budgets": "Presupuestos de tiempo personalizado",
|
||||
"journal_links": "Enlaces de transacciones",
|
||||
"go_to_withdrawals": "Ir a tus retiradas",
|
||||
|
@ -76,6 +76,9 @@
|
||||
"first_split_overrules_source": "The first split may overrule the source account",
|
||||
"first_split_overrules_destination": "The first split may overrule the destination account",
|
||||
"transaction_stored_link": "<a href=\"transactions\/show\/{ID}\">Transaction #{ID} (\"{title}\")<\/a> has been stored.",
|
||||
"custom_period": "Custom period",
|
||||
"reset_to_current": "Reset to current period",
|
||||
"select_period": "Select a period",
|
||||
"other_budgets": "Custom timed budgets",
|
||||
"journal_links": "Tapahtuman linkit",
|
||||
"go_to_withdrawals": "Go to your withdrawals",
|
||||
|
@ -76,6 +76,9 @@
|
||||
"first_split_overrules_source": "The first split may overrule the source account",
|
||||
"first_split_overrules_destination": "The first split may overrule the destination account",
|
||||
"transaction_stored_link": "<a href=\"transactions\/show\/{ID}\">L'op\u00e9ration n\u00b0{ID} (\"{title}\")<\/a> a \u00e9t\u00e9 enregistr\u00e9e.",
|
||||
"custom_period": "Custom period",
|
||||
"reset_to_current": "Reset to current period",
|
||||
"select_period": "Select a period",
|
||||
"other_budgets": "Budgets \u00e0 p\u00e9riode personnalis\u00e9e",
|
||||
"journal_links": "Liens d'op\u00e9ration",
|
||||
"go_to_withdrawals": "Acc\u00e9der \u00e0 vos retraits",
|
||||
|
@ -76,6 +76,9 @@
|
||||
"first_split_overrules_source": "The first split may overrule the source account",
|
||||
"first_split_overrules_destination": "The first split may overrule the destination account",
|
||||
"transaction_stored_link": "<a href=\"transactions\/show\/{ID}\">Transaction #{ID} (\"{title}\")<\/a> mentve.",
|
||||
"custom_period": "Custom period",
|
||||
"reset_to_current": "Reset to current period",
|
||||
"select_period": "Select a period",
|
||||
"other_budgets": "Custom timed budgets",
|
||||
"journal_links": "Tranzakci\u00f3 \u00f6sszekapcsol\u00e1sok",
|
||||
"go_to_withdrawals": "Ugr\u00e1s a k\u00f6lts\u00e9gekhez",
|
||||
|
@ -76,6 +76,9 @@
|
||||
"first_split_overrules_source": "The first split may overrule the source account",
|
||||
"first_split_overrules_destination": "The first split may overrule the destination account",
|
||||
"transaction_stored_link": "La <a href=\"transactions\/show\/{ID}\">transazione #{ID} (\"{title}\")<\/a> \u00e8 stata salvata.",
|
||||
"custom_period": "Custom period",
|
||||
"reset_to_current": "Reset to current period",
|
||||
"select_period": "Select a period",
|
||||
"other_budgets": "Budget a periodi personalizzati",
|
||||
"journal_links": "Collegamenti della transazione",
|
||||
"go_to_withdrawals": "Vai ai tuoi prelievi",
|
||||
|
@ -76,6 +76,9 @@
|
||||
"first_split_overrules_source": "The first split may overrule the source account",
|
||||
"first_split_overrules_destination": "The first split may overrule the destination account",
|
||||
"transaction_stored_link": "<a href=\"transactions\/show\/{ID}\">Transaction #{ID} (\"{title}\")<\/a> has been stored.",
|
||||
"custom_period": "Custom period",
|
||||
"reset_to_current": "Reset to current period",
|
||||
"select_period": "Select a period",
|
||||
"other_budgets": "Custom timed budgets",
|
||||
"journal_links": "Transaksjonskoblinger",
|
||||
"go_to_withdrawals": "Go to your withdrawals",
|
||||
|
@ -76,6 +76,9 @@
|
||||
"first_split_overrules_source": "The first split may overrule the source account",
|
||||
"first_split_overrules_destination": "The first split may overrule the destination account",
|
||||
"transaction_stored_link": "<a href=\"transactions\/show\/{ID}\">Transactie #{ID} (\"{title}\")<\/a> is opgeslagen.",
|
||||
"custom_period": "Custom period",
|
||||
"reset_to_current": "Reset to current period",
|
||||
"select_period": "Select a period",
|
||||
"other_budgets": "Aangepaste budgetten",
|
||||
"journal_links": "Transactiekoppelingen",
|
||||
"go_to_withdrawals": "Ga naar je uitgaven",
|
||||
|
@ -76,6 +76,9 @@
|
||||
"first_split_overrules_source": "The first split may overrule the source account",
|
||||
"first_split_overrules_destination": "The first split may overrule the destination account",
|
||||
"transaction_stored_link": "<a href=\"transactions\/show\/{ID}\">Transakcja #{ID} (\"{title}\")<\/a> zosta\u0142a zapisana.",
|
||||
"custom_period": "Custom period",
|
||||
"reset_to_current": "Reset to current period",
|
||||
"select_period": "Select a period",
|
||||
"other_budgets": "Bud\u017cety niestandardowe",
|
||||
"journal_links": "Powi\u0105zane transakcje",
|
||||
"go_to_withdrawals": "Przejd\u017a do swoich wydatk\u00f3w",
|
||||
|
@ -76,6 +76,9 @@
|
||||
"first_split_overrules_source": "The first split may overrule the source account",
|
||||
"first_split_overrules_destination": "The first split may overrule the destination account",
|
||||
"transaction_stored_link": "<a href=\"transactions\/show\/{ID}\">Transa\u00e7\u00e3o #{ID} (\"{title}\")<\/a> foi salva.",
|
||||
"custom_period": "Custom period",
|
||||
"reset_to_current": "Reset to current period",
|
||||
"select_period": "Select a period",
|
||||
"other_budgets": "Custom timed budgets",
|
||||
"journal_links": "Transa\u00e7\u00f5es ligadas",
|
||||
"go_to_withdrawals": "V\u00e1 para seus saques",
|
||||
|
@ -76,6 +76,9 @@
|
||||
"first_split_overrules_source": "The first split may overrule the source account",
|
||||
"first_split_overrules_destination": "The first split may overrule the destination account",
|
||||
"transaction_stored_link": "<a href=\"transactions\/show\/{ID}\">Transa\u00e7\u00e3o #{ID} (\"{title}\")<\/a> foi guardada.",
|
||||
"custom_period": "Custom period",
|
||||
"reset_to_current": "Reset to current period",
|
||||
"select_period": "Select a period",
|
||||
"other_budgets": "Or\u00e7amentos de tempo personalizado",
|
||||
"journal_links": "Liga\u00e7\u00f5es de transac\u00e7\u00e3o",
|
||||
"go_to_withdrawals": "Ir para os seus levantamentos",
|
||||
|
@ -76,6 +76,9 @@
|
||||
"first_split_overrules_source": "The first split may overrule the source account",
|
||||
"first_split_overrules_destination": "The first split may overrule the destination account",
|
||||
"transaction_stored_link": "<a href=\"transactions\/show\/{ID}\">Tranzac\u021bia #{ID} (\"{title}\")<\/a> a fost stocat\u0103.",
|
||||
"custom_period": "Custom period",
|
||||
"reset_to_current": "Reset to current period",
|
||||
"select_period": "Select a period",
|
||||
"other_budgets": "Custom timed budgets",
|
||||
"journal_links": "Link-uri de tranzac\u021bii",
|
||||
"go_to_withdrawals": "Go to your withdrawals",
|
||||
|
@ -76,6 +76,9 @@
|
||||
"first_split_overrules_source": "The first split may overrule the source account",
|
||||
"first_split_overrules_destination": "The first split may overrule the destination account",
|
||||
"transaction_stored_link": "<a href=\"transactions\/show\/{ID}\">\u0422\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u044f #{ID} (\"{title}\")<\/a> \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0430.",
|
||||
"custom_period": "Custom period",
|
||||
"reset_to_current": "Reset to current period",
|
||||
"select_period": "Select a period",
|
||||
"other_budgets": "\u0411\u044e\u0434\u0436\u0435\u0442\u044b \u043d\u0430 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u043b\u044c\u043d\u044b\u0439 \u043e\u0442\u0440\u0435\u0437\u043e\u043a \u0432\u0440\u0435\u043c\u0435\u043d\u0438",
|
||||
"journal_links": "\u0421\u0432\u044f\u0437\u0438 \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u0438",
|
||||
"go_to_withdrawals": "\u041f\u0435\u0440\u0435\u0439\u0442\u0438 \u043a \u0432\u0430\u0448\u0438\u043c \u0440\u0430\u0441\u0445\u043e\u0434\u0430\u043c",
|
||||
|
@ -76,6 +76,9 @@
|
||||
"first_split_overrules_source": "The first split may overrule the source account",
|
||||
"first_split_overrules_destination": "The first split may overrule the destination account",
|
||||
"transaction_stored_link": "<a href=\"transactions\/show\/{ID}\">Transakcia #{ID} (\"{title}\")<\/a> bola ulo\u017een\u00e1.",
|
||||
"custom_period": "Custom period",
|
||||
"reset_to_current": "Reset to current period",
|
||||
"select_period": "Select a period",
|
||||
"other_budgets": "\u0160pecifick\u00e9 \u010dasovan\u00e9 rozpo\u010dty",
|
||||
"journal_links": "Prepojenia transakcie",
|
||||
"go_to_withdrawals": "Zobrazi\u0165 v\u00fdbery",
|
||||
|
@ -76,6 +76,9 @@
|
||||
"first_split_overrules_source": "The first split may overrule the source account",
|
||||
"first_split_overrules_destination": "The first split may overrule the destination account",
|
||||
"transaction_stored_link": "<a href=\"transactions\/show\/{ID}\">Transaktion #{ID} (\"{title}\")<\/a> sparades.",
|
||||
"custom_period": "Custom period",
|
||||
"reset_to_current": "Reset to current period",
|
||||
"select_period": "Select a period",
|
||||
"other_budgets": "Anpassade tidsinst\u00e4llda budgetar",
|
||||
"journal_links": "Transaktionsl\u00e4nkar",
|
||||
"go_to_withdrawals": "G\u00e5 till dina uttag",
|
||||
|
@ -76,6 +76,9 @@
|
||||
"first_split_overrules_source": "The first split may overrule the source account",
|
||||
"first_split_overrules_destination": "The first split may overrule the destination account",
|
||||
"transaction_stored_link": "<a href=\"transactions\/show\/{ID}\">Giao d\u1ecbch #{ID} (\"{title}\")<\/a> \u0111\u00e3 \u0111\u01b0\u1ee3c l\u01b0u tr\u1eef.",
|
||||
"custom_period": "Custom period",
|
||||
"reset_to_current": "Reset to current period",
|
||||
"select_period": "Select a period",
|
||||
"other_budgets": "Custom timed budgets",
|
||||
"journal_links": "Li\u00ean k\u1ebft giao d\u1ecbch",
|
||||
"go_to_withdrawals": "Go to your withdrawals",
|
||||
|
@ -76,6 +76,9 @@
|
||||
"first_split_overrules_source": "The first split may overrule the source account",
|
||||
"first_split_overrules_destination": "The first split may overrule the destination account",
|
||||
"transaction_stored_link": "<a href=\"transactions\/show\/{ID}\">Transaction #{ID} (\"{title}\")<\/a> has been stored.",
|
||||
"custom_period": "Custom period",
|
||||
"reset_to_current": "Reset to current period",
|
||||
"select_period": "Select a period",
|
||||
"other_budgets": "Custom timed budgets",
|
||||
"journal_links": "\u4ea4\u6613\u8fde\u7ed3",
|
||||
"go_to_withdrawals": "Go to your withdrawals",
|
||||
|
@ -76,6 +76,9 @@
|
||||
"first_split_overrules_source": "The first split may overrule the source account",
|
||||
"first_split_overrules_destination": "The first split may overrule the destination account",
|
||||
"transaction_stored_link": "<a href=\"transactions\/show\/{ID}\">Transaction #{ID} (\"{title}\")<\/a> has been stored.",
|
||||
"custom_period": "Custom period",
|
||||
"reset_to_current": "Reset to current period",
|
||||
"select_period": "Select a period",
|
||||
"other_budgets": "Custom timed budgets",
|
||||
"journal_links": "\u4ea4\u6613\u9023\u7d50",
|
||||
"go_to_withdrawals": "Go to your withdrawals",
|
||||
|
Loading…
Reference in New Issue
Block a user