add add/edit transaction category dialog
This commit is contained in:
@@ -0,0 +1,132 @@
|
|||||||
|
<template>
|
||||||
|
<v-select
|
||||||
|
item-title="icon"
|
||||||
|
item-value="id"
|
||||||
|
persistent-placeholder
|
||||||
|
:disabled="disabled"
|
||||||
|
:label="label"
|
||||||
|
v-model="color"
|
||||||
|
@update:menu="onMenuStateChanged"
|
||||||
|
>
|
||||||
|
<template #selection="{ item }">
|
||||||
|
<v-label>
|
||||||
|
<v-icon :icon="icons.square" :color="`#${item.raw}`" />
|
||||||
|
</v-label>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #no-data>
|
||||||
|
<div class="color-select-dropdown" ref="dropdownMenu">
|
||||||
|
<div class="color-item" :class="{ 'row-has-selected-item': hasSelectedIcon(row) }"
|
||||||
|
:style="`grid-template-columns: repeat(${itemPerRow}, minmax(0, 1fr));`"
|
||||||
|
:key="idx" v-for="(row, idx) in allColorRows">
|
||||||
|
<div class="text-center" :key="colorInfo.color" v-for="colorInfo in row">
|
||||||
|
<div class="cursor-pointer" @click="color = colorInfo.color">
|
||||||
|
<v-icon class="ma-2" :icon="icons.square" :color="`#${colorInfo.color}`" v-if="!modelValue || modelValue !== colorInfo.color" />
|
||||||
|
<v-badge class="right-bottom-icon" color="primary"
|
||||||
|
location="bottom right" offset-x="8" offset-y="8" :icon="icons.checked"
|
||||||
|
v-if="modelValue && modelValue === colorInfo.color">
|
||||||
|
<v-icon class="ma-2" :icon="icons.square" :color="`#${colorInfo.color}`" />
|
||||||
|
</v-badge>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</v-select>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { scrollToSelectedItem } from '@/lib/ui.desktop.js';
|
||||||
|
|
||||||
|
import {
|
||||||
|
mdiSquareRounded,
|
||||||
|
mdiCheck
|
||||||
|
} from '@mdi/js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: [
|
||||||
|
'modelValue',
|
||||||
|
'disabled',
|
||||||
|
'label',
|
||||||
|
'columnCount',
|
||||||
|
'allColorInfos'
|
||||||
|
],
|
||||||
|
emits: [
|
||||||
|
'update:modelValue',
|
||||||
|
],
|
||||||
|
data() {
|
||||||
|
const self = this;
|
||||||
|
|
||||||
|
return {
|
||||||
|
itemPerRow: self.columnCount || 7,
|
||||||
|
icons: {
|
||||||
|
square: mdiSquareRounded,
|
||||||
|
checked: mdiCheck
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
allColorRows() {
|
||||||
|
const ret = [];
|
||||||
|
let rowCount = -1;
|
||||||
|
|
||||||
|
for (let i = 0; i < this.allColorInfos.length; i++) {
|
||||||
|
if (i % this.itemPerRow === 0) {
|
||||||
|
ret[++rowCount] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
ret[rowCount].push({
|
||||||
|
color: this.allColorInfos[i]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
get: function () {
|
||||||
|
return this.modelValue;
|
||||||
|
},
|
||||||
|
set: function (value) {
|
||||||
|
this.$emit('update:modelValue', value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
hasSelectedIcon(row) {
|
||||||
|
if (!this.modelValue || !row || !row.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < row.length; i++) {
|
||||||
|
if (row[i].id === this.modelValue) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
onMenuStateChanged(state) {
|
||||||
|
const self = this;
|
||||||
|
|
||||||
|
if (state) {
|
||||||
|
self.$nextTick(() => {
|
||||||
|
if (self.$refs.dropdownMenu && self.$refs.dropdownMenu.parentElement) {
|
||||||
|
scrollToSelectedItem(self.$refs.dropdownMenu.parentElement, null, '.row-has-selected-item');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.color-select-dropdown {
|
||||||
|
padding-left: 8px;
|
||||||
|
padding-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-select-dropdown .color-item {
|
||||||
|
display: grid;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
<template>
|
||||||
|
<v-select
|
||||||
|
item-title="icon"
|
||||||
|
item-value="id"
|
||||||
|
persistent-placeholder
|
||||||
|
:disabled="disabled"
|
||||||
|
:label="label"
|
||||||
|
v-model="icon"
|
||||||
|
@update:menu="onMenuStateChanged"
|
||||||
|
>
|
||||||
|
<template #selection="{ item }">
|
||||||
|
<v-label>
|
||||||
|
<ItemIcon icon-type="category" size="23px" :icon-id="icon" :color="color" />
|
||||||
|
</v-label>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #no-data>
|
||||||
|
<div class="icon-select-dropdown" ref="dropdownMenu">
|
||||||
|
<div class="icon-item" :class="{ 'row-has-selected-item': hasSelectedIcon(row) }"
|
||||||
|
:style="`grid-template-columns: repeat(${itemPerRow}, minmax(0, 1fr));`"
|
||||||
|
:key="idx" v-for="(row, idx) in allIconRows">
|
||||||
|
<div class="text-center" :key="iconInfo.id" v-for="iconInfo in row">
|
||||||
|
<div class="cursor-pointer" @click="icon = iconInfo.id">
|
||||||
|
<ItemIcon class="ma-2" icon-type="fixed" :icon-id="iconInfo.icon" :color="color" v-if="!modelValue || modelValue !== iconInfo.id" />
|
||||||
|
<v-badge class="right-bottom-icon" color="primary"
|
||||||
|
location="bottom right" offset-x="8" offset-y="10" :icon="icons.checked"
|
||||||
|
v-if="modelValue && modelValue === iconInfo.id">
|
||||||
|
<ItemIcon class="ma-2" icon-type="fixed" :icon-id="iconInfo.icon" :color="color" />
|
||||||
|
</v-badge>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</v-select>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { scrollToSelectedItem } from '@/lib/ui.desktop.js';
|
||||||
|
|
||||||
|
import {
|
||||||
|
mdiCheck
|
||||||
|
} from '@mdi/js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: [
|
||||||
|
'modelValue',
|
||||||
|
'disabled',
|
||||||
|
'label',
|
||||||
|
'color',
|
||||||
|
'columnCount',
|
||||||
|
'allIconInfos'
|
||||||
|
],
|
||||||
|
emits: [
|
||||||
|
'update:modelValue',
|
||||||
|
],
|
||||||
|
data() {
|
||||||
|
const self = this;
|
||||||
|
|
||||||
|
return {
|
||||||
|
itemPerRow: self.columnCount || 7,
|
||||||
|
icons: {
|
||||||
|
checked: mdiCheck
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
allIconRows() {
|
||||||
|
const ret = [];
|
||||||
|
let rowCount = 0;
|
||||||
|
|
||||||
|
for (let iconInfoId in this.allIconInfos) {
|
||||||
|
if (!Object.prototype.hasOwnProperty.call(this.allIconInfos, iconInfoId)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const iconInfo = this.allIconInfos[iconInfoId];
|
||||||
|
|
||||||
|
if (!ret[rowCount]) {
|
||||||
|
ret[rowCount] = [];
|
||||||
|
} else if (ret[rowCount] && ret[rowCount].length >= this.itemPerRow) {
|
||||||
|
rowCount++;
|
||||||
|
ret[rowCount] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
ret[rowCount].push({
|
||||||
|
id: iconInfoId,
|
||||||
|
icon: iconInfo.icon
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
},
|
||||||
|
icon: {
|
||||||
|
get: function () {
|
||||||
|
return this.modelValue;
|
||||||
|
},
|
||||||
|
set: function (value) {
|
||||||
|
this.$emit('update:modelValue', value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
hasSelectedIcon(row) {
|
||||||
|
if (!this.modelValue || !row || !row.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < row.length; i++) {
|
||||||
|
if (row[i].id === this.modelValue) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
onMenuStateChanged(state) {
|
||||||
|
const self = this;
|
||||||
|
|
||||||
|
if (state) {
|
||||||
|
self.$nextTick(() => {
|
||||||
|
if (self.$refs.dropdownMenu && self.$refs.dropdownMenu.parentElement) {
|
||||||
|
scrollToSelectedItem(self.$refs.dropdownMenu.parentElement, null, '.row-has-selected-item');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.icon-select-dropdown {
|
||||||
|
padding-left: 8px;
|
||||||
|
padding-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-select-dropdown .icon-item {
|
||||||
|
display: grid;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -39,6 +39,7 @@ import { VSnackbar } from 'vuetify/components/VSnackbar';
|
|||||||
import { VSwitch } from 'vuetify/components/VSwitch';
|
import { VSwitch } from 'vuetify/components/VSwitch';
|
||||||
import { VTabs, VTab } from 'vuetify/components/VTabs';
|
import { VTabs, VTab } from 'vuetify/components/VTabs';
|
||||||
import { VTable } from 'vuetify/components/VTable';
|
import { VTable } from 'vuetify/components/VTable';
|
||||||
|
import { VTextarea } from 'vuetify/components/VTextarea';
|
||||||
import { VTextField } from 'vuetify/components/VTextField';
|
import { VTextField } from 'vuetify/components/VTextField';
|
||||||
import { VToolbar } from 'vuetify/components/VToolbar';
|
import { VToolbar } from 'vuetify/components/VToolbar';
|
||||||
import { VTooltip } from 'vuetify/components/VTooltip';
|
import { VTooltip } from 'vuetify/components/VTooltip';
|
||||||
@@ -82,6 +83,8 @@ import PinCodeInput from '@/components/common/PinCodeInput.vue';
|
|||||||
import ItemIcon from '@/components/desktop/ItemIcon.vue';
|
import ItemIcon from '@/components/desktop/ItemIcon.vue';
|
||||||
import BtnVerticalGroup from '@/components/desktop/BtnVerticalGroup.vue';
|
import BtnVerticalGroup from '@/components/desktop/BtnVerticalGroup.vue';
|
||||||
import AmountInput from '@/components/desktop/AmountInput.vue';
|
import AmountInput from '@/components/desktop/AmountInput.vue';
|
||||||
|
import ColorSelect from '@/components/desktop/ColorSelect.vue';
|
||||||
|
import IconSelect from '@/components/desktop/IconSelect.vue';
|
||||||
import StepsBar from '@/components/desktop/StepsBar.vue';
|
import StepsBar from '@/components/desktop/StepsBar.vue';
|
||||||
import ConfirmDialog from '@/components/desktop/ConfirmDialog.vue';
|
import ConfirmDialog from '@/components/desktop/ConfirmDialog.vue';
|
||||||
import SnackBar from '@/components/desktop/SnackBar.vue';
|
import SnackBar from '@/components/desktop/SnackBar.vue';
|
||||||
@@ -162,6 +165,7 @@ const vuetify = createVuetify({
|
|||||||
VTabs,
|
VTabs,
|
||||||
VTab,
|
VTab,
|
||||||
VTable,
|
VTable,
|
||||||
|
VTextarea,
|
||||||
VTextField,
|
VTextField,
|
||||||
VToolbar,
|
VToolbar,
|
||||||
VTooltip,
|
VTooltip,
|
||||||
@@ -392,6 +396,8 @@ app.component('PinCodeInput', PinCodeInput);
|
|||||||
app.component('ItemIcon', ItemIcon);
|
app.component('ItemIcon', ItemIcon);
|
||||||
app.component('BtnVerticalGroup', BtnVerticalGroup);
|
app.component('BtnVerticalGroup', BtnVerticalGroup);
|
||||||
app.component('AmountInput', AmountInput);
|
app.component('AmountInput', AmountInput);
|
||||||
|
app.component('ColorSelect', ColorSelect);
|
||||||
|
app.component('IconSelect', IconSelect);
|
||||||
app.component('StepsBar', StepsBar);
|
app.component('StepsBar', StepsBar);
|
||||||
app.component('ConfirmDialog', ConfirmDialog);
|
app.component('ConfirmDialog', ConfirmDialog);
|
||||||
app.component('SnackBar', SnackBar);
|
app.component('SnackBar', SnackBar);
|
||||||
|
|||||||
+12
-7
@@ -19,19 +19,24 @@ export function getCssValue(element, name) {
|
|||||||
return computedStyle.getPropertyValue(name);
|
return computedStyle.getPropertyValue(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function scrollToMenuListItem(listContentEl) {
|
export function scrollToSelectedItem(parentEl, containerSelector, selectedItemSelector) {
|
||||||
if (!listContentEl) {
|
if (!parentEl) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const lists = listContentEl.querySelectorAll('div.v-list');
|
let container = parentEl;
|
||||||
|
|
||||||
if (!lists.length || !lists[0]) {
|
if (containerSelector) {
|
||||||
return;
|
const lists = parentEl.querySelectorAll(containerSelector);
|
||||||
|
|
||||||
|
if (!lists.length || !lists[0]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
container = lists[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
const container = lists[0];
|
const selectedItems = container.querySelectorAll(selectedItemSelector);
|
||||||
const selectedItems = container.querySelectorAll('div.v-list-item.list-item-selected');
|
|
||||||
|
|
||||||
if (!selectedItems.length || !selectedItems[0]) {
|
if (!selectedItems.length || !selectedItems[0]) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -170,7 +170,8 @@
|
|||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
|
|
||||||
<preset-category-dialog :category-type="activeCategoryType" v-model:show="showPresetDialog"
|
<edit-dialog ref="editDialog" :persistent="true" />
|
||||||
|
<preset-dialog :category-type="activeCategoryType" v-model:show="showPresetDialog"
|
||||||
@category:saved="presetCategorySaved" />
|
@category:saved="presetCategorySaved" />
|
||||||
|
|
||||||
<confirm-dialog ref="confirmDialog"/>
|
<confirm-dialog ref="confirmDialog"/>
|
||||||
@@ -178,7 +179,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import PresetCategoryDialog from './list/dialogs/PresetDialog.vue';
|
import EditDialog from './list/dialogs/EditDialog.vue';
|
||||||
|
import PresetDialog from './list/dialogs/PresetDialog.vue';
|
||||||
|
|
||||||
import { useDisplay } from 'vuetify';
|
import { useDisplay } from 'vuetify';
|
||||||
|
|
||||||
@@ -201,7 +203,8 @@ import {
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
PresetCategoryDialog
|
EditDialog,
|
||||||
|
PresetDialog
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
const { mdAndUp } = useDisplay();
|
const { mdAndUp } = useDisplay();
|
||||||
@@ -376,10 +379,36 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
add() {
|
add() {
|
||||||
|
const self = this;
|
||||||
|
|
||||||
|
self.$refs.editDialog.open({
|
||||||
|
type: self.activeCategoryType,
|
||||||
|
parentId: self.primaryCategoryId
|
||||||
|
}).then(result => {
|
||||||
|
if (result && result.message) {
|
||||||
|
self.$refs.snackbar.showMessage(result.message);
|
||||||
|
}
|
||||||
|
}).catch(error => {
|
||||||
|
if (error) {
|
||||||
|
self.$refs.snackbar.showError(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
edit() {
|
edit(category) {
|
||||||
|
const self = this;
|
||||||
|
|
||||||
|
self.$refs.editDialog.open({
|
||||||
|
id: category.id,
|
||||||
|
currentCategory: category
|
||||||
|
}).then(result => {
|
||||||
|
if (result && result.message) {
|
||||||
|
self.$refs.snackbar.showMessage(result.message);
|
||||||
|
}
|
||||||
|
}).catch(error => {
|
||||||
|
if (error) {
|
||||||
|
self.$refs.snackbar.showError(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
hide(category, hidden) {
|
hide(category, hidden) {
|
||||||
const self = this;
|
const self = this;
|
||||||
|
|||||||
@@ -0,0 +1,275 @@
|
|||||||
|
<template>
|
||||||
|
<v-dialog width="600" :persistent="!!persistent" v-model="showState">
|
||||||
|
<v-card>
|
||||||
|
<v-toolbar color="primary">
|
||||||
|
<v-toolbar-title>
|
||||||
|
<span>{{ $t(title) }}</span>
|
||||||
|
<v-progress-circular indeterminate size="22" class="ml-2" v-if="loading"></v-progress-circular>
|
||||||
|
</v-toolbar-title>
|
||||||
|
</v-toolbar>
|
||||||
|
<v-card-text class="pa-4 mt-2">
|
||||||
|
<v-row>
|
||||||
|
<v-col cols="12" md="12">
|
||||||
|
<v-text-field
|
||||||
|
type="text"
|
||||||
|
clearable
|
||||||
|
persistent-placeholder
|
||||||
|
:disabled="loading || submitting"
|
||||||
|
:label="$t('Category Name')"
|
||||||
|
:placeholder="$t('Category Name')"
|
||||||
|
v-model="category.name"
|
||||||
|
/>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12" md="6">
|
||||||
|
<icon-select :all-icon-infos="allCategoryIcons"
|
||||||
|
:label="$t('Category Icon')"
|
||||||
|
:color="category.color"
|
||||||
|
:disabled="loading || submitting"
|
||||||
|
v-model="category.icon" />
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12" md="6">
|
||||||
|
<color-select :all-color-infos="allCategoryColors"
|
||||||
|
:label="$t('Category Color')"
|
||||||
|
:disabled="loading || submitting"
|
||||||
|
v-model="category.color" />
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12" md="12">
|
||||||
|
<v-textarea
|
||||||
|
type="text"
|
||||||
|
persistent-placeholder
|
||||||
|
:disabled="loading || submitting"
|
||||||
|
:label="$t('Description')"
|
||||||
|
:placeholder="$t('Your category description (optional)')"
|
||||||
|
v-model="category.comment"
|
||||||
|
/>
|
||||||
|
</v-col>
|
||||||
|
<v-col class="pt-0" cols="12" md="12" v-if="editCategoryId">
|
||||||
|
<v-switch inset :disabled="loading || submitting"
|
||||||
|
:label="$t('Visible')" v-model="category.visible"/>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-card-text>
|
||||||
|
<v-card-actions>
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
<v-btn color="gray" :disabled="loading || submitting" @click="cancel">{{ $t('Cancel') }}</v-btn>
|
||||||
|
<v-btn :disabled="inputIsEmpty || loading || submitting" @click="save">
|
||||||
|
{{ $t(saveButtonTitle) }}
|
||||||
|
<v-progress-circular indeterminate size="24" class="ml-2" v-if="submitting"></v-progress-circular>
|
||||||
|
</v-btn>
|
||||||
|
</v-card-actions>
|
||||||
|
</v-card>
|
||||||
|
</v-dialog>
|
||||||
|
|
||||||
|
<snack-bar ref="snackbar" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapStores } from 'pinia';
|
||||||
|
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.js';
|
||||||
|
|
||||||
|
import categoryConstants from '@/consts/category.js';
|
||||||
|
import iconConstants from '@/consts/icon.js';
|
||||||
|
import colorConstants from '@/consts/color.js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: [
|
||||||
|
'persistent',
|
||||||
|
'show'
|
||||||
|
],
|
||||||
|
expose: [
|
||||||
|
'open'
|
||||||
|
],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showState: false,
|
||||||
|
editCategoryId: null,
|
||||||
|
loading: false,
|
||||||
|
category: {
|
||||||
|
type: categoryConstants.allCategoryTypes.Income,
|
||||||
|
name: '',
|
||||||
|
parentId: '0',
|
||||||
|
icon: iconConstants.defaultCategoryIconId,
|
||||||
|
color: colorConstants.defaultCategoryColor,
|
||||||
|
comment: '',
|
||||||
|
visible: true
|
||||||
|
},
|
||||||
|
submitting: false,
|
||||||
|
resolve: null,
|
||||||
|
reject: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapStores(useTransactionCategoriesStore),
|
||||||
|
title() {
|
||||||
|
if (!this.editCategoryId) {
|
||||||
|
if (this.category.parentId === '0') {
|
||||||
|
return 'Add Primary Category';
|
||||||
|
} else {
|
||||||
|
return 'Add Secondary Category';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return 'Edit Category';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
saveButtonTitle() {
|
||||||
|
if (!this.editCategoryId) {
|
||||||
|
return 'Add';
|
||||||
|
} else {
|
||||||
|
return 'Save';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
allCategoryIcons() {
|
||||||
|
return iconConstants.allCategoryIcons;
|
||||||
|
},
|
||||||
|
allCategoryColors() {
|
||||||
|
return colorConstants.allCategoryColors;
|
||||||
|
},
|
||||||
|
inputIsEmpty() {
|
||||||
|
return !!this.inputEmptyProblemMessage;
|
||||||
|
},
|
||||||
|
inputEmptyProblemMessage() {
|
||||||
|
if (!this.category.name) {
|
||||||
|
return 'Category name cannot be empty';
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
open(options) {
|
||||||
|
const self = this;
|
||||||
|
self.showState = true;
|
||||||
|
self.loading = true;
|
||||||
|
self.submitting = false;
|
||||||
|
|
||||||
|
self.category.id = null;
|
||||||
|
self.category.type = categoryConstants.allCategoryTypes.Income;
|
||||||
|
self.category.parentId = '0';
|
||||||
|
self.category.name = '';
|
||||||
|
self.category.icon = iconConstants.defaultCategoryIconId;
|
||||||
|
self.category.color = colorConstants.defaultCategoryColor;
|
||||||
|
self.category.comment = '';
|
||||||
|
self.category.visible = true;
|
||||||
|
|
||||||
|
if (options.id) {
|
||||||
|
if (options.currentCategory) {
|
||||||
|
self.setCategory(options.currentCategory);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.editCategoryId = options.id;
|
||||||
|
self.transactionCategoriesStore.getCategory({
|
||||||
|
categoryId: self.editCategoryId
|
||||||
|
}).then(category => {
|
||||||
|
self.setCategory(category);
|
||||||
|
self.loading = false;
|
||||||
|
}).catch(error => {
|
||||||
|
self.loading = false;
|
||||||
|
self.showState = false;
|
||||||
|
|
||||||
|
if (!error.processed) {
|
||||||
|
if (self.reject) {
|
||||||
|
self.reject(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (options.parentId) {
|
||||||
|
self.editCategoryId = null;
|
||||||
|
|
||||||
|
const categoryType = parseInt(options.type);
|
||||||
|
|
||||||
|
if (categoryType !== categoryConstants.allCategoryTypes.Income &&
|
||||||
|
categoryType !== categoryConstants.allCategoryTypes.Expense &&
|
||||||
|
categoryType !== categoryConstants.allCategoryTypes.Transfer) {
|
||||||
|
self.loading = false;
|
||||||
|
self.showState = false;
|
||||||
|
|
||||||
|
if (self.reject) {
|
||||||
|
self.reject('Parameter Invalid');
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.category.type = categoryType;
|
||||||
|
self.category.parentId = options.parentId;
|
||||||
|
|
||||||
|
self.loading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
self.resolve = resolve;
|
||||||
|
self.reject = reject;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
save() {
|
||||||
|
const self = this;
|
||||||
|
|
||||||
|
const problemMessage = self.inputEmptyProblemMessage;
|
||||||
|
|
||||||
|
if (problemMessage) {
|
||||||
|
self.$refs.snackbar.showMessage(problemMessage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.submitting = true;
|
||||||
|
|
||||||
|
const submitCategory = {
|
||||||
|
type: self.category.type,
|
||||||
|
name: self.category.name,
|
||||||
|
parentId: self.category.parentId,
|
||||||
|
icon: self.category.icon,
|
||||||
|
color: self.category.color,
|
||||||
|
comment: self.category.comment
|
||||||
|
};
|
||||||
|
|
||||||
|
if (self.editCategoryId) {
|
||||||
|
submitCategory.id = self.category.id;
|
||||||
|
submitCategory.hidden = !self.category.visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.transactionCategoriesStore.saveCategory({
|
||||||
|
category: submitCategory
|
||||||
|
}).then(() => {
|
||||||
|
self.submitting = false;
|
||||||
|
|
||||||
|
let message = 'You have saved this category';
|
||||||
|
|
||||||
|
if (!self.editCategoryId) {
|
||||||
|
message = 'You have added a new category';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self.resolve) {
|
||||||
|
self.resolve({
|
||||||
|
message: message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
self.showState = false;
|
||||||
|
}).catch(error => {
|
||||||
|
self.submitting = false;
|
||||||
|
|
||||||
|
if (!error.processed) {
|
||||||
|
self.$refs.snackbar.showError(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
cancel() {
|
||||||
|
if (this.reject) {
|
||||||
|
this.reject();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.showState = false;
|
||||||
|
},
|
||||||
|
setCategory(category) {
|
||||||
|
this.category.id = category.id;
|
||||||
|
this.category.type = category.type;
|
||||||
|
this.category.parentId = category.type.parentId;
|
||||||
|
this.category.name = category.name;
|
||||||
|
this.category.icon = category.icon;
|
||||||
|
this.category.color = category.color;
|
||||||
|
this.category.comment = category.comment;
|
||||||
|
this.category.visible = !category.hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -351,7 +351,7 @@ import {
|
|||||||
categoryTypeToTransactionType,
|
categoryTypeToTransactionType,
|
||||||
transactionTypeToCategoryType
|
transactionTypeToCategoryType
|
||||||
} from '@/lib/category.js';
|
} from '@/lib/category.js';
|
||||||
import { scrollToMenuListItem } from '@/lib/ui.desktop.js';
|
import { scrollToSelectedItem } from '@/lib/ui.desktop.js';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
mdiMagnify,
|
mdiMagnify,
|
||||||
@@ -816,7 +816,7 @@ export default {
|
|||||||
},
|
},
|
||||||
scrollMenuToSelectedItem(menu) {
|
scrollMenuToSelectedItem(menu) {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
scrollToMenuListItem(menu.contentEl);
|
scrollToSelectedItem(menu.contentEl, 'div.v-list', 'div.v-list-item.list-item-selected');
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
getDisplayTime(transaction) {
|
getDisplayTime(transaction) {
|
||||||
|
|||||||
Reference in New Issue
Block a user