create transaction template and modify template name in edit dialog

This commit is contained in:
MaysWind
2024-07-31 23:59:37 +08:00
parent b0b330903c
commit a03df7ed36
12 changed files with 236 additions and 366 deletions
+16 -159
View File
@@ -6,12 +6,12 @@
<div class="title-and-toolbar d-flex align-center">
<span>{{ $t('Transaction Templates') }}</span>
<v-btn class="ml-3" color="default" variant="outlined"
:disabled="loading || updating || hasEditingTemplateName" @click="add">{{ $t('Add') }}</v-btn>
:disabled="loading || updating" @click="add">{{ $t('Add') }}</v-btn>
<v-btn class="ml-3" color="primary" variant="tonal"
:disabled="loading || updating || hasEditingTemplateName" @click="saveSortResult"
:disabled="loading || updating" @click="saveSortResult"
v-if="displayOrderModified">{{ $t('Save Display Order') }}</v-btn>
<v-btn density="compact" color="default" variant="text" size="24"
class="ml-2" :icon="true" :disabled="loading || updating || hasEditingTemplateName"
class="ml-2" :icon="true" :disabled="loading || updating"
:loading="loading" @click="reload">
<template #loader>
<v-progress-circular indeterminate size="20"/>
@@ -21,7 +21,7 @@
</v-btn>
<v-spacer/>
<v-btn density="comfortable" color="default" variant="text" class="ml-2"
:disabled="loading || updating || hasEditingTemplateName" :icon="true">
:disabled="loading || updating" :icon="true">
<v-icon :icon="icons.more" />
<v-menu activator="parent">
<v-list>
@@ -50,7 +50,7 @@
</tr>
</thead>
<tbody v-if="loading && noAvailableTemplate && !newTemplate">
<tbody v-if="loading && noAvailableTemplate">
<tr :key="itemIdx" v-for="itemIdx in [ 1, 2, 3 ]">
<td class="px-0">
<v-skeleton-loader type="text" :loading="true"></v-skeleton-loader>
@@ -58,7 +58,7 @@
</tr>
</tbody>
<tbody v-if="!loading && noAvailableTemplate && !newTemplate">
<tbody v-if="!loading && noAvailableTemplate">
<tr>
<td>{{ $t('No available template') }}</td>
</tr>
@@ -68,7 +68,6 @@
item-key="id"
handle=".drag-handle"
ghost-class="dragging-item"
:class="{ 'has-bottom-border': newTemplate }"
:disabled="noAvailableTemplate"
v-model="templates"
@change="onMove">
@@ -76,7 +75,7 @@
<tr class="transaction-templates-table-row text-sm" v-if="showHidden || !element.hidden">
<td>
<div class="d-flex align-center">
<div class="d-flex align-center" v-if="editingTemplateName.id !== element.id">
<div class="d-flex align-center">
<v-badge class="right-bottom-icon" color="secondary"
location="bottom right" offset-x="8" :icon="icons.hide"
v-if="element.hidden">
@@ -86,24 +85,6 @@
<span class="transaction-template-name">{{ element.name }}</span>
</div>
<v-text-field class="w-100 mr-2" type="text"
density="compact" variant="underlined"
:disabled="loading || updating"
:placeholder="$t('Template Name')"
v-model="editingTemplateName.name"
v-else-if="editingTemplateName.id === element.id"
@keyup.enter="saveName(editingTemplateName)"
>
<template #prepend>
<v-badge class="right-bottom-icon" color="secondary"
location="bottom right" offset-x="8" :icon="icons.hide"
v-if="element.hidden">
<v-icon size="20" start :icon="icons.text"/>
</v-badge>
<v-icon size="20" start :icon="icons.text" v-else-if="!element.hidden"/>
</template>
</v-text-field>
<v-spacer/>
<v-btn class="px-2 ml-2" color="default"
@@ -112,7 +93,6 @@
:prepend-icon="element.hidden ? icons.show : icons.hide"
:loading="templateHiding[element.id]"
:disabled="loading || updating"
v-if="editingTemplateName.id !== element.id"
@click="hide(element, !element.hidden)">
<template #loader>
<v-progress-circular indeterminate size="20" width="2"/>
@@ -123,22 +103,7 @@
density="comfortable" variant="text"
:class="{ 'd-none': loading, 'hover-display': !loading }"
:prepend-icon="icons.edit"
:loading="templateNameUpdating[element.id]"
:disabled="loading || updating"
v-if="editingTemplateName.id !== element.id"
@click="modifyName(element)">
<template #loader>
<v-progress-circular indeterminate size="20" width="2"/>
</template>
{{ $t('Modify Name') }}
</v-btn>
<v-btn class="px-2" color="default"
density="comfortable" variant="text"
:class="{ 'd-none': loading, 'hover-display': !loading }"
:prepend-icon="icons.edit"
:loading="templateNameUpdating[element.id]"
:disabled="loading || updating"
v-if="editingTemplateName.id !== element.id"
@click="edit(element)">
<template #loader>
<v-progress-circular indeterminate size="20" width="2"/>
@@ -151,31 +116,12 @@
:prepend-icon="icons.remove"
:loading="templateRemoving[element.id]"
:disabled="loading || updating"
v-if="editingTemplateName.id !== element.id"
@click="remove(element)">
<template #loader>
<v-progress-circular indeterminate size="20" width="2"/>
</template>
{{ $t('Delete') }}
</v-btn>
<v-btn class="px-2"
density="comfortable" variant="text"
:prepend-icon="icons.confirm"
:loading="templateNameUpdating[element.id]"
:disabled="loading || updating || !isTemplateModified(element)"
v-if="editingTemplateName.id === element.id" @click="saveName(editingTemplateName)">
<template #loader>
<v-progress-circular indeterminate size="20" width="2"/>
</template>
{{ $t('Save') }}
</v-btn>
<v-btn class="px-2" color="default"
density="comfortable" variant="text"
:prepend-icon="icons.cancel"
:disabled="loading || updating"
v-if="editingTemplateName.id === element.id" @click="cancelSaveName(editingTemplateName)">
{{ $t('Cancel') }}
</v-btn>
<span class="ml-2">
<v-icon :class="!loading && !updating && availableTemplateCount > 1 ? 'drag-handle' : 'disabled'"
:icon="icons.drag"/>
@@ -186,52 +132,12 @@
</tr>
</template>
</draggable-list>
<tbody v-if="newTemplate">
<tr class="text-sm" :class="{ 'even-row': availableTemplateCount & 1 === 1}">
<td>
<div class="d-flex align-center">
<v-text-field class="w-100 mr-2" type="text" color="primary"
density="compact" variant="underlined"
:disabled="loading || updating" :placeholder="$t('Template Name')"
v-model="newTemplate.name" @keyup.enter="saveName(newTemplate)">
<template #prepend>
<v-icon size="20" start :icon="icons.text"/>
</template>
</v-text-field>
<v-spacer/>
<v-btn class="px-2" density="comfortable" variant="text"
:prepend-icon="icons.confirm"
:loading="templateNameUpdating[null]"
:disabled="loading || updating || !isTemplateModified(newTemplate)"
@click="saveName(newTemplate)">
<template #loader>
<v-progress-circular indeterminate size="20" width="2"/>
</template>
{{ $t('Save') }}
</v-btn>
<v-btn class="px-2" color="default"
density="comfortable" variant="text"
:prepend-icon="icons.cancel"
:disabled="loading || updating"
@click="cancelSaveName(newTemplate)">
{{ $t('Cancel') }}
</v-btn>
<span class="ml-2">
<v-icon class="disabled" :icon="icons.drag"/>
</span>
</div>
</td>
</tr>
</tbody>
</v-table>
</v-card>
</v-col>
</v-row>
<edit-dialog ref="editDialog" :persistent="true" />
<edit-dialog ref="editDialog" type="template" :persistent="true" />
<confirm-dialog ref="confirmDialog"/>
<snack-bar ref="snackbar" />
@@ -244,7 +150,6 @@ import { mapStores } from 'pinia';
import { useTransactionTemplatesStore } from '@/stores/transactionTemplate.js';
import templateConstants from '@/consts/template.js';
import { generateRandomUUID } from '@/lib/misc.js';
import {
mdiRefresh,
@@ -267,11 +172,8 @@ export default {
data() {
return {
templateType: templateConstants.allTemplateTypes.Normal,
newTemplate: null,
editingTemplateName: {},
loading: true,
updating: false,
templateNameUpdating: {},
templateHiding: {},
templateRemoving: {},
displayOrderModified: false,
@@ -315,9 +217,6 @@ export default {
}
return count;
},
hasEditingTemplateName() {
return !!(this.newTemplate || (this.editingTemplateName && this.editingTemplateName.id && this.editingTemplateName.id !== ''));
}
},
created() {
@@ -340,10 +239,6 @@ export default {
},
methods: {
reload() {
if (this.hasEditingTemplateName) {
return;
}
const self = this;
self.loading = true;
@@ -410,56 +305,27 @@ export default {
});
},
add() {
this.newTemplate = {
templateType: this.templateType,
name: '',
clientSessionId: generateRandomUUID()
};
},
modifyName(template) {
this.editingTemplateName.id = template.id;
this.editingTemplateName.templateType = template.templateType;
this.editingTemplateName.name = template.name;
},
saveName(template) {
const self = this;
self.updating = true;
self.templateNameUpdating[template.id || null] = true;
self.transactionTemplatesStore.saveTemplateName({
template: template
}).then(() => {
self.updating = false;
self.templateNameUpdating[template.id || null] = false;
if (template.id) {
self.editingTemplateName = {};
} else {
self.newTemplate = null;
self.$refs.editDialog.open({
templateType: self.templateType
}).then(result => {
if (result && result.message) {
self.$refs.snackbar.showMessage(result.message);
}
}).catch(error => {
self.updating = false;
self.templateNameUpdating[template.id || null] = false;
if (!error.processed) {
if (error) {
self.$refs.snackbar.showError(error);
}
});
},
cancelSaveName(template) {
if (template.id) {
this.editingTemplateName = {};
} else {
this.newTemplate = null;
}
},
edit(template) {
const self = this;
self.$refs.editDialog.open({
editTemplateId: template.id,
id: template.id,
currentTemplate: {
name: template.name,
type: template.type,
categoryId: template.categoryId,
sourceAccountId: template.sourceAccountId,
@@ -474,21 +340,12 @@ export default {
if (result && result.message) {
self.$refs.snackbar.showMessage(result.message);
}
self.reload(false);
}).catch(error => {
if (error) {
self.$refs.snackbar.showError(error);
}
});
},
isTemplateModified(template) {
if (template.id) {
return this.editingTemplateName && this.editingTemplateName.name !== '' && this.editingTemplateName.name !== template.name;
} else {
return template.name !== '';
}
},
hide(template, hidden) {
const self = this;
+1 -1
View File
@@ -480,7 +480,7 @@
:max-time="customMaxDatetime"
v-model:show="showCustomDateRangeDialog"
@dateRange:change="changeCustomDateFilter" />
<edit-dialog ref="editDialog" :persistent="true" />
<edit-dialog ref="editDialog" type="transaction" :persistent="true" />
<v-dialog width="800" v-model="showFilterAccountDialog">
<account-filter-settings-card type="transactionListCurrent" :dialog-mode="true"
@@ -38,18 +38,18 @@
</template>
<v-card-text class="d-flex flex-column flex-md-row mt-md-4 pt-0">
<div class="mb-4">
<v-tabs class="v-tabs-pill" direction="vertical" :class="{ 'readonly': mode !== 'add' && mode !== 'editTemplate' }"
<v-tabs class="v-tabs-pill" direction="vertical" :class="{ 'readonly': type === 'transaction' && mode !== 'add' }"
:disabled="loading || submitting" v-model="transaction.type">
<v-tab :value="allTransactionTypes.Expense" :disabled="mode !== 'add' && mode !== 'editTemplate' && transaction.type !== allTransactionTypes.Expense" v-if="transaction.type !== allTransactionTypes.ModifyBalance">
<v-tab :value="allTransactionTypes.Expense" :disabled="type === 'transaction' && mode !== 'add' && transaction.type !== allTransactionTypes.Expense" v-if="transaction.type !== allTransactionTypes.ModifyBalance">
<span>{{ $t('Expense') }}</span>
</v-tab>
<v-tab :value="allTransactionTypes.Income" :disabled="mode !== 'add' && mode !== 'editTemplate' && transaction.type !== allTransactionTypes.Income" v-if="transaction.type !== allTransactionTypes.ModifyBalance">
<v-tab :value="allTransactionTypes.Income" :disabled="type === 'transaction' && mode !== 'add' && transaction.type !== allTransactionTypes.Income" v-if="transaction.type !== allTransactionTypes.ModifyBalance">
<span>{{ $t('Income') }}</span>
</v-tab>
<v-tab :value="allTransactionTypes.Transfer" :disabled="mode !== 'add' && mode !== 'editTemplate' && transaction.type !== allTransactionTypes.Transfer" v-if="transaction.type !== allTransactionTypes.ModifyBalance">
<v-tab :value="allTransactionTypes.Transfer" :disabled="type === 'transaction' && mode !== 'add' && transaction.type !== allTransactionTypes.Transfer" v-if="transaction.type !== allTransactionTypes.ModifyBalance">
<span>{{ $t('Transfer') }}</span>
</v-tab>
<v-tab :value="allTransactionTypes.ModifyBalance" v-if="mode !== 'editTemplate' && transaction.type === allTransactionTypes.ModifyBalance">
<v-tab :value="allTransactionTypes.ModifyBalance" v-if="type === 'transaction' && transaction.type === allTransactionTypes.ModifyBalance">
<span>{{ $t('Modify Balance') }}</span>
</v-tab>
</v-tabs>
@@ -58,7 +58,7 @@
<v-tab value="basicInfo">
<span>{{ $t('Basic Information') }}</span>
</v-tab>
<v-tab value="map" :disabled="!transaction.geoLocation" v-if="mode !== 'editTemplate' && mapProvider">
<v-tab value="map" :disabled="!transaction.geoLocation" v-if="type === 'transaction' && mapProvider">
<span>{{ $t('Location on Map') }}</span>
</v-tab>
</v-tabs>
@@ -69,6 +69,16 @@
<v-window-item value="basicInfo">
<v-form class="mt-2">
<v-row>
<v-col cols="12" v-if="type === 'template'">
<v-text-field
type="text"
persistent-placeholder
:disabled="loading || submitting"
:label="$t('Template Name')"
:placeholder="$t('Template Name')"
v-model="transaction.name"
/>
</v-col>
<v-col cols="12" :md="transaction.type === allTransactionTypes.Transfer ? 6 : 12">
<amount-input class="transaction-edit-amount font-weight-bold"
:color="sourceAmountColor"
@@ -177,7 +187,7 @@
v-model="transaction.destinationAccountId">
</two-column-select>
</v-col>
<v-col cols="12" md="6" v-if="mode !== 'editTemplate'">
<v-col cols="12" md="6" v-if="type === 'transaction'">
<date-time-select
:readonly="mode === 'view'"
:disabled="loading || submitting"
@@ -185,7 +195,7 @@
v-model="transaction.time"
@error="showDateTimeError" />
</v-col>
<v-col cols="12" md="6" v-if="mode !== 'editTemplate'">
<v-col cols="12" md="6" v-if="type === 'transaction'">
<v-autocomplete
class="transaction-edit-timezone"
item-title="displayNameWithUtcOffset"
@@ -207,7 +217,7 @@
</template>
</v-autocomplete>
</v-col>
<v-col cols="12" md="12" v-if="mode !== 'editTemplate'">
<v-col cols="12" md="12" v-if="type === 'transaction'">
<v-select
persistent-placeholder
:readonly="mode === 'view'"
@@ -368,6 +378,7 @@ import {
export default {
props: [
'persistent',
'type',
'show'
],
expose: [
@@ -381,7 +392,7 @@ export default {
showState: false,
mode: 'add',
activeTab: 'basicInfo',
editTransactionId: null,
editId: null,
originalTransactionEditable: false,
clientSessionId: '',
loading: true,
@@ -404,15 +415,23 @@ export default {
computed: {
...mapStores(useSettingsStore, useUserStore, useAccountsStore, useTransactionCategoriesStore, useTransactionTagsStore, useTransactionsStore, useTransactionTemplatesStore, useExchangeRatesStore),
title() {
if (this.mode === 'add') {
return 'Add Transaction';
} else if (this.mode === 'edit') {
return 'Edit Transaction';
} else if (this.mode === 'editTemplate') {
return 'Edit Transaction Template';
} else {
return 'Transaction Detail';
if (this.type === 'transaction') {
if (this.mode === 'add') {
return 'Add Transaction';
} else if (this.mode === 'edit') {
return 'Edit Transaction';
} else {
return 'Transaction Detail';
}
} else if (this.type === 'template') {
if (this.mode === 'add') {
return 'Add Transaction Template';
} else if (this.mode === 'edit') {
return 'Edit Transaction Template';
}
}
return '';
},
saveButtonTitle() {
if (this.mode === 'add') {
@@ -636,31 +655,48 @@ export default {
self.transactionTagsStore.loadAllTags({ force: false })
];
if (options && options.id) {
if (options.currentTransaction) {
self.setTransaction(options.currentTransaction, options, true, true);
if (self.type === 'transaction') {
if (options && options.id) {
if (options.currentTransaction) {
self.setTransaction(options.currentTransaction, options, true, true);
}
self.mode = 'view';
self.editId = options.id;
promises.push(self.transactionsStore.getTransaction({ transactionId: self.editId }));
} else {
self.mode = 'add';
self.editId = null;
if (self.settingsStore.appSettings.autoGetCurrentGeoLocation
&& !self.geoLocationStatus && !self.transaction.geoLocation) {
self.updateGeoLocation(false);
}
}
} else if (self.type === 'template') {
self.transaction.name = '';
if (options && options.templateType) {
self.transaction.templateType = options.templateType;
}
self.mode = 'view';
self.editTransactionId = options.id;
if (options && options.id) {
if (options.currentTemplate) {
self.setTransaction(options.currentTemplate, options, false, false);
self.transaction.templateType = options.currentTemplate.templateType;
self.transaction.name = options.currentTemplate.name;
}
promises.push(self.transactionsStore.getTransaction({ transactionId: self.editTransactionId }));
} else if (options && options.editTemplateId) {
if (options.currentTemplate) {
self.setTransaction(options.currentTemplate, options, false, false);
}
self.mode = 'edit';
self.editId = options.id;
self.transaction.id = options.id;
self.mode = 'editTemplate';
self.transaction.id = options.editTemplateId;
promises.push(self.transactionTemplatesStore.getTemplate({ templateId: options.editTemplateId }));
} else {
self.mode = 'add';
self.editTransactionId = null;
if (self.settingsStore.appSettings.autoGetCurrentGeoLocation
&& !self.geoLocationStatus && !self.transaction.geoLocation) {
self.updateGeoLocation(false);
promises.push(self.transactionTemplatesStore.getTemplate({ templateId: self.editId }));
} else {
self.mode = 'add';
self.editId = null;
self.transaction.id = null;
}
}
@@ -675,21 +711,27 @@ export default {
}
Promise.all(promises).then(function (responses) {
if (self.editTransactionId && !responses[3]) {
if (self.editId && !responses[3]) {
if (self.reject) {
self.reject('Unable to retrieve transaction');
if (self.type === 'transaction') {
self.reject('Unable to retrieve transaction');
} else if (self.type === 'template') {
self.reject('Unable to retrieve template');
}
}
return;
}
if (options && options.id && responses[3]) {
if (self.type === 'transaction' && options && options.id && responses[3]) {
const transaction = responses[3];
self.setTransaction(transaction, options, true, true);
self.originalTransactionEditable = transaction.editable;
} else if (options && options.editTemplateId && responses[3]) {
} else if (self.type === 'template' && options && options.id && responses[3]) {
const template = responses[3];
self.setTransaction(template, options, false, false);
self.transaction.templateType = template.templateType;
self.transaction.name = template.name;
} else {
self.setTransaction(null, options, true, true);
}
@@ -716,7 +758,7 @@ export default {
save() {
const self = this;
if (self.mode === 'add' || self.mode === 'edit') {
if (self.type === 'transaction' && (self.mode === 'add' || self.mode === 'edit')) {
const doSubmit = function () {
self.submitting = true;
@@ -757,18 +799,26 @@ export default {
} else {
doSubmit();
}
} else if (self.mode === 'editTemplate') {
} else if (self.type === 'template' && (self.mode === 'add' || self.mode === 'edit')) {
self.submitting = true;
self.transactionTemplatesStore.modifyTemplateContent({
template: self.transaction
self.transactionTemplatesStore.saveTemplateContent({
template: self.transaction,
isEdit: self.mode === 'edit',
clientSessionId: self.clientSessionId
}).then(() => {
self.submitting = false;
if (self.resolve) {
self.resolve({
message: 'You have saved this template'
});
if (self.mode === 'add') {
self.resolve({
message: 'You have added a new template'
});
} else if (self.mode === 'edit') {
self.resolve({
message: 'You have saved this template'
});
}
}
self.showState = false;
@@ -782,11 +832,11 @@ export default {
}
},
duplicate() {
if (this.mode !== 'view') {
if (this.type !== 'transaction' || this.mode !== 'view') {
return;
}
this.editTransactionId = null;
this.editId = null;
this.transaction.id = null;
this.transaction.time = getCurrentUnixTime();
this.transaction.timeZone = this.settingsStore.appSettings.timeZone;
@@ -795,7 +845,7 @@ export default {
this.mode = 'add';
},
edit() {
if (this.mode !== 'view') {
if (this.type !== 'transaction' || this.mode !== 'view') {
return;
}
@@ -804,7 +854,7 @@ export default {
remove() {
const self = this;
if (self.mode !== 'view') {
if (this.type !== 'transaction' || self.mode !== 'view') {
return;
}