Allow to edit accounts
Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
parent
7886365acc
commit
ed3f281092
3 changed files with 211 additions and 5 deletions
153
frontend/components/accountEditor.vue
Normal file
153
frontend/components/accountEditor.vue
Normal file
|
@ -0,0 +1,153 @@
|
|||
<template>
|
||||
<div
|
||||
id="editAccountModal"
|
||||
ref="editAccountModal"
|
||||
class="modal fade"
|
||||
tabindex="-1"
|
||||
aria-labelledby="editAccountModalLabel"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<div class="modal-dialog modal-sm">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1
|
||||
id="editAccountModalLabel"
|
||||
class="modal-title fs-5"
|
||||
>
|
||||
Edit Account
|
||||
</h1>
|
||||
<button
|
||||
type="button"
|
||||
class="btn-close"
|
||||
data-bs-dismiss="modal"
|
||||
aria-label="Close"
|
||||
/>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="mb-3">
|
||||
<label
|
||||
for="editAccountModalName"
|
||||
class="form-label"
|
||||
>Name</label>
|
||||
<input
|
||||
id="editAccountModalName"
|
||||
v-model="form.name"
|
||||
class="form-control"
|
||||
type="text"
|
||||
@keypress.enter="updateAccount"
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<div class="form-check">
|
||||
<input
|
||||
id="editAccountModalHidden"
|
||||
v-model="form.hidden"
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
:disabled="form.balance !== 0"
|
||||
>
|
||||
<label
|
||||
for="editAccountModalHidden"
|
||||
class="form-check-label"
|
||||
>Hide account</label>
|
||||
</div>
|
||||
<div class="form-text">
|
||||
Note: You cannot un-hide the account using UI, you need to unhide it via API.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-primary"
|
||||
@click="updateAccount"
|
||||
>
|
||||
<i class="fas fa-fw fa-pencil mr-1" />
|
||||
Update
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Modal } from 'bootstrap'
|
||||
import { responseToJSON } from '../helpers'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {
|
||||
balance: 0,
|
||||
hidden: false,
|
||||
name: '',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
emits: ['editClosed', 'editComplete'],
|
||||
|
||||
methods: {
|
||||
updateAccount() {
|
||||
if (!this.account) {
|
||||
return
|
||||
}
|
||||
|
||||
const update = new URLSearchParams()
|
||||
|
||||
if (this.form.name !== this.account.name) {
|
||||
update.set('name', this.form.name)
|
||||
}
|
||||
|
||||
if (this.form.hidden !== this.account.hidden) {
|
||||
update.set('hidden', String(this.form.hidden))
|
||||
}
|
||||
|
||||
if (update.toString().length === 0) {
|
||||
// No updates, why are we here?
|
||||
return
|
||||
}
|
||||
|
||||
return fetch(`/api/accounts/${this.account.id}?${update.toString()}`, {
|
||||
method: 'PATCH',
|
||||
})
|
||||
.then(responseToJSON)
|
||||
.then(() => this.$emit('editComplete'))
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$refs.editAccountModal
|
||||
.addEventListener('hidden.bs.modal', () => this.$emit('editClosed'))
|
||||
},
|
||||
|
||||
name: 'AccountingAppAccountEditor',
|
||||
|
||||
props: {
|
||||
account: {
|
||||
default: () => ({}),
|
||||
required: false,
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
account(to) {
|
||||
if (!to) {
|
||||
Modal.getOrCreateInstance(this.$refs.editAccountModal).hide()
|
||||
return
|
||||
}
|
||||
|
||||
this.form = {
|
||||
balance: to.balance,
|
||||
hidden: to.hidden,
|
||||
name: to.name,
|
||||
}
|
||||
|
||||
Modal.getOrCreateInstance(this.$refs.editAccountModal).show()
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
|
@ -2,8 +2,17 @@
|
|||
<div>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col d-flex fs-3 align-items-center text-semibold">
|
||||
{{ accountIdToName[accountId] }}
|
||||
<div
|
||||
class="col d-flex fs-3 align-items-center text-semibold"
|
||||
>
|
||||
<a
|
||||
class="text-white text-decoration-none"
|
||||
href="#"
|
||||
title="Click to Edit"
|
||||
@click.prevent="editedAccId = accountId"
|
||||
>
|
||||
{{ accountIdToName[accountId] }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="col d-flex align-items-center justify-content-end">
|
||||
<range-selector
|
||||
|
@ -306,6 +315,12 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<account-editor
|
||||
:account="editedAcc"
|
||||
@editClosed="editedAccId = null"
|
||||
@editComplete="$emit('update-accounts'); editedAccId = null"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -313,12 +328,13 @@
|
|||
/* eslint-disable sort-imports */
|
||||
import { Modal } from 'bootstrap'
|
||||
|
||||
import accountEditor from './accountEditor.vue'
|
||||
import { formatNumber } from '../helpers'
|
||||
import rangeSelector from './rangeSelector.vue'
|
||||
import txEditor from './txEditor.vue'
|
||||
|
||||
export default {
|
||||
components: { rangeSelector, txEditor },
|
||||
components: { accountEditor, rangeSelector, txEditor },
|
||||
|
||||
computed: {
|
||||
account() {
|
||||
|
@ -345,6 +361,13 @@ export default {
|
|||
return cats
|
||||
},
|
||||
|
||||
editedAcc() {
|
||||
if (!this.editedAccId) {
|
||||
return null
|
||||
}
|
||||
return this.accounts.filter(acc => acc.id === this.editedAccId)[0] || null
|
||||
},
|
||||
|
||||
selectedTx() {
|
||||
return Object.entries(this.selectedTxRaw)
|
||||
.filter(e => e[1])
|
||||
|
@ -392,6 +415,7 @@ export default {
|
|||
|
||||
data() {
|
||||
return {
|
||||
editedAccId: null,
|
||||
editedTxId: null,
|
||||
|
||||
modals: {
|
||||
|
@ -410,6 +434,8 @@ export default {
|
|||
}
|
||||
},
|
||||
|
||||
emits: ['update-accounts'],
|
||||
|
||||
methods: {
|
||||
deleteSelected() {
|
||||
const actions = []
|
||||
|
|
|
@ -49,7 +49,16 @@
|
|||
v-for="cat in categories"
|
||||
:key="cat.id"
|
||||
>
|
||||
<td>{{ cat.name }}</td>
|
||||
<td>
|
||||
<a
|
||||
class="text-white text-decoration-none"
|
||||
href="#"
|
||||
title="Click to Edit"
|
||||
@click.prevent="editedAccId = cat.id"
|
||||
>
|
||||
{{ cat.name }}
|
||||
</a>
|
||||
</td>
|
||||
<td :class="{'text-end': true, 'text-danger': (allocatedByCategory[cat.id] || 0) < 0}">
|
||||
{{ formatNumber(allocatedByCategory[cat.id] || 0) }} €
|
||||
</td>
|
||||
|
@ -170,6 +179,12 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<account-editor
|
||||
:account="editedAcc"
|
||||
@editClosed="editedAccId = null"
|
||||
@editComplete="$emit('update-accounts'); editedAccId = null"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -177,12 +192,13 @@
|
|||
/* eslint-disable sort-imports */
|
||||
import { Modal } from 'bootstrap'
|
||||
|
||||
import accountEditor from './accountEditor.vue'
|
||||
import { formatNumber } from '../helpers'
|
||||
import rangeSelector from './rangeSelector.vue'
|
||||
import { unallocatedMoneyAcc } from '../constants'
|
||||
|
||||
export default {
|
||||
components: { rangeSelector },
|
||||
components: { accountEditor, rangeSelector },
|
||||
|
||||
computed: {
|
||||
activityByCategory() {
|
||||
|
@ -211,6 +227,13 @@ export default {
|
|||
return accounts
|
||||
},
|
||||
|
||||
editedAcc() {
|
||||
if (!this.editedAccId) {
|
||||
return null
|
||||
}
|
||||
return this.accounts.filter(acc => acc.id === this.editedAccId)[0] || null
|
||||
},
|
||||
|
||||
transferModalValid() {
|
||||
if (!this.modals.createTransfer.from || !this.modals.createTransfer.to) {
|
||||
return false
|
||||
|
@ -254,6 +277,8 @@ export default {
|
|||
|
||||
data() {
|
||||
return {
|
||||
editedAccId: null,
|
||||
|
||||
modals: {
|
||||
createTransfer: {
|
||||
amount: 0,
|
||||
|
@ -267,6 +292,8 @@ export default {
|
|||
}
|
||||
},
|
||||
|
||||
emits: ['update-accounts'],
|
||||
|
||||
methods: {
|
||||
fetchTransactions() {
|
||||
const since = this.timeRange.start.toISOString()
|
||||
|
|
Loading…
Reference in a new issue