Update vue

This commit is contained in:
James Cole 2020-11-12 05:58:06 +01:00
parent 5e87d7b570
commit 8f42c6e4eb
No known key found for this signature in database
GPG Key ID: B5669F9493CDE38D
7 changed files with 581 additions and 299 deletions

View File

@ -25,120 +25,126 @@
<script>
export default {
name: "DefaultLineOptions",
data() {
return {}
export default {
name: "DefaultLineOptions",
data() {
return {}
},
methods: {
/**
* Takes a string phrase and breaks it into separate phrases no bigger than 'maxwidth', breaks are made at complete words.
* https://stackoverflow.com/questions/21409717/chart-js-and-long-labels
*
* @param str
* @param maxwidth
* @returns {Array}
*/
formatLabel(str, maxwidth) {
var sections = [];
str = String(str);
var words = str.split(" ");
var temp = "";
words.forEach(function (item, index) {
if (temp.length > 0) {
var concat = temp + ' ' + item;
if (concat.length > maxwidth) {
sections.push(temp);
temp = "";
} else {
if (index === (words.length - 1)) {
sections.push(concat);
return;
} else {
temp = concat;
return;
}
}
}
if (index === (words.length - 1)) {
sections.push(item);
return;
}
if (item.length < maxwidth) {
temp = item;
} else {
sections.push(item);
}
});
return sections;
},
getDefaultOptions() {
var self = this;
return {
legend: {
display: false,
},
methods: {
/**
* Takes a string phrase and breaks it into separate phrases no bigger than 'maxwidth', breaks are made at complete words.
* https://stackoverflow.com/questions/21409717/chart-js-and-long-labels
*
* @param str
* @param maxwidth
* @returns {Array}
*/
formatLabel(str, maxwidth) {
var sections = [];
str = String(str);
var words = str.split(" ");
var temp = "";
words.forEach(function (item, index) {
if (temp.length > 0) {
var concat = temp + ' ' + item;
if (concat.length > maxwidth) {
sections.push(temp);
temp = "";
} else {
if (index === (words.length - 1)) {
sections.push(concat);
return;
} else {
temp = concat;
return;
}
}
}
if (index === (words.length - 1)) {
sections.push(item);
return;
}
if (item.length < maxwidth) {
temp = item;
} else {
sections.push(item);
}
});
return sections;
},
getDefaultOptions() {
return {
legend: {
display: false,
},
animation: {
duration: 0,
},
responsive: true,
maintainAspectRatio: false,
elements: {
line: {
cubicInterpolationMode: 'monotone'
}
},
scales: {
xAxes: [
{
gridLines: {
display: false
},
ticks: {
// break ticks when too long.
callback: function (value, index, values) {
//return this.formatLabel(value, 20);
return value;
}
}
}
],
yAxes: [{
display: true,
ticks: {
callback: function (tickValue) {
"use strict";
let currencyCode = this.chart.data.datasets[0].currency_code ? this.chart.data.datasets[0].currency_code : 'EUR';
return new Intl.NumberFormat(window.localeValue, {style: 'currency', currency: currencyCode}).format(tickValue);
},
beginAtZero: true
}
}]
},
tooltips: {
mode: 'label',
callbacks: {
label: function (tooltipItem, data) {
"use strict";
let currencyCode = data.datasets[tooltipItem.datasetIndex].currency_code ? data.datasets[tooltipItem.datasetIndex].currency_code : 'EUR';
let nrString =
new Intl.NumberFormat(window.localeValue, {style: 'currency', currency: currencyCode}).format(tooltipItem.yLabel)
return data.datasets[tooltipItem.datasetIndex].label + ': ' + nrString;
}
}
}
};
animation: {
duration: 0,
},
responsive: true,
maintainAspectRatio: false,
elements: {
line: {
cubicInterpolationMode: 'monotone'
}
},
scales: {
xAxes: [
{
gridLines: {
display: false
},
ticks: {
// break ticks when too long
callback: function (value, index, values) {
// date format
let dateObj = new Date(value);
let options = { year: 'numeric', month: 'long', day: 'numeric' };
let str = new Intl.DateTimeFormat(window.localeValue, options).format(dateObj);
//console.log();
//return self.formatLabel(value, 20);
return self.formatLabel(str, 20);
}
}
}
],
yAxes: [{
display: true,
ticks: {
callback: function (tickValue) {
"use strict";
let currencyCode = this.chart.data.datasets[0].currency_code ? this.chart.data.datasets[0].currency_code : 'EUR';
return new Intl.NumberFormat(window.localeValue, {style: 'currency', currency: currencyCode}).format(tickValue);
},
beginAtZero: true
}
}]
},
tooltips: {
mode: 'index',
callbacks: {
label: function (tooltipItem, data) {
"use strict";
let currencyCode = data.datasets[tooltipItem.datasetIndex].currency_code ? data.datasets[tooltipItem.datasetIndex].currency_code : 'EUR';
let nrString =
new Intl.NumberFormat(window.localeValue, {style: 'currency', currency: currencyCode}).format(tooltipItem.yLabel)
return data.datasets[tooltipItem.datasetIndex].label + ': ' + nrString;
}
}
}
};
}
}
}
</script>
<style scoped>

View File

@ -19,49 +19,78 @@
-->
<template>
<div>
<top-boxes/>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<main-account />
</div>
</div>
<main-account-list/>
<!--
<div class="row">
<div class="col-lg-6 col-md-6 col-sm-12 col-xs-12">
<main-budget/>
</div>
<div class="col-lg-6 col-md-6 col-sm-12 col-xs-12">
<main-category />
</div>
</div>
<div class="row">
<div class="col-lg-8 col-md-8 col-sm-12 col-xs-12">
<main-debit/>
</div>
<div class="col-lg-4 col-md-4 col-sm-12 col-xs-12">
<main-credit/>
</div>
</div>
<div class="row">
<div class="col-lg-6 col-md-6 col-sm-12 col-xs-12">
<main-piggy-list/>
</div>
<div class="col-lg-6 col-md-6 col-sm-12 col-xs-12">
<main-bills-list/>
</div>
</div>
-->
<div>
<top-boxes/>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<main-account/>
</div>
</div>
<main-account-list/>
<!--
<div class="row">
<div class="col-lg-6 col-md-6 col-sm-12 col-xs-12">
<main-budget/>
</div>
<div class="col-lg-6 col-md-6 col-sm-12 col-xs-12">
<main-category />
</div>
</div>
<div class="row">
<div class="col-lg-8 col-md-8 col-sm-12 col-xs-12">
<main-debit/>
</div>
<div class="col-lg-4 col-md-4 col-sm-12 col-xs-12">
<main-credit/>
</div>
</div>
<div class="row">
<div class="col-lg-6 col-md-6 col-sm-12 col-xs-12">
<main-piggy-list/>
</div>
<div class="col-lg-6 col-md-6 col-sm-12 col-xs-12">
<main-bills-list/>
</div>
</div>
-->
</div>
</template>
<script>
export default {
name: "Dashboard"
export default {
name: "Dashboard",
mounted() {
if (!localStorage.currencyPreference) {
this.getCurrencyPreference();
}
},
methods: {
getCurrencyPreference: function () {
axios.get('./api/v1/currencies/default')
.then(response => {
localStorage.currencyPreference = JSON.stringify({
id: parseInt(response.data.data.id),
name: response.data.data.attributes.name,
symbol: response.data.data.attributes.symbol,
code: response.data.data.attributes.code,
decimal_places: parseInt(response.data.data.attributes.decimal_places),
});
}).catch(err => {
console.log('Got error response.');
console.error(err);
localStorage.currencyPreference = JSON.stringify({
id: 1,
name: 'Euro',
symbol: '€',
code: 'EUR',
decimal_places: 2
});
});
}
}
}
</script>

View File

@ -21,7 +21,7 @@
<template>
<div class="card">
<div class="card-header">
<h3 class="card-title">{{ $t('firefly.yourAccounts') }}</h3>
<h3 class="card-title">{{ $t('firefly.yourAccounts') }}</h3>
</div>
<div class="card-body">
<div>

View File

@ -19,165 +19,238 @@
-->
<template>
<div class="row">
<div class="col-md-3 col-sm-6 col-12">
<div class="info-box">
<span class="info-box-icon"><i class="far fa-bookmark text-info"></i></span>
<div class="row">
<div class="col-md-3 col-sm-6 col-12">
<div class="info-box">
<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>
<!-- TODO dont take the first, take default currency OR first -->
<span class="info-box-number" v-if="balances.length > 0">{{ balances[0].value_parsed }}</span>
<div class="info-box-content">
<span class="info-box-text">{{ $t("firefly.balance") }}</span>
<!-- only preferred currency -->
<span class="info-box-number" v-for="balance in prefCurrencyBalances" :title="balance.sub_title">{{ balance.value_parsed }}</span>
<div class="progress bg-info">
<div class="progress-bar" style="width: 0"></div>
</div>
<span class="progress-description">
<span v-for="balance in balances">{{ balance.sub_title }}<br></span>
</span>
</div>
</div>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="info-box">
<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"><span>{{ $t('firefly.bills_to_pay') }}</span></span>
<!-- TODO dont take the first, take default currency OR first -->
<span class="info-box-number" v-if="1 === billsUnpaid.length && billsPaid.length > 0">{{ billsUnpaid[0].value_parsed }}</span>
<div class="progress bg-teal">
<div class="progress-bar" style="width: 0"></div>
</div>
<span class="progress-description">
<!-- TODO dont take the first, take default currency OR first -->
<span v-if="1 === billsUnpaid.length && 1 === billsPaid.length">{{ $t('firefly.paid') }}: {{ billsPaid[0].value_parsed }}</span>
<span v-if="billsUnpaid.length > 1">
<span v-for="(bill, index) in billsUnpaid" :key="bill.key">
{{ bill.value_parsed }}<span v-if="index+1 !== billsUnpaid.length">, </span>
</span>
<div class="progress bg-info">
<div class="progress-bar" style="width: 0"></div>
</div>
<!-- all others -->
<span class="progress-description">
<span v-for="(balance, index) in notPrefCurrencyBalances" :title="balance.sub_title">
{{ balance.value_parsed }}<span v-if="index+1 !== notPrefCurrencyBalances.length">, </span>
</span>
<span v-if="0===notPrefCurrencyBalances.length">&nbsp;</span>
</span>
</div>
</div>
</div>
<!-- altijd iets in bold -->
<!-- subtitle verschilt -->
<!-- fix for small devices only -->
<div class="clearfix hidden-md-up"></div>
<!-- left to spend -->
<div class="col-12 col-sm-6 col-md-3">
<div class="info-box">
<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.left_to_spend') }}</span></span>
<!-- TODO dont take the first, take default currency OR first -->
<!-- TODO change color if negative -->
<span class="info-box-number" v-if="leftToSpend.length > 0">{{ leftToSpend[0].value_parsed }}</span>
<div class="progress bg-success">
<div class="progress-bar" style="width: 0"></div>
</div>
<span class="progress-description">
<!-- TODO list all EXCEPT default currency -->
<span v-for="(spent, index) in leftToSpend" :key="spent.key">
{{ spent.value_parsed }}<span v-if="index+1 !== leftToSpend.length">, </span>
</span>
</span>
</div>
</div>
</div>
<!-- net worth -->
<div class="col-12 col-sm-6 col-md-3">
<div class="info-box">
<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>
<!-- TODO dont take the first, take default currency OR first -->
<span class="info-box-number" v-if="netWorth.length > 0">{{ netWorth[0].value_parsed }}</span>
<div class="progress bg-success">
<div class="progress-bar" style="width: 0"></div>
</div>
<span class="progress-description">
<!-- TODO list all EXCEPT default currency -->
<span v-for="(net, index) in netWorth" :key="net.key">
{{ net.value_parsed }}<span v-if="index+1 !== netWorth.length">, </span>
</span>
</span>
</div>
</div>
</div>
</div>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="info-box">
<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"><span>{{ $t('firefly.bills_to_pay') }}</span></span>
<!-- only preferred currencies -->
<span class="info-box-number" v-for="balance in prefBillsUnpaid">{{ balance.value_parsed }}</span>
<div class="progress bg-teal">
<div class="progress-bar" style="width: 0"></div>
</div>
<!-- all others -->
<span class="progress-description">
<span v-for="(bill, index) in notPrefBillsUnpaid">
{{ bill.value_parsed }}<span v-if="index+1 !== notPrefBillsUnpaid.length">, </span>
</span>
<span v-if="0===notPrefBillsUnpaid.length">&nbsp;</span>
</span>
</div>
</div>
</div>
<!-- altijd iets in bold -->
<!-- subtitle verschilt -->
<!-- fix for small devices only -->
<div class="clearfix hidden-md-up"></div>
<!-- left to spend -->
<div class="col-12 col-sm-6 col-md-3">
<div class="info-box">
<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.left_to_spend') }}</span></span>
<span class="info-box-number" v-for="left in prefLeftToSpend" :title="left.sub_title">{{ left.value_parsed }}</span>
<div class="progress bg-success">
<div class="progress-bar" style="width: 0"></div>
</div>
<!-- others-->
<span class="progress-description">
<span v-for="(left, index) in notPrefLeftToSpend">
{{ left.value_parsed }}<span v-if="index+1 !== notPrefLeftToSpend.length">, </span>
</span>
<span v-if="0===notPrefLeftToSpend.length">&nbsp;</span>
</span>
</div>
</div>
</div>
<!-- net worth -->
<div class="col-12 col-sm-6 col-md-3">
<div class="info-box">
<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-number" v-for="nw in prefNetWorth" :title="nw.sub_title">{{ nw.value_parsed }}</span>
<div class="progress bg-success">
<div class="progress-bar" style="width: 0"></div>
</div>
<span class="progress-description">
<span v-for="(nw, index) in notPrefNetWorth">
{{ nw.value_parsed }}<span v-if="index+1 !== notPrefNetWorth.length">, </span>
</span>
<span v-if="0===notPrefNetWorth.length">&nbsp;</span>
</span>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "TopBoxes",
data() {
return {
summary: [],
balances: [],
billsPaid: [],
billsUnpaid: [],
leftToSpend: [],
netWorth: [],
}
},
mounted() {
this.prepareComponent();
},
methods: {
/**
* Prepare the component.
*/
prepareComponent() {
axios.get('./api/v1/summary/basic?start=' + window.sessionStart + '&end=' + window.sessionEnd)
.then(response => {
this.summary = response.data;
this.buildComponent();
});
},
buildComponent() {
this.getBalanceEntries();
this.getBillsEntries();
this.getLeftToSpend();
this.getNetWorth();
},
getBalanceEntries() {
this.balances = this.getKeyedEntries('balance-in-');
},
getNetWorth() {
this.netWorth = this.getKeyedEntries('net-worth-in-');
},
getLeftToSpend() {
this.leftToSpend = this.getKeyedEntries('left-to-spend-in-');
},
getBillsEntries() {
this.billsPaid = this.getKeyedEntries('bills-paid-in-');
this.billsUnpaid = this.getKeyedEntries('bills-unpaid-in-');
},
getKeyedEntries(expected) {
let result = [];
for (const key in this.summary) {
if (this.summary.hasOwnProperty(key)) {
if (expected === key.substr(0, expected.length)) {
result.push(this.summary[key]);
}
}
}
return result;
}
}
export default {
name: "TopBoxes",
props: {},
data() {
return {
currencyPreference: {},
summary: [],
balances: [],
billsPaid: [],
billsUnpaid: [],
leftToSpend: [],
netWorth: [],
}
},
computed: {
// contains only balances with preferred currency.
prefCurrencyBalances: function () {
return this.filterOnCurrency(this.balances);
},
notPrefCurrencyBalances: function () {
return this.filterOnNotCurrency(this.balances);
},
// contains only bills unpaid in preferred currency or first one.
prefBillsUnpaid: function () {
return this.filterOnCurrency(this.billsUnpaid);
},
notPrefBillsUnpaid: function () {
return this.filterOnNotCurrency(this.billsUnpaid);
},
// left to spend
prefLeftToSpend: function () {
return this.filterOnCurrency(this.leftToSpend);
},
notPrefLeftToSpend: function () {
return this.filterOnNotCurrency(this.leftToSpend);
},
// net worth
prefNetWorth: function () {
return this.filterOnCurrency(this.netWorth);
},
notPrefNetWorth: function () {
return this.filterOnNotCurrency(this.netWorth);
},
},
mounted() {
this.prepareComponent();
this.currencyPreference = localStorage.currencyPreference ? JSON.parse(localStorage.currencyPreference) : {};
},
methods: {
filterOnCurrency(array) {
let ret = [];
for (const key in array) {
if (array.hasOwnProperty(key)) {
if (array[key].currency_id === this.currencyPreference.id) {
ret.push(array[key]);
}
}
}
// or just the first one:
if (0 === ret.length && array.hasOwnProperty(0)) {
ret.push(array[0]);
}
return ret;
},
filterOnNotCurrency(array) {
let ret = [];
for (const key in array) {
if (array.hasOwnProperty(key)) {
if (array[key].currency_id !== this.currencyPreference.id) {
ret.push(array[key]);
}
}
}
return ret;
},
/**
* Prepare the component.
*/
prepareComponent() {
axios.get('./api/v1/summary/basic?start=' + window.sessionStart + '&end=' + window.sessionEnd)
.then(response => {
this.summary = response.data;
this.buildComponent();
});
},
buildComponent() {
this.getBalanceEntries();
this.getBillsEntries();
this.getLeftToSpend();
this.getNetWorth();
},
hasCurrency: function (array) {
for (const key in array) {
if (array.hasOwnProperty(key)) {
if (array[key].currency_id === this.currencyPreference.id) {
return true;
}
}
}
return false;
},
getBalanceEntries() {
this.balances = this.getKeyedEntries('balance-in-');
},
getNetWorth() {
this.netWorth = this.getKeyedEntries('net-worth-in-');
},
getLeftToSpend() {
this.leftToSpend = this.getKeyedEntries('left-to-spend-in-');
},
getBillsEntries() {
this.billsPaid = this.getKeyedEntries('bills-paid-in-');
this.billsUnpaid = this.getKeyedEntries('bills-unpaid-in-');
},
getKeyedEntries(expected) {
let result = [];
for (const key in this.summary) {
if (this.summary.hasOwnProperty(key)) {
if (expected === key.substr(0, expected.length)) {
result.push(this.summary[key]);
}
}
}
return result;
}
}
}
</script>
<style scoped>

View File

@ -0,0 +1,121 @@
<!--
- Create.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="row">
<div class="col">
<div class="card">
<div class="card-header">
<h3 class="card-title">Create a new transaction</h3>
</div>
<!-- /.card-header -->
<div class="card-body">
<div id="accordion">
<!-- we are adding the .class so bootstrap.js collapse plugin detects it -->
<div class="card card-primary">
<div class="card-header">
<h4 class="card-title">
<a data-toggle="collapse" data-parent="#accordion" href="#collapseOne" class="" aria-expanded="true">
Basic transaction information
</a>
</h4>
</div>
<div id="collapseOne" class="panel-collapse in collapse show" style="">
<div class="card-body">
<div class="row">
<div class="col">From</div>
<div class="col">To</div>
</div>
<div class="row">
<div class="col">
Amount, foreign amount, description, time + date
</div>
</div>
</div>
</div>
</div>
<div class="card card-secondary">
<div class="card-header">
<h4 class="card-title">
<a data-toggle="collapse" data-parent="#accordion" href="#collapseTwo" class="collapsed" aria-expanded="false">
Meta information
</a>
</h4>
</div>
<div id="collapseTwo" class="panel-collapse collapse">
<div class="card-body">
Budget, category, bill, tags, contract
</div>
</div>
</div>
<div class="card card-secondary">
<div class="card-header">
<h4 class="card-title">
<a data-toggle="collapse" data-parent="#accordion" href="#collapseThree" class="collapsed" aria-expanded="false">
Extra information
</a>
</h4>
</div>
<div id="collapseThree" class="panel-collapse collapse">
<div class="card-body">
Notes, transaction links, custom fields.
</div>
</div>
</div>
</div>
</div>
<!-- /.card-body -->
</div>
<!-- button -->
<div class="row">
<div class="col">
<a href="#" class="btn btn-primary">Add a split</a>
</div>
<div class="col">
<p class="float-right">
<a href="#" class="btn btn-success">Store transaction</a><br/>
</p>
</div>
</div>
<div class="row">
<div class="col float-right">
<p class="text-right">
<small class="text-muted">Create another another another <input type="checkbox"/></small><br/>
<small class="text-muted">Return here <input type="checkbox"/></small><br/>
</p>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "Create"
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,34 @@
/*
* create.js
* 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/>.
*/
require('../../bootstrap');
import Create from "../../components/transactions/Create";
// i18n
let i18n = require('../../i18n');
let props = {};
new Vue({
i18n,
render(createElement) {
return createElement(Create, {props: props});
}
}).$mount('#transactions_create');

View File

@ -0,0 +1,19 @@
{% extends "./layout/default" %}
{% block breadcrumbs %}
{{ Breadcrumbs.render }}
{% endblock %}
{% block content %}
<!-- Small boxes (Stat box) -->
<!-- Main row -->
<div id="transactions_create"></div>
<!-- /.row (main row) -->
{% endblock %}
{% block styles %}
{% endblock %}
{% block scripts %}
<script src="v2/js/transactions/create.js" nonce="{{ JS_NONCE }}"></script>
{% endblock %}