Allow to edit accounts

Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
Knut Ahlers 2024-01-22 21:37:15 +01:00
parent 7886365acc
commit ed3f281092
Signed by: luzifer
SSH Key Fingerprint: SHA256:/xtE5lCgiRDQr8SLxHMS92ZBlACmATUmF1crK16Ks4E
3 changed files with 211 additions and 5 deletions

View 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>

View File

@ -2,8 +2,17 @@
<div> <div>
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col d-flex fs-3 align-items-center text-semibold"> <div
{{ accountIdToName[accountId] }} 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>
<div class="col d-flex align-items-center justify-content-end"> <div class="col d-flex align-items-center justify-content-end">
<range-selector <range-selector
@ -306,6 +315,12 @@
</div> </div>
</div> </div>
</div> </div>
<account-editor
:account="editedAcc"
@editClosed="editedAccId = null"
@editComplete="$emit('update-accounts'); editedAccId = null"
/>
</div> </div>
</template> </template>
@ -313,12 +328,13 @@
/* eslint-disable sort-imports */ /* eslint-disable sort-imports */
import { Modal } from 'bootstrap' import { Modal } from 'bootstrap'
import accountEditor from './accountEditor.vue'
import { formatNumber } from '../helpers' import { formatNumber } from '../helpers'
import rangeSelector from './rangeSelector.vue' import rangeSelector from './rangeSelector.vue'
import txEditor from './txEditor.vue' import txEditor from './txEditor.vue'
export default { export default {
components: { rangeSelector, txEditor }, components: { accountEditor, rangeSelector, txEditor },
computed: { computed: {
account() { account() {
@ -345,6 +361,13 @@ export default {
return cats return cats
}, },
editedAcc() {
if (!this.editedAccId) {
return null
}
return this.accounts.filter(acc => acc.id === this.editedAccId)[0] || null
},
selectedTx() { selectedTx() {
return Object.entries(this.selectedTxRaw) return Object.entries(this.selectedTxRaw)
.filter(e => e[1]) .filter(e => e[1])
@ -392,6 +415,7 @@ export default {
data() { data() {
return { return {
editedAccId: null,
editedTxId: null, editedTxId: null,
modals: { modals: {
@ -410,6 +434,8 @@ export default {
} }
}, },
emits: ['update-accounts'],
methods: { methods: {
deleteSelected() { deleteSelected() {
const actions = [] const actions = []

View File

@ -49,7 +49,16 @@
v-for="cat in categories" v-for="cat in categories"
:key="cat.id" :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}"> <td :class="{'text-end': true, 'text-danger': (allocatedByCategory[cat.id] || 0) < 0}">
{{ formatNumber(allocatedByCategory[cat.id] || 0) }} {{ formatNumber(allocatedByCategory[cat.id] || 0) }}
</td> </td>
@ -170,6 +179,12 @@
</div> </div>
</div> </div>
</div> </div>
<account-editor
:account="editedAcc"
@editClosed="editedAccId = null"
@editComplete="$emit('update-accounts'); editedAccId = null"
/>
</div> </div>
</template> </template>
@ -177,12 +192,13 @@
/* eslint-disable sort-imports */ /* eslint-disable sort-imports */
import { Modal } from 'bootstrap' import { Modal } from 'bootstrap'
import accountEditor from './accountEditor.vue'
import { formatNumber } from '../helpers' import { formatNumber } from '../helpers'
import rangeSelector from './rangeSelector.vue' import rangeSelector from './rangeSelector.vue'
import { unallocatedMoneyAcc } from '../constants' import { unallocatedMoneyAcc } from '../constants'
export default { export default {
components: { rangeSelector }, components: { accountEditor, rangeSelector },
computed: { computed: {
activityByCategory() { activityByCategory() {
@ -211,6 +227,13 @@ export default {
return accounts return accounts
}, },
editedAcc() {
if (!this.editedAccId) {
return null
}
return this.accounts.filter(acc => acc.id === this.editedAccId)[0] || null
},
transferModalValid() { transferModalValid() {
if (!this.modals.createTransfer.from || !this.modals.createTransfer.to) { if (!this.modals.createTransfer.from || !this.modals.createTransfer.to) {
return false return false
@ -254,6 +277,8 @@ export default {
data() { data() {
return { return {
editedAccId: null,
modals: { modals: {
createTransfer: { createTransfer: {
amount: 0, amount: 0,
@ -267,6 +292,8 @@ export default {
} }
}, },
emits: ['update-accounts'],
methods: { methods: {
fetchTransactions() { fetchTransactions() {
const since = this.timeRange.start.toISOString() const since = this.timeRange.start.toISOString()