Rebuild frontend

This commit is contained in:
James Cole
2020-12-30 18:43:16 +01:00
parent a19e116a15
commit 2a0af839f9
13 changed files with 575 additions and 197 deletions

View File

@@ -15,6 +15,7 @@
"laravel-mix": "^5.0.9",
"laravel-mix-bundle-analyzer": "^1.0.5",
"lodash": "^4.17.20",
"lodash.clonedeep": "^4.5.0",
"node-forge": ">=0.10.0",
"resolve-url-loader": "^3.1.2",
"sass": "^1.30.0",

6
frontend/render.sh Executable file
View File

@@ -0,0 +1,6 @@
#!/usr/bin/env bash
[ -d "~/Sites" ] && exit 1;
# build translations.
php /sites/FF3/dev/tools/cli.php ff3:json-translations v2

View File

@@ -103,7 +103,7 @@ export default {
}
},
created() {
console.log('mounted account list.');
//console.log('mounted account list.');
axios.get('./api/v1/accounts?type=' + this.$props.accountTypes)
.then(response => {
this.loadAccounts(response.data.data);

View File

@@ -178,7 +178,7 @@ export default {
let ret = [];
for (const key in array) {
if (array.hasOwnProperty(key)) {
console.log('Currency ID seems to be ' + this.currencyId);
// console.log('Currency ID seems to be ' + this.currencyId);
if (array[key].currency_id === this.currencyId) {
ret.push(array[key]);
}

View File

@@ -42,8 +42,9 @@ export default new Vuex.Store(
locale: 'en-US'
},
mutations: {
setCurrencyPreference(state, object) {
state.currencyPreference = object;
setCurrencyPreference(state, payload) {
console.log('setCurrencyPreference', payload);
state.currencyPreference = payload.payload;
},
initialiseStore(state) {
// if locale in local storage:
@@ -63,6 +64,9 @@ export default new Vuex.Store(
currencyCode: state => {
return state.currencyPreference.code;
},
currencyPreference: state => {
return state.currencyPreference;
},
currencyId: state => {
return state.currencyPreference.id;
},
@@ -73,7 +77,10 @@ export default new Vuex.Store(
actions: {
updateCurrencyPreference(context) {
if (localStorage.currencyPreference) {
context.commit('setCurrencyPreference', localStorage.currencyPreference);
console.log('set from local storage.');
console.log(localStorage.currencyPreference);
console.log({payload: JSON.parse(localStorage.currencyPreference)});
context.commit('setCurrencyPreference', {payload: JSON.parse(localStorage.currencyPreference)});
return;
}
axios.get('./api/v1/currencies/default')
@@ -85,17 +92,21 @@ export default new Vuex.Store(
code: response.data.data.attributes.code,
decimal_places: parseInt(response.data.data.attributes.decimal_places),
};
localStorage.currencyPreference = currencyResponse;
context.commit('setCurrencyPreference', currencyResponse);
localStorage.currencyPreference = JSON.stringify(currencyResponse);
console.log('getCurrencyPreference from server')
console.log(JSON.stringify(currencyResponse));
context.commit('setCurrencyPreference', {payload: currencyResponse});
}).catch(err => {
console.log('Got error response.');
// console.log('Got error response.');
console.error(err);
context.commit('setCurrencyPreference', {
id: 1,
name: 'Euro',
symbol: '',
code: 'EUR',
decimal_places: 2
payload: {
id: 1,
name: 'Euro',
symbol: '',
code: 'EUR',
decimal_places: 2
}
});
});

View File

@@ -20,11 +20,16 @@
let date = new Date;
const lodashClonedeep = require('lodash.clonedeep');
// initial state
const state = () => ({
transactionType: 'any',
transactions: [],
sourceAllowedTypes: ['Asset account', 'Revenue account', 'Loan', 'Debt', 'Mortgage'],
allowedOpposingTypes: {},
accountToTransaction: {},
sourceAllowedTypes: ['Asset account','Loan','Debt','Mortgage','Revenue account'],
destinationAllowedTypes: ['Asset account','Loan','Debt','Mortgage','Expense account'],
defaultTransaction: {
// basic
description: '',
@@ -35,12 +40,23 @@ const state = () => ({
source_account: {
id: 0,
name: "",
name_with_balance: "",
type: "",
currency_id: 0,
currency_name: '',
currency_code: '',
currency_decimal_places: 2
},
destination_account: {
id: 0,
name: "",
type: "",
currency_id: 0,
currency_name: '',
currency_code: '',
currency_decimal_places: 2
},
source_allowed_types: ['Asset account', 'Revenue account', 'Loan', 'Debt', 'Mortgage'],
// meta data
@@ -64,6 +80,12 @@ const getters = {
sourceAllowedTypes: state => {
return state.sourceAllowedTypes;
},
destinationAllowedTypes: state => {
return state.destinationAllowedTypes;
},
allowedOpposingTypes: state => {
return state.allowedOpposingTypes;
},
// // `getters` is localized to this module's getters
// // you can use rootGetters via 4th argument of getters
// someGetter (state, getters, rootState, rootGetters) {
@@ -75,20 +97,74 @@ const getters = {
}
// actions
const actions = {}
const actions = {
calcTransactionType(context) {
let source = context.state.transactions[0].source_account;
let dest = context.state.transactions[0].destination_account;
if (null === source || null === dest) {
// console.log('transactionType any');
context.commit('setTransactionType', 'any');
return;
}
if ('' === source.type || '' === dest.type) {
// console.log('transactionType any');
context.commit('setTransactionType', 'any');
return;
}
// ok so type is set on both:
let expectedDestinationTypes = context.state.accountToTransaction[source.type];
if ('undefined' !== typeof expectedDestinationTypes) {
let transactionType = expectedDestinationTypes[dest.type];
if ('undefined' !== typeof expectedDestinationTypes[dest.type]) {
// console.log('Found a type: ' + transactionType);
context.commit('setTransactionType', transactionType);
return;
}
}
// console.log('Found no type for ' + source.type + ' --> ' + dest.type);
if('Asset account' !== source.type) {
console.log('Drop ID from source. TODO');
// source.id =null
// context.commit('updateField', {field: 'source_account',index: })
// context.state.transactions[0].source_account.id = null;
}
if('Asset account' !== dest.type) {
console.log('Drop ID from destination. TODO');
//context.state.transactions[0].destination_account.id = null;
}
context.commit('setTransactionType', 'any');
}
}
// mutations
const mutations = {
addTransaction(state) {
state.transactions.push(state.defaultTransaction);
let newTransaction = lodashClonedeep(state.defaultTransaction);
state.transactions.push(newTransaction);
},
deleteTransaction(state, index) {
this.state.transactions.splice(index, 1);
state.transactions.splice(index, 1);
},
setTransactionType(state, transactionType) {
state.transactionType = transactionType;
},
setAllowedOpposingTypes(state, allowedOpposingTypes) {
state.allowedOpposingTypes = allowedOpposingTypes;
},
setAccountToTransaction(state, payload) {
state.accountToTransaction = payload;
},
updateField(state, payload) {
console.log('I am update field');
console.log(payload)
state.transactions[payload.index][payload.field] = payload.value;
},
setDestinationAllowedTypes(state, payload) {
// console.log('Destination allowed types was changed!');
state.destinationAllowedTypes = payload;
},
setSourceAllowedTypes(state, payload) {
state.sourceAllowedTypes = payload;
}
}

View File

@@ -36,7 +36,15 @@
<!-- /.card-header -->
<div class="card-body">
<h4>{{ $t('firefly.basic_journal_information') }}</h4>
<div class="row">
<div class="col">
<p class="d-block d-sm-none">XS</p>
<p class="d-none d-sm-block d-md-none">SM</p>
<p class="d-none d-md-block d-lg-none">MD</p>
<p class="d-none d-lg-block d-xl-none">LG</p>
<p class="d-none d-xl-block">XL</p>
</div>
</div>
<!-- description etc, 3 rows -->
<div class="row">
<div class="col">
@@ -47,15 +55,7 @@
</div>
</div>
<div class="row">
<div class="col">
<p class="d-block d-sm-none">XS</p>
<p class="d-none d-sm-block d-md-none">SM</p>
<p class="d-none d-md-block d-lg-none">MD</p>
<p class="d-none d-lg-block d-xl-none">LG</p>
<p class="d-none d-xl-block">XL</p>
</div>
</div>
<!-- source and destination -->
<div class="row">
@@ -65,54 +65,33 @@
:selectedAccount="transactions[index].source_account"
direction="source"
:index="index"
/>
/>
</div>
<!-- switcharoo! -->
<div class="col-xl-2 col-lg-2 col-md-2 col-sm-12 text-center d-none d-sm-block">
<!--
<div class="btn-group d-flex">
<button class="btn btn-light">&harr;</button>
</div>
-->
<SwitchAccount
:index="index"
/>
</div>
<!-- destination -->
<div class="col-xl-5 col-lg-5 col-md-12 col-sm-12 col-xs-12">
<!--
<div class="input-group">
<input
title="Dest"
autocomplete="off"
autofocus
class="form-control"
name="something[]"
type="text"
placeholder="Dest"
>
</div>
-->
<!-- DESTINATION -->
<TransactionAccount
:selectedAccount="transactions[index].destination_account"
direction="destination"
:index="index"
/>
</div>
</div>
<!-- amount -->
<div class="row">
<div class="col-xl-5 col-lg-5 col-md-10 col-sm-12 col-xs-12">
<!-- SOURCE -->
<!-- AMOUNT -->
<TransactionAmount />
<!--
<div class="form-group">
<div class="text-xs">{{ $t('firefly.amount') }}</div>
<div class="input-group">
<input
title="Amount"
autocomplete="off"
autofocus
class="form-control"
name="amount[]"
type="number"
placeholder="Amount"
>
</div>
</div>
-->
</div>
<div class="col-xl-2 col-lg-2 col-md-2 col-sm-12 text-center d-none d-sm-block">
@@ -131,14 +110,15 @@
<!-- dates -->
<div class="row">
<div class="col">
<div class="col-xl-5 col-lg-5 col-md-12 col-sm-12 col-xs-12">
<TransactionDate
:date="transactions[index].date"
:time="transactions[index].time"
:index="index"
/>
</div>
<div class="col">
<div class="col-xl-5 col-lg-5 col-md-12 col-sm-12 col-xs-12 offset-xl-2 offset-lg-2">
<!-- TODO other time slots -->
<div class="form-group">
<div class="text-xs d-none d-lg-block d-xl-block">
@@ -159,25 +139,25 @@
</div>
</div>
<!--
<p class="d-block d-sm-none">XS</p>
<p class="d-none d-sm-block d-md-none">SM</p>
<p class="d-none d-md-block d-lg-none">MD</p>
<p class="d-none d-lg-block d-xl-none">LG</p>
<p class="d-none d-xl-block">XL</p>
<!--
<p class="d-block d-sm-none">XS</p>
<p class="d-none d-sm-block d-md-none">SM</p>
<p class="d-none d-md-block d-lg-none">MD</p>
<p class="d-none d-lg-block d-xl-none">LG</p>
<p class="d-none d-xl-block">XL</p>
<br/><br/><br/><br/><br/><br/><br/><br/><br/><br/>
<br/><br/><br/><br/><br/><br/><br/><br/><br/><br/>
{# top stuff #}
<div class="row">
<div class="col">
{# top stuff #}
<div class="row">
<div class="col">
</div>
</div>
</div>
</div>
-->
-->
<div class="row">
<div class="col">
@@ -364,14 +344,18 @@ import TransactionDate from "./TransactionDate";
import TransactionBudget from "./TransactionBudget";
import {createNamespacedHelpers} from 'vuex'
import TransactionAccount from "./TransactionAccount";
import SwitchAccount from "./SwitchAccount";
import TransactionAmount from "./TransactionAmount";
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('transactions/create')
export default {
name: "Create",
components: {TransactionAccount, TransactionBudget, TransactionDescription, TransactionDate},
components: {TransactionAmount, SwitchAccount, TransactionAccount, TransactionBudget, TransactionDescription, TransactionDate},
created() {
this.storeAllowedOpposingTypes();
this.storeAccountToTransaction();
this.addTransaction();
},
data() {
@@ -390,15 +374,26 @@ export default {
...mapMutations(
[
'addTransaction',
'deleteTransaction'
]
'deleteTransaction',
'setAllowedOpposingTypes',
'setAccountToTransaction',
],
),
/**
*
*/
storeAllowedOpposingTypes: function () {
this.setAllowedOpposingTypes(window.allowedOpposingTypes);
},
storeAccountToTransaction: function() {
this.setAccountToTransaction(window.accountToTransaction);
},
/**
*
*/
submitTransaction: function () {
this.isSubmitting = true;
console.log('Now in submit()');
// console.log('Now in submit()');
const uri = './api/v1/transactions';
const data = this.convertData();
@@ -411,7 +406,7 @@ export default {
*
*/
convertData: function () {
console.log('now in convertData');
// console.log('now in convertData');
let data = {
//'group_title': null,
'transactions': []
@@ -435,6 +430,12 @@ export default {
description: array.description,
date: array.date + ' ' + array.time,
// account
source_id: array.source_account.id ?? null,
source_name: array.source_account.name ?? null,
destination_id: array.destination_account.id ?? null,
destination_name: array.destination_account.name ?? null,
// meta
budget_id: array.budget_id,

View File

@@ -0,0 +1,70 @@
<!--
- SwitchAccount.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="form-group">
<div class="text-xs d-none d-lg-block d-xl-block">
<span class="text-muted" v-if="'any' !== this.transactionType">
{{ $t('firefly.' + this.transactionType) }}
</span>
<span class="text-muted" v-if="'any' === this.transactionType">&nbsp;</span>
</div>
<div class="btn-group d-flex">
<button class="btn btn-light" @click="switchAccounts">&harr;</button>
</div>
</div>
</template>
<script>
import {createNamespacedHelpers} from "vuex";
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('transactions/create')
export default {
name: "SwitchAccount",
props: ['index'],
methods: {
...mapMutations(
[
'updateField',
],
),
switchAccounts() {
let source = this.transactions[this.index].source_account;
let dest = this.transactions[this.index].destination_account;
this.updateField({field: 'source_account', index: this.index, value: dest});
this.updateField({field: 'destination_account', index: this.index, value: source});
// trigger other components.
}
},
computed: {
...mapGetters(['transactions', 'transactionType']),
}
}
</script>
<style scoped>
</style>

View File

@@ -20,40 +20,45 @@
<template>
<div class="form-group">
<div class="pl-1 pb-2 pt-3">
Selected account: <span v-if="selectedAccount">{{ selectedAccount.name }}</span>
<div class="text-xs d-none d-lg-block d-xl-block">
{{ $t('firefly.' + this.direction + '_account') }}
</div>
<vue-typeahead-bootstrap
v-model="account"
:data="accounts"
inputName="source[]"
:serializer="item => item.name"
:showOnFocus=true
:inputName="direction + '[]'"
:serializer="item => item.name_with_balance"
@hit="selectedAccount = $event"
:minMatchingChars="3"
:placeholder="$t('firefly.source_account')"
:placeholder="$t('firefly.' + this.direction + '_account')"
@input="lookupAccount"
>
<template slot="append">
<div class="input-group-append">
<button class="btn btn-outline-secondary" type="button"><i class="far fa-trash-alt"></i></button>
</div>
</template>
<template slot="suggestion" slot-scope="{ data, htmlText }">
<div class="d-flex align-items-center">
<!-- Note: the v-html binding is used, as htmlText contains
the suggestion text highlighted with <strong> tags -->
<span class="ml-4" v-html="htmlText"></span>
<button class="btn btn-outline-secondary" v-on:click="clearAccount" type="button"><i class="far fa-trash-alt"></i></button>
</div>
</template>
</vue-typeahead-bootstrap>
</div>
</template>
<script>
/*
<template slot="suggestion" slot-scope="{ data, htmlText }">
<div class="d-flex align-items-center">
<span v-html="htmlText"></span>
</div>
</template>
- you get an object from the parent.
- this is the selected account.
<!-- Note: the v-html binding is used, as htmlText contains
the suggestion text highlighted with <strong> tags -->
*/
@@ -72,77 +77,132 @@ export default {
query: '',
accounts: [],
account: '',
accountTypes: []
accountTypes: [],
initialSet: []
}
},
created() {
this.createInitialSet();
},
methods: {
...mapMutations(
[
'updateField',
'setDestinationAllowedTypes',
'setSourceAllowedTypes'
],
),
...mapActions(
[
'calcTransactionType'
]
),
getACURL: function (types, query) {
// update autocomplete URL:
// console.log('getACURL query = ' + query);
// console.log(types);
return document.getElementsByTagName('base')[0].href + 'api/v1/autocomplete/accounts?types=' + types.join(',') + '&query=' + query;
},
clearAccount: function () {
// console.log('clearAccount in ' + this.direction);
this.account = '';
this.selectedAccount = this.defaultTransaction.source_account; // can be either source or dest, does not matter.
// console.log('clearAccount. Selected account (' + this.direction + ') is now:');
// console.log(this.defaultTransaction.source_account);
this.accounts = this.initialSet;
},
lookupAccount: debounce(function () {
console.log('lookup "' + this.account + '"');
if(0===this.accountTypes.length) {
// console.log('lookup account in ' + this.direction)
if (0 === this.accountTypes.length) {
// set the types from the default types for this direction:
this.accountTypes = 'source' === this.direction ? this.sourceAllowedTypes : [];
this.accountTypes = 'source' === this.direction ? this.sourceAllowedTypes : this.destinationAllowedTypes;
}
// the allowed types array comes from the found (selected) account.
// so whatever the user clicks and is stored into selected account.
// must also be expanded with the allowed account types for this
// search. which in turn depends more on the opposing account than the results of
// this particular search and can be changed externally rather than "right here".
// which means that the allowed types should be separate from the selected account
// in the default transaction.
let accountAutoCompleteURL =
document.getElementsByTagName('base')[0].href +
'api/v1/autocomplete/accounts' +
'?types=' +
this.accountTypes.join(',') +
'&query=' + this.account;
console.log('Auto complete URI is now ' + accountAutoCompleteURL);
// in practice this action should be debounced
axios.get(accountAutoCompleteURL)
// update autocomplete URL:
axios.get(this.getACURL(this.accountTypes, this.account))
.then(response => {
console.log('Found ' + response.data.length + ' results.');
this.accounts = response.data;
})
}, 500)
}, 300),
createInitialSet: function () {
// console.log('createInitialSet ' + this.direction);
// initial list of accounts:
let types = this.sourceAllowedTypes;
if ('destination' === this.direction) {
types = this.destinationAllowedTypes;
}
axios.get(this.getACURL(types, ''))
.then(response => {
// console.log('initial set of accounts. ' + this.direction);
this.accounts = response.data;
this.initialSet = response.data;
});
}
},
watch: {
selectedAccount: function (value) {
// console.log('watch selectedAccount ' + this.direction);
// console.log(value);
this.account = value ? value.name_with_balance : null;
// console.log('this.account (' + this.direction + ') = "' + this.account + '"');
this.calcTransactionType();
// set the opposing account allowed set.
// console.log('opposing:');
let opposingAccounts = [];
let type = value.type ? value.type : 'no_type';
if ('undefined' !== typeof this.allowedOpposingTypes[this.direction]) {
if ('undefined' !== typeof this.allowedOpposingTypes[this.direction][type]) {
opposingAccounts = this.allowedOpposingTypes[this.direction][type];
}
}
if ('source' === this.direction) {
this.setDestinationAllowedTypes(opposingAccounts);
}
if ('destination' === this.direction) {
this.setSourceAllowedTypes(opposingAccounts);
}
},
sourceAllowedTypes: function (value) {
if ('source' === this.direction) {
// console.log('do update initial set in direction ' + this.direction + ' because allowed types changed');
// update initial set:
this.createInitialSet();
}
},
destinationAllowedTypes: function (value) {
if ('destination' === this.direction) {
// console.log('do update initial set in direction ' + this.direction + ' because allowed types changed');
// update initial set:
this.createInitialSet();
}
}
},
computed: {
...mapGetters([
'transactionType',
'transactions',
'defaultTransaction',
'sourceAllowedTypes'
'sourceAllowedTypes',
'destinationAllowedTypes',
'allowedOpposingTypes'
]),
accountKey: {
get() {
return 'source' === this.direction ? 'source_account' : 'destination_account';
}
},
selectedAccount: {
get() {
let key = 'source' === this.direction ? 'source_account' : 'destination_account';
console.log('Will now get ' + key);
console.log(this.transactions[this.index][key]);
return this.transactions[this.index][key];
return this.transactions[this.index][this.accountKey];
},
set(value) {
let key = 'source' === this.direction ? 'source_account' : 'destination_account';
console.log('Will now set ' + key + ' to:');
console.log(value);
if('object' !== typeof value) {
// make object manually.
let account = this.defaultTransaction.source_account;
account.name = value;
value = account;
}
if('object' === typeof value) {
console.log('user selected account object:');
console.log(value);
}
this.updateField({field: key, index: this.index, value: value});
// console.log('set selectedAccount for ' + this.direction);
// console.log(value);
this.updateField({field: this.accountKey, index: this.index, value: value});
}
}
}

View File

@@ -0,0 +1,100 @@
<!--
- TransactionAmount.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="form-group">
<div class="text-xs">{{ $t('firefly.amount') }}</div>
<div class="input-group">
<div class="input-group-prepend">
<div class="input-group-text">{{ this.currencySymbol }}</div>
</div>
<input
title="Amount"
autocomplete="off"
autofocus
class="form-control"
name="amount[]"
type="number"
placeholder="Amount"
>
</div>
</div>
</template>
<script>
import {createNamespacedHelpers} from "vuex";
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('transactions/create')
//const {mapRootState, mapRootGetters, mapRootActions, mapRootMutations} = createHelpers('');
export default {
name: "TransactionAmount",
data() {
return {
currencySymbol: '',
}
},
watch: {
selectedTransactionType: function (value) {
console.log('TransactionAmount just noticed transaction type is now ' + value);
}
},
created: function() {
console.log('TransactionAmount is created.');
this.updateCurrency();
},
methods: {
updateCurrency: function() {
if('any' === this.transactionType) {
// use default currency from store.
this.currencySymbol = this.currencyPreference.symbol;
}
}
},
computed: {
currencyPreference: {
get() {
return this.$store.state.currencyPreference;
}
},
...mapGetters([
'transactionType',
'transactions',
]),
selectedTransactionType: {
get() {
return this.transactionType;
},
set(value) {
// console.log('set selectedAccount for ' + this.direction);
// console.log(value);
// this.updateField({field: this.accountKey, index: this.index, value: value});
}
}
}
}
</script>
<style scoped>
</style>

View File

@@ -23,7 +23,35 @@
<div class="text-xs d-none d-lg-block d-xl-block">
{{ $t('firefly.description') }}
</div>
<div class="input-group">
<vue-typeahead-bootstrap
inputName="description[]"
v-model="query"
:data="descriptions"
:placeholder="$t('firefly.description')"
:showOnFocus=true
:minMatchingChars="3"
:serializer="item => item.description"
@hit="selectedDescription = $event"
@input="lookupDescription"
>
<template slot="append">
<div class="input-group-append">
<button v-on:click="clearDescription" class="btn btn-outline-secondary" type="button"><i class="far fa-trash-alt"></i></button>
</div>
</template>
</vue-typeahead-bootstrap>
<!--
<vue-typeahead-bootstrap
v-model="description"
:data="descriptions"
:serializer="item => item.name_with_balance"
@hit="selectedAccount = $event"
:placeholder="$t('firefly.' + this.direction + '_account')"
@input="lookupAccount"
>
</vue-typeahead-bootstrap>
<input
ref="description"
:title="$t('firefly.description')"
@@ -36,10 +64,8 @@
:placeholder="$t('firefly.description')"
v-on:submit.prevent
>
<div class="input-group-append">
<button v-on:click="clearDescription" class="btn btn-outline-secondary" type="button"><i class="far fa-trash-alt"></i></button>
</div>
</div>
-->
</div>
</template>
@@ -47,12 +73,35 @@
<script>
import {createNamespacedHelpers} from "vuex";
import VueTypeaheadBootstrap from 'vue-typeahead-bootstrap';
import {debounce} from "lodash";
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('transactions/create')
// https://firefly.sd.home/api/v1/autocomplete/transactions?query=test
export default {
props: ['index'],
components: {VueTypeaheadBootstrap},
name: "TransactionDescription",
data() {
return {
descriptions: [],
query: '',
initialSet: []
}
},
created() {
// initial list of accounts:
axios.get(this.getACURL(''))
.then(response => {
this.descriptions = response.data;
this.initialSet = response.data;
});
},
methods: {
...mapMutations(
[
@@ -60,15 +109,26 @@ export default {
],
),
clearDescription: function () {
this.description = '';
}
this.selectedDescription = '';
},
getACURL: function (query) {
// update autocomplete URL:
return document.getElementsByTagName('base')[0].href + 'api/v1/autocomplete/transactions?query=' + query;
},
lookupDescription: debounce(function () {
// update autocomplete URL:
axios.get(this.getACURL(this.query))
.then(response => {
this.descriptions = response.data;
})
}, 300)
},
computed: {
...mapGetters([
'transactionType',
'transactions',
]),
description: {
selectedDescription: {
get() {
return this.transactions[this.index].description;
},

View File

@@ -872,9 +872,9 @@
integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
"@types/node@*":
version "14.14.16"
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.16.tgz#3cc351f8d48101deadfed4c9e4f116048d437b4b"
integrity sha512-naXYePhweTi+BMv11TgioE2/FXU4fSl29HAH1ffxVciNsH3rYXjNP2yM8wqmSm7jS20gM8TIklKiTen+1iVncw==
version "14.14.17"
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.17.tgz#29fab92f3986c0e379968ad3c2043683d8020dbb"
integrity sha512-G0lD1/7qD60TJ/mZmhog76k7NcpLWkPVGgzkRy3CTlnFu4LUQh5v2Wa661z6vnXmD8EQrnALUyf0VRtrACYztw==
"@types/q@^1.5.1":
version "1.5.4"
@@ -2399,7 +2399,7 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3:
dependencies:
ms "2.0.0"
debug@^3.1.1, debug@^3.2.5:
debug@^3.1.1, debug@^3.2.6:
version "3.2.7"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==
@@ -3018,14 +3018,7 @@ fastparse@^1.1.1, fastparse@^1.1.2:
resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.2.tgz#91728c5a5942eced8531283c79441ee4122c35a9"
integrity sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==
faye-websocket@^0.10.0:
version "0.10.0"
resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4"
integrity sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=
dependencies:
websocket-driver ">=0.5.1"
faye-websocket@~0.11.1:
faye-websocket@^0.11.3:
version "0.11.3"
resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.3.tgz#5c0e9a8968e8912c286639fde977a8b209f2508e"
integrity sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA==
@@ -4121,7 +4114,7 @@ json-schema-traverse@^0.4.1:
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
json3@^3.3.2:
json3@^3.3.3:
version "3.3.3"
resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.3.tgz#7fc10e375fc5ae42c4705a5cc0aa6f62be305b81"
integrity sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==
@@ -4287,6 +4280,11 @@ locate-path@^5.0.0:
dependencies:
p-locate "^4.1.0"
lodash.clonedeep@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=
lodash.memoize@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
@@ -5957,9 +5955,9 @@ sass-loader@^10.1.0:
semver "^7.3.2"
sass@^1.30.0:
version "1.30.0"
resolved "https://registry.yarnpkg.com/sass/-/sass-1.30.0.tgz#60bbbbaf76ba10117e61c6c24f00161c3d60610e"
integrity sha512-26EUhOXRLaUY7+mWuRFqGeGGNmhB1vblpTENO1Z7mAzzIZeVxZr9EZoaY1kyGLFWdSOZxRMAufiN2mkbO6dAlw==
version "1.32.0"
resolved "https://registry.yarnpkg.com/sass/-/sass-1.32.0.tgz#10101a026c13080b14e2b374d4e15ee24400a4d3"
integrity sha512-fhyqEbMIycQA4blrz/C0pYhv2o4x2y6FYYAH0CshBw3DXh5D5wyERgxw0ptdau1orc/GhNrhF7DFN2etyOCEng==
dependencies:
chokidar ">=2.0.0 <4.0.0"
@@ -6008,7 +6006,7 @@ select-hose@^2.0.0:
resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca"
integrity sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=
selfsigned@^1.10.7:
selfsigned@^1.10.8:
version "1.10.8"
resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.8.tgz#0d17208b7d12c33f8eac85c41835f27fc3d81a30"
integrity sha512-2P4PtieJeEwVgTU9QEcwIRDQ/mXJLX8/+I3ur+Pg16nS8oNbrGxEso9NyYWy8NAmXiNl4dlAp5MwoNeCWzON4w==
@@ -6200,26 +6198,26 @@ snapdragon@^0.8.1:
source-map-resolve "^0.5.0"
use "^3.1.0"
sockjs-client@1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.4.0.tgz#c9f2568e19c8fd8173b4997ea3420e0bb306c7d5"
integrity sha512-5zaLyO8/nri5cua0VtOrFXBPK1jbL4+1cebT/mmKA1E1ZXOvJrII75bPu0l0k843G/+iAbhEqzyKr0w/eCCj7g==
sockjs-client@^1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.5.0.tgz#2f8ff5d4b659e0d092f7aba0b7c386bd2aa20add"
integrity sha512-8Dt3BDi4FYNrCFGTL/HtwVzkARrENdwOUf1ZoW/9p3M8lZdFT35jVdrHza+qgxuG9H3/shR4cuX/X9umUrjP8Q==
dependencies:
debug "^3.2.5"
debug "^3.2.6"
eventsource "^1.0.7"
faye-websocket "~0.11.1"
inherits "^2.0.3"
json3 "^3.3.2"
url-parse "^1.4.3"
faye-websocket "^0.11.3"
inherits "^2.0.4"
json3 "^3.3.3"
url-parse "^1.4.7"
sockjs@0.3.20:
version "0.3.20"
resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.20.tgz#b26a283ec562ef8b2687b44033a4eeceac75d855"
integrity sha512-SpmVOVpdq0DJc0qArhF3E5xsxvaiqGNb73XfgBpK1y3UD5gs8DSo8aCTsuT5pX8rssdc2NDIzANwP9eCAiSdTA==
sockjs@^0.3.21:
version "0.3.21"
resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.21.tgz#b34ffb98e796930b60a0cfa11904d6a339a7d417"
integrity sha512-DhbPFGpxjc6Z3I+uX07Id5ZO2XwYsWOrYjaSeieES78cq+JaJvVe5q/m1uvjIQhXinhIeCFRH6JgXe+mvVMyXw==
dependencies:
faye-websocket "^0.10.0"
faye-websocket "^0.11.3"
uuid "^3.4.0"
websocket-driver "0.6.5"
websocket-driver "^0.7.4"
source-list-map@^2.0.0:
version "2.0.1"
@@ -6803,7 +6801,7 @@ urix@^0.1.0:
resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72"
integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=
url-parse@^1.4.3:
url-parse@^1.4.3, url-parse@^1.4.7:
version "1.4.7"
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278"
integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==
@@ -7044,9 +7042,9 @@ webpack-dev-middleware@^3.7.2:
webpack-log "^2.0.0"
webpack-dev-server@^3.1.14:
version "3.11.0"
resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.11.0.tgz#8f154a3bce1bcfd1cc618ef4e703278855e7ff8c"
integrity sha512-PUxZ+oSTxogFQgkTtFndEtJIPNmml7ExwufBZ9L2/Xyyd5PnOL5UreWe5ZT7IU25DSdykL9p1MLQzmLh2ljSeg==
version "3.11.1"
resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.11.1.tgz#c74028bf5ba8885aaf230e48a20e8936ab8511f0"
integrity sha512-u4R3mRzZkbxQVa+MBWi2uVpB5W59H3ekZAJsQlKUTdl7Elcah2EhygTPLmeFXybQkf9i2+L0kn7ik9SnXa6ihQ==
dependencies:
ansi-html "0.0.7"
bonjour "^3.5.0"
@@ -7068,11 +7066,11 @@ webpack-dev-server@^3.1.14:
p-retry "^3.0.1"
portfinder "^1.0.26"
schema-utils "^1.0.0"
selfsigned "^1.10.7"
selfsigned "^1.10.8"
semver "^6.3.0"
serve-index "^1.9.1"
sockjs "0.3.20"
sockjs-client "1.4.0"
sockjs "^0.3.21"
sockjs-client "^1.5.0"
spdy "^4.0.2"
strip-ansi "^3.0.1"
supports-color "^6.1.0"
@@ -7142,14 +7140,7 @@ webpack@^4.36.1:
watchpack "^1.7.4"
webpack-sources "^1.4.1"
websocket-driver@0.6.5:
version "0.6.5"
resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.6.5.tgz#5cb2556ceb85f4373c6d8238aa691c8454e13a36"
integrity sha1-XLJVbOuF9Dc8bYI4qmkchFThOjY=
dependencies:
websocket-extensions ">=0.1.1"
websocket-driver@>=0.5.1:
websocket-driver@>=0.5.1, websocket-driver@^0.7.4:
version "0.7.4"
resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760"
integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==

View File

@@ -1,3 +1,5 @@
var sessionStart = '{{ start }}';
var sessionEnd = '{{ end }}';
var allowedOpposingTypes = {{ config('firefly.allowed_opposing_types')|json_encode|raw }};
var accountToTransaction = {{ config('firefly.account_to_transaction')|json_encode|raw }};