mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-09-24 22:48:18 +00:00
Continue work on new frontpage.
This commit is contained in:
@@ -85,7 +85,11 @@ const state = () => ({
|
||||
// optional other fields:
|
||||
internal_reference: null,
|
||||
external_url: null,
|
||||
notes: null
|
||||
notes: null,
|
||||
|
||||
// transaction links:
|
||||
links: [],
|
||||
attachments: []
|
||||
},
|
||||
}
|
||||
)
|
||||
|
@@ -154,40 +154,15 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="form-group">
|
||||
<div class="text-xs d-none d-lg-block d-xl-block">
|
||||
{{ $t('firefly.journal_links') }}
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<p>
|
||||
<em>No transaction links</em>
|
||||
</p>
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item">
|
||||
<em>is paid by</em>
|
||||
<a href="#">Some other transaction</a> (<span class="text-success">$ 12.34</span>)
|
||||
<div class="btn-group btn-group-xs float-right">
|
||||
<a href="#" class="btn btn-xs btn-default"><i class="far fa-edit"></i></a>
|
||||
<a href="#" class="btn btn-xs btn-danger"><i class="far fa-trash-alt"></i></a>
|
||||
</div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<em>is paid by</em>
|
||||
<a href="#">Some other transaction</a> (<span class="text-success">$ 12.34</span>)
|
||||
<div class="btn-group btn-group-xs float-right">
|
||||
<a href="#" class="btn btn-xs btn-default"><i class="far fa-edit"></i></a>
|
||||
<a href="#" class="btn btn-xs btn-danger"><i class="far fa-trash-alt"></i></a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="form-text">
|
||||
|
||||
<button data-toggle="modal" data-target="#linkModal" class="btn btn-default"><i class="fas fa-plus"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<TransactionAttachments
|
||||
:index="index"
|
||||
v-model="transaction.attachments"
|
||||
/>
|
||||
|
||||
<TransactionLinks :index="index"
|
||||
v-model="transaction.links"
|
||||
/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -220,50 +195,6 @@
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal" tabindex="-1" id="linkModal">
|
||||
<div class="modal-dialog modal-lg modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Transaction thing dialog.</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<p>
|
||||
Use this form to search for transactions. When in doubt, use <code>id:*</code> where the ID is the number from the URL.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="input-group">
|
||||
<input autocomplete="off" maxlength="255" type="text" name="search" id="query" value="" class="form-control" placeholder="Search query">
|
||||
<div class="input-group-append">
|
||||
<button type="button" class="btn btn-default"><i class="fas fa-search"></i> Search</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
Search results.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
||||
<button type="button" class="btn btn-primary">Save changes</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</template>
|
||||
@@ -287,6 +218,9 @@ import TransactionPiggyBank from "./TransactionPiggyBank";
|
||||
import TransactionInternalReference from "./TransactionInternalReference";
|
||||
import TransactionExternalUrl from "./TransactionExternalUrl";
|
||||
import TransactionNotes from "./TransactionNotes";
|
||||
import TransactionLinks from "./TransactionLinks";
|
||||
import TransactionAttachments from "./TransactionAttachments";
|
||||
|
||||
|
||||
const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers('transactions/create')
|
||||
|
||||
@@ -294,11 +228,13 @@ const {mapState, mapGetters, mapActions, mapMutations} = createNamespacedHelpers
|
||||
export default {
|
||||
name: "Create",
|
||||
components: {
|
||||
TransactionAttachments,
|
||||
TransactionNotes,
|
||||
TransactionExternalUrl,
|
||||
TransactionInternalReference,
|
||||
TransactionPiggyBank,
|
||||
TransactionTags,
|
||||
TransactionLinks,
|
||||
TransactionBill,
|
||||
TransactionCategory,
|
||||
TransactionCustomDates,
|
||||
@@ -314,7 +250,8 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
groupTitle: '',
|
||||
isSubmitting: false
|
||||
isSubmitting: false,
|
||||
linkSearchResults: [],
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -444,7 +381,15 @@ export default {
|
||||
internal_reference: array.internal_reference,
|
||||
external_url: array.external_url,
|
||||
notes: array.notes,
|
||||
|
||||
// links (TODO)
|
||||
links: array.links
|
||||
};
|
||||
for(let i in array.links) {
|
||||
if (this.transactions.hasOwnProperty(key) && /^0$|^[1-9]\d*$/.test(key) && key <= 4294967294) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// return it.
|
||||
return currentSplit;
|
||||
|
@@ -0,0 +1,45 @@
|
||||
<!--
|
||||
- TransactionAttachments.vue
|
||||
- Copyright (c) 2021 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">
|
||||
{{ $t('firefly.attachments') }}
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<input
|
||||
type="file"
|
||||
multiple
|
||||
name="attachments[]"
|
||||
:placeholder="$t('firefly.attachment')"
|
||||
class="form-control"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "TransactionAttachments"
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
355
frontend/src/components/transactions/TransactionLinks.vue
Normal file
355
frontend/src/components/transactions/TransactionLinks.vue
Normal file
@@ -0,0 +1,355 @@
|
||||
<!--
|
||||
- TransactionLinks.vue
|
||||
- Copyright (c) 2021 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>
|
||||
<div class="form-group">
|
||||
<div class="text-xs d-none d-lg-block d-xl-block">
|
||||
{{ $t('firefly.journal_links') }}
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<p v-if="value.length === 0">
|
||||
<button data-toggle="modal" data-target="#linkModal" class="btn btn-default btn-xs"><i class="fas fa-plus"></i> Add transaction link</button>
|
||||
</p>
|
||||
<ul class="list-group" v-if="value.length > 0">
|
||||
<li class="list-group-item" v-for="transaction in value">
|
||||
<em>{{ getTextForLinkType(transaction.link_type_id) }}</em>
|
||||
<a :href='"./transaction/show/" + transaction.transaction_group_id'>{{ transaction.description }}</a>
|
||||
|
||||
<span v-if="transaction.type === 'withdrawal'">
|
||||
(<span class="text-danger">{{
|
||||
Intl.NumberFormat(locale, {
|
||||
style: 'currency',
|
||||
currency: transaction.currency_code
|
||||
}).format(parseFloat(transaction.amount) * -1)
|
||||
}}</span>)
|
||||
</span>
|
||||
<span v-if="transaction.type === 'deposit'">
|
||||
(<span class="text-success">{{
|
||||
Intl.NumberFormat(locale, {
|
||||
style: 'currency',
|
||||
currency: transaction.currency_code
|
||||
}).format(parseFloat(transaction.amount))
|
||||
}}</span>)
|
||||
</span>
|
||||
<span v-if="transaction.type === 'transfer'">
|
||||
(<span class="text-info">{{
|
||||
Intl.NumberFormat(locale, {
|
||||
style: 'currency',
|
||||
currency: transaction.currency_code
|
||||
}).format(parseFloat(transaction.amount))
|
||||
}}</span>)
|
||||
</span>
|
||||
<div class="btn-group btn-group-xs float-right">
|
||||
<a href="#" class="btn btn-xs btn-default"><i class="far fa-edit"></i></a>
|
||||
<a href="#" class="btn btn-xs btn-danger"><i class="far fa-trash-alt"></i></a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="form-text" v-if="value.length > 0">
|
||||
<button data-toggle="modal" data-target="#linkModal" class="btn btn-default"><i class="fas fa-plus"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- modal -->
|
||||
<div class="modal" tabindex="-1" id="linkModal">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Transaction thing dialog.</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<p>
|
||||
Use this form to search for transactions you wish to link to this one. When in doubt, use <code>id:*</code> where the ID is the number from
|
||||
the URL.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<form v-on:submit.prevent="search">
|
||||
<div class="input-group">
|
||||
<input autocomplete="off" maxlength="255" type="text" name="search" v-model="query" id="query"
|
||||
class="form-control" placeholder="Search query">
|
||||
<div class="input-group-append">
|
||||
<button type="submit" class="btn btn-default"><i class="fas fa-search"></i> Search</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span v-if="searching"><i class="fas fa-spinner fa-spin"></i></span>
|
||||
<h4 v-if="searchResults.length > 0">Search results</h4>
|
||||
<table class="table table-sm" v-if="searchResults.length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:33%" colspan="2">Include?</th>
|
||||
<th>Transaction</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="result in searchResults">
|
||||
<td>
|
||||
<input type="checkbox" class="form-control"
|
||||
@change="selectTransaction($event)"
|
||||
v-model="result.selected"
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<select
|
||||
@change="selectLinkType($event)"
|
||||
class="form-control"
|
||||
v-model="result.link_type_id"
|
||||
>
|
||||
<option v-for="linkType in linkTypes" :value="linkType.id + '-' + linkType.direction" :label="linkType.type">{{
|
||||
linkType.type
|
||||
}}
|
||||
</option>
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<a :href="'./transactions/show/' + result.transaction_group_id">{{ result.description }}</a>
|
||||
<span v-if="result.type === 'withdrawal'">
|
||||
(<span class="text-danger">{{
|
||||
Intl.NumberFormat(locale, {
|
||||
style: 'currency',
|
||||
currency: result.currency_code
|
||||
}).format(parseFloat(result.amount) * -1)
|
||||
}}</span>)
|
||||
</span>
|
||||
<span v-if="result.type === 'deposit'">
|
||||
(<span class="text-success">{{
|
||||
Intl.NumberFormat(locale, {
|
||||
style: 'currency',
|
||||
currency: result.currency_code
|
||||
}).format(parseFloat(result.amount))
|
||||
}}</span>)
|
||||
</span>
|
||||
<span v-if="result.type === 'transfer'">
|
||||
(<span class="text-info">{{
|
||||
Intl.NumberFormat(locale, {
|
||||
style: 'currency',
|
||||
currency: result.currency_code
|
||||
}).format(parseFloat(result.amount))
|
||||
}}</span>)
|
||||
</span>
|
||||
<br/>
|
||||
<em>
|
||||
<a :href="'./accounts/show/' + result.source_id">{{ result.source_name }}</a>
|
||||
→
|
||||
<a :href="'./accounts/show/' + result.destination_id">{{ result.destination_name }}</a>
|
||||
</em>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['index', 'value'],
|
||||
name: "TransactionLinks",
|
||||
data() {
|
||||
return {
|
||||
searchResults: [],
|
||||
include: [],
|
||||
locale: 'en-US',
|
||||
linkTypes: [],
|
||||
query: '',
|
||||
searching: false
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.locale = localStorage.locale ?? 'en-US';
|
||||
this.getLinkTypes();
|
||||
},
|
||||
watch: {
|
||||
value: function (value) {
|
||||
console.log('Selected transactions is now:');
|
||||
console.log(value);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getTextForLinkType: function (linkTypeId) {
|
||||
let parts = linkTypeId.split('-');
|
||||
for (let i in this.linkTypes) {
|
||||
if (this.linkTypes.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
|
||||
let current = this.linkTypes[i];
|
||||
console.log(parts);
|
||||
console.log(current);
|
||||
if (parts[0] === current.id && parts[1] === current.direction) {
|
||||
return current.type;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 'text for #' + linkTypeId;
|
||||
},
|
||||
selectTransaction: function (event) {
|
||||
for (let i in this.searchResults) {
|
||||
if (this.searchResults.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
|
||||
let current = this.searchResults[i];
|
||||
if (current.selected) {
|
||||
this.addToSelected(current);
|
||||
}
|
||||
if (!current.selected) {
|
||||
// remove from
|
||||
this.removeFromSelected(current);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
selectLinkType: function (event) {
|
||||
for (let i in this.searchResults) {
|
||||
if (this.searchResults.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
|
||||
let current = this.searchResults[i];
|
||||
this.updateSelected(current.transaction_journal_id, current.link_type_id);
|
||||
}
|
||||
}
|
||||
},
|
||||
updateSelected(journalId, linkTypeId) {
|
||||
for (let i in this.value) {
|
||||
if (this.value.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
|
||||
let current = this.value[i];
|
||||
if (parseInt(current.transaction_journal_id) === journalId) {
|
||||
this.value[i].link_type_id = linkTypeId;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
addToSelected(journal) {
|
||||
const result = this.value.find(({transaction_journal_id}) => transaction_journal_id === journal.transaction_journal_id);
|
||||
if (typeof result === 'undefined') {
|
||||
this.value.push(journal);
|
||||
}
|
||||
},
|
||||
removeFromSelected(journal) {
|
||||
for (let i in this.value) {
|
||||
if (this.value.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
|
||||
let current = this.value[i];
|
||||
if (current.transaction_journal_id === journal.transaction_journal_id) {
|
||||
this.value.splice(parseInt(i), 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
getLinkTypes: function () {
|
||||
let url = './api/v1/link_types';
|
||||
axios.get(url)
|
||||
.then(response => {
|
||||
this.parseLinkTypes(response.data);
|
||||
}
|
||||
);
|
||||
},
|
||||
parseLinkTypes: function (data) {
|
||||
for (let i in data.data) {
|
||||
if (data.data.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
|
||||
let current = data.data[i];
|
||||
let linkTypeInward = {
|
||||
id: current.id,
|
||||
type: current.attributes.inward,
|
||||
direction: 'inward'
|
||||
};
|
||||
let linkTypeOutward = {
|
||||
id: current.id,
|
||||
type: current.attributes.outward,
|
||||
direction: 'outward'
|
||||
};
|
||||
if (linkTypeInward.type === linkTypeOutward.type) {
|
||||
linkTypeInward.type = linkTypeInward.type + ' (←)';
|
||||
linkTypeOutward.type = linkTypeOutward.type + ' (→)';
|
||||
}
|
||||
this.linkTypes.push(linkTypeInward);
|
||||
this.linkTypes.push(linkTypeOutward);
|
||||
}
|
||||
}
|
||||
},
|
||||
search: function () {
|
||||
this.searching = true;
|
||||
this.searchResults = [];
|
||||
let url = './api/v1/search/transactions?limit=10&query=' + this.query;
|
||||
axios.get(url)
|
||||
.then(response => {
|
||||
this.parseSearch(response.data);
|
||||
}
|
||||
);
|
||||
},
|
||||
parseSearch: function (data) {
|
||||
for (let i in data.data) {
|
||||
if (data.data.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
|
||||
for (let ii in data.data[i].attributes.transactions) {
|
||||
if (data.data[i].attributes.transactions.hasOwnProperty(ii) && /^0$|^[1-9]\d*$/.test(ii) && ii <= 4294967294) {
|
||||
let current = data.data[i].attributes.transactions[ii];
|
||||
current.transaction_group_id = parseInt(data.data[i].id);
|
||||
current.selected = this.isJournalSelected(current.transaction_journal_id);
|
||||
current.link_type_id = this.getJournalLinkType(current.transaction_journal_id);
|
||||
current.link_type_text = '';
|
||||
this.searchResults.push(current);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.searching = false;
|
||||
},
|
||||
getJournalLinkType: function (journalId) {
|
||||
for (let i in this.value) {
|
||||
if (this.value.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
|
||||
let current = this.value[i];
|
||||
if (current.transaction_journal_id === journalId) {
|
||||
return current.link_type_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
return '1-inward';
|
||||
},
|
||||
isJournalSelected: function (journalId) {
|
||||
for (let i in this.value) {
|
||||
if (this.value.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
|
||||
let current = this.value[i];
|
||||
if (current.transaction_journal_id === journalId) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
@@ -64,7 +64,7 @@ export default {
|
||||
locale: 'en-US'
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
created() {
|
||||
this.locale = localStorage.locale ?? 'en-US';
|
||||
},
|
||||
methods: {
|
||||
|
@@ -22,8 +22,8 @@
|
||||
"bill": "Contract",
|
||||
"no_bill": "(geen contract)",
|
||||
"tags": "Tags",
|
||||
"internal_reference": "Internal reference",
|
||||
"external_url": "External URL",
|
||||
"internal_reference": "Interne referentie",
|
||||
"external_url": "Externe URL",
|
||||
"no_piggy_bank": "(geen spaarpotje)",
|
||||
"paid": "Betaald",
|
||||
"notes": "Notities",
|
||||
|
@@ -22,8 +22,8 @@
|
||||
"bill": "\u00da\u010det",
|
||||
"no_bill": "(\u017eiadny \u00fa\u010det)",
|
||||
"tags": "\u0160t\u00edtky",
|
||||
"internal_reference": "Internal reference",
|
||||
"external_url": "External URL",
|
||||
"internal_reference": "Intern\u00e1 referencia",
|
||||
"external_url": "Extern\u00e1 URL",
|
||||
"no_piggy_bank": "(\u017eiadna pokladni\u010dka)",
|
||||
"paid": "Uhraden\u00e9",
|
||||
"notes": "Pozn\u00e1mky",
|
||||
|
Reference in New Issue
Block a user